mirror of
https://github.com/andvikt/mega_hacs.git
synced 2025-12-12 09:34:28 +05:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4c5a4a712 | ||
|
|
c60c088795 | ||
|
|
eb3813843c | ||
|
|
804a6ad333 |
@@ -128,8 +128,7 @@ async def get_hub(hass, entry):
|
|||||||
async def _add_mega(hass: HomeAssistant, entry: ConfigEntry):
|
async def _add_mega(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
id = entry.data.get('id', entry.entry_id)
|
id = entry.data.get('id', entry.entry_id)
|
||||||
hub = await get_hub(hass, entry)
|
hub = await get_hub(hass, entry)
|
||||||
hass.data[DOMAIN][id] = hass.data[DOMAIN]['__def'] = hub
|
hass.data[DOMAIN][id] = hub
|
||||||
hass.data[DOMAIN][entry.data.get(CONF_HOST)] = hub
|
|
||||||
hass.data[DOMAIN][CONF_ALL][id] = hub
|
hass.data[DOMAIN][CONF_ALL][id] = hub
|
||||||
if not await hub.authenticate():
|
if not await hub.authenticate():
|
||||||
raise Exception("not authentificated")
|
raise Exception("not authentificated")
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ 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, HomeAssistant
|
from homeassistant.core import callback, HomeAssistant
|
||||||
from .const import DOMAIN, CONF_PORT_TO_SCAN, CONF_RELOAD, PLATFORMS, CONF_MQTT_INPUTS, \
|
from .const import DOMAIN, CONF_PORT_TO_SCAN, CONF_RELOAD, PLATFORMS, CONF_MQTT_INPUTS, \
|
||||||
CONF_NPORTS, CONF_UPDATE_ALL, CONF_POLL_OUTS, CONF_FAKE_RESPONSE, CONF_FORCE_D # pylint:disable=unused-import
|
CONF_NPORTS, CONF_UPDATE_ALL, CONF_POLL_OUTS, CONF_FAKE_RESPONSE, CONF_FORCE_D, \
|
||||||
|
CONF_ALLOW_HOSTS, CONF_PROTECTED # pylint:disable=unused-import
|
||||||
from .hub import MegaD
|
from .hub import MegaD
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
|
|
||||||
@@ -29,6 +30,8 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
|||||||
vol.Optional(CONF_UPDATE_ALL, default=True): bool,
|
vol.Optional(CONF_UPDATE_ALL, default=True): bool,
|
||||||
vol.Optional(CONF_FAKE_RESPONSE, default=True): bool,
|
vol.Optional(CONF_FAKE_RESPONSE, default=True): bool,
|
||||||
vol.Optional(CONF_FORCE_D, default=True): bool,
|
vol.Optional(CONF_FORCE_D, default=True): bool,
|
||||||
|
vol.Optional(CONF_PROTECTED, default=True): bool,
|
||||||
|
vol.Optional(CONF_ALLOW_HOSTS, default='::1;127.0.0.1'): str,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@ async def validate_input(hass: core.HomeAssistant, data):
|
|||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a config flow for mega."""
|
"""Handle a config flow for mega."""
|
||||||
|
|
||||||
VERSION = 10
|
VERSION = 11
|
||||||
CONNECTION_CLASS = config_entries.CONN_CLASS_ASSUMED
|
CONNECTION_CLASS = config_entries.CONN_CLASS_ASSUMED
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
@@ -109,7 +112,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
|
|
||||||
async def async_step_init(self, user_input=None):
|
async def async_step_init(self, user_input=None):
|
||||||
"""Manage the options."""
|
"""Manage the options."""
|
||||||
|
hub = await get_hub(self.hass, self.config_entry.data)
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
reload = user_input.pop(CONF_RELOAD)
|
reload = user_input.pop(CONF_RELOAD)
|
||||||
cfg = dict(self.config_entry.data)
|
cfg = dict(self.config_entry.data)
|
||||||
@@ -142,6 +145,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
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_FAKE_RESPONSE, default=e.get(CONF_FAKE_RESPONSE, True)): bool,
|
vol.Optional(CONF_FAKE_RESPONSE, default=e.get(CONF_FAKE_RESPONSE, True)): bool,
|
||||||
vol.Optional(CONF_FORCE_D, default=e.get(CONF_FORCE_D, False)): bool,
|
vol.Optional(CONF_FORCE_D, default=e.get(CONF_FORCE_D, False)): bool,
|
||||||
|
vol.Optional(CONF_PROTECTED, default=e.get(CONF_PROTECTED, True)): bool,
|
||||||
|
vol.Optional(CONF_ALLOW_HOSTS, default='::1;127.0.0.1'): str,
|
||||||
# vol.Optional(CONF_INVERT, default=''): str,
|
# vol.Optional(CONF_INVERT, default=''): str,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ CONF_UPDATE_ALL = 'update_all'
|
|||||||
CONF_FAKE_RESPONSE = 'fake_response'
|
CONF_FAKE_RESPONSE = 'fake_response'
|
||||||
CONF_GET_VALUE = 'get_value'
|
CONF_GET_VALUE = 'get_value'
|
||||||
CONF_ALLOW_HOSTS = 'allow_hosts'
|
CONF_ALLOW_HOSTS = 'allow_hosts'
|
||||||
|
CONF_PROTECTED = 'protected'
|
||||||
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'
|
CONF_FORCE_D = 'force_d'
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ class MegaView(HomeAssistantView):
|
|||||||
|
|
||||||
def __init__(self, cfg: dict):
|
def __init__(self, cfg: dict):
|
||||||
self._try = 0
|
self._try = 0
|
||||||
|
self.protected = True
|
||||||
self.allowed_hosts = {'::1', '127.0.0.1'}
|
self.allowed_hosts = {'::1', '127.0.0.1'}
|
||||||
|
self.notified_attempts = defaultdict(lambda : False)
|
||||||
self.callbacks = defaultdict(lambda: defaultdict(list))
|
self.callbacks = defaultdict(lambda: defaultdict(list))
|
||||||
self.templates: typing.Dict[str, typing.Dict[str, Template]] = {
|
self.templates: typing.Dict[str, typing.Dict[str, Template]] = {
|
||||||
mid: {
|
mid: {
|
||||||
@@ -34,23 +36,42 @@ class MegaView(HomeAssistantView):
|
|||||||
} for mid in cfg if isinstance(cfg[mid], dict)
|
} for mid in cfg if isinstance(cfg[mid], dict)
|
||||||
}
|
}
|
||||||
_LOGGER.debug('templates: %s', self.templates)
|
_LOGGER.debug('templates: %s', self.templates)
|
||||||
|
self.hubs = {}
|
||||||
|
|
||||||
async def get(self, request: Request) -> Response:
|
async def get(self, request: Request) -> Response:
|
||||||
|
_LOGGER.debug('request from %s %s', request.remote, request.headers)
|
||||||
|
hass: HomeAssistant = request.app['hass']
|
||||||
|
if self.protected:
|
||||||
auth = False
|
auth = False
|
||||||
for x in self.allowed_hosts:
|
for x in self.allowed_hosts:
|
||||||
if request.remote.startswith(x):
|
if request.remote.startswith(x):
|
||||||
auth = True
|
auth = True
|
||||||
break
|
break
|
||||||
if not auth:
|
if not auth:
|
||||||
_LOGGER.warning(f'unauthorised attempt to connect from {request.remote}')
|
msg = f"Non-authorised request from {request.remote} to `/mega`. "\
|
||||||
|
f"If you want to accept requests from this host "\
|
||||||
|
f"please add it to allowed hosts in `mega` UI-configuration"
|
||||||
|
if not self.notified_attempts[request.remote]:
|
||||||
|
await hass.services.async_call(
|
||||||
|
'persistent_notification',
|
||||||
|
'create',
|
||||||
|
{
|
||||||
|
"notification_id": request.remote,
|
||||||
|
"title": "Non-authorised request",
|
||||||
|
"message": msg
|
||||||
|
}
|
||||||
|
)
|
||||||
|
_LOGGER.warning(msg)
|
||||||
return Response(status=401)
|
return Response(status=401)
|
||||||
|
|
||||||
hass: HomeAssistant = request.app['hass']
|
hub: 'h.MegaD' = self.hubs.get(request.remote)
|
||||||
hub: 'h.MegaD' = hass.data.get(DOMAIN).get(request.remote) # TODO: проверить какой remote
|
if hub is None and 'mdid' in request.query:
|
||||||
if hub is None and request.remote == '::1':
|
hub = self.hubs.get(request.query['mdid'])
|
||||||
hub = hass.data.get(DOMAIN).get('__def')
|
|
||||||
if hub is None:
|
if hub is None:
|
||||||
|
_LOGGER.warning(f'can not find mdid={request.query["mdid"]} in {list(self.hubs)}')
|
||||||
|
if hub is None and request.remote in ['::1', '127.0.0.1']:
|
||||||
|
hub = self.hubs.get('__def')
|
||||||
|
elif hub is None:
|
||||||
return Response(status=400)
|
return Response(status=400)
|
||||||
data = dict(request.query)
|
data = dict(request.query)
|
||||||
hass.bus.async_fire(
|
hass.bus.async_fire(
|
||||||
|
|||||||
@@ -72,11 +72,12 @@ class MegaD:
|
|||||||
scan_interval=60,
|
scan_interval=60,
|
||||||
port_to_scan=0,
|
port_to_scan=0,
|
||||||
nports=38,
|
nports=38,
|
||||||
inverted: typing.List[int] = None,
|
update_all: bool=True,
|
||||||
update_all=True,
|
poll_outs: bool=False,
|
||||||
poll_outs=False,
|
fake_response: bool=True,
|
||||||
fake_response=True,
|
force_d: bool=None,
|
||||||
force_d=None,
|
allow_hosts: str=None,
|
||||||
|
protected=True,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
@@ -84,6 +85,11 @@ class MegaD:
|
|||||||
self.http = hass.data.get(DOMAIN, {}).get(CONF_HTTP)
|
self.http = hass.data.get(DOMAIN, {}).get(CONF_HTTP)
|
||||||
if not self.http is None:
|
if not self.http is None:
|
||||||
self.http.allowed_hosts |= {host}
|
self.http.allowed_hosts |= {host}
|
||||||
|
self.http.hubs[host] = self
|
||||||
|
if len(self.http.hubs) == 1:
|
||||||
|
self.http.hubs['__def'] = self
|
||||||
|
if mqtt_id:
|
||||||
|
self.http.hubs[mqtt_id] = self
|
||||||
else:
|
else:
|
||||||
self.http = None
|
self.http = None
|
||||||
self.poll_outs = poll_outs
|
self.poll_outs = poll_outs
|
||||||
@@ -133,6 +139,14 @@ class MegaD:
|
|||||||
|
|
||||||
if force_d is not None:
|
if force_d is not None:
|
||||||
self.customize[CONF_FORCE_D] = force_d
|
self.customize[CONF_FORCE_D] = force_d
|
||||||
|
try:
|
||||||
|
if allow_hosts is not None:
|
||||||
|
allow_hosts = set(allow_hosts.split(';'))
|
||||||
|
hass.data[DOMAIN][CONF_HTTP].allowed_hosts |= allow_hosts
|
||||||
|
hass.data[DOMAIN][CONF_HTTP].protected = protected
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
self.lg.exception('while setting allowed hosts')
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
self.loop = asyncio.get_event_loop()
|
self.loop = asyncio.get_event_loop()
|
||||||
@@ -466,6 +480,7 @@ class MegaD:
|
|||||||
|
|
||||||
async def get_config(self, nports=37):
|
async def get_config(self, nports=37):
|
||||||
ret = defaultdict(lambda: defaultdict(list))
|
ret = defaultdict(lambda: defaultdict(list))
|
||||||
|
ret['mqtt_id'] = await self.get_mqtt_id()
|
||||||
async for port, pty, m in self.scan_ports(nports):
|
async for port, pty, m in self.scan_ports(nports):
|
||||||
if pty == "0":
|
if pty == "0":
|
||||||
ret['binary_sensor'][port].append({})
|
ret['binary_sensor'][port].append({})
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
"update_all": "[%key:common::config_flow::data::update_all%]",
|
"update_all": "[%key:common::config_flow::data::update_all%]",
|
||||||
"fake_response": "[%key:common::config_flow::data::fake_response%]",
|
"fake_response": "[%key:common::config_flow::data::fake_response%]",
|
||||||
"force_d": "[%key:common::config_flow::data::force_d%]",
|
"force_d": "[%key:common::config_flow::data::force_d%]",
|
||||||
|
"protected": "[%key:common::config_flow::data::protected%]",
|
||||||
|
"allow_hosts": "[%key:common::config_flow::data::allow_hosts%]",
|
||||||
"poll_outs": "[%key:common::config_flow::data::poll_outs%]"
|
"poll_outs": "[%key:common::config_flow::data::poll_outs%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
"mqtt_inputs": "Use MQTT (Deprecated)",
|
"mqtt_inputs": "Use MQTT (Deprecated)",
|
||||||
"fake_response": "Fake response",
|
"fake_response": "Fake response",
|
||||||
"force_d": "Force 'd' response",
|
"force_d": "Force 'd' response",
|
||||||
|
"protected": "Protected",
|
||||||
|
"allow_hosts": "Allowed hosts",
|
||||||
"poll_outs": "Poll outs"
|
"poll_outs": "Poll outs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,8 @@
|
|||||||
"update_all": "Update all outs when input",
|
"update_all": "Update all outs when input",
|
||||||
"fake_response": "Fake response",
|
"fake_response": "Fake response",
|
||||||
"force_d": "Force 'd' response",
|
"force_d": "Force 'd' response",
|
||||||
|
"protected": "Protected",
|
||||||
|
"allow_hosts": "Allowed hosts",
|
||||||
"poll_outs": "Poll outs"
|
"poll_outs": "Poll outs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
"update_all": "Обновить все выходы когда срабатывает вход",
|
"update_all": "Обновить все выходы когда срабатывает вход",
|
||||||
"fake_response": "Имитация http-ответа",
|
"fake_response": "Имитация http-ответа",
|
||||||
"force_d": "Ответ 'd' по умолчанию",
|
"force_d": "Ответ 'd' по умолчанию",
|
||||||
|
"protected": "Блокировать неразрешенные соединения",
|
||||||
|
"allow_hosts": "Разрешенные ip (через ;)",
|
||||||
"poll_outs": "Обновлять выходы (регулярно)"
|
"poll_outs": "Обновлять выходы (регулярно)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +44,8 @@
|
|||||||
"force_d": "Ответ 'd' по умолчанию",
|
"force_d": "Ответ 'd' по умолчанию",
|
||||||
"nports": "Кол-во портов",
|
"nports": "Кол-во портов",
|
||||||
"update_all": "Обновить все выходы когда срабатывает вход",
|
"update_all": "Обновить все выходы когда срабатывает вход",
|
||||||
|
"protected": "Блокировать неразрешенные соединения",
|
||||||
|
"allow_hosts": "Разрешенные ip (через ;)",
|
||||||
"poll_outs": "Обновлять выходы (регулярно)"
|
"poll_outs": "Обновлять выходы (регулярно)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
"update_all": "Оновити всі виходи коли спрацьовує вхід",
|
"update_all": "Оновити всі виходи коли спрацьовує вхід",
|
||||||
"fake_response": "Имитация http-ответа",
|
"fake_response": "Имитация http-ответа",
|
||||||
"force_d": "Ответ 'd' по умолчанию",
|
"force_d": "Ответ 'd' по умолчанию",
|
||||||
|
"protected": "Блокировать неразрешенные соединения",
|
||||||
|
"allow_hosts": "Разрешенные ip (через ;)",
|
||||||
"poll_outs": "Оновити виходи"
|
"poll_outs": "Оновити виходи"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +44,8 @@
|
|||||||
"fake_response": "Имитация http-ответа",
|
"fake_response": "Имитация http-ответа",
|
||||||
"force_d": "Ответ 'd' по умолчанию",
|
"force_d": "Ответ 'd' по умолчанию",
|
||||||
"update_all": "Оновити всі виходи коли спрацьовує вхід",
|
"update_all": "Оновити всі виходи коли спрацьовує вхід",
|
||||||
|
"protected": "Блокировать неразрешенные соединения",
|
||||||
|
"allow_hosts": "Разрешенные ip (через ;)",
|
||||||
"poll_outs": "Оновити виходи"
|
"poll_outs": "Оновити виходи"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
readme.md
16
readme.md
@@ -16,18 +16,18 @@
|
|||||||
Предложения по доработкам просьба писать в [discussions](https://github.com/andvikt/mega_hacs/discussions), о проблемах
|
Предложения по доработкам просьба писать в [discussions](https://github.com/andvikt/mega_hacs/discussions), о проблемах
|
||||||
создавать [issue](https://github.com/andvikt/mega_hacs/issues/new/choose)
|
создавать [issue](https://github.com/andvikt/mega_hacs/issues/new/choose)
|
||||||
## Основные особенности:
|
## Основные особенности:
|
||||||
- Настройка в веб-интерфейсе + yaml
|
- Настройка в веб-интерфейсе + [yaml](https://github.com/andvikt/mega_hacs/wiki/Кастомизация)
|
||||||
- Все порты автоматически добавляются как устройства (для обычных релейных выходов создается
|
- Все порты автоматически добавляются как устройства (для обычных релейных выходов создается
|
||||||
`light`, для шим - `light` с поддержкой яркости, для цифровых входов `binary_sensor`, для датчиков
|
`light`, для шим - `light` с поддержкой яркости, для цифровых входов `binary_sensor`, для датчиков
|
||||||
`sensor`)
|
`sensor`)
|
||||||
- Возможность работы с несколькими megad
|
- Возможность работы с несколькими megad
|
||||||
- Обратная связь по http или mqtt (`deprecated`, поддержка mqtt будет выключена в версиях >= 1.0.0, тк в нем нет
|
- Обратная связь по [http](https://github.com/andvikt/mega_hacs/wiki/http) или mqtt (`deprecated`, поддержка mqtt
|
||||||
необходимости)
|
будет выключена в версиях >= 1.0.0, тк в нем нет необходимости)
|
||||||
- События на двойные/долгие нажатия
|
- [События](https://github.com/andvikt/mega_hacs/wiki/События) на двойные/долгие нажатия
|
||||||
- Команды выполняются друг за другом без конкурентного доступа к ресурсам megad, это дает гарантии надежного исполнения
|
- Команды выполняются друг за другом без конкурентного доступа к ресурсам megad, это дает гарантии надежного исполнения
|
||||||
большого кол-ва команд (например в сценах). Каждая следующая команда отправляется только после получения ответа о
|
большого кол-ва команд (например в сценах). Каждая следующая команда отправляется только после получения ответа о
|
||||||
выполнении предыдущей.
|
выполнении предыдущей.
|
||||||
- поддержка ds2413 (начиная с версии 0.4.1)
|
- поддержка [ds2413](https://www.ab-log.ru/smart-house/ethernet/megad-2w) (начиная с версии 0.4.1)
|
||||||
|
|
||||||
## Установка
|
## Установка
|
||||||
Рекомендованный способ с поддержкой обновлений - [HACS](https://hacs.xyz/docs/installation/installation):
|
Рекомендованный способ с поддержкой обновлений - [HACS](https://hacs.xyz/docs/installation/installation):
|
||||||
@@ -58,11 +58,15 @@ wget -q -O - https://raw.githubusercontent.com/andvikt/mega_hacs/master/install.
|
|||||||
srv: "192.168.1.4:8123" # ip:port вашего HA
|
srv: "192.168.1.4:8123" # ip:port вашего HA
|
||||||
script: "mega" # это api интеграции, к которому будет обращаться контроллер
|
script: "mega" # это api интеграции, к которому будет обращаться контроллер
|
||||||
```
|
```
|
||||||
|
Так же необходимо настроить Mega-ID в настройках контроллера, для каждой меги id должен быть разным.
|
||||||
|
|
||||||
|
При любых изменениях настроек контроллера (типы входов, id и тд) необходимо в настройках интеграции нажать `Обновить
|
||||||
|
объекты`
|
||||||
|
|
||||||
## Зависимости
|
## Зависимости
|
||||||
Для максимальной скорости реакции на команды сервера, рекомендуется выключить `имитацию http-ответа` в
|
Для максимальной скорости реакции на команды сервера, рекомендуется выключить `имитацию http-ответа` в
|
||||||
настройках интеграции и настроить proxy_pass к HA, самый простой способ сделать это - воспользоваться
|
настройках интеграции и настроить proxy_pass к HA, самый простой способ сделать это - воспользоваться
|
||||||
[специальным аддоном](https://github.com/andvikt/mega_addon/tree/master/mega-updater)
|
[специальным аддоном](https://github.com/andvikt/mega_addon/tree/master/mega-proxy)
|
||||||
|
|
||||||
Обновить ваш контроллер до последней версии, обновление прошивки MegaD можно делать
|
Обновить ваш контроллер до последней версии, обновление прошивки MegaD можно делать
|
||||||
из HA с помощью [аддона](https://github.com/andvikt/mega_addon.git)
|
из HA с помощью [аддона](https://github.com/andvikt/mega_addon.git)
|
||||||
|
|||||||
Reference in New Issue
Block a user