From 6d3391bc45b34d4b3a34b037b02afe503ae807a9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 27 Jan 2021 21:40:55 +0300 Subject: [PATCH] add new events, fix binsensor --- .experiment.py | 32 ++++++++++++++++ custom_components/mega/.experiment.py | 36 ------------------ custom_components/mega/binary_sensor.py | 2 +- custom_components/mega/const.py | 11 +++++- custom_components/mega/entities.py | 50 ++++++++++++++++++++++++- custom_components/mega/http.py | 7 ++-- custom_components/mega/hub.py | 4 +- custom_components/mega/tools.py | 6 ++- readme.md | 23 ++++++++++++ 9 files changed, 124 insertions(+), 47 deletions(-) create mode 100644 .experiment.py delete mode 100644 custom_components/mega/.experiment.py diff --git a/.experiment.py b/.experiment.py new file mode 100644 index 0000000..9d7532b --- /dev/null +++ b/.experiment.py @@ -0,0 +1,32 @@ +import asyncio + + +async def handle_echo(reader: asyncio.StreamReader, writer: asyncio.StreamWriter): + data = await reader.read(100) + message = data.decode() + addr = writer.get_extra_info('peername') + + print(f"Received {message!r} from {addr!r}") + + print(f"Send: {message!r}") + ans = '''HTTP/1.1 200 OK\nContent-Length: 6\n\nhello\n'''.encode() + writer.write(ans) + await writer.drain() + + print("Close the connection") + writer.transport.close() + writer.close() + await writer.wait_closed() + + +async def main(): + server = await asyncio.start_server( + handle_echo, '127.0.0.1', 8888) + + addr = server.sockets[0].getsockname() + print(f'Serving on {addr}') + + async with server: + await server.serve_forever() + +asyncio.run(main()) \ No newline at end of file diff --git a/custom_components/mega/.experiment.py b/custom_components/mega/.experiment.py deleted file mode 100644 index 485f324..0000000 --- a/custom_components/mega/.experiment.py +++ /dev/null @@ -1,36 +0,0 @@ -import asyncio -from bs4 import BeautifulSoup -import aiohttp - -host = '192.168.88.14/sec' - - - - -# page = ''' -# Back
P7/ON
ON OFF
Type
Default:
Mode
Group
-# -# Back
P7/ON
ON OFF
Type
Default:
Mode
Group
-# Back -#
-# P7/ON -#
-# ON -# OFF -#
-#
Type
Default:
Mode
Group
-# Back
P7/ON
ON OFF
Type
Default:
Mode
Group
-# Back
P7/ON
ON OFF
Type
Default:
Mode
Group
-# ''' -# tree = BeautifulSoup(page, features="lxml") -# pty = tree.find('select', attrs={'name': 'pty'}).find(selected=True)['value'] -# m = tree.find('select', attrs={'name': 'm'}) -# if m: -# m = m.find(selected=True)['value'] -# -# print(pty, m) - -if __name__ == '__main__': - asyncio.get_event_loop().run_until_complete( - scan_port(0) - ) \ No newline at end of file diff --git a/custom_components/mega/binary_sensor.py b/custom_components/mega/binary_sensor.py index 39607be..0490d20 100644 --- a/custom_components/mega/binary_sensor.py +++ b/custom_components/mega/binary_sensor.py @@ -78,7 +78,7 @@ class MegaBinarySensor(BinarySensorEntity, MegaPushEntity): if val is None and self._state is not None: return self._state == 'ON' elif val is not None: - return val == 'ON' or val == 1 + return val == 'ON' or val != 1 def _update(self, payload: dict): self.mega.values[self.port] = payload diff --git a/custom_components/mega/const.py b/custom_components/mega/const.py index 00b694a..f284702 100644 --- a/custom_components/mega/const.py +++ b/custom_components/mega/const.py @@ -31,4 +31,13 @@ PLATFORMS = [ "sensor", ] EVENT_BINARY_SENSOR = f'{DOMAIN}.sensor' -PATT_SPLIT = re.compile('[;/]') \ No newline at end of file +EVENT_BINARY = f'{DOMAIN}.binary' + +PATT_SPLIT = re.compile('[;/]') + +LONG = 'long' +RELEASE = 'release' +LONG_RELEASE = 'long_release' +PRESS = 'press' +SINGLE_CLICK = 'single' +DOUBLE_CLICK = 'double' diff --git a/custom_components/mega/entities.py b/custom_components/mega/entities.py index 270fc4b..c24e8c5 100644 --- a/custom_components/mega/entities.py +++ b/custom_components/mega/entities.py @@ -1,12 +1,14 @@ import logging import asyncio + from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_NAME from homeassistant.core import State from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.restore_state import RestoreEntity from .hub import MegaD -from .const import DOMAIN, CONF_CUSTOM, CONF_INVERT +from .const import DOMAIN, CONF_CUSTOM, CONF_INVERT, EVENT_BINARY_SENSOR, LONG, \ + LONG_RELEASE, RELEASE, PRESS, SINGLE_CLICK, DOUBLE_CLICK class BaseMegaEntity(CoordinatorEntity, RestoreEntity): @@ -111,7 +113,51 @@ class MegaPushEntity(BaseMegaEntity): self._update(value) self.async_write_ha_state() self.lg.debug(f'state after update %s', self.state) - self.is_first_update = False + if not self.entity_id.startswith('binary_sensor'): + return + ll: bool = self.mega.last_long.get(self.port, False) + if safe_int(value.get('click', 0)) == 1: + self.hass.bus.async_fire( + event_type=EVENT_BINARY_SENSOR, + event_data={ + 'entity_id': self.entity_id, + 'type': SINGLE_CLICK + } + ) + elif safe_int(value.get('click', 0)) == 2: + self.hass.bus.async_fire( + event_type=EVENT_BINARY_SENSOR, + event_data={ + 'entity_id': self.entity_id, + 'type': DOUBLE_CLICK + } + ) + elif safe_int(value.get('m', 0)) == 2: + self.mega.last_long[self.port] = True + self.hass.bus.async_fire( + event_type=EVENT_BINARY_SENSOR, + event_data={ + 'entity_id': self.entity_id, + 'type': LONG + } + ) + elif safe_int(value.get('m', 0)) == 1: + self.hass.bus.async_fire( + event_type=EVENT_BINARY_SENSOR, + event_data={ + 'entity_id': self.entity_id, + 'type': LONG_RELEASE if ll else RELEASE, + } + ) + elif safe_int(value.get('m', None)) == 0: + self.hass.bus.async_fire( + event_type=EVENT_BINARY_SENSOR, + event_data={ + 'entity_id': self.entity_id, + 'type': PRESS, + } + ) + self.mega.last_long[self.port] = False return def _update(self, payload: dict): diff --git a/custom_components/mega/http.py b/custom_components/mega/http.py index ab52f3b..6bf33e7 100644 --- a/custom_components/mega/http.py +++ b/custom_components/mega/http.py @@ -12,7 +12,7 @@ from .const import EVENT_BINARY_SENSOR, DOMAIN, CONF_RESPONSE_TEMPLATE from homeassistant.components.http import HomeAssistantView from homeassistant.core import HomeAssistant from .tools import make_ints - +from . import hub as h _LOGGER = logging.getLogger(__name__).getChild('http') @@ -37,6 +37,7 @@ class MegaView(HomeAssistantView): _LOGGER.debug('templates: %s', self.templates) async def get(self, request: Request) -> Response: + auth = False for x in self.allowed_hosts: if request.remote.startswith(x): @@ -47,7 +48,7 @@ class MegaView(HomeAssistantView): return Response(status=401) hass: HomeAssistant = request.app['hass'] - hub: 'hub.MegaD' = hass.data.get(DOMAIN).get(request.remote) # TODO: проверить какой remote + hub: 'h.MegaD' = hass.data.get(DOMAIN).get(request.remote) # TODO: проверить какой remote if hub is None and request.remote == '::1': hub = hass.data.get(DOMAIN).get('__def') if hub is None: @@ -64,6 +65,7 @@ class MegaView(HomeAssistantView): data['mega_id'] = hub.id ret = 'd' if port is not None: + hub.values[port] = data for cb in self.callbacks[hub.id][port]: cb(data) template: Template = self.templates.get(hub.id, {}).get(port) @@ -81,4 +83,3 @@ class MegaView(HomeAssistantView): await asyncio.sleep(1) await hub.updater.async_refresh() - diff --git a/custom_components/mega/hub.py b/custom_components/mega/hub.py index 0c6debb..ee13990 100644 --- a/custom_components/mega/hub.py +++ b/custom_components/mega/hub.py @@ -15,8 +15,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import Entity from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import TEMP, HUM, PATT_SPLIT, DOMAIN, CONF_HTTP, EVENT_BINARY_SENSOR -from .exceptions import CannotConnect, MqttNotConfigured -from .http import MegaView +from .exceptions import CannotConnect from .tools import make_ints TEMP_PATT = re.compile(r'temp:([01234567890\.]+)') @@ -76,6 +75,7 @@ class MegaD: self.mqtt = mqtt self.id = id self.lck = asyncio.Lock() + self.last_long = {} self._http_lck = asyncio.Lock() self._notif_lck = asyncio.Lock() self.cnd = asyncio.Condition() diff --git a/custom_components/mega/tools.py b/custom_components/mega/tools.py index d1d8876..e93b0f5 100644 --- a/custom_components/mega/tools.py +++ b/custom_components/mega/tools.py @@ -1,7 +1,9 @@ +_params = ['m', 'click', 'cnt', 'pt'] + def make_ints(d: dict): - for x in d: + for x in _params: try: - d[x] = float(d[x]) + d[x] = int(d.get(x, 0)) except (ValueError, TypeError): pass if 'm' not in d: diff --git a/readme.md b/readme.md index 52c802c..06eea34 100644 --- a/readme.md +++ b/readme.md @@ -167,6 +167,7 @@ curl -v -X GET 'http://192.168.88.1.4:8123/mega?pt=5&m=1' При каждом срабатывании `binary_sensor` так же сообщает о событии типа `mega.sensor`. События можно использовать в автоматизациях, например так: ```yaml +# Пример события с полями как есть прямо из меги - alias: some double click trigger: - platform: event @@ -187,6 +188,28 @@ curl -v -X GET 'http://192.168.88.1.4:8123/mega?pt=5&m=1' - value: текущее значение (только для mqtt) - port: номер порта +Начиная с версии 0.3.7 появилось так же событие типа mega.binary: +```yaml +# Пример события с полями как есть прямо из меги +- alias: some long click + trigger: + - platform: event + event_type: mega.binary + event_data: + entity_id: binary_sensor.some_id + type: long + action: + - service: light.toggle + entity_id: light.some_light +``` +Возможные варианты поля `type`: +- `long`: долгое нажатие +- `release`: размыкание (с гарантией что не было долгого нажатия) +- `long_release`: размыкание после долгого нажатия +- `press`: замыкание +- `single`: одинарный клик (в режиме кликов) +- `double`: двойной клик + Чтобы понять, какие события происходят, лучше всего воспользоваться панелью разработчика и подписаться на вкладке события на событие `mega.sensor`, понажимать кнопки.