wip
This commit is contained in:
parent
7058d0f506
commit
da5db8bc28
@ -2,16 +2,18 @@
|
||||
import asyncio
|
||||
import jinja2
|
||||
import aiohttp_jinja2
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import __py_include
|
||||
|
||||
from io import StringIO
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
from homekit.config import config, AppConfigUnit
|
||||
from homekit.util import homekit_path
|
||||
from homekit.util import homekit_path, filesize_fmt, seconds_to_human_readable_string
|
||||
from aiohttp import web
|
||||
from homekit import http
|
||||
from homekit.modem import ModemsConfig
|
||||
from homekit.modem import ModemsConfig, E3372, MacroNetWorkType
|
||||
|
||||
|
||||
class WebKbnConfig(AppConfigUnit):
|
||||
@ -49,7 +51,7 @@ def get_css_link(file, version) -> str:
|
||||
def get_head_static() -> str:
|
||||
buf = StringIO()
|
||||
for file in STATIC_FILES:
|
||||
v = 1
|
||||
v = 2
|
||||
try:
|
||||
q_ind = file.index('?')
|
||||
v = file[q_ind+1:]
|
||||
@ -64,19 +66,52 @@ def get_head_static() -> str:
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def get_modem_data(modem_cfg: dict, get_raw=False) -> Union[dict, tuple]:
|
||||
cl = E3372(modem_cfg['ip'], legacy_token_auth=modem_cfg['legacy_auth'])
|
||||
|
||||
signal = cl.device_signal
|
||||
status = cl.monitoring_status
|
||||
traffic = cl.traffic_stats
|
||||
|
||||
if get_raw:
|
||||
device_info = cl.device_information
|
||||
dialup_conn = cl.dialup_connection
|
||||
return signal, status, traffic, device_info, dialup_conn
|
||||
else:
|
||||
network_type_label = re.sub('^MACRO_NET_WORK_TYPE(_EX)?_', '', MacroNetWorkType(int(status['CurrentNetworkType'])).name)
|
||||
return {
|
||||
'type': network_type_label,
|
||||
'level': int(status['SignalIcon']) if 'SignalIcon' in status else 0,
|
||||
'rssi': signal['rssi'],
|
||||
'sinr': signal['sinr'],
|
||||
'connected_time': seconds_to_human_readable_string(int(traffic['CurrentConnectTime'])),
|
||||
'downloaded': filesize_fmt(int(traffic['CurrentDownload'])),
|
||||
'uploaded': filesize_fmt(int(traffic['CurrentUpload']))
|
||||
}
|
||||
|
||||
|
||||
class WebSite(http.HTTPServer):
|
||||
_modems_config: ModemsConfig
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self._modems_config = ModemsConfig()
|
||||
|
||||
aiohttp_jinja2.setup(
|
||||
self.app,
|
||||
loader=jinja2.FileSystemLoader(homekit_path('web', 'kbn_templates'))
|
||||
loader=jinja2.FileSystemLoader(homekit_path('web', 'kbn_templates')),
|
||||
autoescape=jinja2.select_autoescape(['html', 'xml']),
|
||||
)
|
||||
env = aiohttp_jinja2.get_env(self.app)
|
||||
env.filters['tojson'] = lambda obj: json.dumps(obj, separators=(',', ':'))
|
||||
|
||||
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)
|
||||
|
||||
async def render_page(self,
|
||||
req: http.Request,
|
||||
@ -99,19 +134,50 @@ class WebSite(http.HTTPServer):
|
||||
title="Home web site")
|
||||
|
||||
async def get_modems(self, req: http.Request):
|
||||
mc = ModemsConfig()
|
||||
print(mc)
|
||||
return await self.render_page(req, 'modems',
|
||||
title='Состояние модемов',
|
||||
context=dict(modems=ModemsConfig()))
|
||||
context=dict(modems=self._modems_config))
|
||||
|
||||
async def get_modems_ajax(self, req: http.Request):
|
||||
modem = req.query.get('id', None)
|
||||
if modem not in self._modems_config.getkeys():
|
||||
raise ValueError('invalid modem id')
|
||||
|
||||
modem_cfg = self._modems_config.get(modem)
|
||||
loop = asyncio.get_event_loop()
|
||||
modem_data = await loop.run_in_executor(None, lambda: get_modem_data(modem_cfg))
|
||||
|
||||
html = aiohttp_jinja2.render_string('modem_data.j2', req, context=dict(
|
||||
modem_data=modem_data,
|
||||
modem=modem
|
||||
))
|
||||
|
||||
return self.ok({'html': html})
|
||||
|
||||
async def get_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')
|
||||
|
||||
modem_cfg = self._modems_config.get(modem)
|
||||
loop = asyncio.get_event_loop()
|
||||
signal, status, traffic, device, dialup_conn = await loop.run_in_executor(None, lambda: get_modem_data(modem_cfg, True))
|
||||
data = [
|
||||
['Signal', signal],
|
||||
['Connection', status],
|
||||
['Traffic', traffic],
|
||||
['Device info', device],
|
||||
['Dialup connection', dialup_conn]
|
||||
]
|
||||
|
||||
modem_name = self._modems_config.getfullname(modem)
|
||||
return await self.render_page(req, 'modem_verbose',
|
||||
title=f'Подробная информация о модеме "{modem_name}"',
|
||||
context=dict(data=data, modem_name=modem_name))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
config.load_app(WebKbnConfig)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
# print(config.app_config)
|
||||
|
||||
print(config.app_config['listen_addr'].host)
|
||||
server = WebSite(config.app_config['listen_addr'])
|
||||
server.run()
|
||||
|
@ -78,6 +78,9 @@ class BaseConfigUnit(ABC):
|
||||
|
||||
raise KeyError(f'option {key} not found')
|
||||
|
||||
def getkeys(self):
|
||||
return list(self._data.keys())
|
||||
|
||||
|
||||
class ConfigUnit(BaseConfigUnit):
|
||||
NAME = 'dumb'
|
||||
|
@ -1,2 +1,2 @@
|
||||
from .http import serve, ok, routes, HTTPServer
|
||||
from .http import serve, ok, routes, HTTPServer, HTTPMethod
|
||||
from aiohttp.web import FileResponse, StreamResponse, Request, Response
|
@ -1,6 +1,7 @@
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from enum import Enum
|
||||
from aiohttp import web
|
||||
from aiohttp.web import Response
|
||||
from aiohttp.web_exceptions import HTTPNotFound
|
||||
@ -104,3 +105,8 @@ class HTTPServer:
|
||||
|
||||
def plain(self, text: str):
|
||||
return Response(text=text, content_type='text/plain')
|
||||
|
||||
|
||||
class HTTPMethod(Enum):
|
||||
GET = 'GET'
|
||||
POST = 'POST'
|
||||
|
@ -1 +1,2 @@
|
||||
from .config import ModemsConfig
|
||||
from .e3372 import E3372, MacroNetWorkType
|
||||
|
253
include/py/homekit/modem/e3372.py
Normal file
253
include/py/homekit/modem/e3372.py
Normal file
@ -0,0 +1,253 @@
|
||||
import requests
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
|
||||
from ..util import Addr
|
||||
from enum import Enum
|
||||
from ..http import HTTPMethod
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Error(Enum):
|
||||
ERROR_SYSTEM_NO_SUPPORT = 100002
|
||||
ERROR_SYSTEM_NO_RIGHTS = 100003
|
||||
ERROR_SYSTEM_BUSY = 100004
|
||||
ERROR_LOGIN_USERNAME_WRONG = 108001
|
||||
ERROR_LOGIN_PASSWORD_WRONG = 108002
|
||||
ERROR_LOGIN_ALREADY_LOGIN = 108003
|
||||
ERROR_LOGIN_USERNAME_PWD_WRONG = 108006
|
||||
ERROR_LOGIN_USERNAME_PWD_ORERRUN = 108007
|
||||
ERROR_LOGIN_TOUCH_ALREADY_LOGIN = 108009
|
||||
ERROR_VOICE_BUSY = 120001
|
||||
ERROR_WRONG_TOKEN = 125001
|
||||
ERROR_WRONG_SESSION = 125002
|
||||
ERROR_WRONG_SESSION_TOKEN = 125003
|
||||
|
||||
|
||||
class WifiStatus(Enum):
|
||||
WIFI_CONNECTING = '900'
|
||||
WIFI_CONNECTED = '901'
|
||||
WIFI_DISCONNECTED = '902'
|
||||
WIFI_DISCONNECTING = '903'
|
||||
|
||||
|
||||
class Cradle(Enum):
|
||||
CRADLE_CONNECTING = '900'
|
||||
CRADLE_CONNECTED = '901'
|
||||
CRADLE_DISCONNECTED = '902'
|
||||
CRADLE_DISCONNECTING = '903'
|
||||
CRADLE_CONNECTFAILED = '904'
|
||||
CRADLE_CONNECTSTATUSNULL = '905'
|
||||
CRANDLE_CONNECTSTATUSERRO = '906'
|
||||
|
||||
|
||||
class MacroEVDOLevel(Enum):
|
||||
MACRO_EVDO_LEVEL_ZERO = '0'
|
||||
MACRO_EVDO_LEVEL_ONE = '1'
|
||||
MACRO_EVDO_LEVEL_TWO = '2'
|
||||
MACRO_EVDO_LEVEL_THREE = '3'
|
||||
MACRO_EVDO_LEVEL_FOUR = '4'
|
||||
MACRO_EVDO_LEVEL_FIVE = '5'
|
||||
|
||||
|
||||
class MacroNetWorkType(Enum):
|
||||
MACRO_NET_WORK_TYPE_NOSERVICE = 0
|
||||
MACRO_NET_WORK_TYPE_GSM = 1
|
||||
MACRO_NET_WORK_TYPE_GPRS = 2
|
||||
MACRO_NET_WORK_TYPE_EDGE = 3
|
||||
MACRO_NET_WORK_TYPE_WCDMA = 4
|
||||
MACRO_NET_WORK_TYPE_HSDPA = 5
|
||||
MACRO_NET_WORK_TYPE_HSUPA = 6
|
||||
MACRO_NET_WORK_TYPE_HSPA = 7
|
||||
MACRO_NET_WORK_TYPE_TDSCDMA = 8
|
||||
MACRO_NET_WORK_TYPE_HSPA_PLUS = 9
|
||||
MACRO_NET_WORK_TYPE_EVDO_REV_0 = 10
|
||||
MACRO_NET_WORK_TYPE_EVDO_REV_A = 11
|
||||
MACRO_NET_WORK_TYPE_EVDO_REV_B = 12
|
||||
MACRO_NET_WORK_TYPE_1xRTT = 13
|
||||
MACRO_NET_WORK_TYPE_UMB = 14
|
||||
MACRO_NET_WORK_TYPE_1xEVDV = 15
|
||||
MACRO_NET_WORK_TYPE_3xRTT = 16
|
||||
MACRO_NET_WORK_TYPE_HSPA_PLUS_64QAM = 17
|
||||
MACRO_NET_WORK_TYPE_HSPA_PLUS_MIMO = 18
|
||||
MACRO_NET_WORK_TYPE_LTE = 19
|
||||
MACRO_NET_WORK_TYPE_EX_NOSERVICE = 0
|
||||
MACRO_NET_WORK_TYPE_EX_GSM = 1
|
||||
MACRO_NET_WORK_TYPE_EX_GPRS = 2
|
||||
MACRO_NET_WORK_TYPE_EX_EDGE = 3
|
||||
MACRO_NET_WORK_TYPE_EX_IS95A = 21
|
||||
MACRO_NET_WORK_TYPE_EX_IS95B = 22
|
||||
MACRO_NET_WORK_TYPE_EX_CDMA_1x = 23
|
||||
MACRO_NET_WORK_TYPE_EX_EVDO_REV_0 = 24
|
||||
MACRO_NET_WORK_TYPE_EX_EVDO_REV_A = 25
|
||||
MACRO_NET_WORK_TYPE_EX_EVDO_REV_B = 26
|
||||
MACRO_NET_WORK_TYPE_EX_HYBRID_CDMA_1x = 27
|
||||
MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_0 = 28
|
||||
MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_A = 29
|
||||
MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_B = 30
|
||||
MACRO_NET_WORK_TYPE_EX_EHRPD_REL_0 = 31
|
||||
MACRO_NET_WORK_TYPE_EX_EHRPD_REL_A = 32
|
||||
MACRO_NET_WORK_TYPE_EX_EHRPD_REL_B = 33
|
||||
MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_0 = 34
|
||||
MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_A = 35
|
||||
MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_B = 36
|
||||
MACRO_NET_WORK_TYPE_EX_WCDMA = 41
|
||||
MACRO_NET_WORK_TYPE_EX_HSDPA = 42
|
||||
MACRO_NET_WORK_TYPE_EX_HSUPA = 43
|
||||
MACRO_NET_WORK_TYPE_EX_HSPA = 44
|
||||
MACRO_NET_WORK_TYPE_EX_HSPA_PLUS = 45
|
||||
MACRO_NET_WORK_TYPE_EX_DC_HSPA_PLUS = 46
|
||||
MACRO_NET_WORK_TYPE_EX_TD_SCDMA = 61
|
||||
MACRO_NET_WORK_TYPE_EX_TD_HSDPA = 62
|
||||
MACRO_NET_WORK_TYPE_EX_TD_HSUPA = 63
|
||||
MACRO_NET_WORK_TYPE_EX_TD_HSPA = 64
|
||||
MACRO_NET_WORK_TYPE_EX_TD_HSPA_PLUS = 65
|
||||
MACRO_NET_WORK_TYPE_EX_802_16E = 81
|
||||
MACRO_NET_WORK_TYPE_EX_LTE = 101
|
||||
|
||||
|
||||
def post_data_to_xml(data: dict, depth: int = 1) -> str:
|
||||
if depth == 1:
|
||||
return '<?xml version: "1.0" encoding="UTF-8"?>'+post_data_to_xml({'request': data}, depth+1)
|
||||
|
||||
items = []
|
||||
for k, v in data.items():
|
||||
if isinstance(v, dict):
|
||||
v = post_data_to_xml(v, depth+1)
|
||||
elif isinstance(v, list):
|
||||
raise TypeError('list type is unsupported here')
|
||||
items.append(f'<{k}>{v}</{k}>')
|
||||
|
||||
return ''.join(items)
|
||||
|
||||
|
||||
class E3372:
|
||||
_addr: Addr
|
||||
_need_auth: bool
|
||||
_legacy_token_auth: bool
|
||||
_get_raw_data: bool
|
||||
_headers: dict[str, str]
|
||||
_authorized: bool
|
||||
|
||||
def __init__(self,
|
||||
addr: Addr,
|
||||
need_auth: bool = True,
|
||||
legacy_token_auth: bool = False,
|
||||
get_raw_data: bool = False):
|
||||
self._addr = addr
|
||||
self._need_auth = need_auth
|
||||
self._legacy_token_auth = legacy_token_auth
|
||||
self._get_raw_data = get_raw_data
|
||||
self._authorized = False
|
||||
self._headers = {}
|
||||
|
||||
@property
|
||||
def device_information(self):
|
||||
self.auth()
|
||||
return self.request('device/information')
|
||||
|
||||
@property
|
||||
def device_signal(self):
|
||||
self.auth()
|
||||
return self.request('device/signal')
|
||||
|
||||
@property
|
||||
def monitoring_status(self):
|
||||
self.auth()
|
||||
return self.request('monitoring/status')
|
||||
|
||||
@property
|
||||
def notifications(self):
|
||||
self.auth()
|
||||
return self.request('monitoring/check-notifications')
|
||||
|
||||
@property
|
||||
def dialup_connection(self):
|
||||
self.auth()
|
||||
return self.request('dialup/connection')
|
||||
|
||||
@property
|
||||
def traffic_stats(self):
|
||||
self.auth()
|
||||
return self.request('monitoring/traffic-statistics')
|
||||
|
||||
@property
|
||||
def sms_count(self):
|
||||
self.auth()
|
||||
return self.request('sms/sms-count')
|
||||
|
||||
def sms_send(self, phone: str, text: str):
|
||||
self.auth()
|
||||
return self.request('sms/send-sms', HTTPMethod.POST, {
|
||||
'Index': -1,
|
||||
'Phones': {
|
||||
'Phone': phone
|
||||
},
|
||||
'Sca': '',
|
||||
'Content': text,
|
||||
'Length': -1,
|
||||
'Reserved': 1,
|
||||
'Date': -1
|
||||
})
|
||||
|
||||
def sms_list(self, page: int = 1, count: int = 20, outbox: bool = False):
|
||||
self.auth()
|
||||
xml = self.request('sms/sms-list', HTTPMethod.POST, {
|
||||
'PageIndex': page,
|
||||
'ReadCount': count,
|
||||
'BoxType': 1 if not outbox else 2,
|
||||
'SortType': 0,
|
||||
'Ascending': 0,
|
||||
'UnreadPreferred': 1 if not outbox else 0
|
||||
}, return_body=True)
|
||||
|
||||
root = ElementTree.fromstring(xml)
|
||||
messages = []
|
||||
for message_elem in root.find('Messages').findall('Message'):
|
||||
message_dict = {child.tag: child.text for child in message_elem}
|
||||
messages.append(message_dict)
|
||||
return messages
|
||||
|
||||
def auth(self):
|
||||
if self._authorized:
|
||||
return
|
||||
|
||||
if not self._legacy_token_auth:
|
||||
data = self.request('webserver/SesTokInfo')
|
||||
self._headers = {
|
||||
'Cookie': data['SesInfo'],
|
||||
'__RequestVerificationToken': data['TokInfo'],
|
||||
'Content-Type': 'text/xml'
|
||||
}
|
||||
else:
|
||||
data = self.request('webserver/token')
|
||||
self._headers = {
|
||||
'__RequestVerificationToken': data['token'],
|
||||
'Content-Type': 'text/xml'
|
||||
}
|
||||
|
||||
self._authorized = True
|
||||
|
||||
def request(self,
|
||||
method: str,
|
||||
http_method: HTTPMethod = HTTPMethod.GET,
|
||||
data: dict = {},
|
||||
return_body: bool = False) -> Union[str, dict]:
|
||||
url = f'http://{self._addr}/api/{method}'
|
||||
if http_method == HTTPMethod.POST:
|
||||
data = post_data_to_xml(data)
|
||||
f = requests.post
|
||||
else:
|
||||
data = None
|
||||
f = requests.get
|
||||
r = f(url, data=data, headers=self._headers)
|
||||
r.raise_for_status()
|
||||
r.encoding = 'utf-8'
|
||||
|
||||
if return_body:
|
||||
return r.text
|
||||
|
||||
root = ElementTree.fromstring(r.text)
|
||||
data_dict = {}
|
||||
for elem in root:
|
||||
data_dict[elem.tag] = elem.text
|
||||
return data_dict
|
@ -12,7 +12,7 @@ import re
|
||||
import os
|
||||
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, List
|
||||
from zlib import adler32
|
||||
|
||||
@ -255,6 +255,25 @@ def filesize_fmt(num, suffix="B") -> str:
|
||||
return f"{num:.1f} Yi{suffix}"
|
||||
|
||||
|
||||
def seconds_to_human_readable_string(seconds: int) -> str:
|
||||
duration = timedelta(seconds=seconds)
|
||||
days, remainder = divmod(duration.total_seconds(), 86400)
|
||||
hours, remainder = divmod(remainder, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
|
||||
parts = []
|
||||
if days > 0:
|
||||
parts.append(f"{int(days)} day{'s' if days > 1 else ''}")
|
||||
if hours > 0:
|
||||
parts.append(f"{int(hours)} hour{'s' if hours > 1 else ''}")
|
||||
if minutes > 0:
|
||||
parts.append(f"{int(minutes)} minute{'s' if minutes > 1 else ''}")
|
||||
if seconds > 0:
|
||||
parts.append(f"{int(seconds)} second{'s' if seconds > 1 else ''}")
|
||||
|
||||
return ' '.join(parts)
|
||||
|
||||
|
||||
class HashableEnum(Enum):
|
||||
def hash(self) -> int:
|
||||
return adler32(self.name.encode())
|
||||
|
@ -1,310 +0,0 @@
|
||||
<?php
|
||||
|
||||
class E3372
|
||||
{
|
||||
|
||||
const WIFI_CONNECTING = '900';
|
||||
const WIFI_CONNECTED = '901';
|
||||
const WIFI_DISCONNECTED = '902';
|
||||
const WIFI_DISCONNECTING = '903';
|
||||
|
||||
const CRADLE_CONNECTING = '900';
|
||||
const CRADLE_CONNECTED = '901';
|
||||
const CRADLE_DISCONNECTED = '902';
|
||||
const CRADLE_DISCONNECTING = '903';
|
||||
const CRADLE_CONNECTFAILED = '904';
|
||||
const CRADLE_CONNECTSTATUSNULL = '905';
|
||||
const CRANDLE_CONNECTSTATUSERRO = '906';
|
||||
|
||||
const MACRO_EVDO_LEVEL_ZERO = '0';
|
||||
const MACRO_EVDO_LEVEL_ONE = '1';
|
||||
const MACRO_EVDO_LEVEL_TWO = '2';
|
||||
const MACRO_EVDO_LEVEL_THREE = '3';
|
||||
const MACRO_EVDO_LEVEL_FOUR = '4';
|
||||
const MACRO_EVDO_LEVEL_FIVE = '5';
|
||||
|
||||
// CurrentNetworkType
|
||||
const MACRO_NET_WORK_TYPE_NOSERVICE = 0;
|
||||
const MACRO_NET_WORK_TYPE_GSM = 1;
|
||||
const MACRO_NET_WORK_TYPE_GPRS = 2;
|
||||
const MACRO_NET_WORK_TYPE_EDGE = 3;
|
||||
const MACRO_NET_WORK_TYPE_WCDMA = 4;
|
||||
const MACRO_NET_WORK_TYPE_HSDPA = 5;
|
||||
const MACRO_NET_WORK_TYPE_HSUPA = 6;
|
||||
const MACRO_NET_WORK_TYPE_HSPA = 7;
|
||||
const MACRO_NET_WORK_TYPE_TDSCDMA = 8;
|
||||
const MACRO_NET_WORK_TYPE_HSPA_PLUS = 9;
|
||||
const MACRO_NET_WORK_TYPE_EVDO_REV_0 = 10;
|
||||
const MACRO_NET_WORK_TYPE_EVDO_REV_A = 11;
|
||||
const MACRO_NET_WORK_TYPE_EVDO_REV_B = 12;
|
||||
const MACRO_NET_WORK_TYPE_1xRTT = 13;
|
||||
const MACRO_NET_WORK_TYPE_UMB = 14;
|
||||
const MACRO_NET_WORK_TYPE_1xEVDV = 15;
|
||||
const MACRO_NET_WORK_TYPE_3xRTT = 16;
|
||||
const MACRO_NET_WORK_TYPE_HSPA_PLUS_64QAM = 17;
|
||||
const MACRO_NET_WORK_TYPE_HSPA_PLUS_MIMO = 18;
|
||||
const MACRO_NET_WORK_TYPE_LTE = 19;
|
||||
const MACRO_NET_WORK_TYPE_EX_NOSERVICE = 0;
|
||||
const MACRO_NET_WORK_TYPE_EX_GSM = 1;
|
||||
const MACRO_NET_WORK_TYPE_EX_GPRS = 2;
|
||||
const MACRO_NET_WORK_TYPE_EX_EDGE = 3;
|
||||
const MACRO_NET_WORK_TYPE_EX_IS95A = 21;
|
||||
const MACRO_NET_WORK_TYPE_EX_IS95B = 22;
|
||||
const MACRO_NET_WORK_TYPE_EX_CDMA_1x = 23;
|
||||
const MACRO_NET_WORK_TYPE_EX_EVDO_REV_0 = 24;
|
||||
const MACRO_NET_WORK_TYPE_EX_EVDO_REV_A = 25;
|
||||
const MACRO_NET_WORK_TYPE_EX_EVDO_REV_B = 26;
|
||||
const MACRO_NET_WORK_TYPE_EX_HYBRID_CDMA_1x = 27;
|
||||
const MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_0 = 28;
|
||||
const MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_A = 29;
|
||||
const MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_B = 30;
|
||||
const MACRO_NET_WORK_TYPE_EX_EHRPD_REL_0 = 31;
|
||||
const MACRO_NET_WORK_TYPE_EX_EHRPD_REL_A = 32;
|
||||
const MACRO_NET_WORK_TYPE_EX_EHRPD_REL_B = 33;
|
||||
const MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_0 = 34;
|
||||
const MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_A = 35;
|
||||
const MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_B = 36;
|
||||
const MACRO_NET_WORK_TYPE_EX_WCDMA = 41;
|
||||
const MACRO_NET_WORK_TYPE_EX_HSDPA = 42;
|
||||
const MACRO_NET_WORK_TYPE_EX_HSUPA = 43;
|
||||
const MACRO_NET_WORK_TYPE_EX_HSPA = 44;
|
||||
const MACRO_NET_WORK_TYPE_EX_HSPA_PLUS = 45;
|
||||
const MACRO_NET_WORK_TYPE_EX_DC_HSPA_PLUS = 46;
|
||||
const MACRO_NET_WORK_TYPE_EX_TD_SCDMA = 61;
|
||||
const MACRO_NET_WORK_TYPE_EX_TD_HSDPA = 62;
|
||||
const MACRO_NET_WORK_TYPE_EX_TD_HSUPA = 63;
|
||||
const MACRO_NET_WORK_TYPE_EX_TD_HSPA = 64;
|
||||
const MACRO_NET_WORK_TYPE_EX_TD_HSPA_PLUS = 65;
|
||||
const MACRO_NET_WORK_TYPE_EX_802_16E = 81;
|
||||
const MACRO_NET_WORK_TYPE_EX_LTE = 101;
|
||||
|
||||
|
||||
const ERROR_SYSTEM_NO_SUPPORT = 100002;
|
||||
const ERROR_SYSTEM_NO_RIGHTS = 100003;
|
||||
const ERROR_SYSTEM_BUSY = 100004;
|
||||
const ERROR_LOGIN_USERNAME_WRONG = 108001;
|
||||
const ERROR_LOGIN_PASSWORD_WRONG = 108002;
|
||||
const ERROR_LOGIN_ALREADY_LOGIN = 108003;
|
||||
const ERROR_LOGIN_USERNAME_PWD_WRONG = 108006;
|
||||
const ERROR_LOGIN_USERNAME_PWD_ORERRUN = 108007;
|
||||
const ERROR_LOGIN_TOUCH_ALREADY_LOGIN = 108009;
|
||||
const ERROR_VOICE_BUSY = 120001;
|
||||
const ERROR_WRONG_TOKEN = 125001;
|
||||
const ERROR_WRONG_SESSION = 125002;
|
||||
const ERROR_WRONG_SESSION_TOKEN = 125003;
|
||||
|
||||
private string $host;
|
||||
private array $headers = [];
|
||||
private bool $authorized = false;
|
||||
private bool $useLegacyTokenAuth = false;
|
||||
|
||||
public function __construct(string $host, bool $legacy_token_auth = false) {
|
||||
$this->host = $host;
|
||||
$this->useLegacyTokenAuth = $legacy_token_auth;
|
||||
}
|
||||
|
||||
public function auth() {
|
||||
if ($this->authorized)
|
||||
return;
|
||||
|
||||
if (!$this->useLegacyTokenAuth) {
|
||||
$data = $this->request('webserver/SesTokInfo');
|
||||
$this->headers = [
|
||||
'Cookie: '.$data['SesInfo'],
|
||||
'__RequestVerificationToken: '.$data['TokInfo'],
|
||||
'Content-Type: text/xml'
|
||||
];
|
||||
} else {
|
||||
$data = $this->request('webserver/token');
|
||||
$this->headers = [
|
||||
'__RequestVerificationToken: '.$data['token'],
|
||||
'Content-Type: text/xml'
|
||||
];
|
||||
}
|
||||
$this->authorized = true;
|
||||
}
|
||||
|
||||
public function getDeviceInformation() {
|
||||
$this->auth();
|
||||
return $this->request('device/information');
|
||||
}
|
||||
|
||||
public function getDeviceSignal() {
|
||||
$this->auth();
|
||||
return $this->request('device/signal');
|
||||
}
|
||||
|
||||
public function getMonitoringStatus() {
|
||||
$this->auth();
|
||||
return $this->request('monitoring/status');
|
||||
}
|
||||
|
||||
public function getNotifications() {
|
||||
$this->auth();
|
||||
return $this->request('monitoring/check-notifications');
|
||||
}
|
||||
|
||||
public function getDialupConnection() {
|
||||
$this->auth();
|
||||
return $this->request('dialup/connection');
|
||||
}
|
||||
|
||||
public function getTrafficStats() {
|
||||
$this->auth();
|
||||
return $this->request('monitoring/traffic-statistics');
|
||||
}
|
||||
|
||||
public function getSMSCount() {
|
||||
$this->auth();
|
||||
return $this->request('sms/sms-count');
|
||||
}
|
||||
|
||||
public function sendSMS(string $phone, string $text) {
|
||||
$this->auth();
|
||||
return $this->request('sms/send-sms', 'POST', [
|
||||
'Index' => -1,
|
||||
'Phones' => [
|
||||
'Phone' => $phone
|
||||
],
|
||||
'Sca' => '',
|
||||
'Content' => $text,
|
||||
'Length' => -1,
|
||||
'Reserved' => 1,
|
||||
'Date' => -1
|
||||
]);
|
||||
}
|
||||
|
||||
public function getSMSList(int $page = 1, int $count = 20, bool $outbox = false) {
|
||||
$this->auth();
|
||||
$xml = $this->request('sms/sms-list', 'POST', [
|
||||
'PageIndex' => $page,
|
||||
'ReadCount' => $count,
|
||||
'BoxType' => !$outbox ? 1 : 2,
|
||||
'SortType' => 0,
|
||||
'Ascending' => 0,
|
||||
'UnreadPreferred' => !$outbox ? 1 : 0
|
||||
], true);
|
||||
$xml = simplexml_load_string($xml);
|
||||
|
||||
$messages = [];
|
||||
foreach ($xml->Messages->Message as $message) {
|
||||
$dt = DateTime::createFromFormat("Y-m-d H:i:s", (string)$message->Date);
|
||||
$messages[] = [
|
||||
'date' => (string)$message->Date,
|
||||
'timestamp' => $dt->getTimestamp(),
|
||||
'phone' => (string)$message->Phone,
|
||||
'content' => (string)$message->Content
|
||||
];
|
||||
}
|
||||
return $messages;
|
||||
}
|
||||
|
||||
private function xmlToAssoc(string $xml): array {
|
||||
$xml = new SimpleXMLElement($xml);
|
||||
$data = [];
|
||||
foreach ($xml as $name => $value) {
|
||||
$data[$name] = (string)$value;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function request(string $method, string $http_method = 'GET', array $data = [], bool $return_body = false) {
|
||||
$ch = curl_init();
|
||||
$url = 'http://'.$this->host.'/api/'.$method;
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
if (!empty($this->headers))
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers);
|
||||
if ($http_method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
|
||||
$post_data = $this->postDataToXML($data);
|
||||
// debugLog('post_data:', $post_data);
|
||||
|
||||
if (!empty($data))
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
}
|
||||
$body = curl_exec($ch);
|
||||
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($code != 200)
|
||||
throw new Exception('e3372 host returned code '.$code);
|
||||
|
||||
curl_close($ch);
|
||||
return $return_body ? $body : $this->xmlToAssoc($body);
|
||||
}
|
||||
|
||||
private function postDataToXML(array $data, int $depth = 1): string {
|
||||
if ($depth == 1)
|
||||
return '<?xml version: "1.0" encoding="UTF-8"?>'.$this->postDataToXML(['request' => $data], $depth+1);
|
||||
|
||||
$items = [];
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value))
|
||||
$value = $this->postDataToXML($value, $depth+1);
|
||||
$items[] = "<{$key}>{$value}</{$key}>";
|
||||
}
|
||||
|
||||
return implode('', $items);
|
||||
}
|
||||
|
||||
public static function getNetworkTypeLabel($type): string {
|
||||
switch ((int)$type) {
|
||||
case self::MACRO_NET_WORK_TYPE_NOSERVICE: return 'NOSERVICE';
|
||||
case self::MACRO_NET_WORK_TYPE_GSM: return 'GSM';
|
||||
case self::MACRO_NET_WORK_TYPE_GPRS: return 'GPRS';
|
||||
case self::MACRO_NET_WORK_TYPE_EDGE: return 'EDGE';
|
||||
case self::MACRO_NET_WORK_TYPE_WCDMA: return 'WCDMA';
|
||||
case self::MACRO_NET_WORK_TYPE_HSDPA: return 'HSDPA';
|
||||
case self::MACRO_NET_WORK_TYPE_HSUPA: return 'HSUPA';
|
||||
case self::MACRO_NET_WORK_TYPE_HSPA: return 'HSPA';
|
||||
case self::MACRO_NET_WORK_TYPE_TDSCDMA: return 'TDSCDMA';
|
||||
case self::MACRO_NET_WORK_TYPE_HSPA_PLUS: return 'HSPA_PLUS';
|
||||
case self::MACRO_NET_WORK_TYPE_EVDO_REV_0: return 'EVDO_REV_0';
|
||||
case self::MACRO_NET_WORK_TYPE_EVDO_REV_A: return 'EVDO_REV_A';
|
||||
case self::MACRO_NET_WORK_TYPE_EVDO_REV_B: return 'EVDO_REV_B';
|
||||
case self::MACRO_NET_WORK_TYPE_1xRTT: return '1xRTT';
|
||||
case self::MACRO_NET_WORK_TYPE_UMB: return 'UMB';
|
||||
case self::MACRO_NET_WORK_TYPE_1xEVDV: return '1xEVDV';
|
||||
case self::MACRO_NET_WORK_TYPE_3xRTT: return '3xRTT';
|
||||
case self::MACRO_NET_WORK_TYPE_HSPA_PLUS_64QAM: return 'HSPA_PLUS_64QAM';
|
||||
case self::MACRO_NET_WORK_TYPE_HSPA_PLUS_MIMO: return 'HSPA_PLUS_MIMO';
|
||||
case self::MACRO_NET_WORK_TYPE_LTE: return 'LTE';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_NOSERVICE: return 'NOSERVICE';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_GSM: return 'GSM';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_GPRS: return 'GPRS';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_EDGE: return 'EDGE';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_IS95A: return 'IS95A';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_IS95B: return 'IS95B';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_CDMA_1x: return 'CDMA_1x';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_EVDO_REV_0: return 'EVDO_REV_0';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_EVDO_REV_A: return 'EVDO_REV_A';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_EVDO_REV_B: return 'EVDO_REV_B';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HYBRID_CDMA_1x: return 'HYBRID_CDMA_1x';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_0: return 'HYBRID_EVDO_REV_0';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_A: return 'HYBRID_EVDO_REV_A';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HYBRID_EVDO_REV_B: return 'HYBRID_EVDO_REV_B';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_EHRPD_REL_0: return 'EHRPD_REL_0';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_EHRPD_REL_A: return 'EHRPD_REL_A';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_EHRPD_REL_B: return 'EHRPD_REL_B';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_0: return 'HYBRID_EHRPD_REL_0';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_A: return 'HYBRID_EHRPD_REL_A';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HYBRID_EHRPD_REL_B: return 'HYBRID_EHRPD_REL_B';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_WCDMA: return 'WCDMA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HSDPA: return 'HSDPA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HSUPA: return 'HSUPA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HSPA: return 'HSPA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_HSPA_PLUS: return 'HSPA_PLUS';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_DC_HSPA_PLUS: return 'DC_HSPA_PLUS';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_TD_SCDMA: return 'TD_SCDMA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_TD_HSDPA: return 'TD_HSDPA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_TD_HSUPA: return 'TD_HSUPA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_TD_HSPA: return 'TD_HSPA';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_TD_HSPA_PLUS: return 'TD_HSPA_PLUS';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_802_16E: return '802_16E';
|
||||
case self::MACRO_NET_WORK_TYPE_EX_LTE: return 'LTE';
|
||||
default: return '?';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -7,65 +7,6 @@ use libphonenumber\PhoneNumberUtil;
|
||||
class ModemHandler extends RequestHandler
|
||||
{
|
||||
|
||||
public function GET_status_page() {
|
||||
global $config;
|
||||
|
||||
$this->tpl->set([
|
||||
'modems' => $config['modems'],
|
||||
'js_modems' => array_keys($config['modems']),
|
||||
]);
|
||||
|
||||
$this->tpl->set_title('Состояние модемов');
|
||||
$this->tpl->render_page('modem_status_page.twig');
|
||||
}
|
||||
|
||||
public function GET_status_get_ajax() {
|
||||
global $config;
|
||||
list($id) = $this->input('id');
|
||||
if (!isset($config['modems'][$id]))
|
||||
ajax_error('invalid modem id: '.$id);
|
||||
|
||||
$modem_data = self::getModemData(
|
||||
$config['modems'][$id]['ip'],
|
||||
$config['modems'][$id]['legacy_token_auth']);
|
||||
|
||||
ajax_ok([
|
||||
'html' => $this->tpl->render('modem_data.twig', [
|
||||
'loading' => false,
|
||||
'modem' => $id,
|
||||
'modem_data' => $modem_data
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
public function GET_verbose_page() {
|
||||
global $config;
|
||||
|
||||
list($modem) = $this->input('modem');
|
||||
if (!$modem)
|
||||
$modem = array_key_first($config['modems']);
|
||||
|
||||
list($signal, $status, $traffic, $device, $dialup_conn) = self::getModemData(
|
||||
$config['modems'][$modem]['ip'],
|
||||
$config['modems'][$modem]['legacy_token_auth'],
|
||||
true);
|
||||
|
||||
$data = [
|
||||
['Signal', $signal],
|
||||
['Connection', $status],
|
||||
['Traffic', $traffic],
|
||||
['Device info', $device],
|
||||
['Dialup connection', $dialup_conn]
|
||||
];
|
||||
$this->tpl->set([
|
||||
'data' => $data,
|
||||
'modem_name' => $config['modems'][$modem]['label'],
|
||||
]);
|
||||
$this->tpl->set_title('Подробная информация о модеме '.$modem);
|
||||
$this->tpl->render_page('modem_verbose_page.twig');
|
||||
}
|
||||
|
||||
|
||||
public function GET_routing_smallhome_page() {
|
||||
global $config;
|
||||
|
||||
@ -233,32 +174,6 @@ class ModemHandler extends RequestHandler
|
||||
$go_back();
|
||||
}
|
||||
|
||||
protected static function getModemData(string $ip,
|
||||
bool $need_auth = true,
|
||||
bool $get_raw_data = false): array {
|
||||
$modem = new E3372($ip, $need_auth);
|
||||
|
||||
$signal = $modem->getDeviceSignal();
|
||||
$status = $modem->getMonitoringStatus();
|
||||
$traffic = $modem->getTrafficStats();
|
||||
|
||||
if ($get_raw_data) {
|
||||
$device_info = $modem->getDeviceInformation();
|
||||
$dialup_conn = $modem->getDialupConnection();
|
||||
return [$signal, $status, $traffic, $device_info, $dialup_conn];
|
||||
} else {
|
||||
return [
|
||||
'type' => e3372::getNetworkTypeLabel($status['CurrentNetworkType']),
|
||||
'level' => $status['SignalIcon'] ?? 0,
|
||||
'rssi' => $signal['rssi'],
|
||||
'sinr' => $signal['sinr'],
|
||||
'connected_time' => secondsToTime($traffic['CurrentConnectTime']),
|
||||
'downloaded' => bytesToUnitsLabel(gmp_init($traffic['CurrentDownload'])),
|
||||
'uploaded' => bytesToUnitsLabel(gmp_init($traffic['CurrentUpload'])),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
protected static function getCurrentUpstream() {
|
||||
global $config;
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
{% if not loading %}
|
||||
<span class="text-secondary">Сигнал:</span> {% include 'signal_level.twig' with {'level': modem_data.level} %}<br>
|
||||
<span class="text-secondary">Тип сети:</span> <b>{{ modem_data.type }}</b><br>
|
||||
<span class="text-secondary">RSSI:</span> {{ modem_data.rssi }}<br/>
|
||||
{% if modem_data.sinr %}
|
||||
<span class="text-secondary">SINR:</span> {{ modem_data.sinr }}<br/>
|
||||
{% endif %}
|
||||
<span class="text-secondary">Время соединения:</span> {{ modem_data.connected_time }}<br>
|
||||
<span class="text-secondary">Принято/передано:</span> {{ modem_data.downloaded }} / {{ modem_data.uploaded }}
|
||||
<br>
|
||||
<a href="/modem/verbose/?modem={{ modem }}">Подробная информация</a>
|
||||
{% else %}
|
||||
{% include 'spinner.twig' %}
|
||||
{% endif %}
|
@ -1,19 +0,0 @@
|
||||
{% include 'bc.twig' with {
|
||||
history: [
|
||||
{text: "Модемы" }
|
||||
]
|
||||
} %}
|
||||
|
||||
{% for modem_key, modem in modems %}
|
||||
<h6 class="text-primary{% if not loop.first %} mt-4{% endif %}">{{ modem.label }}</h6>
|
||||
<div id="modem_data_{{ modem_key }}">
|
||||
{% include 'modem_data.twig' with {
|
||||
loading: true,
|
||||
modem: modem_key
|
||||
} %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% js %}
|
||||
ModemStatus.init({{ js_modems|json_encode|raw }});
|
||||
{% endjs %}
|
@ -1,15 +0,0 @@
|
||||
{% include 'bc.twig' with {
|
||||
history: [
|
||||
{link: '/modem/', text: "Модемы" },
|
||||
{text: modem_name}
|
||||
]
|
||||
} %}
|
||||
|
||||
{% for item in data %}
|
||||
{% set item_name = item[0] %}
|
||||
{% set item_data = item[1] %}
|
||||
<h6 class="text-primary mt-4">{{ item_name }}</h6>
|
||||
{% for k, v in item_data %}
|
||||
{{ k }} = {{ v }}<br>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
@ -1,14 +0,0 @@
|
||||
<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>
|
9
test/test_modems.py
Executable file
9
test/test_modems.py
Executable file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
import __py_include
|
||||
from homekit.modem import E3372, ModemsConfig
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mc = ModemsConfig()
|
||||
modem = mc.get('mts-azov')
|
||||
cl = E3372(modem['ip'], legacy_token_auth=modem['legacy_auth'])
|
@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
|
||||
/** spinner.twig **/
|
||||
/** spinner.j2 **/
|
||||
|
||||
.sk-fading-circle {
|
||||
margin-top: 10px;
|
||||
|
@ -319,6 +319,26 @@ window.Cameras = {
|
||||
})();
|
||||
|
||||
|
||||
class ModemStatusUpdater {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
this.elem = ge('modem_data_'+id);
|
||||
this.fetch()
|
||||
}
|
||||
|
||||
fetch() {
|
||||
ajax.get('/modems/info.ajx', {
|
||||
id: this.id
|
||||
}).then(({response}) => {
|
||||
const {html} = response;
|
||||
this.elem.innerHTML = html;
|
||||
|
||||
// TODO enqueue rerender
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var ModemStatus = {
|
||||
_modems: [],
|
||||
|
||||
@ -329,21 +349,3 @@ var ModemStatus = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function ModemStatusUpdater(id) {
|
||||
this.id = id;
|
||||
this.elem = ge('modem_data_'+id);
|
||||
this.fetch();
|
||||
}
|
||||
extend(ModemStatusUpdater.prototype, {
|
||||
fetch: function() {
|
||||
ajax.get('/modem/get.ajax', {
|
||||
id: this.id
|
||||
}).then(({response}) => {
|
||||
var {html} = response;
|
||||
this.elem.innerHTML = html;
|
||||
|
||||
// TODO enqueue rerender
|
||||
});
|
||||
},
|
||||
});
|
@ -35,9 +35,9 @@
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
{% if js %}
|
||||
<script>{{ js|raw }}</script>
|
||||
{% endif %}
|
||||
<script>
|
||||
{% block js %}{% endblock %}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
13
web/kbn_templates/modem_data.j2
Normal file
13
web/kbn_templates/modem_data.j2
Normal file
@ -0,0 +1,13 @@
|
||||
{% with level=modem_data.level %}
|
||||
<span class="text-secondary">Сигнал:</span> {% include 'signal_level.j2' %}<br>
|
||||
{% endwith %}
|
||||
|
||||
<span class="text-secondary">Тип сети:</span> <b>{{ modem_data.type }}</b><br>
|
||||
<span class="text-secondary">RSSI:</span> {{ modem_data.rssi }}<br/>
|
||||
{% if modem_data.sinr %}
|
||||
<span class="text-secondary">SINR:</span> {{ modem_data.sinr }}<br/>
|
||||
{% endif %}
|
||||
<span class="text-secondary">Время соединения:</span> {{ modem_data.connected_time }}<br>
|
||||
<span class="text-secondary">Принято/передано:</span> {{ modem_data.downloaded }} / {{ modem_data.uploaded }}
|
||||
<br>
|
||||
<a href="/modems/verbose.cgi?id={{ modem }}">Подробная информация</a>
|
18
web/kbn_templates/modem_verbose.j2
Normal file
18
web/kbn_templates/modem_verbose.j2
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends "base.j2" %}
|
||||
|
||||
{% block content %}
|
||||
{{ breadcrumbs([
|
||||
{'link': '/modems.cgi', 'text': "Модемы"},
|
||||
{'text': modem_name}
|
||||
]) }}
|
||||
|
||||
{% for item in data %}
|
||||
{% set item_name = item[0] %}
|
||||
{% set item_data = item[1] %}
|
||||
<h6 class="text-primary mt-4">{{ item_name }}</h6>
|
||||
{% for k, v in item_data.items() %}
|
||||
{{ k }} = {{ v }}<br>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
@ -10,3 +10,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
ModemStatus.init({{ modems.getkeys()|tojson }});
|
||||
{% endblock %}
|
@ -1,5 +1,5 @@
|
||||
<div class="signal_level">
|
||||
{% for i in 0..4 %}
|
||||
{% for i in range(5) %}
|
||||
<div{% if i < level %} class="yes"{% endif %}></div>
|
||||
{% endfor %}
|
||||
</div>
|
Loading…
x
Reference in New Issue
Block a user