diff options
author | Evgeny Sorokin <me@ch1p.io> | 2024-01-16 03:31:55 +0300 |
---|---|---|
committer | Evgeny Sorokin <me@ch1p.io> | 2024-01-16 03:32:07 +0300 |
commit | 8a89dd77be03ca8eb9cdc378ba8e912292494fa9 (patch) | |
tree | f2d04c1a6ce8b2731febbf6b66f6127057f9477d | |
parent | de56aa3ae916ac0d51e503648fae8f3fa2d97951 (diff) |
inverter page
-rw-r--r-- | bin/web_kbn.py | 108 | ||||
-rw-r--r-- | include/py/homekit/http/http.py | 5 | ||||
-rw-r--r-- | include/py/homekit/inverter/config.py | 4 | ||||
-rw-r--r-- | localwebsite/handlers/InverterHandler.php | 104 | ||||
-rw-r--r-- | localwebsite/htdocs/index.php | 7 | ||||
-rw-r--r-- | localwebsite/templates-web/inverter_page.twig | 20 | ||||
-rw-r--r-- | web/kbn_assets/app.js | 17 | ||||
-rw-r--r-- | web/kbn_assets/inverter.js | 15 | ||||
-rw-r--r-- | web/kbn_templates/inverter.j2 | 20 |
9 files changed, 141 insertions, 159 deletions
diff --git a/bin/web_kbn.py b/bin/web_kbn.py index 113554e..d9d0035 100644 --- a/bin/web_kbn.py +++ b/bin/web_kbn.py @@ -3,16 +3,17 @@ import asyncio import jinja2 import aiohttp_jinja2 import json -import os import re +import inverterd import __py_include from io import StringIO +from aiohttp.web import HTTPFound from typing import Optional, Union from homekit.config import config, AppConfigUnit from homekit.util import homekit_path, filesize_fmt, seconds_to_human_readable_string from homekit.modem import E3372, ModemsConfig, MacroNetWorkType -from aiohttp import web +from homekit.inverter.config import InverterdConfig from homekit import http @@ -90,6 +91,69 @@ def get_modem_data(modem_cfg: dict, get_raw=False) -> Union[dict, tuple]: } +def get_inverter_client() -> inverterd.Client: + cl = inverterd.Client(host=InverterdConfig()['remote_addr'].host) + cl.connect() + cl.format(inverterd.Format.JSON) + return cl + + +def get_inverter_data() -> tuple: + cl = get_inverter_client() + + status = json.loads(cl.exec('get-status'))['data'] + rated = json.loads(cl.exec('get-rated'))['data'] + + power_direction = status['battery_power_direction'].lower() + power_direction = re.sub('ge$', 'ging', power_direction) + + charging_rate = '' + if power_direction == 'charging': + charging_rate = ' @ %s %s' % ( + status['battery_charge_current']['value'], + status['battery_charge_current']['unit']) + elif power_direction == 'discharging': + charging_rate = ' @ %s %s' % ( + status['battery_discharge_current']['value'], + status['battery_discharge_current']['unit']) + + html = '<b>Battery:</b> %s %s' % ( + status['battery_voltage']['value'], + status['battery_voltage']['unit']) + html += ' (%s%s, ' % ( + status['battery_capacity']['value'], + status['battery_capacity']['unit']) + html += '%s%s)' % (power_direction, charging_rate) + + html += "\n" + html += '<b>Load:</b> %s %s' % ( + status['ac_output_active_power']['value'], + status['ac_output_active_power']['unit']) + html += ' (%s%%)' % (status['output_load_percent']['value'],) + + if status['pv1_input_power']['value'] > 0: + html += "\n" + html += '<b>Input power:</b> %s %s' % ( + status['pv1_input_power']['value'], + status['pv1_input_power']['unit']) + + if status['grid_voltage']['value'] > 0 or status['grid_freq']['value'] > 0: + html += "\n" + html += '<b>AC input:</b> %s %s' % ( + status['grid_voltage']['value'], + status['grid_voltage']['unit']) + html += ', %s %s' % ( + status['grid_freq']['value'], + status['grid_freq']['unit']) + + html += "\n" + html += '<b>Priority:</b> %s' % (rated['output_source_priority'],) + + html = html.replace("\n", '<br>') + + return status, rated, html + + class WebSite(http.HTTPServer): _modems_config: ModemsConfig @@ -108,10 +172,14 @@ class WebSite(http.HTTPServer): self.app.router.add_static('/assets/', path=homekit_path('web', 'kbn_assets')) - self.get('/main.cgi', self.get_index) - self.get('/modems.cgi', self.get_modems) - self.get('/modems/info.ajx', self.get_modems_ajax) - self.get('/modems/verbose.cgi', self.get_modems_verbose) + self.get('/main.cgi', self.index) + + self.get('/modems.cgi', self.modems) + self.get('/modems/info.ajx', self.modems_ajx) + self.get('/modems/verbose.cgi', self.modems_verbose) + + self.get('/inverter.cgi', self.inverter) + self.get('/inverter.ajx', self.inverter_ajx) async def render_page(self, req: http.Request, @@ -129,16 +197,16 @@ class WebSite(http.HTTPServer): response = aiohttp_jinja2.render_template(template_name+'.j2', req, context=context) return response - async def get_index(self, req: http.Request): + async def index(self, req: http.Request): return await self.render_page(req, 'index', title="Home web site") - async def get_modems(self, req: http.Request): + async def modems(self, req: http.Request): return await self.render_page(req, 'modems', title='Состояние модемов', context=dict(modems=self._modems_config)) - async def get_modems_ajax(self, req: http.Request): + async def modems_ajx(self, req: http.Request): modem = req.query.get('id', None) if modem not in self._modems_config.getkeys(): raise ValueError('invalid modem id') @@ -154,7 +222,7 @@ class WebSite(http.HTTPServer): return self.ok({'html': html}) - async def get_modems_verbose(self, req: http.Request): + async def modems_verbose(self, req: http.Request): modem = req.query.get('id', None) if modem not in self._modems_config.getkeys(): raise ValueError('invalid modem id') @@ -175,6 +243,26 @@ class WebSite(http.HTTPServer): title=f'Подробная информация о модеме "{modem_name}"', context=dict(data=data, modem_name=modem_name)) + async def inverter(self, req: http.Request): + action = req.query.get('do', None) + if action == 'set-osp': + val = req.query.get('value') + if val not in ('sub', 'sbu'): + raise ValueError('invalid osp value') + cl = get_inverter_client() + cl.exec('set-output-source-priority', + arguments=(val.upper(),)) + raise HTTPFound('/inverter.cgi') + + status, rated, html = await asyncio.get_event_loop().run_in_executor(None, get_inverter_data) + return await self.render_page(req, 'inverter', + title='Инвертор', + context=dict(status=status, rated=rated, html=html)) + + async def inverter_ajx(self, req: http.Request): + status, rated, html = await asyncio.get_event_loop().run_in_executor(None, get_inverter_data) + return self.ok({'html': html}) + if __name__ == '__main__': config.load_app(WebKbnConfig) diff --git a/include/py/homekit/http/http.py b/include/py/homekit/http/http.py index 9b76d9a..82c5aae 100644 --- a/include/py/homekit/http/http.py +++ b/include/py/homekit/http/http.py @@ -3,7 +3,7 @@ import asyncio from enum import Enum from aiohttp import web -from aiohttp.web import Response +from aiohttp.web import Response, HTTPFound from aiohttp.web_exceptions import HTTPNotFound from ..util import stringify, format_tb, Addr @@ -21,6 +21,9 @@ async def errors_handler_middleware(request, handler): except HTTPNotFound: return web.json_response({'error': 'not found'}, status=404) + except HTTPFound as exc: + raise exc + except Exception as exc: _logger.exception(exc) data = { diff --git a/include/py/homekit/inverter/config.py b/include/py/homekit/inverter/config.py index e284dfe..694ddae 100644 --- a/include/py/homekit/inverter/config.py +++ b/include/py/homekit/inverter/config.py @@ -8,6 +8,6 @@ class InverterdConfig(ConfigUnit): @classmethod def schema(cls) -> Optional[dict]: return { - 'remote_addr': {'type': 'string'}, - 'local_addr': {'type': 'string'}, + 'remote_addr': cls._addr_schema(required=True), + 'local_addr': cls._addr_schema(required=True), }
\ No newline at end of file diff --git a/localwebsite/handlers/InverterHandler.php b/localwebsite/handlers/InverterHandler.php deleted file mode 100644 index 5fa269f..0000000 --- a/localwebsite/handlers/InverterHandler.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php - -class InverterHandler extends RequestHandler -{ - - public function __construct() { - parent::__construct(); - $this->tpl->add_static('inverter.js'); - } - - public function GET_status_page() { - $inv = $this->getClient(); - - $status = jsonDecode($inv->exec('get-status'))['data']; - $rated = jsonDecode($inv->exec('get-rated'))['data']; - - $this->tpl->set([ - 'status' => $status, - 'rated' => $rated, - 'html' => $this->renderStatusHtml($status, $rated) - ]); - $this->tpl->set_title('Инвертор'); - $this->tpl->render_page('inverter_page.twig'); - } - - public function GET_set_osp() { - list($osp) = $this->input('e:value(=sub|sbu)'); - $inv = $this->getClient(); - try { - $inv->exec('set-output-source-priority', [strtoupper($osp)]); - } catch (Exception $e) { - die('Ошибка: '.jsonDecode($e->getMessage())['message']); - } - redirect('/inverter/'); - } - - public function GET_status_ajax() { - $inv = $this->getClient(); - $status = jsonDecode($inv->exec('get-status'))['data']; - $rated = jsonDecode($inv->exec('get-rated'))['data']; - ajax_ok(['html' => $this->renderStatusHtml($status, $rated)]); - } - - protected function renderStatusHtml(array $status, array $rated) { - $power_direction = strtolower($status['battery_power_direction']); - $power_direction = preg_replace('/ge$/', 'ging', $power_direction); - - $charging_rate = ''; - if ($power_direction == 'charging') - $charging_rate = sprintf(' @ %s %s', - $status['battery_charge_current']['value'], - $status['battery_charge_current']['unit']); - else if ($power_direction == 'discharging') - $charging_rate = sprintf(' @ %s %s', - $status['battery_discharge_current']['value'], - $status['battery_discharge_current']['unit']); - - $html = sprintf('<b>Battery:</b> %s %s', - $status['battery_voltage']['value'], - $status['battery_voltage']['unit']); - $html .= sprintf(' (%s%s, ', - $status['battery_capacity']['value'], - $status['battery_capacity']['unit']); - $html .= sprintf('%s%s)', - $power_direction, - $charging_rate); - - $html .= "\n".sprintf('<b>Load:</b> %s %s', - $status['ac_output_active_power']['value'], - $status['ac_output_active_power']['unit']); - $html .= sprintf(' (%s%%)', - $status['output_load_percent']['value']); - - if ($status['pv1_input_power']['value'] > 0) - $html .= "\n".sprintf('<b>Input power:</b> %s %s', - $status['pv1_input_power']['value'], - $status['pv1_input_power']['unit']); - - if ($status['grid_voltage']['value'] > 0 or $status['grid_freq']['value'] > 0) { - $html .= "\n".sprintf('<b>AC input:</b> %s %s', - $status['grid_voltage']['value'], - $status['grid_voltage']['unit']); - $html .= sprintf(', %s %s', - $status['grid_freq']['value'], - $status['grid_freq']['unit']); - } - - $html .= "\n".sprintf('<b>Priority:</b> %s', - $rated['output_source_priority']); - - return nl2br($html); - } - - protected function getClient(): InverterdClient { - global $config; - if (isset($_GET['alt']) && $_GET['alt'] == 1) - $config['inverterd_host'] = '192.168.5.223'; - $inv = new InverterdClient($config['inverterd_host'], $config['inverterd_port']); - $inv->setFormat('json'); - return $inv; - } - - -} diff --git a/localwebsite/htdocs/index.php b/localwebsite/htdocs/index.php index d6034e6..eeeaacb 100644 --- a/localwebsite/htdocs/index.php +++ b/localwebsite/htdocs/index.php @@ -4,11 +4,6 @@ require_once __DIR__.'/../init.php'; $router = new router; -// modem -$router->add('modem/', 'Modem status_page'); -$router->add('modem/verbose/', 'Modem verbose_page'); -$router->add('modem/get.ajax', 'Modem status_get_ajax'); - $router->add('routing/', 'Modem routing_smallhome_page'); $router->add('routing/switch-small-home/', 'Modem routing_smallhome_switch'); $router->add('routing/{ipsets,dhcp}/', 'Modem routing_${1}_page'); @@ -18,9 +13,7 @@ $router->add('sms/', 'Modem sms'); // $router->add('modem/set.ajax', 'Modem ctl_set_ajax'); // inverter -$router->add('inverter/', 'Inverter status_page'); $router->add('inverter/set-osp/', 'Inverter set_osp'); -$router->add('inverter/status.ajax', 'Inverter status_ajax'); // misc $router->add('/', 'Misc main'); diff --git a/localwebsite/templates-web/inverter_page.twig b/localwebsite/templates-web/inverter_page.twig deleted file mode 100644 index c51e1bf..0000000 --- a/localwebsite/templates-web/inverter_page.twig +++ /dev/null @@ -1,20 +0,0 @@ -{% include 'bc.twig' with { - history: [ - {text: "Инвертор" } - ] -} %} - -<h6 class="text-primary">Статус</h6> -<div id="inverter_status"> - {{ html|raw }} -</div> - -<div class="pt-3"> - <a href="/inverter/set-osp/?value={{ rated.output_source_priority == 'Solar-Battery-Utility' ? 'sub' : 'sbu' }}"> - <button type="button" class="btn btn-primary">Переключить на <b>{{ rated.output_source_priority == 'Solar-Battery-Utility' ? 'Solar-Utility-Battery' : 'Solar-Battery-Utility' }}</b></button> - </a> -</div> - -{% js %} -Inverter.poll(); -{% endjs %}
\ No newline at end of file diff --git a/web/kbn_assets/app.js b/web/kbn_assets/app.js index eaac003..d575a5a 100644 --- a/web/kbn_assets/app.js +++ b/web/kbn_assets/app.js @@ -349,3 +349,20 @@ var ModemStatus = { } } }; + + +var Inverter = { + poll: function () { + setInterval(this._tick, 1000); + }, + + _tick: function() { + ajax.get('/inverter.ajx') + .then(({response}) => { + if (response) { + var el = document.getElementById('inverter_status'); + el.innerHTML = response.html; + } + }); + } +};
\ No newline at end of file diff --git a/web/kbn_assets/inverter.js b/web/kbn_assets/inverter.js deleted file mode 100644 index 72d985c..0000000 --- a/web/kbn_assets/inverter.js +++ /dev/null @@ -1,15 +0,0 @@ -var Inverter = { - poll: function () { - setInterval(this._tick, 1000); - }, - - _tick: function() { - ajax.get('/inverter/status.ajax') - .then(({response}) => { - if (response) { - var el = document.getElementById('inverter_status'); - el.innerHTML = response.html; - } - }); - } -};
\ No newline at end of file diff --git a/web/kbn_templates/inverter.j2 b/web/kbn_templates/inverter.j2 new file mode 100644 index 0000000..26491f3 --- /dev/null +++ b/web/kbn_templates/inverter.j2 @@ -0,0 +1,20 @@ +{% extends "base.j2" %} + +{% block content %} +{{ breadcrumbs([{'text': 'Инвертор'}]) }} + +<h6 class="text-primary">Статус</h6> +<div id="inverter_status"> + {{ html|safe }} +</div> + +<div class="pt-3"> + <a href="/inverter.cgi?do=set-osp&value={{ 'sub' if rated.output_source_priority == 'Solar-Battery-Utility' else 'sbu' }}"> + <button type="button" class="btn btn-primary">Переключить на <b>{{ 'Solar-Utility-Battery' if rated.output_source_priority == 'Solar-Battery-Utility' else 'Solar-Battery-Utility' }}</b></button> + </a> +</div> +{% endblock %} + +{% block js %} +Inverter.poll(); +{% endblock %}
\ No newline at end of file |