Compare commits

...

7 Commits

Author SHA1 Message Date
Andrey
fa2bb2674e fix updater 2021-03-04 21:52:25 +03:00
Andrey
a1ae4e294b fix pop 2021-03-04 21:28:01 +03:00
Andrey
81d85ba1ed remove crap entities 2021-03-04 20:45:42 +03:00
Andrey
7135bb273e add sync_time 2021-03-04 20:25:59 +03:00
Andrey
7d8554a7aa add force_i2c, add new port namings 2021-03-04 19:45:37 +03:00
Andrey
40dcadc109 fix ptsensor 2021-03-04 16:52:07 +03:00
Andrey
35f99877ca add delay on ptsensor 2021-03-04 16:49:02 +03:00
14 changed files with 99 additions and 60 deletions

View File

@@ -16,7 +16,7 @@ from homeassistant.components import mqtt
from homeassistant.config_entries import ConfigEntry
from .const import DOMAIN, CONF_INVERT, CONF_RELOAD, PLATFORMS, CONF_PORTS, CONF_CUSTOM, CONF_SKIP, CONF_PORT_TO_SCAN, \
CONF_MQTT_INPUTS, CONF_HTTP, CONF_RESPONSE_TEMPLATE, CONF_ACTION, CONF_GET_VALUE, CONF_ALLOW_HOSTS, \
CONF_CONV_TEMPLATE, CONF_ALL, CONF_FORCE_D, CONF_DEF_RESPONSE
CONF_CONV_TEMPLATE, CONF_ALL, CONF_FORCE_D, CONF_DEF_RESPONSE, CONF_FORCE_I2C_SCAN
from .hub import MegaD
from .config_flow import ConfigFlow
from .http import MegaView
@@ -46,6 +46,7 @@ CUSTOMIZE_PORT = {
vol.Optional(CONF_GET_VALUE, default=True): bool,
vol.Optional(CONF_CONV_TEMPLATE): cv.template,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_FORCE_I2C_SCAN): bool,
}
CUSTOMIZE_DS2413 = {
vol.Optional(str.lower, description='адрес и индекс устройства'): CUSTOMIZE_PORT

View File

@@ -57,6 +57,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
continue
hub.lg.debug(f'add binary_sensor on port %s', port)
sensor = MegaBinarySensor(mega=hub, port=port, config_entry=config_entry)
if '<' in sensor.name:
continue
devices.append(sensor)
async_add_devices(devices)

View File

@@ -11,7 +11,8 @@ from homeassistant.const import CONF_HOST, CONF_ID, CONF_PASSWORD, CONF_SCAN_INT
from homeassistant.core import callback, HomeAssistant
from .const import DOMAIN, CONF_PORT_TO_SCAN, CONF_RELOAD, PLATFORMS, CONF_MQTT_INPUTS, \
CONF_NPORTS, CONF_UPDATE_ALL, CONF_POLL_OUTS, CONF_FAKE_RESPONSE, CONF_FORCE_D, \
CONF_ALLOW_HOSTS, CONF_PROTECTED, CONF_RESTORE_ON_RESTART # pylint:disable=unused-import
CONF_ALLOW_HOSTS, CONF_PROTECTED, CONF_RESTORE_ON_RESTART, CONF_UPDATE_TIME, \
REMOVE_CONFIG # pylint:disable=unused-import
from .hub import MegaD
from . import exceptions
@@ -24,7 +25,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
vol.Required(CONF_PASSWORD, default="sec"): str,
vol.Optional(CONF_SCAN_INTERVAL, default=0): int,
vol.Optional(CONF_POLL_OUTS, default=False): bool,
vol.Optional(CONF_PORT_TO_SCAN, default=0): int,
# vol.Optional(CONF_PORT_TO_SCAN, default=0): int,
vol.Optional(CONF_MQTT_INPUTS, default=False): bool,
vol.Optional(CONF_NPORTS, default=37): int,
vol.Optional(CONF_UPDATE_ALL, default=True): bool,
@@ -33,6 +34,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
vol.Optional(CONF_RESTORE_ON_RESTART, default=True): bool,
vol.Optional(CONF_PROTECTED, default=True): bool,
vol.Optional(CONF_ALLOW_HOSTS, default='::1;127.0.0.1'): str,
vol.Optional(CONF_UPDATE_TIME, default=True): bool,
},
)
@@ -82,6 +84,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
await hub.stop()
hub.lg.debug(f'config loaded: %s', config)
config.update(user_input)
config['new_naming'] = True
return self.async_create_entry(
title=user_input.get(CONF_ID, user_input[CONF_HOST]),
data=config,
@@ -113,6 +116,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
async def async_step_init(self, user_input=None):
"""Manage the options."""
new_naming = self.config_entry.data.get('new_naming', False)
hub = await get_hub(self.hass, self.config_entry.data)
if user_input is not None:
reload = user_input.pop(CONF_RELOAD)
@@ -128,7 +132,11 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
cfg = dict(self.config_entry.data)
for x in PLATFORMS:
cfg.pop(x, None)
for x in REMOVE_CONFIG:
cfg.pop(x, None)
cfg.update(new)
cfg['new_naming'] = new_naming
return self.async_create_entry(
title='',
data=cfg,
@@ -139,7 +147,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
data_schema=vol.Schema({
vol.Optional(CONF_SCAN_INTERVAL, default=e.get(CONF_SCAN_INTERVAL, 0)): int,
vol.Optional(CONF_POLL_OUTS, default=e.get(CONF_POLL_OUTS, False)): bool,
vol.Optional(CONF_PORT_TO_SCAN, default=e.get(CONF_PORT_TO_SCAN, 0)): int,
# vol.Optional(CONF_PORT_TO_SCAN, default=e.get(CONF_PORT_TO_SCAN, 0)): int,
vol.Optional(CONF_MQTT_INPUTS, default=e.get(CONF_MQTT_INPUTS, True)): bool,
vol.Optional(CONF_NPORTS, default=e.get(CONF_NPORTS, 37)): int,
vol.Optional(CONF_RELOAD, default=False): bool,
@@ -149,6 +157,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
vol.Optional(CONF_RESTORE_ON_RESTART, default=e.get(CONF_RESTORE_ON_RESTART, False)): bool,
vol.Optional(CONF_PROTECTED, default=e.get(CONF_PROTECTED, True)): bool,
vol.Optional(CONF_ALLOW_HOSTS, default='::1;127.0.0.1'): str,
vol.Optional(CONF_UPDATE_TIME, default=e.get(CONF_UPDATE_TIME, False)): bool,
# vol.Optional(CONF_INVERT, default=''): str,
}),
)

View File

@@ -34,6 +34,8 @@ CONF_DEF_RESPONSE = 'def_response'
CONF_RESTORE_ON_RESTART = 'restore_on_restart'
CONF_CLICK_TIME = 'click_time'
CONF_LONG_TIME = 'long_time'
CONF_FORCE_I2C_SCAN = 'force_i2c_scan'
CONF_UPDATE_TIME = 'update_time'
PLATFORMS = [
"light",
"switch",
@@ -53,4 +55,15 @@ LUX = 'lux'
SINGLE_CLICK = 'single'
DOUBLE_CLICK = 'double'
PATT_FW = re.compile(r'fw:\s(.+)\)')
PATT_FW = re.compile(r'fw:\s(.+)\)')
REMOVE_CONFIG = [
'extenders',
'ext_in',
'ext_acts',
'i2c_sensors',
'binary_sensor',
'light',
'i2c',
'sensor',
]

View File

@@ -60,7 +60,8 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
self._lg = None
self._unique_id = unique_id or f"mega_{mega.id}_{port}" + \
(f"_{id_suffix}" if id_suffix else "")
self._name = name or f"{mega.id}_{port}" + \
_pt = port if not mega.new_naming else f'{port:02}'
self._name = name or f"{mega.id}_{_pt}" + \
(f"_{id_suffix}" if id_suffix else "")
self._customize: dict = None
self.index = index
@@ -68,6 +69,13 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
if self.http_cmd == 'ds2413':
self.mega.ds2413_ports |= {self.port}
@property
def enabled(self):
if '<' in self.name:
return False
else:
return super().enabled
@property
def customize(self):
if self.hass is None:
@@ -85,6 +93,7 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
@property
def device_info(self):
_pt = self.port if not self.mega.new_naming else f'{self.port:02}'
return {
"identifiers": {
# Serial numbers are unique identifiers within a specific domain
@@ -93,7 +102,7 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
"config_entries": [
self.config_entry,
],
"name": f'{self._mega_id} port {self.port}',
"name": f'{self._mega_id} port {_pt}',
"manufacturer": 'ab-log.ru',
# "model": self.light.productname,
"sw_version": self.mega.fw,
@@ -114,7 +123,8 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
def name(self):
c = self.customize.get(CONF_NAME)
if not isinstance(c, str):
c = self._name or f"{self.mega.id}_p{self.port}"
_pt = self.port if not self.mega.new_naming else f'{self.port:02}'
c = self._name or f"{self.mega.id}_p{_pt}"
return c
@property
@@ -147,6 +157,8 @@ class MegaPushEntity(BaseMegaEntity):
def __update(self, value: dict):
self._update(value)
if self.hass is None:
return
self.async_write_ha_state()
self.lg.debug(f'state after update %s', self.state)
if self.mega.mqtt_inputs and not _events_on:

View File

@@ -20,7 +20,7 @@ from .config_parser import parse_config, DS2413, MCP230, MCP230_OUT, MCP230_IN,
from .const import (
TEMP, HUM, PRESS,
LUX, PATT_SPLIT, DOMAIN,
CONF_HTTP, EVENT_BINARY_SENSOR, CONF_CUSTOM, CONF_FORCE_D, CONF_DEF_RESPONSE, PATT_FW
CONF_HTTP, EVENT_BINARY_SENSOR, CONF_CUSTOM, CONF_FORCE_D, CONF_DEF_RESPONSE, PATT_FW, CONF_FORCE_I2C_SCAN
)
from .entities import set_events_off, BaseMegaEntity, MegaOutPort
from .exceptions import CannotConnect, NoPort
@@ -85,6 +85,8 @@ class MegaD:
ext_in=None,
ext_acts=None,
i2c_sensors=None,
new_naming=False,
update_time=False,
**kwargs,
):
"""Initialize."""
@@ -99,10 +101,12 @@ class MegaD:
self.http.hubs[mqtt_id] = self
else:
self.http = None
self.new_naming = new_naming
self.extenders = extenders or []
self.ext_in = ext_in or {}
self.ext_act = ext_acts or {}
self.i2c_sensors = i2c_sensors or []
self._update_time = update_time
self.poll_outs = poll_outs
self.update_all = update_all if update_all is not None else True
self.nports = nports
@@ -248,6 +252,8 @@ class MegaD:
Polling ports
"""
self.lg.debug('poll')
if self._update_time:
await self.update_time()
for x in self.i2c_sensors:
if not isinstance(x, dict):
continue
@@ -267,8 +273,8 @@ class MegaD:
await self.get_all_ports(check_skip=True)
elif len(self.sensors) > 0:
await self.get_sensors()
else:
await self.get_port(self.port_to_scan)
# else:
# await self.get_port(self.port_to_scan)
await self._get_ds2413()
return self.values
@@ -464,45 +470,6 @@ class MegaD:
async def scan_port(self, port):
data = await self.request(pt=port)
return parse_config(data)
# async with self.lck:
# if port in self._scanned:
# return self._scanned[port]
# url = f'http://{self.host}/{self.sec}/?pt={port}'
# self.lg.debug(
# f'scan port %s: %s', port, url
# )
# async with aiohttp.request('get', url) as req:
# html = await req.text()
# if req.status != 200:
# return
# tree = BeautifulSoup(html, features="lxml")
# pty = tree.find('select', attrs={'name': 'pty'})
# if pty is None:
# return
# else:
# pty = pty.find(selected=True)
# if pty:
# pty = pty['value']
# else:
# return
# if pty in ['0', '1']:
# m = tree.find('select', attrs={'name': 'm'})
# if m:
# m = m.find(selected=True)['value']
# self._scanned[port] = (pty, m)
# return pty, m
# elif pty == '3':
# m = tree.find('select', attrs={'name': 'd'})
# if m:
# m = m.find(selected=True)['value']
# self._scanned[port] = (pty, m)
# return pty, m
# elif pty in ('2', '4'): # эта часть не очень проработана, тут есть i2c который может работать неправильно
# m = tree.find('select', attrs={'name': 'd'})
# if m:
# m = m.find(selected=True)['value']
# self._scanned[port] = (pty, m or '0')
# return pty, m or '0'
async def scan_ports(self, nports=37):
for x in range(0, nports+1):
@@ -530,10 +497,16 @@ class MegaD:
:return:
"""
_params = tuple(params.items())
delay = None
if 'delay' in params:
delay = params.pop('delay')
ret = {
_params: await self.request(**params)
}
self.lg.debug('i2c response: %s', ret)
if delay:
self.lg.debug('delay %s', delay)
await asyncio.sleep(delay)
return ret
async def get_config(self, nports=37):
@@ -544,6 +517,9 @@ class MegaD:
ret['ext_acts'] = ext_acts = {}
ret['i2c_sensors'] = i2c_sensors = []
async for port, cfg in self.scan_ports(nports):
_cust = self.customize.get(port)
if not isinstance(_cust, dict):
_cust = {}
if cfg.pty == "0":
ret['binary_sensor'][port].append({})
elif cfg.pty == "1" and (cfg.m in ['0', '1', '3'] or cfg.m is None):
@@ -582,7 +558,7 @@ class MegaD:
values = values.split(';')
for n in range(len(values)):
ret['light'][f'{port}e{n}'].append({'dimmer': True, 'dimmer_scale': 16})
elif cfg.pty == '4' and cfg.gr == '0':
elif cfg.pty == '4' and (cfg.gr == '0' or _cust.get(CONF_FORCE_I2C_SCAN)):
# i2c в режиме ANY
scan = cfg.src.find('a', text='I2C Scan')
self.lg.debug(f'find scan link: %s', scan)
@@ -638,4 +614,8 @@ class MegaD:
else:
await x.async_turn_off()
async def update_time(self):
await self.request(
cf=7,
stime=datetime.now().strftime('%H:%M:%S')
)

View File

@@ -1,3 +1,4 @@
from dataclasses import dataclass, field
from urllib.parse import parse_qsl, urlparse
from bs4 import BeautifulSoup
from homeassistant.const import (
@@ -27,6 +28,12 @@ def parse_scan_page(page: str):
elif c is Request:
req.append(params)
continue
elif isinstance(c, Request):
if c.delay:
params = params.copy()
params['delay'] = c.delay
req.append(params)
continue
elif isinstance(c, tuple):
suffix, c = c
elif isinstance(c, str):
@@ -55,8 +62,9 @@ class Skip:
pass
@dataclass
class Request:
pass
delay: float = None
i2c_classes = {
@@ -92,7 +100,8 @@ i2c_classes = {
('object', DEVICE_CLASS_TEMPERATURE),
],
'ptsensor': [
Request, # запрос на измерение
Skip,
Request(delay=1), # запрос на измерение
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
],

View File

@@ -69,6 +69,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
for data in cfg:
hub.lg.debug(f'add light on port %s with data %s', port, data)
light = MegaLight(mega=hub, port=port, config_entry=config_entry, **data)
if '<' in light.name:
continue
devices.append(light)
async_add_devices(devices)

View File

@@ -94,6 +94,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
config_entry=config_entry,
**data,
)
if '<' in sensor.name:
continue
devices.append(sensor)
async_add_devices(devices)

View File

@@ -20,7 +20,9 @@
"protected": "[%key:common::config_flow::data::protected%]",
"allow_hosts": "[%key:common::config_flow::data::allow_hosts%]",
"restore_on_restart": "[%key:common::config_flow::data::restore_on_restart%]",
"poll_outs": "[%key:common::config_flow::data::poll_outs%]"
"poll_outs": "[%key:common::config_flow::data::poll_outs%]",
"update_time": "[%key:common::config_flow::data::update_time%]"
}
}
},

View File

@@ -58,6 +58,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
for data in cfg:
hub.lg.debug(f'add switch on port %s with data %s', port, data)
light = MegaSwitch(mega=hub, port=port, config_entry=config_entry, **data)
if '<' in light.name:
continue
devices.append(light)
async_add_devices(devices)

View File

@@ -28,7 +28,8 @@
"protected": "Protected",
"allow_hosts": "Allowed hosts",
"restore_on_restart": "Restore outs on restart",
"poll_outs": "Poll outs"
"poll_outs": "Poll outs",
"update_time": "Sync time"
}
}
}
@@ -47,7 +48,8 @@
"protected": "Protected",
"allow_hosts": "Allowed hosts",
"restore_on_restart": "Restore outs on restart",
"poll_outs": "Poll outs"
"poll_outs": "Poll outs",
"update_time": "Sync time"
}
}
}

View File

@@ -27,7 +27,8 @@
"protected": "Блокировать неразрешенные соединения",
"restore_on_restart": "Восстанавливать выходы при перезагрузке",
"allow_hosts": "Разрешенные ip (через ;)",
"poll_outs": "Обновлять выходы (регулярно)"
"poll_outs": "Обновлять выходы (регулярно)",
"update_time": "Синхронизировать время"
}
}
}
@@ -48,7 +49,8 @@
"protected": "Блокировать неразрешенные соединения",
"allow_hosts": "Разрешенные ip (через ;)",
"restore_on_restart": "Восстанавливать выходы при перезагрузке",
"poll_outs": "Обновлять выходы (регулярно)"
"poll_outs": "Обновлять выходы (регулярно)",
"update_time": "Синхронизировать время"
}
}
}

View File

@@ -27,7 +27,8 @@
"protected": "Блокувати недозволені з'єднання",
"allow_hosts": "Дозволені ip (через ;)",
"restore_on_restart": "Відновлювати виходи при перезавантаженні",
"poll_outs": "Оновити виходи"
"poll_outs": "Оновити виходи",
"update_time": "Осинхронізувати час"
}
}
}