aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Sorokin <me@ch1p.io>2024-01-16 03:31:55 +0300
committerEvgeny Sorokin <me@ch1p.io>2024-01-16 03:32:07 +0300
commit8a89dd77be03ca8eb9cdc378ba8e912292494fa9 (patch)
treef2d04c1a6ce8b2731febbf6b66f6127057f9477d
parentde56aa3ae916ac0d51e503648fae8f3fa2d97951 (diff)
inverter page
-rw-r--r--bin/web_kbn.py108
-rw-r--r--include/py/homekit/http/http.py5
-rw-r--r--include/py/homekit/inverter/config.py4
-rw-r--r--localwebsite/handlers/InverterHandler.php104
-rw-r--r--localwebsite/htdocs/index.php7
-rw-r--r--localwebsite/templates-web/inverter_page.twig20
-rw-r--r--web/kbn_assets/app.js17
-rw-r--r--web/kbn_assets/inverter.js15
-rw-r--r--web/kbn_templates/inverter.j220
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&amp;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