aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2024-01-10 03:20:10 +0300
committerEvgeny Zinoviev <me@ch1p.io>2024-01-10 03:20:10 +0300
commit05c5d18f7619c28e620d42c0921f81ced780cc2d (patch)
tree26ab7a74daba5d0f37ddb0bbe467e84fdc7fb1b6
parent54ddea4614dbd31dad577ae5fdb8ec4821490199 (diff)
save
-rwxr-xr-xbin/mqtt_node_util.py5
-rw-r--r--bin/web_kbn.py22
-rw-r--r--include/py/homekit/config/config.py19
-rw-r--r--include/py/homekit/modem/config.py28
-rw-r--r--include/py/homekit/mqtt/_config.py20
-rw-r--r--include/py/homekit/mqtt/module/temphum.py39
-rw-r--r--include/py/homekit/util.py31
-rw-r--r--web/kbn_templates/base.html25
-rw-r--r--web/kbn_templates/base.j244
-rw-r--r--web/kbn_templates/index.j2 (renamed from web/kbn_templates/index.html)14
-rw-r--r--web/kbn_templates/loading.j214
-rw-r--r--web/kbn_templates/modems.j212
12 files changed, 191 insertions, 82 deletions
diff --git a/bin/mqtt_node_util.py b/bin/mqtt_node_util.py
index c1d457c..5587739 100755
--- a/bin/mqtt_node_util.py
+++ b/bin/mqtt_node_util.py
@@ -48,7 +48,6 @@ if __name__ == '__main__':
help='mqtt modules to include')
parser.add_argument('--switch-relay', choices=[0, 1], type=int,
help='send relay state')
- parser.add_argument('--legacy-relay', action='store_true')
parser.add_argument('--push-ota', type=str, metavar='OTA_FILENAME',
help='push OTA, receives path to firmware.bin')
parser.add_argument('--no-wait', action='store_true',
@@ -80,8 +79,10 @@ if __name__ == '__main__':
if arg.modules:
for m in arg.modules:
kwargs = {}
- if m == 'relay' and arg.legacy_relay:
+ if m == 'relay' and MqttNodesConfig().node_uses_legacy_relay_power_payload(arg.node_id):
kwargs['legacy_topics'] = True
+ if m == 'temphum' and MqttNodesConfig().node_uses_legacy_temphum_data_payload(arg.node_id):
+ kwargs['legacy_payload'] = True
module_instance = mqtt_node.load_module(m, **kwargs)
if m == 'relay' and arg.switch_relay is not None:
relay_module = module_instance
diff --git a/bin/web_kbn.py b/bin/web_kbn.py
index e160fde..8b4ca6f 100644
--- a/bin/web_kbn.py
+++ b/bin/web_kbn.py
@@ -75,27 +75,35 @@ class WebSite(http.HTTPServer):
self.app.router.add_static('/assets/', path=homekit_path('web', 'kbn_assets'))
- self.get('/', self.get_index)
- self.get('/modems', self.get_modems)
+ self.get('/main.cgi', self.get_index)
+ self.get('/modems.cgi', self.get_modems)
async def render_page(self,
req: http.Request,
+ template_name: str,
+ title: Optional[str] = None,
context: Optional[dict] = None):
if context is None:
context = {}
context = {
**context,
- 'head_static': get_head_static(),
- 'title': 'this is title'
+ 'head_static': get_head_static()
}
- response = aiohttp_jinja2.render_template('index.html', req, context=context)
+ if title is not None:
+ context['title'] = title
+ response = aiohttp_jinja2.render_template(template_name+'.j2', req, context=context)
return response
async def get_index(self, req: http.Request):
- return await self.render_page(req)
+ return await self.render_page(req, 'index',
+ title="Home web site")
async def get_modems(self, req: http.Request):
- pass
+ mc = ModemsConfig()
+ print(mc)
+ return await self.render_page(req, 'modems',
+ title='Состояние модемов',
+ context=dict(modems=ModemsConfig()))
if __name__ == '__main__':
diff --git a/include/py/homekit/config/config.py b/include/py/homekit/config/config.py
index d424888..abdedad 100644
--- a/include/py/homekit/config/config.py
+++ b/include/py/homekit/config/config.py
@@ -41,6 +41,9 @@ class BaseConfigUnit(ABC):
self._data = {}
self._logger = logging.getLogger(self.__class__.__name__)
+ def __iter__(self):
+ return iter(self._data)
+
def __getitem__(self, key):
return self._data[key]
@@ -123,10 +126,10 @@ class ConfigUnit(BaseConfigUnit):
return None
@classmethod
- def _addr_schema(cls, required=False, **kwargs):
+ def _addr_schema(cls, required=False, only_ip=False, **kwargs):
return {
'type': 'addr',
- 'coerce': Addr.fromstring,
+ 'coerce': Addr.fromstring if not only_ip else Addr.fromipstring,
'required': required,
**kwargs
}
@@ -158,6 +161,7 @@ class ConfigUnit(BaseConfigUnit):
pass
v = MyValidator()
+ need_document = False
if rst == RootSchemaType.DICT:
normalized = v.validated({'document': self._data},
@@ -165,16 +169,21 @@ class ConfigUnit(BaseConfigUnit):
'type': 'dict',
'keysrules': {'type': 'string'},
'valuesrules': schema
- }})['document']
+ }})
+ need_document = True
elif rst == RootSchemaType.LIST:
v = MyValidator()
- normalized = v.validated({'document': self._data}, {'document': schema})['document']
+ normalized = v.validated({'document': self._data}, {'document': schema})
+ need_document = True
else:
normalized = v.validated(self._data, schema)
if not normalized:
raise cerberus.DocumentError(f'validation failed: {v.errors}')
+ if need_document:
+ normalized = normalized['document']
+
self._data = normalized
try:
@@ -235,6 +244,8 @@ class TranslationUnit(BaseConfigUnit):
class Translation:
LANGUAGES = ('en', 'ru')
+ DEFAULT_LANGUAGE = 'ru'
+
_langs: dict[str, TranslationUnit]
def __init__(self, name: str):
diff --git a/include/py/homekit/modem/config.py b/include/py/homekit/modem/config.py
index 039d759..16d1ba0 100644
--- a/include/py/homekit/modem/config.py
+++ b/include/py/homekit/modem/config.py
@@ -1,5 +1,29 @@
-from ..config import ConfigUnit
+from ..config import ConfigUnit, Translation
+from typing import Optional
class ModemsConfig(ConfigUnit):
- pass
+ NAME = 'modems'
+
+ _strings: Translation
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._strings = Translation('modems')
+
+ @classmethod
+ def schema(cls) -> Optional[dict]:
+ return {
+ 'type': 'dict',
+ 'schema': {
+ 'ip': cls._addr_schema(required=True, only_ip=True),
+ 'gateway_ip': cls._addr_schema(required=False, only_ip=True),
+ 'legacy_auth': {'type': 'boolean', 'required': True}
+ }
+ }
+
+ def getshortname(self, modem: str, lang=Translation.DEFAULT_LANGUAGE):
+ return self._strings.get(lang)[modem]['short']
+
+ def getfullname(self, modem: str, lang=Translation.DEFAULT_LANGUAGE):
+ return self._strings.get(lang)[modem]['full'] \ No newline at end of file
diff --git a/include/py/homekit/mqtt/_config.py b/include/py/homekit/mqtt/_config.py
index e5f2c56..8aa3bfe 100644
--- a/include/py/homekit/mqtt/_config.py
+++ b/include/py/homekit/mqtt/_config.py
@@ -92,6 +92,7 @@ class MqttNodesConfig(ConfigUnit):
'type': 'dict',
'schema': {
'module': {'type': 'string', 'required': True, 'allowed': ['si7021', 'dht12']},
+ 'legacy_payload': {'type': 'boolean', 'required': False, 'default': False},
'interval': {'type': 'integer'},
'i2c_bus': {'type': 'integer'},
'tcpserver': {
@@ -109,7 +110,12 @@ class MqttNodesConfig(ConfigUnit):
'legacy_topics': {'type': 'boolean'}
}
},
- 'password': {'type': 'string'}
+ 'password': {'type': 'string'},
+ 'defines': {
+ 'type': 'dict',
+ 'keysrules': {'type': 'string'},
+ 'valuesrules': {'type': ['string', 'integer']}
+ }
}
}
}
@@ -163,3 +169,15 @@ class MqttNodesConfig(ConfigUnit):
else:
resdict[name] = node
return reslist if only_names else resdict
+
+ def node_uses_legacy_temphum_data_payload(self, node_id: str) -> bool:
+ try:
+ return self.get_node(node_id)['temphum']['legacy_payload']
+ except KeyError:
+ return False
+
+ def node_uses_legacy_relay_power_payload(self, node_id: str) -> bool:
+ try:
+ return self.get_node(node_id)['relay']['legacy_topics']
+ except KeyError:
+ return False
diff --git a/include/py/homekit/mqtt/module/temphum.py b/include/py/homekit/mqtt/module/temphum.py
index fd02cca..6deccfe 100644
--- a/include/py/homekit/mqtt/module/temphum.py
+++ b/include/py/homekit/mqtt/module/temphum.py
@@ -10,8 +10,8 @@ MODULE_NAME = 'MqttTempHumModule'
DATA_TOPIC = 'temphum/data'
-class MqttTemphumDataPayload(MqttPayload):
- FORMAT = '=ddb'
+class MqttTemphumLegacyDataPayload(MqttPayload):
+ FORMAT = '=dd'
UNPACKER = {
'temp': two_digits_precision,
'rh': two_digits_precision
@@ -19,39 +19,26 @@ class MqttTemphumDataPayload(MqttPayload):
temp: float
rh: float
- error: int
-# class MqttTempHumNodes(HashableEnum):
-# KBN_SH_HALL = auto()
-# KBN_SH_BATHROOM = auto()
-# KBN_SH_LIVINGROOM = auto()
-# KBN_SH_BEDROOM = auto()
-#
-# KBN_BH_2FL = auto()
-# KBN_BH_2FL_STREET = auto()
-# KBN_BH_1FL_LIVINGROOM = auto()
-# KBN_BH_1FL_BEDROOM = auto()
-# KBN_BH_1FL_BATHROOM = auto()
-#
-# KBN_NH_1FL_INV = auto()
-# KBN_NH_1FL_CENTER = auto()
-# KBN_NH_1LF_KT = auto()
-# KBN_NH_1FL_DS = auto()
-# KBN_NH_1FS_EZ = auto()
-#
-# SPB_FLAT120_CABINET = auto()
+class MqttTemphumDataPayload(MqttTemphumLegacyDataPayload):
+ FORMAT = '=ddb'
+ error: int
class MqttTempHumModule(MqttModule):
+ _legacy_payload: bool
+
def __init__(self,
sensor: Optional[BaseSensor] = None,
+ legacy_payload=False,
write_to_database=False,
*args, **kwargs):
if sensor is not None:
kwargs['tick_interval'] = 10
super().__init__(*args, **kwargs)
self._sensor = sensor
+ self._legacy_payload = legacy_payload
def on_connect(self, mqtt: MqttNode):
super().on_connect(mqtt)
@@ -69,7 +56,7 @@ class MqttTempHumModule(MqttModule):
rh = self._sensor.humidity()
except:
error = 1
- pld = MqttTemphumDataPayload(temp=temp, rh=rh, error=error)
+ pld = self._get_data_payload_cls()(temp=temp, rh=rh, error=error)
self._mqtt_node_ref.publish(DATA_TOPIC, pld.pack())
def handle_payload(self,
@@ -77,6 +64,10 @@ class MqttTempHumModule(MqttModule):
topic: str,
payload: bytes) -> Optional[MqttPayload]:
if topic == DATA_TOPIC:
- message = MqttTemphumDataPayload.unpack(payload)
+ message = self._get_data_payload_cls().unpack(payload)
self._logger.debug(message)
return message
+
+ def _get_data_payload_cls(self):
+ return MqttTemphumLegacyDataPayload if self._legacy_payload else MqttTemphumDataPayload
+
diff --git a/include/py/homekit/util.py b/include/py/homekit/util.py
index 2680c37..3c73440 100644
--- a/include/py/homekit/util.py
+++ b/include/py/homekit/util.py
@@ -53,17 +53,21 @@ class Addr:
self.host = host
self.port = port
- @staticmethod
- def fromstring(addr: str) -> Addr:
- colons = addr.count(':')
- if colons != 1:
- raise ValueError('invalid host:port format')
-
- if not colons:
- host = addr
- port = None
+ @classmethod
+ def fromstring(cls, addr: str, port_required=True) -> Addr:
+ if port_required:
+ colons = addr.count(':')
+ if colons != 1:
+ raise ValueError('invalid host:port format')
+
+ if not colons:
+ host = addr
+ port = None
+ else:
+ host, port = addr.split(':')
else:
- host, port = addr.split(':')
+ port = None
+ host = addr
validate_ipv4_or_hostname(host, raise_exception=True)
@@ -74,12 +78,19 @@ class Addr:
return Addr(host, port)
+ @classmethod
+ def fromipstring(cls, addr: str) -> Addr:
+ return cls.fromstring(addr, port_required=False)
+
def __str__(self):
buf = self.host
if self.port is not None:
buf += ':'+str(self.port)
return buf
+ def __repr__(self):
+ return self.__str__()
+
def __iter__(self):
yield self.host
yield self.port
diff --git a/web/kbn_templates/base.html b/web/kbn_templates/base.html
deleted file mode 100644
index 43f7d2a..0000000
--- a/web/kbn_templates/base.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!doctype html>
-<html>
-<head>
- <title>{{ title }}</title>
- <meta http-equiv="content-type" content="text/html; charset=utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
- <script>
- window.onerror = function(error) {
- window.console && console.error(error);
- }
- </script>
- {{ head_static | safe }}
-</head>
-<body>
-<div class="container py-3">
-
-{% block content %} {% endblock %}
-
-{% if js %}
-<script>{{ js|raw }}</script>
-{% endif %}
-
-</div>
-</body>
-</html>
diff --git a/web/kbn_templates/base.j2 b/web/kbn_templates/base.j2
new file mode 100644
index 0000000..d43a08b
--- /dev/null
+++ b/web/kbn_templates/base.j2
@@ -0,0 +1,44 @@
+{% macro breadcrumbs(history) %}
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a href="main.cgi">Главная</a></li>
+ {% for item in history %}
+ <li class="breadcrumb-item"{% if loop.last %} aria-current="page"{% endif %}>
+ {% if item.link %}<a href="{{ item.link }}">{% endif %}
+ {% if item.html %}
+ {% raw %}{{ item.html }}{% endraw %}
+ {% else %}
+ {{ item.text }}
+ {% endif %}
+ {% if item.link %}</a>{% endif %}
+ </li>
+ {% endfor %}
+ </ol>
+ </nav>
+{% endmacro %}
+
+<!doctype html>
+<html>
+<head>
+ <title>{{ title }}</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
+ <script>
+ window.onerror = function(error) {
+ window.console && console.error(error);
+ }
+ </script>
+ {{ head_static | safe }}
+</head>
+<body>
+<div class="container py-3">
+
+{% block content %}{% endblock %}
+
+{% if js %}
+<script>{{ js|raw }}</script>
+{% endif %}
+
+</div>
+</body>
+</html>
diff --git a/web/kbn_templates/index.html b/web/kbn_templates/index.j2
index 1921b87..e3ab421 100644
--- a/web/kbn_templates/index.html
+++ b/web/kbn_templates/index.j2
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "base.j2" %}
{% block content %}
<div class="container py-4">
@@ -16,16 +16,16 @@
<h6>Интернет</h6>
<ul class="list-group list-group-flush">
- <li class="list-group-item"><a href="/modem/">Модемы</a></li>
- <li class="list-group-item"><a href="/routing/">Маршрутизация</a></li>
- <li class="list-group-item"><a href="/sms/">SMS-сообщения</a></li>
+ <li class="list-group-item"><a href="/modems.cgi">Модемы</a></li>
+ <li class="list-group-item"><a href="/routing.cgi">Маршрутизация</a></li>
+ <li class="list-group-item"><a href="/sms.cgi">SMS-сообщения</a></li>
</ul>
<h6 class="mt-4">Другое</h6>
<ul class="list-group list-group-flush">
- <li class="list-group-item"><a href="/inverter/">Инвертор</a> (<a href="{{ grafana_inverter_url }}">Grafana</a>)</li>
- <li class="list-group-item"><a href="/pump/">Насос</a></li>
- <li class="list-group-item"><a href="/sensors/">Датчики</a> (<a href="{{ grafana_sensors_url }}">Grafana</a>)</li>
+ <li class="list-group-item"><a href="/inverter.cgi">Инвертор</a> (<a href="{{ grafana_inverter_url }}">Grafana</a>)</li>
+ <li class="list-group-item"><a href="/pump.cgi">Насос</a></li>
+ <li class="list-group-item"><a href="/sensors.cgi">Датчики</a> (<a href="{{ grafana_sensors_url }}">Grafana</a>)</li>
</ul>
<h6 class="mt-4"><a href="/cams/"><b>Все камеры</b></a> (<a href="/cams/?high=1">HQ</a>)</h6>
diff --git a/web/kbn_templates/loading.j2 b/web/kbn_templates/loading.j2
new file mode 100644
index 0000000..d064a48
--- /dev/null
+++ b/web/kbn_templates/loading.j2
@@ -0,0 +1,14 @@
+<div class="sk-fading-circle">
+ <div class="sk-circle1 sk-circle"></div>
+ <div class="sk-circle2 sk-circle"></div>
+ <div class="sk-circle3 sk-circle"></div>
+ <div class="sk-circle4 sk-circle"></div>
+ <div class="sk-circle5 sk-circle"></div>
+ <div class="sk-circle6 sk-circle"></div>
+ <div class="sk-circle7 sk-circle"></div>
+ <div class="sk-circle8 sk-circle"></div>
+ <div class="sk-circle9 sk-circle"></div>
+ <div class="sk-circle10 sk-circle"></div>
+ <div class="sk-circle11 sk-circle"></div>
+ <div class="sk-circle12 sk-circle"></div>
+</div> \ No newline at end of file
diff --git a/web/kbn_templates/modems.j2 b/web/kbn_templates/modems.j2
new file mode 100644
index 0000000..f148140
--- /dev/null
+++ b/web/kbn_templates/modems.j2
@@ -0,0 +1,12 @@
+{% extends "base.j2" %}
+
+{% block content %}
+{{ breadcrumbs([{'text': 'Модемы'}]) }}
+
+{% for modem in modems %}
+<h6 class="text-primary{% if not loop.first %} mt-4{% endif %}">{{ modems.getfullname(modem) }}</h6>
+<div id="modem_data_{{ modem }}">
+ {% include "loading.j2" %}
+</div>
+{% endfor %}
+{% endblock %} \ No newline at end of file