Compare commits

...

9 Commits

Author SHA1 Message Date
Stefan Allius
ab1b245e2a update favicon.svg 2025-04-20 01:28:04 +02:00
Stefan Allius
2430aef541 remove unusage statements 2025-04-20 00:33:28 +02:00
Stefan Allius
509d26471f increase test coverage 2025-04-20 00:30:59 +02:00
Stefan Allius
e5a08c3150 fix route for fetch data
- for running in iframes (e.g. HA ingress) we must
  use relative path in the URLs
2025-04-20 00:07:34 +02:00
Stefan Allius
d386456a88 change file extension to html.j2 for templates 2025-04-20 00:06:34 +02:00
Stefan Allius
a527926e57 change file extension to html.j2 for templates 2025-04-19 21:26:15 +02:00
Stefan Allius
fe915f524d change file extension to html.j2 for templates 2025-04-19 21:23:34 +02:00
Stefan Allius
55972b2240 add optional java scriot to fetch data regullary 2025-04-19 21:12:11 +02:00
Stefan Allius
c270edff15 S allius/issue385 (#386)
* ignore translation and log files

* add quart-babel

* build and install translation files

* don't export the web-ui port 8127

- for security reason the user should use the
  HA ingress and not the direkt access to the web
  dashboard

* set 'lang' in html tag
2025-04-19 16:51:06 +02:00
17 changed files with 175 additions and 26 deletions

4
.gitignore vendored
View File

@@ -13,3 +13,7 @@ Doku/**
.env
.venv
coverage.xml
*.pot
*.mo
*.log
*.log.*

View File

@@ -1,12 +1,13 @@
.PHONY: build clean addon-dev addon-debug addon-rc addon-rel debug dev preview rc rel check-docker-compose install
.PHONY: build babel clean addon-dev addon-debug addon-rc addon-rel debug dev preview rc rel check-docker-compose install
debug dev preview rc rel:
babel debug dev preview rc rel:
$(MAKE) -C app $@
clean build:
$(MAKE) -C ha_addons $@
addon-dev addon-debug addon-rc addon-rel:
$(MAKE) -C app babel
$(MAKE) -C ha_addons $(patsubst addon-%,%,$@)
check-docker-compose:

View File

@@ -2,4 +2,6 @@ tests/
**/__pycache__
*.pyc
.DS_Store
build.sh
build.sh
*.pot
*.po

View File

@@ -6,10 +6,16 @@ IMAGE = tsun-gen3-proxy
# Folders
SRC=.
APP=.
SRC=$(APP)/src
# Folders for Babel translation
BABEL_INPUT_JINJA=$(SRC)/web/templates
BABEL_INPUT= $(foreach dir,$(BABEL_INPUT_JINJA),$(wildcard $(dir)/*.html.j2)) \
BABEL_TRANSLATIONS=$(APP)/translations
export BUILD_DATE := ${shell date -Iminutes}
VERSION := $(shell cat $(SRC)/.version)
VERSION := $(shell cat $(APP)/.version)
export MAJOR := $(shell echo $(VERSION) | cut -f1 -d.)
PUBLIC_URL := $(shell echo $(PUBLIC_CONTAINER_REGISTRY) | cut -f1 -d/)
@@ -39,5 +45,17 @@ preview rel:
export IMAGE=$(PUBLIC_CONTAINER_REGISTRY)$(IMAGE) && \
docker buildx bake -f docker-bake.hcl $@
babel: $(BABEL_TRANSLATIONS)/de/LC_MESSAGES/messages.mo $(BABEL_TRANSLATIONS)/de/LC_MESSAGES/messages.po $(BABEL_TRANSLATIONS)/messages.pot
.PHONY: debug dev preview rc rel
$(BABEL_TRANSLATIONS)/%.pot : $(SRC)/.babel.cfg $(BABEL_INPUT)
@mkdir -p $(@D)
@pybabel extract -F $< --project=$(IMAGE) --version=$(VERSION) -o $@ $(SRC)
$(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.po : $(BABEL_TRANSLATIONS)/messages.pot
@mkdir -p $(@D)
@pybabel update --init-missing -i $< -d $(BABEL_TRANSLATIONS) -l $*
$(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.mo : $(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.po
@pybabel compile -d $(BABEL_TRANSLATIONS) -l $*
.PHONY: babel debug dev preview rc rel

View File

@@ -1,4 +1,5 @@
aiomqtt==2.3.2
schema==0.7.7
aiocron==2.1
quart==0.20
quart==0.20
quart-babel==1.0.7

3
app/src/.babel.cfg Normal file
View File

@@ -0,0 +1,3 @@
[python: **.py]
[jinja2: web/templates/**.html]
[jinja2: web/templates/**.html.j2]

View File

@@ -4,7 +4,9 @@ import logging.handlers
import os
import argparse
from asyncio import StreamReader, StreamWriter
from quart import Quart, Response
from quart import Quart, Response, request
from quart_babel import Babel
from quart_babel.locale import get_locale
from logging import config # noqa F401
from proxy import Proxy
from inverter_ifc import InverterIfc
@@ -31,12 +33,28 @@ class ProxyState:
ProxyState._is_up = value
def my_get_locale():
# check how to get the locale form for the add-on - hass.selectedLanguage
# logging.info("get_locale(%s)", request.accept_languages)
return request.accept_languages.best_match(
['de', 'en']
)
app = Quart(__name__,
template_folder='web/templates',
static_folder='web/static')
babel = Babel(app,
locale_selector=my_get_locale,
default_translation_directories='../translations')
app.register_blueprint(web_routes)
@app.context_processor
def utility_processor():
return dict(lang=get_locale())
@app.route('/-/ready')
async def ready():
if ProxyState.is_up():

View File

@@ -1,5 +1,5 @@
from quart import Blueprint
from quart import render_template
from quart import render_template, url_for
from quart import send_from_directory
import os
@@ -13,14 +13,44 @@ async def get_icon(file: str, mime: str = 'image/png'):
mimetype=mime)
def get_inv_count():
return 1234
TsunCnt = 0
def get_tsun_count():
global TsunCnt
TsunCnt += 1
return TsunCnt
@web_routes.context_processor
def utility_processor():
return dict(inv_count=get_inv_count(),
tsun_count=get_tsun_count())
@web_routes.route('/')
async def index():
return await render_template('index.html')
return await render_template(
'index.html.j2',
fetch_url='.'+url_for('web_routes.data_fetch'))
@web_routes.route('/page')
async def empty():
return await render_template('empty.html')
return await render_template('empty.html.j2')
@web_routes.route('/data-fetch')
async def data_fetch():
global TsunCnt
TsunCnt += 1
return {
"geology-fact": f"<h3>{TsunCnt}</h3>",
}
@web_routes.route('/favicon-96x96.png')

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 729 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "TSUN-Proxy",
"short_name": "TsunProxy",
"short_name": "Proxy",
"icons": [
{
"src": "/web-app-manifest-192x192.png",

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{lang}}" >
<head>
<title>{% block title %}{% endblock title %}</title>
<meta charset="UTF-8">
@@ -106,6 +106,36 @@
mySidebar.style.display = "none";
overlayBg.style.display = "none";
}
{% if fetch_url is defined %}
function fetch_data() {
fetch("{{fetch_url}}")
.then(response => response.json())
.then(function (data) {
Object.keys(data).forEach(key => {
//console.log(`${key}: ${data[key]}`);
try {
elm = document.getElementById(key)
elm.innerHTML = data[key]
}
catch(err) {
console.log('error: ' + err + ' (for key: ' + key + ')');
}
});
})
.catch(function (err) {
console.log('error: ' + err);
});
}
window.addEventListener('load', function () {
// Your document is loaded.
var fetchInterval = 5000; // 5 seconds.
// Invoke the request every 5 seconds.
setInterval(fetch_data, fetchInterval);
});
{% endif %}
</script>
{% endblock trailer %}

View File

@@ -1,4 +1,4 @@
{% extends 'base.html' %}
{% extends 'base.html.j2' %}
{% block title %} TSUN Proxy - View {% endblock title%}
{% block menu2_class %}w3-blue{% endblock %}

View File

@@ -1,8 +1,8 @@
{% extends 'base.html' %}
{% extends 'base.html.j2' %}
{% block title %} TSUN Proxy - Dashboard {% endblock title%}
{% block menu1_class %}w3-blue{% endblock %}
{% block headline %}<i class="fa fa-dashboard"></i> My Dashboard{% endblock headline %}
{% block headline %}<i class="fa fa-dashboard"></i> {{_('My Dashboard')}}{% endblock headline %}
{% block content %}
<div class="w3-row-padding w3-margin-bottom">
@@ -10,7 +10,7 @@
<div class="w3-container w3-red w3-padding-16">
<div class="w3-left"><i class="fa fa-comment w3-xxxlarge"></i></div>
<div class="w3-right">
<h3>52</h3>
<h3>{{inv_count}}</h3>
</div>
<div class="w3-clear"></div>
<h4>Messages</h4>
@@ -20,7 +20,7 @@
<div class="w3-container w3-blue w3-padding-16">
<div class="w3-left"><i class="fa fa-eye w3-xxxlarge"></i></div>
<div class="w3-right">
<h3>99</h3>
<h3>{{tsun_count}}</h3>
</div>
<div class="w3-clear"></div>
<h4>Views</h4>
@@ -29,7 +29,7 @@
<div class="w3-quarter">
<div class="w3-container w3-teal w3-padding-16">
<div class="w3-left"><i class="fa fa-share-alt w3-xxxlarge"></i></div>
<div class="w3-right">
<div id = "geology-fact" class="w3-right">
<h3>23</h3>
</div>
<div class="w3-clear"></div>
@@ -52,7 +52,6 @@
<div class="w3-row-padding" style="margin:0 -16px">
<div class="w3-third">
<h5>Regions</h5>
<img src="/w3images/region.jpg" style="width:100%" alt="Google Regional Map">
</div>
<div class="w3-twothird">
<h5>Feeds</h5>
@@ -178,5 +177,3 @@
</div>
</div>
{% endblock content%}

View File

@@ -61,3 +61,13 @@ async def test_manifest():
response = await client.get('/site.webmanifest')
assert response.status_code == 200
assert response.mimetype == 'application/manifest+json'
@pytest.mark.asyncio
async def test_data_fetch():
"""Test the healthy route."""
client = app.test_client()
response = await client.get('/data-fetch')
assert response.status_code == 200
response = await client.get('/data-fetch')
assert response.status_code == 200

View File

@@ -0,0 +1,25 @@
# German translations for tsun-gen3-proxy.
# Copyright (C) 2025 ORGANIZATION
# This file is distributed under the same license as the tsun-gen3-proxy
# project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: tsun-gen3-proxy 0.14.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-04-20 00:01+0200\n"
"PO-Revision-Date: 2025-04-18 16:24+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
"Language-Team: de <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.17.0\n"
#: src/web/templates/index.html.j2:5
msgid "My Dashboard"
msgstr "Mein Dashboard"

View File

@@ -12,11 +12,14 @@ IMAGE = tsun-gen3-addon
SRC=../app
SRC_PROXY=$(SRC)/src
CNF_PROXY=$(SRC)/config
TRANSLATION_PROXY=$(SRC)/translations
# Target folders for building the local add-on and the docker container
ADDON_PATH = ha_addon
DST=$(ADDON_PATH)/rootfs
DST_PROXY=$(DST)/home/proxy
DST_TRANSLATION=$(DST)/home/translations
# base director of the add-on repro for installing the add-on git repros
INST_BASE=../../ha-addons
@@ -85,19 +88,21 @@ SRC_FILES := $(wildcard $(SRC_PROXY)/*.py)\
$(wildcard $(SRC_PROXY)/gen3/*.py)\
$(wildcard $(SRC_PROXY)/gen3plus/*.py)\
$(wildcard $(SRC_PROXY)/web/*.py)\
$(wildcard $(SRC_PROXY)/web/templates/*.html)\
$(wildcard $(SRC_PROXY)/web/templates/*.html.j2)\
$(wildcard $(SRC_PROXY)/web/static/css/*.css)\
$(wildcard $(SRC_PROXY)/web/static/font/*)\
$(wildcard $(SRC_PROXY)/web/static/font-awesome/*/*)\
$(wildcard $(SRC_PROXY)/web/static/images/*)
CNF_FILES := $(wildcard $(CNF_PROXY)/*.toml)
MO_FILES := $(wildcard $(TRANSLATION_PROXY)/de/LC_MESSAGES/*.mo)
# determine destination files
TARGET_FILES = $(SRC_FILES:$(SRC_PROXY)/%=$(DST_PROXY)/%)
CONFIG_FILES = $(CNF_FILES:$(CNF_PROXY)/%=$(DST_PROXY)/%)
TRANSLATION_FILES = $(MO_FILES:$(TRANSLATION_PROXY)/%=$(DST_TRANSLATION)/%)
rootfs: $(TARGET_FILES) $(CONFIG_FILES) $(DST)/requirements.txt
rootfs: $(TARGET_FILES) $(CONFIG_FILES) $(TRANSLATION_FILES) $(DST)/requirements.txt
$(CONFIG_FILES): $(DST_PROXY)/% : $(CNF_PROXY)/%
@echo Copy $< to $@
@@ -109,6 +114,11 @@ $(TARGET_FILES): $(DST_PROXY)/% : $(SRC_PROXY)/%
@mkdir -p $(@D)
@cp $< $@
$(TRANSLATION_FILES): $(DST_TRANSLATION)/% : $(TRANSLATION_PROXY)/%
@echo Copy $< to $@
@mkdir -p $(@D)
@cp $< $@
$(DST)/requirements.txt : $(SRC)/requirements.txt
@echo Copy $< to $@
@cp $< $@

View File

@@ -23,11 +23,11 @@ services:
ports:
5005/tcp: 5005
10000/tcp: 10000
8127/tcp: 8127
webui: "http://[HOST]:[PORT:8127]/"
watchdog: "http://[HOST]:[PORT:8127]/-/healthy"
ingress: true
ingress_port: 8127
panel_icon: "mdi:application-cog-outline"
# Definition of parameters in the configuration tab of the addon
# parameters are available within the container as /data/options.json