Compare commits

..

15 Commits

Author SHA1 Message Date
Andrey
f09610355b add version to manifest 2021-01-31 11:46:43 +03:00
Andrey
70e182fec3 Merge remote-tracking branch 'origin/master' 2021-01-31 11:42:11 +03:00
Andrey
ef3152a086 add adc port and template rendering values 2021-01-31 11:41:27 +03:00
andvikt
9b9443864c Update readme.md 2021-01-29 17:57:33 +03:00
andvikt
b7669ac407 Update readme.md 2021-01-29 17:55:45 +03:00
andvikt
2b308a71a1 Update readme.md 2021-01-29 17:55:12 +03:00
Andrey
8f67652c0e Merge remote-tracking branch 'origin/master' 2021-01-29 09:53:49 +03:00
Andrey
f83cdaa583 event monitoring propper restarting 2021-01-29 09:53:10 +03:00
andvikt
8dfa5926ad Merge pull request #4 from Foroxon/master
Ukrainian translation updating
2021-01-29 09:09:40 +03:00
Andrey
3978ce2203 fix 1-wire updating while busy 2021-01-29 09:08:34 +03:00
Andrey
72cf516353 fix mqtt sensor 2021-01-28 12:17:38 +03:00
Andrey
3b6459468b fix mqtt sensor 2021-01-28 12:04:43 +03:00
Andrey
c36c1369aa fix binary sensors 2021-01-28 10:29:16 +03:00
Vladyslav Heneraliuk
2e025eb0c2 updated Ukrainian translation 2021-01-26 12:16:42 +02:00
Vladyslav Heneraliuk
20d5b8ff40 Merge pull request #1 from andvikt/master
Sync
2021-01-26 12:10:46 +02:00
11 changed files with 94 additions and 31 deletions

View File

@@ -16,7 +16,8 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.components import mqtt from homeassistant.components import mqtt
from homeassistant.config_entries import ConfigEntry 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, \ 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_MQTT_INPUTS, CONF_HTTP, CONF_RESPONSE_TEMPLATE, CONF_ACTION, CONF_GET_VALUE, CONF_ALLOW_HOSTS, \
CONF_CONV_TEMPLATE
from .hub import MegaD from .hub import MegaD
from .config_flow import ConfigFlow from .config_flow import ConfigFlow
from .http import MegaView from .http import MegaView
@@ -45,6 +46,7 @@ CONFIG_SCHEMA = vol.Schema(
'сообщение из меги '): cv.template, 'сообщение из меги '): cv.template,
vol.Optional(CONF_ACTION): cv.script_action, vol.Optional(CONF_ACTION): cv.script_action,
vol.Optional(CONF_GET_VALUE, default=True): bool, vol.Optional(CONF_GET_VALUE, default=True): bool,
vol.Optional(CONF_CONV_TEMPLATE): cv.template
} }
} }
} }
@@ -160,7 +162,8 @@ async def async_remove_entry(hass, entry) -> None:
if hub is None: if hub is None:
return return
_LOGGER.debug(f'remove {id}') _LOGGER.debug(f'remove {id}')
_hubs.pop(entry.entry_id) _hubs.pop(id, None)
hass.data[DOMAIN].pop(id, None)
task: asyncio.Task = _POLL_TASKS.pop(id, None) task: asyncio.Task = _POLL_TASKS.pop(id, None)
if task is not None: if task is not None:
task.cancel() task.cancel()

View File

@@ -78,7 +78,10 @@ class MegaBinarySensor(BinarySensorEntity, MegaPushEntity):
if val is None and self._state is not None: if val is None and self._state is not None:
return self._state == 'ON' return self._state == 'ON'
elif val is not None: elif val is not None:
return val == 'ON' or val != 1 if val in ['ON', 'OFF']:
return val == 'ON'
else:
return val != 1
def _update(self, payload: dict): def _update(self, payload: dict):
self.mega.values[self.port] = payload self.mega.values[self.port] = payload

View File

@@ -25,7 +25,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
vol.Optional(CONF_PORT_TO_SCAN, default=0): int, vol.Optional(CONF_PORT_TO_SCAN, default=0): int,
vol.Optional(CONF_MQTT_INPUTS, default=True): bool, vol.Optional(CONF_MQTT_INPUTS, default=True): bool,
vol.Optional(CONF_NPORTS, default=37): int, vol.Optional(CONF_NPORTS, default=37): int,
# vol.Optional(CONF_UPDATE_ALL, default=True): bool, vol.Optional(CONF_UPDATE_ALL, default=True): bool,
}, },
) )
@@ -135,7 +135,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
vol.Optional(CONF_MQTT_INPUTS, default=e.get(CONF_MQTT_INPUTS, True)): bool, 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_NPORTS, default=e.get(CONF_NPORTS, 37)): int,
vol.Optional(CONF_RELOAD, default=False): bool, vol.Optional(CONF_RELOAD, default=False): bool,
# vol.Optional(CONF_UPDATE_ALL, default=e.get(CONF_UPDATE_ALL, True)): bool, vol.Optional(CONF_UPDATE_ALL, default=e.get(CONF_UPDATE_ALL, True)): bool,
# vol.Optional(CONF_INVERT, default=''): str, # vol.Optional(CONF_INVERT, default=''): str,
}), }),
) )

View File

@@ -24,6 +24,7 @@ CONF_ACTION = 'action'
CONF_UPDATE_ALL = 'update_all' CONF_UPDATE_ALL = 'update_all'
CONF_GET_VALUE = 'get_value' CONF_GET_VALUE = 'get_value'
CONF_ALLOW_HOSTS = 'allow_hosts' CONF_ALLOW_HOSTS = 'allow_hosts'
CONF_CONV_TEMPLATE = 'conv_template'
PLATFORMS = [ PLATFORMS = [
"light", "light",
"switch", "switch",

View File

@@ -6,9 +6,27 @@ from homeassistant.const import CONF_NAME
from homeassistant.core import State from homeassistant.core import State
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
from .hub import MegaD from . import hub as h
from .const import DOMAIN, CONF_CUSTOM, CONF_INVERT, EVENT_BINARY_SENSOR, LONG, \ from .const import DOMAIN, CONF_CUSTOM, CONF_INVERT, EVENT_BINARY_SENSOR, LONG, \
LONG_RELEASE, RELEASE, PRESS, SINGLE_CLICK, DOUBLE_CLICK LONG_RELEASE, RELEASE, PRESS, SINGLE_CLICK, DOUBLE_CLICK, EVENT_BINARY
_events_on = False
_LOGGER = logging.getLogger(__name__)
async def _set_events_on():
global _events_on, _task_set_ev_on
await asyncio.sleep(10)
_LOGGER.debug('events on')
_events_on = True
def set_events_off():
global _events_on, _task_set_ev_on
_events_on = False
_task_set_ev_on = None
_task_set_ev_on = None
class BaseMegaEntity(CoordinatorEntity, RestoreEntity): class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
@@ -19,7 +37,7 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
""" """
def __init__( def __init__(
self, self,
mega: MegaD, mega: 'h.MegaD',
port: int, port: int,
config_entry: ConfigEntry = None, config_entry: ConfigEntry = None,
id_suffix=None, id_suffix=None,
@@ -90,8 +108,11 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
return self._unique_id return self._unique_id
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
global _task_set_ev_on
await super().async_added_to_hass() await super().async_added_to_hass()
self._state = await self.async_get_last_state() self._state = await self.async_get_last_state()
if self.mega.mqtt_inputs and _task_set_ev_on is None:
_task_set_ev_on = asyncio.create_task(_set_events_on())
async def get_state(self): async def get_state(self):
if self.mega.mqtt is None: if self.mega.mqtt is None:
@@ -113,12 +134,16 @@ class MegaPushEntity(BaseMegaEntity):
self._update(value) self._update(value)
self.async_write_ha_state() self.async_write_ha_state()
self.lg.debug(f'state after update %s', self.state) self.lg.debug(f'state after update %s', self.state)
if self.mega.mqtt_inputs and not _events_on:
_LOGGER.debug('skip event because events are off')
return
if not self.entity_id.startswith('binary_sensor'): if not self.entity_id.startswith('binary_sensor'):
_LOGGER.debug('skip event because not a bnary sens')
return return
ll: bool = self.mega.last_long.get(self.port, False) ll: bool = self.mega.last_long.get(self.port, False)
if safe_int(value.get('click', 0)) == 1: if safe_int(value.get('click', 0)) == 1:
self.hass.bus.async_fire( self.hass.bus.async_fire(
event_type=EVENT_BINARY_SENSOR, event_type=EVENT_BINARY,
event_data={ event_data={
'entity_id': self.entity_id, 'entity_id': self.entity_id,
'type': SINGLE_CLICK 'type': SINGLE_CLICK
@@ -126,7 +151,7 @@ class MegaPushEntity(BaseMegaEntity):
) )
elif safe_int(value.get('click', 0)) == 2: elif safe_int(value.get('click', 0)) == 2:
self.hass.bus.async_fire( self.hass.bus.async_fire(
event_type=EVENT_BINARY_SENSOR, event_type=EVENT_BINARY,
event_data={ event_data={
'entity_id': self.entity_id, 'entity_id': self.entity_id,
'type': DOUBLE_CLICK 'type': DOUBLE_CLICK
@@ -135,7 +160,7 @@ class MegaPushEntity(BaseMegaEntity):
elif safe_int(value.get('m', 0)) == 2: elif safe_int(value.get('m', 0)) == 2:
self.mega.last_long[self.port] = True self.mega.last_long[self.port] = True
self.hass.bus.async_fire( self.hass.bus.async_fire(
event_type=EVENT_BINARY_SENSOR, event_type=EVENT_BINARY,
event_data={ event_data={
'entity_id': self.entity_id, 'entity_id': self.entity_id,
'type': LONG 'type': LONG
@@ -143,7 +168,7 @@ class MegaPushEntity(BaseMegaEntity):
) )
elif safe_int(value.get('m', 0)) == 1: elif safe_int(value.get('m', 0)) == 1:
self.hass.bus.async_fire( self.hass.bus.async_fire(
event_type=EVENT_BINARY_SENSOR, event_type=EVENT_BINARY,
event_data={ event_data={
'entity_id': self.entity_id, 'entity_id': self.entity_id,
'type': LONG_RELEASE if ll else RELEASE, 'type': LONG_RELEASE if ll else RELEASE,
@@ -151,7 +176,7 @@ class MegaPushEntity(BaseMegaEntity):
) )
elif safe_int(value.get('m', None)) == 0: elif safe_int(value.get('m', None)) == 0:
self.hass.bus.async_fire( self.hass.bus.async_fire(
event_type=EVENT_BINARY_SENSOR, event_type=EVENT_BINARY,
event_data={ event_data={
'entity_id': self.entity_id, 'entity_id': self.entity_id,
'type': PRESS, 'type': PRESS,

View File

@@ -15,6 +15,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import TEMP, HUM, PATT_SPLIT, DOMAIN, CONF_HTTP, EVENT_BINARY_SENSOR from .const import TEMP, HUM, PATT_SPLIT, DOMAIN, CONF_HTTP, EVENT_BINARY_SENSOR
from .entities import set_events_off
from .exceptions import CannotConnect from .exceptions import CannotConnect
from .tools import make_ints from .tools import make_ints
@@ -109,6 +110,7 @@ class MegaD:
async def start(self): async def start(self):
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
if self.mqtt is not None: if self.mqtt is not None:
set_events_off()
self.subs = await self.mqtt.async_subscribe( self.subs = await self.mqtt.async_subscribe(
topic=f"{self.mqtt_id}/+", topic=f"{self.mqtt_id}/+",
msg_callback=self._process_msg, msg_callback=self._process_msg,
@@ -207,11 +209,13 @@ class MegaD:
def parse_response(self, ret): def parse_response(self, ret):
if ret is None: if ret is None:
raise NoPort() raise NoPort()
if 'busy' in ret:
return None
if ':' in ret: if ':' in ret:
ret = PATT_SPLIT.split(ret) ret = PATT_SPLIT.split(ret)
ret = dict([ ret = {'value': dict([
x.split(':') for x in ret if x.count(':') == 1 x.split(':') for x in ret if x.count(':') == 1
]) ])}
elif 'ON' in ret: elif 'ON' in ret:
ret = {'value': 'ON'} ret = {'value': 'ON'}
elif 'OFF' in ret: elif 'OFF' in ret:
@@ -227,13 +231,16 @@ class MegaD:
""" """
self.lg.debug(f'get port %s', port) self.lg.debug(f'get port %s', port)
if self.mqtt is None or force_http: if self.mqtt is None or force_http:
ret = await self.request(pt=port, cmd=http_cmd) if http_cmd == 'list':
ret = self.parse_response(ret) await self.request(pt=port, cmd='conv')
self.lg.debug('parsed: %s', ret)
if http_cmd == 'list' and isinstance(ret, dict) and 'value' in ret:
await asyncio.sleep(1) await asyncio.sleep(1)
ret = await self.request(pt=port, http_cmd=http_cmd) ret = self.parse_response(await self.request(pt=port, cmd=http_cmd))
ret = self.parse_response(ret) ntry = 0
while http_cmd == 'list' and ret is None and ntry < 3:
await asyncio.sleep(1)
ret = self.parse_response(await self.request(pt=port, cmd=http_cmd))
ntry += 1
self.lg.debug('parsed: %s', ret)
self.values[port] = ret self.values[port] = ret
return ret return ret
@@ -252,7 +259,7 @@ class MegaD:
except asyncio.TimeoutError: except asyncio.TimeoutError:
self.lg.error(f'timeout when getting port {port}') self.lg.error(f'timeout when getting port {port}')
async def get_all_ports(self): async def get_all_ports(self, only_out=False):
if not self.mqtt_inputs: if not self.mqtt_inputs:
ret = await self.request(cmd='all') ret = await self.request(cmd='all')
for port, x in enumerate(ret.split(';')): for port, x in enumerate(ret.split(';')):
@@ -369,6 +376,9 @@ class MegaD:
m = m.find(selected=True)['value'] m = m.find(selected=True)['value']
self._scanned[port] = (pty, m) self._scanned[port] = (pty, m)
return pty, m return pty, m
elif pty == '2':
self._scanned[port] = (pty, '0')
return pty, '0'
async def scan_ports(self, nports=37): async def scan_ports(self, nports=37):
for x in range(0, nports+1): for x in range(0, nports+1):
@@ -384,7 +394,7 @@ class MegaD:
ret['binary_sensor'][port].append({}) ret['binary_sensor'][port].append({})
elif pty == "1" and (m in ['0', '1', '3'] or m is None): elif pty == "1" and (m in ['0', '1', '3'] or m is None):
ret['light'][port].append({'dimmer': m == '1'}) ret['light'][port].append({'dimmer': m == '1'})
elif pty == '3': elif pty in ('3', '2'):
try: try:
http_cmd = 'get' http_cmd = 'get'
values = await self.get_port(port, force_http=True) values = await self.get_port(port, force_http=True)

View File

@@ -14,5 +14,6 @@
"codeowners": [ "codeowners": [
"@andvikt" "@andvikt"
], ],
"issue_tracker": "https://github.com/andvikt/mega_hacs/issues" "issue_tracker": "https://github.com/andvikt/mega_hacs/issues",
"version": "v0.3.11"
} }

View File

@@ -16,8 +16,9 @@ from homeassistant.const import (
CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, CONF_TYPE, CONF_UNIT_OF_MEASUREMENT,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.template import Template
from .entities import MegaPushEntity from .entities import MegaPushEntity
from .const import CONF_KEY, TEMP, HUM, W1, W1BUS from .const import CONF_KEY, TEMP, HUM, W1, W1BUS, CONF_CONV_TEMPLATE
from .hub import MegaD from .hub import MegaD
import re import re
@@ -148,6 +149,8 @@ class Mega1WSensor(MegaPushEntity):
if self.key: if self.key:
try: try:
ret = self.mega.values.get(self.port, {}) ret = self.mega.values.get(self.port, {})
if isinstance(ret, dict):
ret = ret.get('value', {})
if isinstance(ret, dict): if isinstance(ret, dict):
ret = ret.get(self.key) ret = ret.get(self.key)
except: except:
@@ -162,6 +165,10 @@ class Mega1WSensor(MegaPushEntity):
ret = str(ret) ret = str(ret)
except: except:
ret = None ret = None
tmpl: Template = self.customize.get(CONF_CONV_TEMPLATE)
if tmpl is not None and self.hass is not None:
tmpl.hass = self.hass
ret = tmpl.async_render({'value': ret})
return ret return ret
@property @property

View File

@@ -15,7 +15,7 @@ from homeassistant.const import (
CONF_DOMAIN, CONF_DOMAIN,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .entities import MegaD from . import hub as h
from .entities import MegaOutPort from .entities import MegaOutPort
from .const import CONF_DIMMER, CONF_SWITCH, DOMAIN, CONF_CUSTOM, CONF_SKIP from .const import CONF_DIMMER, CONF_SWITCH, DOMAIN, CONF_CUSTOM, CONF_SKIP
@@ -45,7 +45,7 @@ async def async_setup_platform(hass, config, add_entities, discovery_info=None):
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_devices): async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_devices):
mid = config_entry.data[CONF_ID] mid = config_entry.data[CONF_ID]
hub: MegaD = hass.data['mega'][mid] hub: 'h.MegaD' = hass.data['mega'][mid]
devices = [] devices = []
customize = hass.data.get(DOMAIN, {}).get(CONF_CUSTOM, {}) customize = hass.data.get(DOMAIN, {}).get(CONF_CUSTOM, {})

View File

@@ -17,8 +17,11 @@
"username": "Користувач", "username": "Користувач",
"id": "ID", "id": "ID",
"mqtt_id": "MQTT id", "mqtt_id": "MQTT id",
"scan_interval": "Період оновлення (сек.)", "scan_interval": "Період оновлення (сек.), 0 - не оновлювати",
"port_to_scan": "Порт для сканування при відсутності датчиків" "port_to_scan": "Порт для сканування при відсутності датчиків",
"mqtt_inputs": "Використовувати MQTT",
"nports": "Кількість портів",
"update_all": "Оновити всі виходи коли спрацьовує вхід"
} }
} }
} }
@@ -30,7 +33,10 @@
"scan_interval": "Період оновлення (сек.)", "scan_interval": "Період оновлення (сек.)",
"port_to_scan": "Порт для сканування при відсутності датчиків", "port_to_scan": "Порт для сканування при відсутності датчиків",
"reload": "Оновити об'єкти", "reload": "Оновити об'єкти",
"invert": "Список портів з інвертованою логікою (через ,)" "invert": "Список портів з інвертованою логікою (через ,)",
"mqtt_inputs": "Використовувати MQTT",
"nports": "Кількість портів",
"update_all": "Оновити всі виходи коли спрацьовує вхід"
} }
} }
} }

View File

@@ -1,4 +1,8 @@
# MegaD HomeAssistant integration # MegaD HomeAssistant integration
[![hacs_badge](https://img.shields.io/badge/HACS-Custom-orange.svg)](https://github.com/custom-components/hacs)
[![Donate](https://img.shields.io/badge/donate-Yandex-red.svg)](https://yoomoney.ru/to/410013955329136)
Интеграция с [MegaD-2561](https://www.ab-log.ru/smart-house/ethernet/megad-2561) Интеграция с [MegaD-2561](https://www.ab-log.ru/smart-house/ethernet/megad-2561)
@@ -53,6 +57,9 @@ mega:
unit_of_measurement: unit_of_measurement:
hum: "%" # если датчиков несколько, то можно указывать юниты по их ключам hum: "%" # если датчиков несколько, то можно указывать юниты по их ключам
temp: "°C" temp: "°C"
# можно так же указать шаблон для конвертации значения, может быть полезно для ацп-входа
# текущее значение порта передается в шаблон в переменной "value"
conv_template: "{{(value|float)/100}}"
14: 14:
name: какой-то датчик name: какой-то датчик
unit_of_measurement: "°C" # если датчик один, то просто строчкой unit_of_measurement: "°C" # если датчик один, то просто строчкой