Compare commits

...

8 Commits

Author SHA1 Message Date
Andrey
012d12437b hotfix 2021-01-14 21:40:19 +03:00
Andrey
7063575957 hotfix 2021-01-14 21:35:15 +03:00
Andrey
6a43198d81 hotfix 2021-01-14 21:33:44 +03:00
Andrey
242386bfe8 edit readme 2021-01-14 21:25:48 +03:00
Andrey
34d31d2879 edit readme 2021-01-14 21:07:30 +03:00
Andrey
6a02a7e98c башфикс сервисов, ускорение загрузки 2021-01-14 20:46:05 +03:00
Andrey
d6191ba46a fix services 2021-01-05 09:02:35 +03:00
Andrey
f19d3daeff add yaml deprecation 2021-01-04 09:59:44 +03:00
8 changed files with 126 additions and 163 deletions

View File

@@ -1,13 +1,11 @@
"""The mega integration.""" """The mega integration."""
import asyncio import asyncio
import logging import logging
import typing
from functools import partial from functools import partial
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PLATFORM, CONF_SCAN_INTERVAL, CONF_ID from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_ID
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.service import bind_hass from homeassistant.helpers.service import bind_hass
from homeassistant.components import mqtt from homeassistant.components import mqtt
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@@ -52,18 +50,18 @@ async def async_setup(hass: HomeAssistant, config: dict):
conf = config.get(DOMAIN) conf = config.get(DOMAIN)
hass.data[DOMAIN] = {} hass.data[DOMAIN] = {}
hass.services.async_register( hass.services.async_register(
DOMAIN, 'save', _save_service, schema=vol.Schema({ DOMAIN, 'save', partial(_save_service, hass), schema=vol.Schema({
vol.Optional('mega_id'): str vol.Optional('mega_id'): str
}) })
) )
hass.services.async_register( hass.services.async_register(
DOMAIN, 'get_port', _get_port, schema=vol.Schema({ DOMAIN, 'get_port', partial(_get_port, hass), schema=vol.Schema({
vol.Optional('mega_id'): str, vol.Optional('mega_id'): str,
vol.Optional('port'): int, vol.Optional('port'): int,
}) })
) )
hass.services.async_register( hass.services.async_register(
DOMAIN, 'run_cmd', _run_cmd, schema=vol.Schema({ DOMAIN, 'run_cmd', partial(_run_cmd, hass), schema=vol.Schema({
vol.Required('port'): int, vol.Required('port'): int,
vol.Required('cmd'): str, vol.Required('cmd'): str,
vol.Optional('mega_id'): str, vol.Optional('mega_id'): str,
@@ -74,9 +72,10 @@ async def async_setup(hass: HomeAssistant, config: dict):
if CONF_HOST in conf: if CONF_HOST in conf:
conf = {DEF_ID: conf} conf = {DEF_ID: conf}
for id, data in conf.items(): for id, data in conf.items():
_LOGGER.warning('YAML configuration is deprecated, please use web-interface')
await _add_mega(hass, id, data) await _add_mega(hass, id, data)
for id, hub in hass.data[DOMAIN].__items__(): for id, hub in hass.data[DOMAIN].items():
_POLL_TASKS[id] = asyncio.create_task(hub.poll()) _POLL_TASKS[id] = asyncio.create_task(hub.poll())
return True return True
@@ -133,8 +132,8 @@ async def async_remove_entry(hass, entry) -> None:
unsub() unsub()
@bind_hass async def _save_service(hass: HomeAssistant, call: ServiceCall):
async def _save_service(hass: HomeAssistant, mega_id=None): mega_id = call.data.get('mega_id')
if mega_id: if mega_id:
hub: MegaD = hass.data[DOMAIN][mega_id] hub: MegaD = hass.data[DOMAIN][mega_id]
await hub.save() await hub.save()
@@ -144,7 +143,9 @@ async def _save_service(hass: HomeAssistant, mega_id=None):
@bind_hass @bind_hass
async def _get_port(hass: HomeAssistant, port=None, mega_id=None): async def _get_port(hass: HomeAssistant, call: ServiceCall):
port = call.data.get('port')
mega_id = call.data.get('mega_id')
if mega_id: if mega_id:
hub: MegaD = hass.data[DOMAIN][mega_id] hub: MegaD = hass.data[DOMAIN][mega_id]
if port is None: if port is None:
@@ -158,8 +159,12 @@ async def _get_port(hass: HomeAssistant, port=None, mega_id=None):
else: else:
await hub.get_port(port) await hub.get_port(port)
@bind_hass @bind_hass
async def _run_cmd(hass: HomeAssistant, port: int, cmd: str, mega_id=None): async def _run_cmd(hass: HomeAssistant, call: ServiceCall):
port = call.data.get('port')
mega_id = call.data.get('mega_id')
cmd = call.data.get('cmd')
if mega_id: if mega_id:
hub: MegaD = hass.data[DOMAIN][mega_id] hub: MegaD = hass.data[DOMAIN][mega_id]
await hub.send_command(port=port, cmd=cmd) await hub.send_command(port=port, cmd=cmd)

View File

@@ -1,6 +1,4 @@
"""Platform for light integration.""" """Platform for light integration."""
import asyncio
import json
import logging import logging
import voluptuous as vol import voluptuous as vol
@@ -18,7 +16,6 @@ from homeassistant.const import (
CONF_ID CONF_ID
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.restore_state import RestoreEntity
from .entities import BaseMegaEntity from .entities import BaseMegaEntity
from .hub import MegaD from .hub import MegaD
@@ -63,13 +60,16 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
mid = config_entry.data[CONF_ID] mid = config_entry.data[CONF_ID]
hub: MegaD = hass.data['mega'][mid] hub: MegaD = hass.data['mega'][mid]
devices = [] devices = []
async for port, pty, m in hub.scan_ports():
if pty == "0":
sensor = MegaBinarySensor(mega_id=mid, port=port)
devices.append(sensor)
async_add_devices(devices) async def scan():
async for port, pty, m in hub.scan_ports():
if pty == "0":
sensor = MegaBinarySensor(mega_id=mid, port=port)
devices.append(sensor)
async_add_devices(devices)
await scan()
class MegaBinarySensor(BinarySensorEntity, BaseMegaEntity): class MegaBinarySensor(BinarySensorEntity, BaseMegaEntity):

View File

@@ -9,7 +9,7 @@ from homeassistant.components import mqtt
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PASSWORD, CONF_SCAN_INTERVAL from homeassistant.const import CONF_HOST, CONF_ID, CONF_PASSWORD, CONF_SCAN_INTERVAL
from homeassistant.core import callback from homeassistant.core import callback
from .const import DOMAIN, CONF_PORT_TO_SCAN, CONF_RELOAD, CONF_INVERT # pylint:disable=unused-import from .const import DOMAIN, CONF_PORT_TO_SCAN, CONF_RELOAD # pylint:disable=unused-import
from .hub import MegaD from .hub import MegaD
from . import exceptions from . import exceptions

View File

@@ -140,36 +140,45 @@ class MegaD:
async def save(self): async def save(self):
await self.send_command(cmd='s') await self.send_command(cmd='s')
async def get_port(self, port, get_value=False): async def get_port(self, port):
if get_value: """
ftr = asyncio.get_event_loop().create_future() Опрашивает порт с помощью mqtt. Ждет ответ, возвращает ответ.
def cb(msg): :param port:
try: :return:
ftr.set_result(json.loads(msg.payload).get('value')) """
except Exception as exc: ftr = asyncio.get_event_loop().create_future()
self.lg.warning(f'could not parse {msg.payload}: {exc}')
ftr.set_result(None)
unsub = await self.mqtt.async_subscribe(
topic=f'{self.mqtt_id}/{port}',
msg_callback=cb,
qos=1,
)
self.lg.debug( def cb(msg):
f'get port: %s', port
)
async with self.lck:
await self.mqtt.async_publish(
topic=f'{self.mqtt_id}/cmd',
payload=f'get:{port}',
qos=0,
retain=False,
)
await asyncio.sleep(0.1)
if get_value:
try: try:
if '"value":NA' in msg.payload.decode():
ftr.set_result(None)
return
ret = json.loads(msg.payload).get('value')
ftr.set_result(ret)
except Exception as exc:
ret = None
self.lg.exception(f'while parsing response from port {port}: {msg.payload}')
ftr.set_result(None)
self.lg.debug(
f'port: %s response: %s', port, ret
)
async with self.lck:
try:
unsub = await self.mqtt.async_subscribe(
topic=f'{self.mqtt_id}/{port}',
msg_callback=cb,
qos=1,
)
await self.mqtt.async_publish(
topic=f'{self.mqtt_id}/cmd',
payload=f'get:{port}',
qos=1,
retain=False,
)
return await asyncio.wait_for(ftr, timeout=2) return await asyncio.wait_for(ftr, timeout=2)
except asyncio.TimeoutError: except asyncio.TimeoutError:
self.lg.warning(f'timeout on port {port}') self.lg.warning(f'timeout on port {port}')
@@ -259,7 +268,8 @@ class MegaD:
return pty, m return pty, m
async def scan_ports(self,): async def scan_ports(self,):
for x in range(38): async with self.lck:
ret = await self.scan_port(x) for x in range(38):
if ret: ret = await self.scan_port(x)
yield [x, *ret] if ret:
yield [x, *ret]

View File

@@ -1,6 +1,4 @@
"""Platform for light integration.""" """Platform for light integration."""
import asyncio
import json
import logging import logging
import voluptuous as vol import voluptuous as vol
@@ -19,7 +17,6 @@ from homeassistant.const import (
CONF_ID CONF_ID
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.restore_state import RestoreEntity
from .entities import BaseMegaEntity from .entities import BaseMegaEntity
from .hub import MegaD from .hub import MegaD
@@ -80,11 +77,15 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
mid = config_entry.data[CONF_ID] mid = config_entry.data[CONF_ID]
hub: MegaD = hass.data['mega'][mid] hub: MegaD = hass.data['mega'][mid]
devices = [] devices = []
async for port, pty, m in hub.scan_ports():
if pty == "1" and m in ['0', '1']: async def scan_ports():
light = MegaLight(mega_id=mid, port=port, dimmer=m == '1') async for port, pty, m in hub.scan_ports():
devices.append(light) if pty == "1" and m in ['0', '1']:
async_add_devices(devices) light = MegaLight(mega_id=mid, port=port, dimmer=m == '1')
devices.append(light)
async_add_devices(devices)
await scan_ports()
class MegaLight(LightEntity, BaseMegaEntity): class MegaLight(LightEntity, BaseMegaEntity):

View File

@@ -1,7 +1,5 @@
"""Platform for light integration.""" """Platform for light integration."""
import logging import logging
import typing
import voluptuous as vol import voluptuous as vol
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
@@ -87,29 +85,33 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
mid = config_entry.data[CONF_ID] mid = config_entry.data[CONF_ID]
hub: MegaD = hass.data['mega'][mid] hub: MegaD = hass.data['mega'][mid]
devices = [] devices = []
async for port, pty, m in hub.scan_ports():
if pty == "3":
values = await hub.get_port(port, get_value=True)
lg.debug(f'values: %s', values)
if values is None:
continue
if isinstance(values, str) and TEMP_PATT.search(values):
values = {TEMP: values}
elif not isinstance(values, dict):
values = {None: values}
for key in values:
hub.lg.debug(f'add sensor {W1}:{key}')
sensor = _make_entity(
mid=mid,
port=port,
conf={
CONF_TYPE: W1,
CONF_KEY: key,
})
devices.append(sensor)
hub.sensors.append(sensor)
async_add_devices(devices) async def scan():
async for port, pty, m in hub.scan_ports():
if pty == "3":
values = await hub.get_port(port)
lg.debug(f'values: %s', values)
if values is None:
continue
if isinstance(values, str) and TEMP_PATT.search(values):
values = {TEMP: values}
elif not isinstance(values, dict):
values = {None: values}
for key in values:
hub.lg.debug(f'add sensor {W1}:{key}')
sensor = _make_entity(
mid=mid,
port=port,
conf={
CONF_TYPE: W1,
CONF_KEY: key,
})
devices.append(sensor)
hub.sensors.append(sensor)
async_add_devices(devices)
await scan()
class Mega1WSensor(BaseMegaEntity): class Mega1WSensor(BaseMegaEntity):

View File

@@ -1,5 +1,4 @@
"""Platform for light integration.""" """Platform for light integration."""
import json
import logging import logging
import voluptuous as vol import voluptuous as vol
@@ -13,11 +12,7 @@ from homeassistant.const import (
CONF_PLATFORM, CONF_PLATFORM,
CONF_PORT, CONF_PORT,
) )
from homeassistant.core import HomeAssistant
from homeassistant.helpers.restore_state import RestoreEntity
from .entities import BaseMegaEntity from .entities import BaseMegaEntity
from .hub import MegaD
from .const import CONF_DIMMER, CONF_SWITCH from .const import CONF_DIMMER, CONF_SWITCH
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

100
readme.md
View File

@@ -1,111 +1,63 @@
# MegaD HomeAssistant custom component # MegaD HomeAssistant integration
Интеграция с [MegaD-2561](https://www.ab-log.ru/smart-house/ethernet/megad-2561) Интеграция с [MegaD-2561](https://www.ab-log.ru/smart-house/ethernet/megad-2561)
## Основные особенности: ## Основные особенности:
- Настройка как из yaml так и из веб-интерфейса - Все порты автоматически добавляются как устройства (для обычных релейных выходов создается
- При настройки из веба все порты автоматически добавляются как устройства (для обычных релейных выходов создается
`light`, для шим - `light` с поддержкой яркости, для цифровых входов `binary_sensor`, для температурных датчиков `light`, для шим - `light` с поддержкой яркости, для цифровых входов `binary_sensor`, для температурных датчиков
`sensor`) `sensor`)
- Возможность работы с несколькими megad - Возможность работы с несколькими megad
- Обратная связь по mqtt - Обратная связь по mqtt
- Команды выполняются друг за другом без конкурентного доступа к ресурсам megad - Команды выполняются друг за другом без конкурентного доступа к ресурсам megad
## Устройства - Поддержка температурных датчиков в режиме шины
Поддерживаются устройства: light, switch, binary_sensor, sensor. light может работать как диммер
## Зависимости
**Важно!!** Перед использованием необходимо настроить интеграцию mqtt в HomeAssistant
Для максимальной совместимости необходимо обновить ваш контроллер до последней версии, тк были важные обновления в части
mqtt
## Установка ## Установка
Рекомендованнй способ - через [HACS](https://hacs.xyz/docs/installation/installation). Рекомендованнй способ с поддержкой обновлений - через [HACS](https://hacs.xyz/docs/installation/installation).
После установки HACS, нужно перейти в меню hacs -> integrations, далее в верхнем правом углу После установки перейти в меню HACS - Integrations - Explore, в поиске ищем MegaD
нажать три точки, где будет `Custom repositories`, открыть, нажать add и добавить `https://github.com/andvikt/mega_hacs.git`
Альтернативный способ установки: Альтернативный способ установки:
```shell ```shell
# из папки с конфигом # из папки с конфигом
wget -q -O - https://raw.githubusercontent.com/andvikt/mega_hacs/master/install.sh | bash - wget -q -O - https://raw.githubusercontent.com/andvikt/mega_hacs/master/install.sh | bash -
``` ```
Перезагрузить HA Не забываем перезагрузить HA
## Устройства
Для обновления повторить Поддерживаются устройства: light, switch, binary_sensor, sensor. light может работать как диммер
## Зависимости
Перед использованием необходимо настроить интеграцию mqtt в HomeAssistant
## Настройка из веб-интерфейса ## Настройка из веб-интерфейса
`Настройки` -> `Интеграции` -> `Добавить интеграцию` в поиске ищем mega `Настройки` -> `Интеграции` -> `Добавить интеграцию` в поиске ищем mega
## Пример настройки с помощью yaml:
```yaml
mega:
mega1:
host: 192.168.0.14
name: hello
password: sec
mqtt_id: mega # это id в конфиге меги
light:
- platform: mega
mega1:
switch:
- 1 # можно просто перечислить порты
- 2
- 3
dimmer:
- port: 7
name: hello # можно использовать расширенный вариант с названиями
- 9
- 10
binary_sensor:
- platform: mega
mega1:
- port: 16
name: sensor1
- port: 18
name: sensor2
sensor:
- platform: mega
mega1:
- port: 10
name: some temp
type: w1
key: temp
- port: 10
name: some hum
type: w1
key: hum
switch:
- platform: mega
mega1:
- 11
```
## Сервисы ## Сервисы
Все сервисы доступны в меню разработчика с описанием и примерами использования
```yaml ```yaml
save: mega.save:
description: Сохраняет текущее состояние портов (?cmd=s) description: Сохраняет текущее состояние портов (?cmd=s)
fields: fields:
mega_id: mega_id:
description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги
example: "mega" example: "mega"
get_port: mega.get_port:
description: Запросить текущий статус порта (или всех) description: Запросить текущий статус порта (или всех)
fields: fields:
mega_id: mega_id:
description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги description: ID меги, можно оставить пустым, тогда будут порты всех зарегистрированных мег
example: "mega" example: "mega"
port: port:
description: Номер порта (если не заполнять, будут запрошены все порты сразу) description: Номер порта (если не заполнять, будут запрошены все порты сразу)
example: 1 example: 1
run_cmd: mega.run_cmd:
description: Выполнить любую произвольную команду description: Выполнить любую произвольную команду
fields: fields:
mega_id: mega_id:
description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги description: ID меги
example: "mega" example: "mega"
port: port:
description: Номер порта (это не порт, которым мы управляем, а порт с которого шлем команду) description: Номер порта (это не порт, которым мы управляем, а порт с которого шлем команду)
@@ -116,15 +68,13 @@ run_cmd:
``` ```
## Состояния
Так же каждое устройство megad опрашивается на предмет работоспособности, текущий статус
хранится в mega.<id>
## Отладка ## Отладка
Если возникают проблемы, можно включить детальный лог, для этого в конфиг добавить: Интеграция находится в активной разработке, при возникновении проблем [заводите issue](https://github.com/andvikt/mega_hacs/issues/new/choose)
Просьба прикладывать детальный лог, который можно включить в конфиге так:
```yaml ```yaml
logger: logger:
default: info default: info
logs: logs:
custom_components.mega: debug custom_components.mega: debug
``` ```