Compare commits

...

18 Commits

Author SHA1 Message Date
Andrey
87492be38f fix dimmer_scale for PCA9685 2021-07-25 08:36:12 +03:00
Andrey
0fab2384b0 fix patt 2021-04-21 10:08:28 +03:00
Andrey
70a2e5bbb0 fix patt 2021-04-20 17:00:31 +03:00
Andrey
83c843d722 fix validation error 2021-04-20 09:23:36 +03:00
Andrey
af684a6c00 fix validation error 2021-04-20 09:19:37 +03:00
Andrey
d94efbe1a7 add ADS1115
add DEVICE_CLASS_CO2
2021-04-20 09:11:00 +03:00
Andrey
eaa46a99ae fix fw pattern
add dps368 support
fix docs url
2021-04-20 07:37:39 +03:00
Andrey
e65598fe63 fix int mega id 2021-04-20 07:35:59 +03:00
Andrey
e12ab45c04 fix int mega id 2021-04-20 07:35:06 +03:00
Andrey
7fb72be646 add skip sensors 2021-04-19 22:03:30 +03:00
Andrey
f9f97a91b6 add timeouts 2021-04-19 21:46:12 +03:00
Andrey
ad1210d5cc fix some timeouts 2021-03-29 21:43:25 +03:00
Andrey Viktorov
687e80f729 fix empty config while first setup 2021-03-29 19:54:48 +03:00
Andrey Viktorov
7d777c9e82 fix sensors 2021-03-24 18:14:15 +03:00
Andrey Viktorov
c9f0e85f6a edit readme 2021-03-24 17:04:37 +03:00
andvikt
e75f8b91ef Update sensor.py 2021-03-24 12:40:39 +03:00
andvikt
bf15d4f3f9 Update hub.py 2021-03-24 10:57:42 +03:00
Andrey Viktorov
124ef36564 - fix order for ws28xx 2021-03-24 08:09:09 +03:00
11 changed files with 115 additions and 54 deletions

View File

@@ -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)

View File

@@ -99,7 +99,7 @@ CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: { DOMAIN: {
vol.Optional(CONF_ALLOW_HOSTS): [str], vol.Optional(CONF_ALLOW_HOSTS): [str],
vol.Required(str, description='id меги из веб-интерфейса'): { vol.Optional(vol.Any(str, int), description='id меги из веб-интерфейса'): {
vol.Optional(CONF_FORCE_D, description='Принудительно слать d после срабатывания входа', default=False): bool, vol.Optional(CONF_FORCE_D, description='Принудительно слать d после срабатывания входа', default=False): bool,
vol.Optional( vol.Optional(
CONF_DEF_RESPONSE, CONF_DEF_RESPONSE,

View File

@@ -64,7 +64,7 @@ LUX = 'lux'
SINGLE_CLICK = 'single' SINGLE_CLICK = 'single'
DOUBLE_CLICK = 'double' DOUBLE_CLICK = 'double'
PATT_FW = re.compile(r'fw:\s(.+)\)') PATT_FW = re.compile(r'fw:\s(.+?)\)')
REMOVE_CONFIG = [ REMOVE_CONFIG = [
'extenders', 'extenders',
@@ -77,4 +77,5 @@ REMOVE_CONFIG = [
'sensor', 'sensor',
'smooth', 'smooth',
] ]
RGB_COMBINATIONS = [''.join(x) for x in permutations('rgb')] RGB_COMBINATIONS = [''.join(x) for x in permutations('rgb')]
RGB = 'rgb'

View File

@@ -296,7 +296,7 @@ class MegaOutPort(MegaPushEntity):
def max_dim(self): def max_dim(self):
if self.dimmer_scale == 1: if self.dimmer_scale == 1:
return 255 return 255
elif self.dimmer == 16: elif self.dimmer_scale == 16:
return 4095 return 4095
else: else:
return 255 return 255

View File

@@ -91,6 +91,7 @@ class MegaD:
**kwargs, **kwargs,
): ):
"""Initialize.""" """Initialize."""
self.skip_ports = set()
if config is not None: if config is not None:
lg.debug(f'load config: %s', config.data) lg.debug(f'load config: %s', config.data)
self.config = config self.config = config
@@ -157,11 +158,10 @@ 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: try:
if allow_hosts is not None: if allow_hosts is not None and DOMAIN in hass.data:
allow_hosts = set(allow_hosts.split(';')) allow_hosts = set(allow_hosts.split(';'))
hass.data[DOMAIN][CONF_HTTP].allowed_hosts |= allow_hosts hass.data[DOMAIN][CONF_HTTP].allowed_hosts |= allow_hosts
hass.data[DOMAIN][CONF_HTTP].protected = protected hass.data[DOMAIN][CONF_HTTP].protected = protected
except Exception: except Exception:
self.lg.exception('while setting allowed hosts') self.lg.exception('while setting allowed hosts')
@@ -186,7 +186,10 @@ class MegaD:
continue continue
if x.port in ports: if x.port in ports:
continue continue
await self.get_port(x.port, force_http=True, http_cmd=x.http_cmd) try:
await self.get_port(x.port, force_http=True, http_cmd=x.http_cmd)
except asyncio.TimeoutError:
continue
ports.append(x.port) ports.append(x.port)
@property @property
@@ -233,12 +236,15 @@ class MegaD:
""" """
for x in self.ds2413_ports: for x in self.ds2413_ports:
self.lg.debug(f'poll ds2413 for %s', x) self.lg.debug(f'poll ds2413 for %s', x)
await self.get_port( try:
port=x, await self.get_port(
force_http=True, port=x,
http_cmd='list', force_http=True,
conv=False http_cmd='list',
) conv=False
)
except asyncio.TimeoutError:
continue
async def poll(self): async def poll(self):
""" """
@@ -253,6 +259,7 @@ class MegaD:
ret = await self._update_i2c(x) ret = await self._update_i2c(x)
if isinstance(ret, dict): if isinstance(ret, dict):
self.values.update(ret) self.values.update(ret)
for x in self.extenders: for x in self.extenders:
ret = await self._update_extender(x) ret = await self._update_extender(x)
if not isinstance(ret, dict): if not isinstance(ret, dict):
@@ -290,14 +297,21 @@ class MegaD:
url = f"{url}/?{cmd}" url = f"{url}/?{cmd}"
self.lg.debug('request: %s', url) self.lg.debug('request: %s', url)
async with self._http_lck(priority): async with self._http_lck(priority):
async with aiohttp.request("get", url=url) as req: for _ntry in range(3):
if req.status != 200: try:
self.lg.warning('%s returned %s (%s)', url, req.status, await req.text()) async with aiohttp.request("get", url=url, timeout=aiohttp.ClientTimeout(total=5)) as req:
return None if req.status != 200:
else: self.lg.warning('%s returned %s (%s)', url, req.status, await req.text())
ret = await req.text() return None
self.lg.debug('response %s', ret) else:
return ret 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): async def save(self):
await self.send_command(cmd='s') await self.send_command(cmd='s')
@@ -350,7 +364,10 @@ class MegaD:
return {e.port for e in self.entities} return {e.port for e in self.entities}
async def get_all_ports(self, only_out=False, check_skip=False): async def get_all_ports(self, only_out=False, check_skip=False):
ret = await self.request(cmd='all') try:
ret = await self.request(cmd='all')
except asyncio.TimeoutError:
return
for port, x in enumerate(ret.split(';')): for port, x in enumerate(ret.split(';')):
if port in self.ds2413_ports: if port in self.ds2413_ports:
continue continue
@@ -446,7 +463,10 @@ class MegaD:
:param port: :param port:
:return: :return:
""" """
values = await self.request(pt=port, cmd='get') try:
values = await self.request(pt=port, cmd='get')
except asyncio.TimeoutError:
return
ret = {} ret = {}
for i, x in enumerate(values.split(';')): for i, x in enumerate(values.split(';')):
ret[f'{port}e{i}'] = x ret[f'{port}e{i}'] = x
@@ -458,13 +478,21 @@ class MegaD:
:param params: параметры url :param params: параметры url
:return: :return:
""" """
pt = params.get('pt')
if pt in self.skip_ports:
return
if pt is not None:
pass
_params = tuple(params.items()) _params = tuple(params.items())
delay = None delay = None
if 'delay' in params: if 'delay' in params:
delay = params.pop('delay') delay = params.pop('delay')
ret = { try:
_params: await self.request(**params) ret = {
} _params: await self.request(**params)
}
except asyncio.TimeoutError:
return
self.lg.debug('i2c response: %s', ret) self.lg.debug('i2c response: %s', ret)
if delay: if delay:
self.lg.debug('delay %s', delay) self.lg.debug('delay %s', delay)
@@ -673,7 +701,7 @@ class MegaD:
cmd = dict( cmd = dict(
pt=config[0][0], pt=config[0][0],
chip=chip, 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) await self.request(**cmd)

View File

@@ -6,6 +6,7 @@ from homeassistant.const import (
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_PRESSURE, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_CO2,
PERCENTAGE, PERCENTAGE,
LIGHT_LUX, LIGHT_LUX,
TEMP_CELSIUS, TEMP_CELSIUS,
@@ -103,6 +104,10 @@ i2c_classes = {
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None), DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None),
DeviceType(DEVICE_CLASS_HUMIDITY, PERCENTAGE, None) DeviceType(DEVICE_CLASS_HUMIDITY, PERCENTAGE, None)
], ],
'dps368': [
DeviceType(DEVICE_CLASS_PRESSURE, PRESSURE_BAR, None),
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None),
],
'mlx90614': [ 'mlx90614': [
Skip, Skip,
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, 'temp'), DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, 'temp'),
@@ -119,9 +124,21 @@ i2c_classes = {
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None), # сенсор встроенный в микросхему DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None), # сенсор встроенный в микросхему
], ],
't67xx': [ 't67xx': [
DeviceType(None, CONCENTRATION_PARTS_PER_MILLION, None) # для co2 нет класса в HA DeviceType(DEVICE_CLASS_CO2, CONCENTRATION_PARTS_PER_MILLION, None)
], ],
'tmp117': [ 'tmp117': [
DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None), DeviceType(DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None),
] ],
'ads1115': [
DeviceType(None, None, 'ch0'),
DeviceType(None, None, 'ch1'),
DeviceType(None, None, 'ch2'),
DeviceType(None, None, 'ch3'),
],
'ads1015': [
DeviceType(None, None, 'ch0'),
DeviceType(None, None, 'ch1'),
DeviceType(None, None, 'ch2'),
DeviceType(None, None, 'ch3'),
],
} }

View File

@@ -33,9 +33,9 @@ from .const import (
CONF_SWITCH, CONF_SWITCH,
DOMAIN, DOMAIN,
CONF_CUSTOM, 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__) lg = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=5) SCAN_INTERVAL = timedelta(seconds=5)
@@ -119,14 +119,8 @@ class MegaRGBW(LightEntity, BaseMegaEntity):
self._task: asyncio.Task = None self._task: asyncio.Task = None
self._restore = None self._restore = None
self.smooth: timedelta = self.customize[CONF_SMOOTH] 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 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 self._max_values = None
@property @property
@@ -189,11 +183,9 @@ class MegaRGBW(LightEntity, BaseMegaEntity):
rgb = [ rgb = [
round(x * self.max_values[i]) for i, x in enumerate(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 = map_reorder_rgb(rgb, RGB, self._color_order)
rgb[self._color_map[x]] for x in 'rgb'
]
return rgb return rgb
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
@@ -267,7 +259,10 @@ class MegaRGBW(LightEntity, BaseMegaEntity):
else: else:
w = None w = None
rgb = rgbw 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, s, v = colorsys.rgb_to_hsv(*[x/self.max_values[i] for i, x in enumerate(rgb)])
h *= 360 h *= 360
s *= 100 s *= 100

View File

@@ -2,7 +2,7 @@
"domain": "mega", "domain": "mega",
"name": "mega", "name": "mega",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/mega_hacs", "documentation": "https://github.com/andvikt/mega_hacs",
"requirements": [ "requirements": [
"beautifulsoup4", "beautifulsoup4",
"lxml" "lxml"
@@ -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": "v1.0.0b1" "version": "v1.0.7"
} }

View File

@@ -20,7 +20,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.template import Template from homeassistant.helpers.template import Template
from .entities import MegaPushEntity 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 from .hub import MegaD
import re import re
@@ -84,9 +84,14 @@ 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 = []
customize = hass.data.get(DOMAIN, {}).get(CONF_CUSTOM, {}).get(mid, {})
for tp in ['sensor', 'i2c']: for tp in ['sensor', 'i2c']:
for port, cfg in config_entry.data.get(tp, {}).items(): for port, cfg in config_entry.data.get(tp, {}).items():
port = int_ignore(port) port = int_ignore(port)
c = customize.get(port, {})
if c.get(CONF_SKIP):
hub.skip_ports |= {port}
continue
for data in cfg: for data in cfg:
hub.lg.debug(f'add sensor on port %s with data %s', port, data) hub.lg.debug(f'add sensor on port %s with data %s', port, data)
sensor = _constructors[tp]( sensor = _constructors[tp](
@@ -146,7 +151,7 @@ class MegaI2C(MegaPushEntity):
ret = tmpl.async_render({'value': ret}) ret = tmpl.async_render({'value': ret})
except: except:
ret = ret ret = ret
return ret return str(ret)
@property @property
def device_class(self): def device_class(self):
@@ -241,7 +246,8 @@ class Mega1WSensor(MegaPushEntity):
tmpl.hass = self.hass tmpl.hass = self.hass
ret = tmpl.async_render({'value': ret}) ret = tmpl.async_render({'value': ret})
except: except:
return ret pass
return str(ret)
@property @property
def name(self): def name(self):
@@ -255,4 +261,4 @@ class Mega1WSensor(MegaPushEntity):
_constructors = { _constructors = {
'sensor': Mega1WSensor, 'sensor': Mega1WSensor,
'i2c': MegaI2C, 'i2c': MegaI2C,
} }

View File

@@ -117,3 +117,8 @@ class PriorityLock(asyncio.Lock):
fut.set_result(True) 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]

View File

@@ -25,8 +25,7 @@
- Плавное диммирование для любых диммируемых объектов (в том числе с аппаратной поддержкой и без), - Плавное диммирование для любых диммируемых объектов (в том числе с аппаратной поддержкой и без),
[подробнее про smooth](https://github.com/andvikt/mega_hacs/wiki/smooth) [подробнее про smooth](https://github.com/andvikt/mega_hacs/wiki/smooth)
- Возможность работы с несколькими megad - Возможность работы с несколькими megad
- Обратная связь по [http](https://github.com/andvikt/mega_hacs/wiki/http) - Обратная связь по [http](https://github.com/andvikt/mega_hacs/wiki/http)
будет выключена в версиях >= 1.0.0, тк в нем нет необходимости)
- Автоматическое восстановление состояний выходов после перезагрузки контроллера - Автоматическое восстановление состояний выходов после перезагрузки контроллера
- Автоматическое добавление/изменение объектов после перезагрузки контроллера - Автоматическое добавление/изменение объектов после перезагрузки контроллера
- [События](https://github.com/andvikt/mega_hacs/wiki/События) на двойные/долгие нажатия - [События](https://github.com/andvikt/mega_hacs/wiki/События) на двойные/долгие нажатия