mirror of
https://github.com/andvikt/mega_hacs.git
synced 2025-12-12 01:24:29 +05:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eaa46a99ae | ||
|
|
e65598fe63 | ||
|
|
e12ab45c04 | ||
|
|
7fb72be646 | ||
|
|
f9f97a91b6 | ||
|
|
ad1210d5cc | ||
|
|
687e80f729 | ||
|
|
7d777c9e82 | ||
|
|
c9f0e85f6a | ||
|
|
e75f8b91ef | ||
|
|
bf15d4f3f9 | ||
|
|
124ef36564 |
@@ -1,5 +1,15 @@
|
||||
from itertools import permutations
|
||||
order ='brg'
|
||||
rgb = 'rgb'
|
||||
|
||||
RGB_COMBINATIONS = [''.join(x) for x in permutations('rgb')]
|
||||
map_to_order = [rgb.index(x) for x in order]
|
||||
map_from_order = [order.index(x) for x in rgb]
|
||||
|
||||
print(RGB_COMBINATIONS)
|
||||
|
||||
_rgb = [
|
||||
rgb[x] for x in map_to_order
|
||||
]
|
||||
_order = [
|
||||
_rgb[x] for x in map_from_order
|
||||
]
|
||||
|
||||
print(_rgb, _order)
|
||||
@@ -99,7 +99,7 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: {
|
||||
vol.Optional(CONF_ALLOW_HOSTS): [str],
|
||||
vol.Required(str, description='id меги из веб-интерфейса'): {
|
||||
vol.Optional([str, int], description='id меги из веб-интерфейса'): {
|
||||
vol.Optional(CONF_FORCE_D, description='Принудительно слать d после срабатывания входа', default=False): bool,
|
||||
vol.Optional(
|
||||
CONF_DEF_RESPONSE,
|
||||
@@ -114,7 +114,7 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
}
|
||||
}
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
extra=vol.PREVENT_EXTRA,
|
||||
)
|
||||
|
||||
ALIVE_STATE = 'alive'
|
||||
|
||||
@@ -64,7 +64,7 @@ LUX = 'lux'
|
||||
SINGLE_CLICK = 'single'
|
||||
DOUBLE_CLICK = 'double'
|
||||
|
||||
PATT_FW = re.compile(r'fw:\s(.+)\)')
|
||||
PATT_FW = re.compile(r'fw:\s(.+)\b')
|
||||
|
||||
REMOVE_CONFIG = [
|
||||
'extenders',
|
||||
@@ -78,3 +78,4 @@ REMOVE_CONFIG = [
|
||||
'smooth',
|
||||
]
|
||||
RGB_COMBINATIONS = [''.join(x) for x in permutations('rgb')]
|
||||
RGB = 'rgb'
|
||||
|
||||
@@ -91,6 +91,7 @@ class MegaD:
|
||||
**kwargs,
|
||||
):
|
||||
"""Initialize."""
|
||||
self.skip_ports = set()
|
||||
if config is not None:
|
||||
lg.debug(f'load config: %s', config.data)
|
||||
self.config = config
|
||||
@@ -157,11 +158,10 @@ class MegaD:
|
||||
if force_d is not None:
|
||||
self.customize[CONF_FORCE_D] = force_d
|
||||
try:
|
||||
if allow_hosts is not None:
|
||||
if allow_hosts is not None and DOMAIN in hass.data:
|
||||
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')
|
||||
|
||||
@@ -186,7 +186,10 @@ class MegaD:
|
||||
continue
|
||||
if x.port in ports:
|
||||
continue
|
||||
try:
|
||||
await self.get_port(x.port, force_http=True, http_cmd=x.http_cmd)
|
||||
except asyncio.TimeoutError:
|
||||
continue
|
||||
ports.append(x.port)
|
||||
|
||||
@property
|
||||
@@ -233,12 +236,15 @@ class MegaD:
|
||||
"""
|
||||
for x in self.ds2413_ports:
|
||||
self.lg.debug(f'poll ds2413 for %s', x)
|
||||
try:
|
||||
await self.get_port(
|
||||
port=x,
|
||||
force_http=True,
|
||||
http_cmd='list',
|
||||
conv=False
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
continue
|
||||
|
||||
async def poll(self):
|
||||
"""
|
||||
@@ -253,6 +259,7 @@ class MegaD:
|
||||
ret = await self._update_i2c(x)
|
||||
if isinstance(ret, dict):
|
||||
self.values.update(ret)
|
||||
|
||||
for x in self.extenders:
|
||||
ret = await self._update_extender(x)
|
||||
if not isinstance(ret, dict):
|
||||
@@ -290,7 +297,9 @@ class MegaD:
|
||||
url = f"{url}/?{cmd}"
|
||||
self.lg.debug('request: %s', url)
|
||||
async with self._http_lck(priority):
|
||||
async with aiohttp.request("get", url=url) as req:
|
||||
for _ntry in range(3):
|
||||
try:
|
||||
async with aiohttp.request("get", url=url, timeout=aiohttp.ClientTimeout(total=5)) as req:
|
||||
if req.status != 200:
|
||||
self.lg.warning('%s returned %s (%s)', url, req.status, await req.text())
|
||||
return None
|
||||
@@ -298,6 +307,11 @@ class MegaD:
|
||||
ret = await req.text()
|
||||
self.lg.debug('response %s', ret)
|
||||
return ret
|
||||
except asyncio.TimeoutError:
|
||||
self.lg.warning(f'timeout while requesting {url}')
|
||||
raise
|
||||
# await asyncio.sleep(1)
|
||||
raise asyncio.TimeoutError('after 3 tries')
|
||||
|
||||
async def save(self):
|
||||
await self.send_command(cmd='s')
|
||||
@@ -350,7 +364,10 @@ class MegaD:
|
||||
return {e.port for e in self.entities}
|
||||
|
||||
async def get_all_ports(self, only_out=False, check_skip=False):
|
||||
try:
|
||||
ret = await self.request(cmd='all')
|
||||
except asyncio.TimeoutError:
|
||||
return
|
||||
for port, x in enumerate(ret.split(';')):
|
||||
if port in self.ds2413_ports:
|
||||
continue
|
||||
@@ -446,7 +463,10 @@ class MegaD:
|
||||
:param port:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
values = await self.request(pt=port, cmd='get')
|
||||
except asyncio.TimeoutError:
|
||||
return
|
||||
ret = {}
|
||||
for i, x in enumerate(values.split(';')):
|
||||
ret[f'{port}e{i}'] = x
|
||||
@@ -458,13 +478,21 @@ class MegaD:
|
||||
:param params: параметры url
|
||||
:return:
|
||||
"""
|
||||
pt = params.get('pt')
|
||||
if pt in self.skip_ports:
|
||||
return
|
||||
if pt is not None:
|
||||
pass
|
||||
_params = tuple(params.items())
|
||||
delay = None
|
||||
if 'delay' in params:
|
||||
delay = params.pop('delay')
|
||||
try:
|
||||
ret = {
|
||||
_params: await self.request(**params)
|
||||
}
|
||||
except asyncio.TimeoutError:
|
||||
return
|
||||
self.lg.debug('i2c response: %s', ret)
|
||||
if delay:
|
||||
self.lg.debug('delay %s', delay)
|
||||
@@ -673,7 +701,7 @@ class MegaD:
|
||||
cmd = dict(
|
||||
pt=config[0][0],
|
||||
chip=chip,
|
||||
ws=''.join([hex(x).split('x')[1].ljust(2, '0').upper() for x in _next_val])
|
||||
ws=''.join([hex(x).split('x')[1].rjust(2, '0').upper() for x in _next_val])
|
||||
)
|
||||
await self.request(**cmd)
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ i2c_classes = {
|
||||
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None),
|
||||
DeviceType(DEVICE_CLASS_HUMIDITY, PERCENTAGE, None)
|
||||
],
|
||||
'dps368': [
|
||||
DeviceType(DEVICE_CLASS_PRESSURE, PRESSURE_BAR, None),
|
||||
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None),
|
||||
],
|
||||
'mlx90614': [
|
||||
Skip,
|
||||
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, 'temp'),
|
||||
|
||||
@@ -33,9 +33,9 @@ from .const import (
|
||||
CONF_SWITCH,
|
||||
DOMAIN,
|
||||
CONF_CUSTOM,
|
||||
CONF_SKIP, CONF_LED, CONF_WS28XX, CONF_PORTS, CONF_WHITE_SEP, CONF_SMOOTH, CONF_ORDER, CONF_CHIP,
|
||||
CONF_SKIP, CONF_LED, CONF_WS28XX, CONF_PORTS, CONF_WHITE_SEP, CONF_SMOOTH, CONF_ORDER, CONF_CHIP, RGB,
|
||||
)
|
||||
from .tools import int_ignore
|
||||
from .tools import int_ignore, map_reorder_rgb
|
||||
|
||||
lg = logging.getLogger(__name__)
|
||||
SCAN_INTERVAL = timedelta(seconds=5)
|
||||
@@ -119,14 +119,8 @@ class MegaRGBW(LightEntity, BaseMegaEntity):
|
||||
self._task: asyncio.Task = None
|
||||
self._restore = None
|
||||
self.smooth: timedelta = self.customize[CONF_SMOOTH]
|
||||
self._color_map = self.customize.get(CONF_ORDER, 'rgb')
|
||||
self._color_order = self.customize.get(CONF_ORDER, 'rgb')
|
||||
self._last_called: float = 0
|
||||
if self._color_map == 'rgb':
|
||||
self._color_map = None
|
||||
else:
|
||||
self._color_map = {
|
||||
x: i for i, x in enumerate(self._color_map)
|
||||
}
|
||||
self._max_values = None
|
||||
|
||||
@property
|
||||
@@ -189,11 +183,9 @@ class MegaRGBW(LightEntity, BaseMegaEntity):
|
||||
rgb = [
|
||||
round(x * self.max_values[i]) for i, x in enumerate(rgb)
|
||||
]
|
||||
if self.is_ws and self._color_map is not None:
|
||||
if self.is_ws:
|
||||
# восстанавливаем мэпинг
|
||||
rgb = [
|
||||
rgb[self._color_map[x]] for x in 'rgb'
|
||||
]
|
||||
rgb = map_reorder_rgb(rgb, RGB, self._color_order)
|
||||
return rgb
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
@@ -267,7 +259,10 @@ class MegaRGBW(LightEntity, BaseMegaEntity):
|
||||
else:
|
||||
w = None
|
||||
rgb = rgbw
|
||||
|
||||
if self.is_ws:
|
||||
rgb = map_reorder_rgb(
|
||||
rgb, self._color_order, RGB
|
||||
)
|
||||
h, s, v = colorsys.rgb_to_hsv(*[x/self.max_values[i] for i, x in enumerate(rgb)])
|
||||
h *= 360
|
||||
s *= 100
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "mega",
|
||||
"name": "mega",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/mega_hacs",
|
||||
"documentation": "https://github.com/andvikt/mega_hacs",
|
||||
"requirements": [
|
||||
"beautifulsoup4",
|
||||
"lxml"
|
||||
@@ -15,5 +15,5 @@
|
||||
"@andvikt"
|
||||
],
|
||||
"issue_tracker": "https://github.com/andvikt/mega_hacs/issues",
|
||||
"version": "v1.0.0b1"
|
||||
"version": "v1.0.6b4"
|
||||
}
|
||||
@@ -20,7 +20,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.template import Template
|
||||
from .entities import MegaPushEntity
|
||||
from .const import CONF_KEY, TEMP, HUM, W1, W1BUS, CONF_CONV_TEMPLATE, CONF_HEX_TO_FLOAT
|
||||
from .const import CONF_KEY, TEMP, HUM, W1, W1BUS, CONF_CONV_TEMPLATE, CONF_HEX_TO_FLOAT, DOMAIN, CONF_CUSTOM, CONF_SKIP
|
||||
from .hub import MegaD
|
||||
import re
|
||||
|
||||
@@ -84,9 +84,14 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
|
||||
mid = config_entry.data[CONF_ID]
|
||||
hub: MegaD = hass.data['mega'][mid]
|
||||
devices = []
|
||||
customize = hass.data.get(DOMAIN, {}).get(CONF_CUSTOM, {}).get(mid, {})
|
||||
for tp in ['sensor', 'i2c']:
|
||||
for port, cfg in config_entry.data.get(tp, {}).items():
|
||||
port = int_ignore(port)
|
||||
c = customize.get(port, {})
|
||||
if c.get(CONF_SKIP):
|
||||
hub.skip_ports |= {port}
|
||||
continue
|
||||
for data in cfg:
|
||||
hub.lg.debug(f'add sensor on port %s with data %s', port, data)
|
||||
sensor = _constructors[tp](
|
||||
@@ -146,7 +151,7 @@ class MegaI2C(MegaPushEntity):
|
||||
ret = tmpl.async_render({'value': ret})
|
||||
except:
|
||||
ret = ret
|
||||
return ret
|
||||
return str(ret)
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
@@ -241,7 +246,8 @@ class Mega1WSensor(MegaPushEntity):
|
||||
tmpl.hass = self.hass
|
||||
ret = tmpl.async_render({'value': ret})
|
||||
except:
|
||||
return ret
|
||||
pass
|
||||
return str(ret)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
@@ -117,3 +117,8 @@ class PriorityLock(asyncio.Lock):
|
||||
fut.set_result(True)
|
||||
|
||||
|
||||
def map_reorder_rgb(rgb: list, from_: str, to_: str):
|
||||
if from_ == to_:
|
||||
return rgb
|
||||
mapping = [from_.index(x) for x in to_]
|
||||
return [rgb[x] for x in mapping]
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
[подробнее про smooth](https://github.com/andvikt/mega_hacs/wiki/smooth)
|
||||
- Возможность работы с несколькими megad
|
||||
- Обратная связь по [http](https://github.com/andvikt/mega_hacs/wiki/http)
|
||||
будет выключена в версиях >= 1.0.0, тк в нем нет необходимости)
|
||||
- Автоматическое восстановление состояний выходов после перезагрузки контроллера
|
||||
- Автоматическое добавление/изменение объектов после перезагрузки контроллера
|
||||
- [События](https://github.com/andvikt/mega_hacs/wiki/События) на двойные/долгие нажатия
|
||||
|
||||
Reference in New Issue
Block a user