Compare commits

...

8 Commits

Author SHA1 Message Date
Andrey
5b86ceefe4 Merge remote-tracking branch 'origin/master' 2021-02-05 21:06:40 +03:00
Andrey
8cf000beae fix get_port 2021-02-05 21:06:27 +03:00
andvikt
99317da9f6 Update bug-report.md 2021-02-05 20:47:58 +03:00
Andrey
97911d0241 fix get_port 2021-02-05 20:21:14 +03:00
Andrey
d9925a2de0 i2c sensors 2021-02-05 20:08:28 +03:00
Andrey
f197a09072 invert inputs 2021-01-31 17:38:44 +03:00
Andrey
e8d92cfa36 invert inputs 2021-01-31 17:36:56 +03:00
Andrey
5f94186a14 edit readme 2021-01-31 12:10:17 +03:00
7 changed files with 87 additions and 90 deletions

View File

@@ -8,19 +8,26 @@ assignees: ''
--- ---
**Описание** **Описание**
A clear and concise description of what the bug is. Описание проблемы
**Версии систем** **Версии систем**
Enviroment: raspberry/linux/windows/macos/docker Enviroment: raspberry/linux/windows/macos/docker
HA version: HA version:
mega_hacs version: mega_hacs version:
megad firmware version: megad firmware version:
используется mqtt: true/false
**Ожидаемое поведение** **Ожидаемое поведение**
A clear and concise description of what you expected to happen. Описание правильного поведения
**Screenshots** **Screenshots**
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**LOG** **LOG**
Прочитайте в документации как включить подробный лог интеграции и приложите его здесь Просьба прикладывать детальный лог, который можно включить в конфиге так:
```yaml
logger:
default: info
logs:
custom_components.mega: debug
```

View File

@@ -75,7 +75,7 @@ async def async_setup(hass: HomeAssistant, config: dict):
hass.services.async_register( hass.services.async_register(
DOMAIN, 'get_port', partial(_get_port, hass), 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'): vol.Any(int, [int]),
}) })
) )
hass.services.async_register( hass.services.async_register(
@@ -208,17 +208,23 @@ async def _get_port(hass: HomeAssistant, call: ServiceCall):
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:
await hub.get_all_ports() await hub.get_all_ports(check_skip=True)
else: elif isinstance(port, int):
await hub.get_port(port) await hub.get_port(port)
elif isinstance(port, list):
for x in port:
await hub.get_port(x)
else: else:
for hub in hass.data[DOMAIN].values(): for hub in hass.data[DOMAIN].values():
if not isinstance(hub, MegaD): if not isinstance(hub, MegaD):
continue continue
if port is None: if port is None:
await hub.get_all_ports() await hub.get_all_ports(check_skip=True)
else: elif isinstance(port, int):
await hub.get_port(port) await hub.get_port(port)
elif isinstance(port, list):
for x in port:
await hub.get_port(x)
@bind_hass @bind_hass

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
CONF_ENTITY_ID, CONF_ENTITY_ID,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import EVENT_BINARY_SENSOR, DOMAIN, CONF_CUSTOM, CONF_SKIP from .const import EVENT_BINARY_SENSOR, DOMAIN, CONF_CUSTOM, CONF_SKIP, CONF_INVERT
from .entities import MegaPushEntity from .entities import MegaPushEntity
from .hub import MegaD from .hub import MegaD
@@ -71,6 +71,10 @@ class MegaBinarySensor(BinarySensorEntity, MegaPushEntity):
def state_attributes(self): def state_attributes(self):
return self._attrs return self._attrs
@property
def invert(self):
return self.customize.get(CONF_INVERT, False)
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
val = self.mega.values.get(self.port, {}).get("value") \ val = self.mega.values.get(self.port, {}).get("value") \
@@ -79,9 +83,9 @@ class MegaBinarySensor(BinarySensorEntity, MegaPushEntity):
return self._state == 'ON' return self._state == 'ON'
elif val is not None: elif val is not None:
if val in ['ON', 'OFF']: if val in ['ON', 'OFF']:
return val == 'ON' return val == 'ON' if not self.invert else val == 'OFF'
else: else:
return val != 1 return val != 1 if not self.invert else 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

@@ -14,7 +14,7 @@ from homeassistant.const import DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY
from homeassistant.core import HomeAssistant 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, CONF_CUSTOM, CONF_SKIP
from .entities import set_events_off from .entities import set_events_off
from .exceptions import CannotConnect from .exceptions import CannotConnect
from .tools import make_ints from .tools import make_ints
@@ -91,6 +91,7 @@ class MegaD:
self.last_update = datetime.now() self.last_update = datetime.now()
self._callbacks: typing.DefaultDict[int, typing.List[typing.Callable[[dict], typing.Coroutine]]] = defaultdict(list) self._callbacks: typing.DefaultDict[int, typing.List[typing.Callable[[dict], typing.Coroutine]]] = defaultdict(list)
self._loop = loop self._loop = loop
self._customize = None
self.values = {} self.values = {}
self.last_port = None self.last_port = None
self.updater = DataUpdateCoordinator( self.updater = DataUpdateCoordinator(
@@ -138,6 +139,14 @@ class MegaD:
await self.get_port(x.port, force_http=True, http_cmd=x.http_cmd) await self.get_port(x.port, force_http=True, http_cmd=x.http_cmd)
ports.append(x.port) ports.append(x.port)
@property
def customize(self):
if self._customize is None:
c = self.hass.data.get(DOMAIN, {}).get(CONF_CUSTOM) or {}
c = c.get(self.id) or {}
self._customize = c
return self._customize
@property @property
def is_online(self): def is_online(self):
return (datetime.now() - self.last_update).total_seconds() < (self.poll_interval + 10) return (datetime.now() - self.last_update).total_seconds() < (self.poll_interval + 10)
@@ -259,14 +268,20 @@ 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, only_out=False): async def get_all_ports(self, only_out=False, check_skip=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(';')):
if check_skip:
if self.customize.get(port, {}).get(CONF_SKIP, False):
continue
ret = self.parse_response(x) ret = self.parse_response(x)
self.values[port] = ret self.values[port] = ret
else: else:
for x in range(self.nports + 1): for x in range(self.nports + 1):
if check_skip:
if self.customize.get(x, {}).get(CONF_SKIP, False):
continue
await self.get_port(x) await self.get_port(x)
async def reboot(self, save=True): async def reboot(self, save=True):
@@ -376,7 +391,7 @@ 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': elif pty in ('2', '4'): # эта часть не очень проработана, тут есть i2c который может работать неправильно
self._scanned[port] = (pty, '0') self._scanned[port] = (pty, '0')
return pty, '0' return pty, '0'
@@ -394,7 +409,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 in ('3', '2'): elif pty in ('3', '2', '4'):
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

@@ -15,5 +15,5 @@
"@andvikt" "@andvikt"
], ],
"issue_tracker": "https://github.com/andvikt/mega_hacs/issues", "issue_tracker": "https://github.com/andvikt/mega_hacs/issues",
"version": "v0.3.11" "version": "v0.3.13"
} }

View File

@@ -15,7 +15,7 @@ get_port:
description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги
example: "mega" example: "mega"
port: port:
description: Номер порта (если не заполнять, будут запрошены все порты сразу) description: Номер порта или список портов (если не заполнять, будут запрошены все порты сразу)
example: 1 example: 1
run_cmd: run_cmd:

111
readme.md
View File

@@ -79,50 +79,11 @@ srv: "192.168.1.4:8123" # ip:port вашего HA
script: "mega" # это api интеграции, к которому будет обращаться контроллер script: "mega" # это api интеграции, к которому будет обращаться контроллер
``` ```
Входы будут доступны как binary_sensor, а так же в виде событий `mega.sensor`. #### Ответ на входящие события от контроллера
События можно обрабатывать так:
```yaml
- alias: some double click
trigger:
- platform: event
event_type: mega.sensor
event_data:
pt: 1
click: 2
action:
- service: light.toggle
entity_id: light.some_light
```
Для binary_sensor имеет смысл использовать режим P&R, для остальных режимов - лучше пользоваться событиями.
Примеры использования binary_sensor:
```yaml
- alias: обработка долгих/коротких нажатий
trigger:
- platform: state
entity_id: binary_sensor.some_sensor
to: on
for: 1 # задержка на секунду
action:
- choose:
# если кнопка все еще нажата - значит это долгое нажатие
- conditions: "{{ is_state('binary_sensor.some_sensor', 'on')}}"
sequence:
- service: light.turn_on
entity_id: light.some_light
# если кнопка уже не нажата - значит это короткое нажатие
- conditions: "{{ is_state('binary_sensor.some_sensor', 'off')}}"
sequence:
- service: light.turn_off
entity_id: light.some_light
```
## Ответ на входящие события от контроллера
Контроллер ожидает ответ от сервера, который может быть сценарием (по умолчанию интеграция отвечает `d`, что означает Контроллер ожидает ответ от сервера, который может быть сценарием (по умолчанию интеграция отвечает `d`, что означает
запустить то что прописано в поле act в настройках порта). запустить то что прописано в поле act в настройках порта).
Поддерживаеются шаблоны HA. Это может быть использовано, например, для запоминания яркости (тк сам контроллер этого не Поддерживаются шаблоны HA. Это может быть использовано, например, для запоминания яркости (тк сам контроллер этого не
умеет). В шаблоне можно использовать параметры, которые передает контроллер (m, click, pt, mdid, mega_id) умеет). В шаблоне можно использовать параметры, которые передает контроллер (m, click, pt, mdid, mega_id)
Примеры: Примеры:
@@ -130,7 +91,7 @@ script: "mega" # это api интеграции, к которому будет
mega: mega:
mega1: # id меги, который вы сами придумываете в конфиге в UI mega1: # id меги, который вы сами придумываете в конфиге в UI
4: # номер порта, с которого ожидаются события 4: # номер порта, с которого ожидаются события
response_template: 5:2 # простейший пример без шаблона. Каждый раз когда будет приходить сообщение на этот порт, response_template: "5:2" # простейший пример без шаблона. Каждый раз когда будет приходить сообщение на этот порт,
# будем менять состояние на противоположное # будем менять состояние на противоположное
5: 5:
# пример с использованием шаблона, порт 1 будет выключен если он сейчас включен и включен с последней сохраненной # пример с использованием шаблона, порт 1 будет выключен если он сейчас включен и включен с последней сохраненной
@@ -148,30 +109,13 @@ mega:
{% if m==2 %}1:0{% else %}d{% endif %} {% if m==2 %}1:0{% else %}d{% endif %}
``` ```
## Отладка ответов
Для отладки ответов сервера можно самим имитировать запросы контроллера, если у вас есть доступ к консоли
HA:
```shell
curl -v -X GET 'http://localhost:8123/mega?pt=5&m=1'
```
Если доступа нет, нужно в файл конфигурации добавить ip компьюетра, с которого вы хотите делать запросы, например:
```yaml
mega:
allow_hosts:
- 192.168.1.1
```
И тогда можно с локальной машины делать запросы на ваш сервер HA:
```shell
curl -v -X GET 'http://192.168.88.1.4:8123/mega?pt=5&m=1'
```
В ответ будет приходить либо `d`, либо скрипт, который вы настроили
## binary_sensor и события
## События Входы будут доступны как binary_sensor, а так же в виде событий `mega.sensor` и `mega.binary`.
`binary_sensor` срабатывает когда цифровой выход принимает значение 'ON'. `binary_sensor` имеет смысл использовать Для корректной работы binary_sensor имеет смысл использовать режим P&R, для остальных режимов - лучше пользоваться
только с режимом входа P&R событиями.
При каждом срабатывании `binary_sensor` так же сообщает о событии типа `mega.sensor`.
События можно использовать в автоматизациях, например так: События можно использовать в автоматизациях, например так:
```yaml ```yaml
# Пример события с полями как есть прямо из меги # Пример события с полями как есть прямо из меги
@@ -181,21 +125,21 @@ curl -v -X GET 'http://192.168.88.1.4:8123/mega?pt=5&m=1'
event_type: mega.sensor event_type: mega.sensor
event_data: event_data:
pt: 1 pt: 1
cnt: 2 click: 2
action: action:
- service: light.toggle - service: light.toggle
entity_id: light.some_light entity_id: light.some_light
``` ```
События могут содержать следующие поля: События могут содержать следующие поля:
- mega_id: id как в конфиге HA - `mega_id`: id как в конфиге HA
- pt: номер порта - `pt`: номер порта
- cnt: счетчик срабатываний - `cnt`: счетчик срабатываний
- mdid: if как в конфиге контроллера - `mdid`: if как в конфиге контроллера
- click: клик (подробнее в документации меги) - `click`: клик (подробнее в документации меги)
- value: текущее значение (только для mqtt) - `value`: текущее значение (только для mqtt)
- port: номер порта - `port`: номер порта
Начиная с версии 0.3.7 появилось так же событие типа mega.binary: Начиная с версии 0.3.7 появилось так же событие типа `mega.binary`:
```yaml ```yaml
# Пример события с полями как есть прямо из меги # Пример события с полями как есть прямо из меги
- alias: some long click - alias: some long click
@@ -211,12 +155,15 @@ curl -v -X GET 'http://192.168.88.1.4:8123/mega?pt=5&m=1'
``` ```
Возможные варианты поля `type`: Возможные варианты поля `type`:
- `long`: долгое нажатие - `long`: долгое нажатие
- `release`: размыкание (с гарантией что не было долгого нажатия) - `release`: размыкание (с гарантией** что не было долгого нажатия)
- `long_release`: размыкание после долгого нажатия - `long_release`: размыкание после долгого нажатия
- `press`: замыкание - `press`: замыкание
- `single`: одинарный клик (в режиме кликов) - `single`: одинарный клик (в режиме кликов)
- `double`: двойной клик - `double`: двойной клик
**гарантия есть только при использовании http-метода синхронизации, mqtt не гарантирует порядок доставки сообщений, хотя
маловероятно, что порядок будет нарушен, но все же сам протокол не дает таких гарантий.
Чтобы понять, какие события происходят, лучше всего воспользоваться панелью разработчика и подписаться Чтобы понять, какие события происходят, лучше всего воспользоваться панелью разработчика и подписаться
на вкладке события на событие `mega.sensor`, понажимать кнопки. на вкладке события на событие `mega.sensor`, понажимать кнопки.
@@ -264,3 +211,21 @@ logger:
logs: logs:
custom_components.mega: debug custom_components.mega: debug
``` ```
#### Отладка ответов http-сервера
Для отладки ответов сервера можно самим имитировать запросы контроллера, если у вас есть доступ к консоли
HA:
```shell
curl -v -X GET 'http://localhost:8123/mega?pt=5&m=1'
```
Если доступа нет, нужно в файл конфигурации добавить ip, с которого вы хотите делать запросы, например:
```yaml
mega:
allow_hosts:
- 192.168.1.1
```
И тогда можно с локальной машины делать запросы на ваш сервер HA:
```shell
curl -v -X GET 'http://192.168.88.1.4:8123/mega?pt=5&m=1'
```
В ответ будет приходить либо `d`, либо скрипт, который вы настроили