Compare commits

...

8 Commits

Author SHA1 Message Date
Andrey
a9896c82fe add server response in mqtt mode 2021-02-11 12:00:01 +03:00
Andrey
5ed0b74eff add server response in mqtt mode 2021-02-11 11:57:17 +03:00
Andrey
4fccb23c39 remove port from command sendings 2021-02-10 14:05:16 +03:00
Andrey
03e4d3ff7e Merge remote-tracking branch 'origin/master' 2021-02-09 22:15:27 +03:00
Andrey
7907e0cd85 edit readme 2021-02-09 22:04:39 +03:00
andvikt
5211ee5330 Merge pull request #8 from r7sa/master
Улучшение поддержки I2C устройств

Спасибо большое!
2021-02-09 21:35:05 +03:00
Andrey
4e0f1dddcb add readme about srv loop 2021-02-09 21:30:26 +03:00
Sergey
742a0a9a09 - добавлены два новых класса устройства (давление, освещённость)
- улучшена поддержка I2C устройств, возвращающих только одно значение
2021-02-07 12:29:40 +03:00
9 changed files with 83 additions and 24 deletions

View File

@@ -17,7 +17,7 @@ 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, CONF_ALL CONF_CONV_TEMPLATE, CONF_ALL, CONF_FORCE_D
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
@@ -28,6 +28,7 @@ CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: { DOMAIN: {
vol.Optional(CONF_ALLOW_HOSTS): [str], vol.Optional(CONF_ALLOW_HOSTS): [str],
vol.Optional(CONF_FORCE_D, description='Принудительно слать d после срабатывания входа', default=False): bool,
vol.Required(str, description='id меги из веб-интерфейса'): { vol.Required(str, description='id меги из веб-интерфейса'): {
vol.Optional(int, description='номер порта'): { vol.Optional(int, description='номер порта'): {
vol.Optional(CONF_SKIP, description='исключить порт из сканирования', default=False): bool, vol.Optional(CONF_SKIP, description='исключить порт из сканирования', default=False): bool,
@@ -44,7 +45,7 @@ CONFIG_SCHEMA = vol.Schema(
CONF_RESPONSE_TEMPLATE, CONF_RESPONSE_TEMPLATE,
description='шаблон ответа когда на этот порт приходит' description='шаблон ответа когда на этот порт приходит'
'сообщение из меги '): 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 vol.Optional(CONF_CONV_TEMPLATE): cv.template
} }
@@ -81,7 +82,7 @@ async def async_setup(hass: HomeAssistant, config: dict):
) )
hass.services.async_register( hass.services.async_register(
DOMAIN, 'run_cmd', partial(_run_cmd, hass), schema=vol.Schema({ DOMAIN, 'run_cmd', partial(_run_cmd, hass), schema=vol.Schema({
vol.Required('port'): int, vol.Optional('port'): int,
vol.Required('cmd'): str, vol.Required('cmd'): str,
vol.Optional('mega_id'): str, vol.Optional('mega_id'): str,
}) })
@@ -232,12 +233,11 @@ async def _get_port(hass: HomeAssistant, call: ServiceCall):
@bind_hass @bind_hass
async def _run_cmd(hass: HomeAssistant, call: ServiceCall): async def _run_cmd(hass: HomeAssistant, call: ServiceCall):
port = call.data.get('port')
mega_id = call.data.get('mega_id') mega_id = call.data.get('mega_id')
cmd = call.data.get('cmd') 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.request(cmd=cmd)
else: else:
for hub in hass.data[DOMAIN].values(): for hub in hass.data[DOMAIN].values():
await hub.send_command(port=port, cmd=cmd) await hub.request(cmd=cmd)

View File

@@ -16,7 +16,8 @@ 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, CONF_INVERT from homeassistant.helpers.template import Template
from .const import EVENT_BINARY_SENSOR, DOMAIN, CONF_CUSTOM, CONF_SKIP, CONF_INVERT, CONF_RESPONSE_TEMPLATE
from .entities import MegaPushEntity from .entities import MegaPushEntity
from .hub import MegaD from .hub import MegaD
@@ -89,4 +90,20 @@ class MegaBinarySensor(BinarySensorEntity, MegaPushEntity):
def _update(self, payload: dict): def _update(self, payload: dict):
self.mega.values[self.port] = payload self.mega.values[self.port] = payload
if not self.mega.mqtt_inputs:
return
template: Template = self.customize.get(CONF_RESPONSE_TEMPLATE, None)
if template is not None:
template.hass = self.hass
ret = template.async_render(payload)
self.mega.lg.debug(f'response: %s', ret)
self.hass.async_create_task(
self.mega.request(pt=self.port, cmd=ret)
)
elif self.mega.force_d:
self.mega.lg.debug(f'response d')
self.hass.async_create_task(
self.mega.request(pt=self.port, cmd='d')
)

View File

@@ -18,7 +18,7 @@ _LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema( STEP_USER_DATA_SCHEMA = vol.Schema(
{ {
vol.Required(CONF_ID, default='def'): str, vol.Required(CONF_ID, default='mega'): str,
vol.Required(CONF_HOST, default="192.168.0.14"): str, vol.Required(CONF_HOST, default="192.168.0.14"): str,
vol.Required(CONF_PASSWORD, default="sec"): str, vol.Required(CONF_PASSWORD, default="sec"): str,
vol.Optional(CONF_SCAN_INTERVAL, default=0): int, vol.Optional(CONF_SCAN_INTERVAL, default=0): int,

View File

@@ -27,6 +27,7 @@ CONF_GET_VALUE = 'get_value'
CONF_ALLOW_HOSTS = 'allow_hosts' CONF_ALLOW_HOSTS = 'allow_hosts'
CONF_CONV_TEMPLATE = 'conv_template' CONF_CONV_TEMPLATE = 'conv_template'
CONF_POLL_OUTS = 'poll_outs' CONF_POLL_OUTS = 'poll_outs'
CONF_FORCE_D = 'force_d'
PLATFORMS = [ PLATFORMS = [
"light", "light",
"switch", "switch",
@@ -42,5 +43,6 @@ LONG = 'long'
RELEASE = 'release' RELEASE = 'release'
LONG_RELEASE = 'long_release' LONG_RELEASE = 'long_release'
PRESS = 'press' PRESS = 'press'
LUX = 'lux'
SINGLE_CLICK = 'single' SINGLE_CLICK = 'single'
DOUBLE_CLICK = 'double' DOUBLE_CLICK = 'double'

View File

@@ -246,16 +246,15 @@ class MegaOutPort(MegaPushEntity):
cmd = brightness cmd = brightness
else: else:
cmd = 1 if not self.invert else 0 cmd = 1 if not self.invert else 0
await self.mega.send_command(self.port, f"{self.port}:{cmd}") await self.mega.request(cmd=f"{self.port}:{cmd}")
self.mega.values[self.port] = {'value': cmd} self.mega.values[self.port] = {'value': cmd}
await self.get_state() await self.get_state()
async def async_turn_off(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None:
cmd = "0" if not self.invert else "1" cmd = "0" if not self.invert else "1"
await self.mega.send_command(self.port, f"{self.port}:{cmd}") await self.mega.request(cmd=f"{self.port}:{cmd}")
self.mega.values[self.port] = {'value': cmd} self.mega.values[self.port] = {'value': cmd}
await self.get_state() await self.get_state()

View File

@@ -10,30 +10,45 @@ import json
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from homeassistant.components import mqtt from homeassistant.components import mqtt
from homeassistant.const import DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY from homeassistant.const import (DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_ILLUMINANCE, TEMP_CELSIUS, PERCENTAGE, LIGHT_LUX)
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, CONF_CUSTOM, CONF_SKIP from .const import TEMP, HUM, PRESS, LUX, PATT_SPLIT, DOMAIN, CONF_HTTP, EVENT_BINARY_SENSOR, CONF_CUSTOM, CONF_SKIP, \
CONF_FORCE_D
from .entities import set_events_off, BaseMegaEntity from .entities import set_events_off, BaseMegaEntity
from .exceptions import CannotConnect from .exceptions import CannotConnect
from .tools import make_ints from .tools import make_ints
TEMP_PATT = re.compile(r'temp:([01234567890\.]+)') TEMP_PATT = re.compile(r'temp:([01234567890\.]+)')
HUM_PATT = re.compile(r'hum:([01234567890\.]+)') HUM_PATT = re.compile(r'hum:([01234567890\.]+)')
PRESS_PATT = re.compile(r'press:([01234567890\.]+)')
LUX_PATT = re.compile(r'lux:([01234567890\.]+)')
PATTERNS = { PATTERNS = {
TEMP: TEMP_PATT, TEMP: TEMP_PATT,
HUM: HUM_PATT, HUM: HUM_PATT,
PRESS: PRESS_PATT,
LUX: LUX_PATT
} }
UNITS = { UNITS = {
TEMP: '°C', TEMP: TEMP_CELSIUS,
HUM: '%' HUM: PERCENTAGE,
PRESS: 'mmHg',
LUX: LIGHT_LUX
} }
CLASSES = { CLASSES = {
TEMP: DEVICE_CLASS_TEMPERATURE, TEMP: DEVICE_CLASS_TEMPERATURE,
HUM: DEVICE_CLASS_HUMIDITY HUM: DEVICE_CLASS_HUMIDITY,
PRESS: DEVICE_CLASS_PRESSURE,
LUX: DEVICE_CLASS_ILLUMINANCE
}
I2C_DEVICE_TYPES = {
"2": LUX, # BH1750
"3": LUX, # TSL2591
"7": LUX, # MAX44009
"70": LUX, # OPT3001
} }
class NoPort(Exception): class NoPort(Exception):
pass pass
@@ -149,6 +164,10 @@ class MegaD:
self._customize = c self._customize = c
return self._customize return self._customize
@property
def force_d(self):
return self.customize.get(CONF_FORCE_D, False)
@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)
@@ -398,8 +417,11 @@ class MegaD:
self._scanned[port] = (pty, m) self._scanned[port] = (pty, m)
return pty, m return pty, m
elif pty in ('2', '4'): # эта часть не очень проработана, тут есть i2c который может работать неправильно elif pty in ('2', '4'): # эта часть не очень проработана, тут есть i2c который может работать неправильно
self._scanned[port] = (pty, '0') m = tree.find('select', attrs={'name': 'd'})
return pty, '0' 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): async def scan_ports(self, nports=37):
for x in range(0, nports+1): for x in range(0, nports+1):
@@ -434,7 +456,10 @@ class MegaD:
if isinstance(values, str) and TEMP_PATT.search(values): if isinstance(values, str) and TEMP_PATT.search(values):
values = {TEMP: values} values = {TEMP: values}
elif not isinstance(values, dict): elif not isinstance(values, dict):
values = {None: values} if pty == '4' and m in I2C_DEVICE_TYPES:
values = {I2C_DEVICE_TYPES[m]: values}
else:
values = {None: values}
for key in values: for key in values:
self.lg.debug(f'add sensor {key}') self.lg.debug(f'add sensor {key}')
ret['sensor'][port].append(dict( ret['sensor'][port].append(dict(

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.13" "version": "v0.3.17"
} }

View File

@@ -25,9 +25,10 @@ run_cmd:
mega_id: mega_id:
description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги description: ID меги, можно оставить пустым, тогда будут сохранены все зарегистрированные меги
example: "mega" example: "mega"
port:
description: Номер порта (это не порт, которым мы управляем, а порт с которого шлем команду)
example: 1
cmd: cmd:
description: Любая поддерживаемая мегой команда description: Любая поддерживаемая мегой команда
example: "1:0" example: "1:0"
port:
description: (Deprecated, больше не нужен) Номер порта (это не порт, которым мы управляем, а порт с которого шлем команду)
example: 1

View File

@@ -73,6 +73,9 @@ mega:
Начиная с версии `0.3.1` интеграция стала поддерживать обратную связь без mqtt, используя http-сервер. Для этого в настройках Начиная с версии `0.3.1` интеграция стала поддерживать обратную связь без mqtt, используя http-сервер. Для этого в настройках
интеграции необходимо снять галку с `использовать mqtt` интеграции необходимо снять галку с `использовать mqtt`
**Внимание!** Не используйте srv loop на контроллере, это может приводить к ложным срабатываниям входов. Вместо srv loop
интеграция будет сама обновлять все состояния портов с заданным интервалом
В самой меге необходимо прописать настройки: В самой меге необходимо прописать настройки:
```yaml ```yaml
srv: "192.168.1.4:8123" # ip:port вашего HA srv: "192.168.1.4:8123" # ip:port вашего HA
@@ -110,6 +113,18 @@ mega:
``` ```
Начиная с версии v0.3.17 ответ можно слать так же и в режиме MQTT. Аналогично, темплейт должен возвращать готовую команду
такую же как требует команда cmd, так же можно использовать d, но d не отправляется по умолчанию, это сделано чтобы не
сломать текущую логику у пользователей предыдущих версий. Чтобы включить для всех входов в режиме mqtt отправку команды
d необходимо в конфиге прописать следующее:
```yaml
mega:
mega1:
force_d: true
```
**Внимание!** Нельзя использовать чекбокс напротив поля act если планируется использовать ответ сервера - у вас и
сработает act и команда от сервера, а вслучае ответа d сработает act два раза.
## binary_sensor и события ## binary_sensor и события
Входы будут доступны как binary_sensor, а так же в виде событий `mega.sensor` и `mega.binary`. Входы будут доступны как binary_sensor, а так же в виде событий `mega.sensor` и `mega.binary`.