Compare commits

...

56 Commits

Author SHA1 Message Date
Викторов Андрей Германович
3ee464a896 Bump version: 1.0.10b18 → 1.0.10b19 2021-10-29 15:26:28 +03:00
Викторов Андрей Германович
6368fd7cfc add fill_na 2021-10-29 15:26:21 +03:00
Викторов Андрей Германович
647baa9bb1 Bump version: 1.0.10b17 → 1.0.10b18 2021-10-29 14:56:34 +03:00
Викторов Андрей Германович
614525b315 fix customization i2c 2021-10-29 14:56:30 +03:00
Викторов Андрей Германович
a80b9b4293 Bump version: 1.0.10b16 → 1.0.10b17 2021-10-29 13:22:20 +03:00
Викторов Андрей Германович
f6ae5a9f04 add fill_na
add range
2021-10-29 13:22:14 +03:00
Викторов Андрей Германович
9eed9995fb Bump version: 1.0.10b15 → 1.0.10b16 2021-10-29 12:53:23 +03:00
Викторов Андрей Германович
40ff7cca2e add brightness range 2021-10-29 12:53:17 +03:00
Викторов Андрей Германович
86f58bbfd9 Bump version: 1.0.10b14 → 1.0.10b15 2021-10-29 12:18:29 +03:00
Викторов Андрей Германович
633d5e67bd Bump version: 1.0.10b13 → 1.0.10b14 2021-10-29 12:15:23 +03:00
Викторов Андрей Германович
79e3f33345 add sensor filters 2021-10-29 12:15:02 +03:00
Викторов Андрей Германович
74235a39ad Bump version: 1.0.10b12 → 1.0.10b13 2021-10-29 10:03:22 +03:00
Викторов Андрей Германович
61bcbbd103 add more debug log info 2021-10-29 10:03:18 +03:00
andvikt
6411e6e5f9 Bump version: 1.0.10b11 → 1.0.10b12 2021-10-29 09:13:52 +03:00
andvikt
2020a840a8 timeouts 2021-10-29 09:13:48 +03:00
andvikt
f6c2cd025e Bump version: 1.0.10b10 → 1.0.10b11 2021-10-28 20:14:33 +03:00
andvikt
ab599cd59d . 2021-10-28 20:14:30 +03:00
andvikt
a349cbd4a4 . 2021-10-28 20:13:49 +03:00
andvikt
bd1ba0379f Bump version: 1.0.10b9 → 1.0.10b10 2021-10-28 20:03:15 +03:00
andvikt
70016dd69d . 2021-10-28 20:03:09 +03:00
andvikt
5617e7afa5 Bump version: 1.0.10b9 → 1.0.11b0 2021-10-28 20:01:49 +03:00
andvikt
bcdec2d793 . 2021-10-28 20:01:18 +03:00
Викторов Андрей Германович
c4aae6b3ec Bump version: 1.0.10b8 → 1.0.10b9 2021-10-28 16:41:39 +03:00
Викторов Андрей Германович
58d243dcc3 новы способ адресации кастомизаций 2021-10-28 16:41:36 +03:00
Викторов Андрей Германович
3488d4593a Bump version: 1.0.10b7 → 1.0.10b8 2021-10-28 15:57:59 +03:00
Викторов Андрей Германович
76b8988b83 only bin sensors can update state in http mode 2021-10-28 15:57:55 +03:00
Викторов Андрей Германович
4145eebbdd Bump version: 1.0.10b6 → 1.0.10b7 2021-10-28 15:02:18 +03:00
Викторов Андрей Германович
d4dddc61fd add sensor filters 2021-10-28 15:02:11 +03:00
Викторов Андрей Германович
bc34c1a0b3 add sensor filters 2021-09-21 17:49:43 +03:00
Викторов Андрей Германович
2662b9c3bd Bump version: 1.0.10b5 → 1.0.10b6 2021-09-21 17:33:30 +03:00
Викторов Андрей Германович
8399bbd1de fix focs about i2c id 2021-09-21 17:33:22 +03:00
Викторов Андрей Германович
975b2e8a38 Bump version: 1.0.10b4 → 1.0.10b5 2021-09-21 14:51:36 +03:00
Викторов Андрей Германович
919f1ceb12 add sensor filters 2021-09-21 14:51:33 +03:00
Викторов Андрей Германович
2c58f8e67e add sensor filters 2021-09-21 14:41:32 +03:00
Викторов Андрей Германович
1c65c341b7 add sensor filters 2021-09-21 14:38:31 +03:00
Викторов Андрей Германович
2ef0ebf702 add sensor filters 2021-09-21 14:34:44 +03:00
Викторов Андрей Германович
b8ceec0386 Bump version: 1.0.10b3 → 1.0.10b4 2021-09-21 14:26:04 +03:00
Викторов Андрей Германович
496713cfec add sensor filters 2021-09-21 14:26:00 +03:00
andvikt
1512238763 Bump version: 1.0.10b2 → 1.0.10b3 2021-09-18 22:22:20 +03:00
andvikt
ce1987d0c0 Merge pull request #59 from andvikt/feature/adc328
Feature/adc328
2021-09-18 22:21:30 +03:00
andvikt
b4521a93bb Merge branch 'master' into feature/adc328 2021-09-18 22:21:10 +03:00
andvikt
9322f9cae1 Bump version: 1.0.10b1 → 1.0.10b2 2021-09-18 22:11:56 +03:00
andvikt
52ace90600 add adc support for 328 mega 2021-09-18 22:11:52 +03:00
andvikt
8738a936fe Bump version: 1.0.10b0 → 1.0.10b1 2021-09-18 22:10:50 +03:00
andvikt
2490e09748 add adc support for 328 mega 2021-09-18 22:10:47 +03:00
andvikt
e134a874fa add adc support for 328 mega 2021-09-18 22:08:52 +03:00
andvikt
6fc5391490 Update index.md 2021-09-15 14:09:21 +03:00
andvikt
d37fa11ab3 Merge pull request #58 from andvikt/fix/57
Fix/57
2021-09-15 14:02:52 +03:00
andvikt
1db5670cfd fix #57 2021-09-15 13:59:30 +03:00
andvikt
74b6e200a5 add bumb2version 2021-09-15 13:58:22 +03:00
andvikt
3737bf91a3 Bump version: 1.0.9 → 1.0.10b0 2021-09-15 13:56:37 +03:00
andvikt
747b8e6c36 add bumb2version 2021-09-15 13:56:33 +03:00
andvikt
fec5cef05c add bumb2version 2021-09-15 13:54:34 +03:00
Викторов Андрей Германович
1ec581b08b fix #54 2021-08-26 11:49:26 +03:00
Викторов Андрей Германович
c62a5a2d92 fix formating 2021-08-26 11:16:57 +03:00
Викторов Андрей Германович
b6349eece8 fix formating 2021-08-26 08:40:35 +03:00
13 changed files with 239 additions and 47 deletions

21
.bumpversion.cfg Normal file
View File

@@ -0,0 +1,21 @@
[bumpversion]
current_version = 1.0.10b19
parse = (?P<major>\d+)(\.(?P<minor>\d+))(\.(?P<patch>\d+))(?P<release>[bf]*)(?P<build>\d*)
commit = True
tag = True
serialize =
{major}.{minor}.{patch}{release}{build}
{major}.{minor}.{patch}
[bumpversion:part:release]
optional_value = f
values =
b
f
[bumpversion:part:build]
first_value = 0
[bumpversion:file:custom_components/mega/manifest.json]
search = "version": "v{current_version}"
replace = "version": "v{new_version}"

View File

@@ -16,7 +16,8 @@ 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_FORCE_D, CONF_DEF_RESPONSE, CONF_FORCE_I2C_SCAN, CONF_HEX_TO_FLOAT, \ CONF_CONV_TEMPLATE, CONF_ALL, CONF_FORCE_D, CONF_DEF_RESPONSE, CONF_FORCE_I2C_SCAN, CONF_HEX_TO_FLOAT, \
RGB_COMBINATIONS, CONF_WS28XX, CONF_ORDER, CONF_SMOOTH, CONF_LED, CONF_WHITE_SEP, CONF_CHIP, CONF_RANGE RGB_COMBINATIONS, CONF_WS28XX, CONF_ORDER, CONF_SMOOTH, CONF_LED, CONF_WHITE_SEP, CONF_CHIP, CONF_RANGE, \
CONF_FILTER_VALUES, CONF_FILTER_SCALE, CONF_FILTER_LOW, CONF_FILTER_HIGH, CONF_FILL_NA
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
@@ -51,6 +52,14 @@ LED_LIGHT = \
CUSTOMIZE_PORT = { CUSTOMIZE_PORT = {
vol.Optional(CONF_SKIP, description='исключить порт из сканирования', default=False): bool, vol.Optional(CONF_SKIP, description='исключить порт из сканирования', default=False): bool,
vol.Optional(CONF_FILL_NA, default='last'): vol.Any(
'last',
'none'
),
vol.Optional(CONF_RANGE, description='диапазон диммирования'): [
vol.Range(0, 255),
vol.Range(0, 255),
],
vol.Optional(CONF_INVERT, default=False): bool, vol.Optional(CONF_INVERT, default=False): bool,
vol.Optional(CONF_NAME): vol.Any(str, { vol.Optional(CONF_NAME): vol.Any(str, {
vol.Required(str): str vol.Required(str): str
@@ -74,6 +83,10 @@ CUSTOMIZE_PORT = {
vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_FORCE_I2C_SCAN): bool, vol.Optional(CONF_FORCE_I2C_SCAN): bool,
vol.Optional(CONF_HEX_TO_FLOAT): bool, vol.Optional(CONF_HEX_TO_FLOAT): bool,
vol.Optional(CONF_FILTER_VALUES): [cv.positive_float],
vol.Optional(CONF_FILTER_SCALE): cv.positive_float,
vol.Optional(CONF_FILTER_LOW): cv.positive_float,
vol.Optional(CONF_FILTER_HIGH): cv.positive_float,
vol.Optional(CONF_SMOOTH): cv.time_period_seconds, vol.Optional(CONF_SMOOTH): cv.time_period_seconds,
# vol.Optional(CONF_RANGE): vol.ExactSequence([int, int]), TODO: сделать отбрасывание "плохих" значений # vol.Optional(CONF_RANGE): vol.ExactSequence([int, int]), TODO: сделать отбрасывание "плохих" значений
vol.Optional(str): { vol.Optional(str): {
@@ -99,6 +112,11 @@ CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: { DOMAIN: {
vol.Optional(CONF_ALLOW_HOSTS): [str], vol.Optional(CONF_ALLOW_HOSTS): [str],
vol.Optional('entities'): {
vol.Optional(str): vol.Any(
CUSTOMIZE_PORT,
CUSTOMIZE_DS2413
)},
vol.Optional(vol.Any(str, int), 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(
@@ -111,6 +129,10 @@ CONFIG_SCHEMA = vol.Schema(
CUSTOMIZE_PORT, CUSTOMIZE_PORT,
CUSTOMIZE_DS2413, CUSTOMIZE_DS2413,
), ),
vol.Optional(CONF_FILTER_VALUES): [cv.positive_float],
vol.Optional(CONF_FILTER_SCALE): cv.positive_float,
vol.Optional(CONF_FILTER_LOW): cv.positive_float,
vol.Optional(CONF_FILTER_HIGH): cv.positive_float,
} }
} }
}, },
@@ -210,7 +232,7 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
id = entry.data.get('id', entry.entry_id) id = entry.data.get('id', entry.entry_id)
hub: MegaD = hass.data[DOMAIN].get(id) hub: MegaD = hass.data[DOMAIN].get(id)
if hub is None: if hub is None:
return return True
_LOGGER.debug(f'remove {id}') _LOGGER.debug(f'remove {id}')
_hubs.pop(id, None) _hubs.pop(id, None)
hass.data[DOMAIN].pop(id, None) hass.data[DOMAIN].pop(id, None)
@@ -221,7 +243,7 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
if task is not None: if task is not None:
task.cancel() task.cancel()
if hub is None: if hub is None:
return return True
await hub.stop() await hub.stop()
return True return True

View File

@@ -67,6 +67,7 @@ class MegaBinarySensor(BinarySensorEntity, MegaPushEntity):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.mega.binary_sensors.append(self.port)
self._is_on = None self._is_on = None
self._attrs = None self._attrs = None
self._click_task = None self._click_task = None

View File

@@ -63,7 +63,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 = 24 VERSION = 25
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):

View File

@@ -45,6 +45,11 @@ CONF_SMOOTH = 'smooth'
CONF_WHITE_SEP = 'white_sep' CONF_WHITE_SEP = 'white_sep'
CONF_CHIP = 'chip' CONF_CHIP = 'chip'
CONF_RANGE = 'range' CONF_RANGE = 'range'
CONF_FILL_NA = 'fill_na'
CONF_FILTER_VALUES = 'filter_values'
CONF_FILTER_SCALE = 'filter_scale'
CONF_FILTER_LOW = 'filter_low'
CONF_FILTER_HIGH = 'filter_high'
PLATFORMS = [ PLATFORMS = [
"light", "light",
"switch", "switch",

View File

@@ -12,7 +12,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
from . import hub as h from . import hub as h
from .const import DOMAIN, CONF_CUSTOM, CONF_INVERT, EVENT_BINARY_SENSOR, LONG, \ from .const import DOMAIN, CONF_CUSTOM, CONF_INVERT, EVENT_BINARY_SENSOR, LONG, \
LONG_RELEASE, RELEASE, PRESS, SINGLE_CLICK, DOUBLE_CLICK, EVENT_BINARY, CONF_SMOOTH LONG_RELEASE, RELEASE, PRESS, SINGLE_CLICK, DOUBLE_CLICK, EVENT_BINARY, CONF_SMOOTH, CONF_RANGE
_events_on = False _events_on = False
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -54,7 +54,6 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
smooth=None, smooth=None,
**kwargs, **kwargs,
): ):
super().__init__(mega.updater)
self._smooth = smooth self._smooth = smooth
self.http_cmd = http_cmd self.http_cmd = http_cmd
self._state: State = None self._state: State = None
@@ -85,6 +84,8 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
self._can_smooth_hard = None self._can_smooth_hard = None
if self.http_cmd == 'ds2413': if self.http_cmd == 'ds2413':
self.mega.ds2413_ports |= {self.port} self.mega.ds2413_ports |= {self.port}
super().__init__(coordinator=mega.updater)
@property @property
def is_ws(self): def is_ws(self):
@@ -126,15 +127,20 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
def customize(self): def customize(self):
if self._customize is not None: if self._customize is not None:
return self._customize return self._customize
if self.hass is None: if self.hass is None or self.entity_id is None:
return {} return {}
if self._customize is None: if self._customize is None:
c_entity_id = self.hass.data.get(DOMAIN, {}).get(CONF_CUSTOM).get('entities', {}).get(self.entity_id, {})
self.lg.debug(
'customize %s with %s', self.entity_id, c_entity_id
)
c = self.hass.data.get(DOMAIN, {}).get(CONF_CUSTOM) or {} c = self.hass.data.get(DOMAIN, {}).get(CONF_CUSTOM) or {}
c = c.get(self._mega_id) or {} c = c.get(self._mega_id) or {}
c = c.get(self.port) or {} c = c.get(self.port) or {}
if self.addr is not None and self.index is not None and isinstance(c, dict): if self.addr is not None and self.index is not None and isinstance(c, dict):
idx = self.addr.lower() + f'_a' if self.index == 0 else '_b' idx = self.addr.lower() + f'_a' if self.index == 0 else '_b'
c = c.get(idx, {}) c = c.get(idx, {})
c.update(c_entity_id)
self._customize = c self._customize = c
return self._customize return self._customize
@@ -166,9 +172,9 @@ class BaseMegaEntity(CoordinatorEntity, RestoreEntity):
@property @property
def lg(self) -> logging.Logger: def lg(self) -> logging.Logger:
if self._lg is None: # if self._lg is None:
self._lg = self.mega.lg.getChild(self._name or self.unique_id) # self._lg = self.mega.lg.getChild(self._name or self.unique_id)
return self._lg return _LOGGER
@property @property
def available(self) -> bool: def available(self) -> bool:
@@ -301,35 +307,43 @@ class MegaOutPort(MegaPushEntity):
else: else:
return 255 return 255
@property
def range(self) -> typing.List[int]:
return self.customize.get(CONF_RANGE, [0, 255])
@property @property
def invert(self): def invert(self):
return self.customize.get(CONF_INVERT, False) return self.customize.get(CONF_INVERT, False)
@property @property
def brightness(self): def brightness(self):
ret = None
if not self.dimmer: if not self.dimmer:
return return
val = self.mega.values.get(self.port, {}) val = self.mega.values.get(self.port, {})
if isinstance(val, dict) and len(val) == 0 and self._state is not None: if isinstance(val, dict) and len(val) == 0 and self._state is not None:
return self._state.attributes.get("brightness") ret = safe_int(self._state.attributes.get("brightness"), def_on=self.max_dim, def_off=0, def_val=0)
ret = self._calc_brightness(ret)
elif isinstance(self.port, str) and 'e' in self.port: elif isinstance(self.port, str) and 'e' in self.port:
if isinstance(val, str): if isinstance(val, str):
val = safe_int(val) val = safe_int(val, def_on=self.max_dim, def_off=0, def_val=0)
else: else:
val = 0 val = 0
if val == 0: if val == 0:
return self._brightness ret = self._brightness
elif isinstance(val, (int, float)): elif isinstance(val, (int, float)):
return int(val / self.dimmer_scale) ret = int(val / self.dimmer_scale)
elif val is not None: elif val is not None:
val = val.get("value") val = val.get("value")
if val is None: if val is None:
return return
try: try:
val = int(val) val = int(val)
return val ret = val
except Exception: except Exception:
pass pass
ret = self._cal_reverse_brightness(ret)
return ret
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
@@ -415,6 +429,24 @@ class MegaOutPort(MegaPushEntity):
updater=partial(self.update_from_smooth, update_state=update_state), updater=partial(self.update_from_smooth, update_state=update_state),
)) ))
def _calc_brightness(self, brightness):
pct = brightness / 255
pct = max((0, pct))
pct = min((pct, 1))
l, h = self.range
d = h - l
brightness = round(pct * d + l)
return brightness
def _cal_reverse_brightness(self, brightness):
l, h = self.range
d = h - l
pct = (brightness - l) / d
pct = max((0, pct))
pct = min((pct, 1))
brightness = round(pct * 255)
return brightness
async def async_turn_on(self, brightness=None, transition=None, **kwargs): async def async_turn_on(self, brightness=None, transition=None, **kwargs):
if (time.time() - self._last_called) < 0.1: if (time.time() - self._last_called) < 0.1:
return return
@@ -424,6 +456,7 @@ class MegaOutPort(MegaPushEntity):
if not self.is_on and (brightness is None or brightness == 0): if not self.is_on and (brightness is None or brightness == 0):
brightness = self._restore_brightness brightness = self._restore_brightness
brightness = brightness or self.brightness or 255 brightness = brightness or self.brightness or 255
brightness = self._calc_brightness(brightness)
_prev = safe_int(self.brightness) or 0 _prev = safe_int(self.brightness) or 0
self._brightness = brightness self._brightness = brightness
if self.dimmer and brightness == 0: if self.dimmer and brightness == 0:
@@ -467,7 +500,7 @@ class MegaOutPort(MegaPushEntity):
if (time.time() - self._last_called) < 0.1: if (time.time() - self._last_called) < 0.1:
return return
self._last_called = time.time() self._last_called = time.time()
self._restore_brightness = safe_int(self._brightness) self._restore_brightness = self._cal_reverse_brightness(safe_int(self._brightness))
if not self.dimmer: if not self.dimmer:
transition = None transition = None
cmd = "0" if not self.invert else "1" cmd = "0" if not self.invert else "1"
@@ -503,12 +536,12 @@ class MegaOutPort(MegaPushEntity):
def safe_int(v): def safe_int(v, def_on=1, def_off=0, def_val=None):
if v == 'ON': if v == 'ON':
return 1 return def_on
elif v == 'OFF': elif v == 'OFF':
return 0 return def_off
try: try:
return int(v) return int(v)
except (ValueError, TypeError): except (ValueError, TypeError):
return None return def_val

View File

@@ -117,7 +117,7 @@ class MegaView(HomeAssistantView):
_data = data.copy() _data = data.copy()
if e.startswith('ext'): if e.startswith('ext'):
idx = e[3:] idx = e[3:]
pt = f'{pt_orig}e{idx}' if not hub.new_naming else f'{pt_orig:02}e{idx:02}' pt = f'{pt_orig}e{idx}' if not hub.new_naming else f'{int(pt_orig):02d}e{int(idx):02d}'
_data['pt_orig'] = pt_orig _data['pt_orig'] = pt_orig
_data['value'] = 'ON' if v == '1' else 'OFF' _data['value'] = 'ON' if v == '1' else 'OFF'
_data['m'] = 1 if _data[e] == '0' else 0 # имитация поведения обычного входа, чтобы события обрабатывались аналогично _data['m'] = 1 if _data[e] == '0' else 0 # имитация поведения обычного входа, чтобы события обрабатывались аналогично
@@ -134,7 +134,7 @@ class MegaView(HomeAssistantView):
if ret == 'd' and act: if ret == 'd' and act:
await hub.request(cmd=act.replace(':3', f':{v}')) await hub.request(cmd=act.replace(':3', f':{v}'))
ret = 'd' if hub.force_d else '' ret = 'd' if hub.force_d else ''
else: elif port in hub.binary_sensors:
hub.values[port] = data hub.values[port] = data
for cb in self.callbacks[hub.id][port]: for cb in self.callbacks[hub.id][port]:
cb(data) cb(data)
@@ -147,7 +147,7 @@ class MegaView(HomeAssistantView):
_LOGGER.debug('response %s', ret) _LOGGER.debug('response %s', ret)
Response(body='' if hub.fake_response else ret, content_type='text/plain') Response(body='' if hub.fake_response else ret, content_type='text/plain')
if hub.fake_response and 'value' not in data and 'pt' in data: if hub.fake_response and 'value' not in data and 'pt' in data and port in hub.binary_sensors:
if 'd' in ret: if 'd' in ret:
await hub.request(pt=port, cmd=ret) await hub.request(pt=port, cmd=ret)
else: else:

View File

@@ -164,6 +164,7 @@ class MegaD:
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')
self.binary_sensors = []
async def start(self): async def start(self):
pass pass
@@ -309,8 +310,8 @@ class MegaD:
return ret return ret
except asyncio.TimeoutError: except asyncio.TimeoutError:
self.lg.warning(f'timeout while requesting {url}') self.lg.warning(f'timeout while requesting {url}')
raise # raise
# await asyncio.sleep(1) await asyncio.sleep(1)
raise asyncio.TimeoutError('after 3 tries') raise asyncio.TimeoutError('after 3 tries')
async def save(self): async def save(self):
@@ -426,7 +427,7 @@ class MegaD:
def subscribe(self, port, callback): def subscribe(self, port, callback):
port = int_ignore(port) port = int_ignore(port)
self.lg.debug( self.lg.debug(
f'subscribe %s %s', port, callback f'subscribe %s %s', port, str(callback)
) )
self.http.callbacks[self.id][port].append(callback) self.http.callbacks[self.id][port].append(callback)
@@ -538,7 +539,7 @@ class MegaD:
for n in range(len(values)): for n in range(len(values)):
ext_page = await self.request(pt=port, ext=n) ext_page = await self.request(pt=port, ext=n)
ext_cfg = parse_config(ext_page) ext_cfg = parse_config(ext_page)
pt = f'{port}e{n}' if not self.new_naming else f'{port:02}e{n:02}' pt = f'{port}e{n}' if not self.new_naming else f'{port:02d}e{n:02d}'
if ext_cfg.ety == '1': if ext_cfg.ety == '1':
ret['light'][pt].append({}) ret['light'][pt].append({})
elif ext_cfg.ety == '0': elif ext_cfg.ety == '0':
@@ -566,6 +567,9 @@ class MegaD:
elif cfg.pty == '4' and cfg.m == '2': elif cfg.pty == '4' and cfg.m == '2':
# scl исключаем из сканирования # scl исключаем из сканирования
continue continue
elif cfg.pty is None and nports < 30:
# вроде как это ADC на 328 меге
ret['sensor'][port].append(dict())
elif cfg.pty in ('3', '2', '4'): elif cfg.pty in ('3', '2', '4'):
http_cmd = 'get' http_cmd = 'get'
if cfg.d == '5' and cfg.pty == '3': if cfg.d == '5' and cfg.pty == '3':

View File

@@ -1,6 +1,7 @@
"""Platform for light integration.""" """Platform for light integration."""
import asyncio import asyncio
import logging import logging
import typing
from datetime import timedelta, datetime from datetime import timedelta, datetime
from functools import partial from functools import partial

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": "v1.0.9b1" "version": "v1.0.10b19"
} }

View File

@@ -20,7 +20,8 @@ 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, DOMAIN, CONF_CUSTOM, CONF_SKIP from .const import CONF_KEY, TEMP, HUM, W1, W1BUS, CONF_CONV_TEMPLATE, CONF_HEX_TO_FLOAT, DOMAIN, CONF_CUSTOM, \
CONF_SKIP, CONF_FILTER_VALUES, CONF_FILTER_SCALE, CONF_FILTER_LOW, CONF_FILTER_HIGH, CONF_FILL_NA
from .hub import MegaD from .hub import MegaD
import re import re
@@ -93,7 +94,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
hub.skip_ports |= {port} hub.skip_ports |= {port}
continue 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, constructor: %s', port, data, _constructors[tp])
sensor = _constructors[tp]( sensor = _constructors[tp](
mega=hub, mega=hub,
port=port, port=port,
@@ -107,7 +108,53 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
async_add_devices(devices) async_add_devices(devices)
class MegaI2C(MegaPushEntity): class FilterBadValues(MegaPushEntity):
def __init__(self, *args, **kwargs):
self._prev_value = None
super().__init__(*args, **kwargs)
def filter_value(self, value):
if value \
in self.filter_values \
or (self.filter_low is not None and value < self.filter_low) \
or (self.filter_high is not None and value > self.filter_high) \
or (
self._prev_value is not None
and self.filter_scale is not None
and (
abs(value - self._prev_value) / self._prev_value > self.filter_scale
)
):
if self.fill_na == 'last':
value = self._prev_value
else:
value = None
self._prev_value = value
return value
@property
def filter_values(self):
return self.customize.get(CONF_FILTER_VALUES, self.mega.customize.get(CONF_FILTER_VALUES, []))
@property
def filter_scale(self):
return self.customize.get(CONF_FILTER_SCALE, self.mega.customize.get(CONF_FILTER_SCALE, None))
@property
def filter_low(self):
return self.customize.get(CONF_FILTER_LOW, self.mega.customize.get(CONF_FILTER_LOW, None))
@property
def filter_high(self):
return self.customize.get(CONF_FILTER_HIGH, self.mega.customize.get(CONF_FILTER_HIGH, None))
@property
def fill_na(self):
return self.customize.get(CONF_FILL_NA, 'last')
class MegaI2C(FilterBadValues):
def __init__( def __init__(
self, self,
@@ -124,7 +171,20 @@ class MegaI2C(MegaPushEntity):
@property @property
def customize(self): def customize(self):
return super().customize.get(self.id_suffix, {}) or {} ret = super().customize
_old = ret.get(self.id_suffix)
if _old is not None:
ret = ret.copy()
ret.update(_old)
return ret
@property
def extra_state_attributes(self):
attrs = super().extra_state_attributes or {}
attrs.update({
'i2c_id': self.id_suffix,
})
return attrs
@property @property
def device_class(self): def device_class(self):
@@ -136,11 +196,10 @@ class MegaI2C(MegaPushEntity):
@property @property
def state(self): def state(self):
# self.lg.debug(f'get % all states: %', self._params, self.mega.values)
ret = self.mega.values.get(self._params) ret = self.mega.values.get(self._params)
if self.customize.get(CONF_HEX_TO_FLOAT): if self.customize.get(CONF_HEX_TO_FLOAT):
try: try:
ret = struct.unpack('!f', bytes.fromhex('41973333'))[0] ret = struct.unpack('!f', bytes.fromhex(ret))[0]
except: except:
self.lg.warning(f'could not convert {ret} form hex to float') self.lg.warning(f'could not convert {ret} form hex to float')
tmpl: Template = self.customize.get(CONF_CONV_TEMPLATE, self.customize.get(CONF_VALUE_TEMPLATE)) tmpl: Template = self.customize.get(CONF_CONV_TEMPLATE, self.customize.get(CONF_VALUE_TEMPLATE))
@@ -151,14 +210,16 @@ class MegaI2C(MegaPushEntity):
ret = tmpl.async_render({'value': ret}) ret = tmpl.async_render({'value': ret})
except: except:
ret = ret ret = ret
return str(ret) ret = self.filter_value(ret)
if ret is not None:
return str(ret)
@property @property
def device_class(self): def device_class(self):
return self._device_class return self._device_class
class Mega1WSensor(MegaPushEntity): class Mega1WSensor(FilterBadValues):
def __init__( def __init__(
self, self,
@@ -175,11 +236,12 @@ class Mega1WSensor(MegaPushEntity):
:param patt: pattern to extract value, must have at least one group that will contain parsed value :param patt: pattern to extract value, must have at least one group that will contain parsed value
""" """
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._value = None
self.key = key self.key = key
self._value = None
self._device_class = device_class self._device_class = device_class
self._unit_of_measurement = unit_of_measurement self._unit_of_measurement = unit_of_measurement
self.mega.sensors.append(self) self.mega.sensors.append(self)
self.prev_value = None
@property @property
def unit_of_measurement(self): def unit_of_measurement(self):
@@ -198,7 +260,7 @@ class Mega1WSensor(MegaPushEntity):
if self.key: if self.key:
return super().unique_id + f'_{self.key}' return super().unique_id + f'_{self.key}'
else: else:
return super(Mega1WSensor, self).unique_id return super().unique_id
@property @property
def device_class(self): def device_class(self):
@@ -215,6 +277,8 @@ class Mega1WSensor(MegaPushEntity):
@property @property
def state(self): def state(self):
ret = None ret = None
if not hasattr(self, 'key'):
return None
if self.key: if self.key:
try: try:
ret = self.mega.values.get(self.port, {}) ret = self.mega.values.get(self.port, {})
@@ -227,13 +291,16 @@ class Mega1WSensor(MegaPushEntity):
return return
else: else:
ret = self.mega.values.get(self.port, {}).get('value') ret = self.mega.values.get(self.port, {}).get('value')
if ret is None and self._state is not None: if ret is None and self.fill_na == 'fill_na' and self.prev_value is not None:
ret = self.prev_value
elif ret is None and self.fill_na == 'fill_na' and self._state is not None:
ret = self._state.state ret = self._state.state
try: try:
ret = float(ret) ret = float(ret)
ret = str(ret) ret = str(ret)
except: except:
ret = None self.lg.debug(f'could not convert to float "{ret}"')
ret = self.prev_value
if self.customize.get(CONF_HEX_TO_FLOAT): if self.customize.get(CONF_HEX_TO_FLOAT):
try: try:
ret = struct.unpack('!f', bytes.fromhex(ret))[0] ret = struct.unpack('!f', bytes.fromhex(ret))[0]
@@ -247,14 +314,20 @@ class Mega1WSensor(MegaPushEntity):
ret = tmpl.async_render({'value': ret}) ret = tmpl.async_render({'value': ret})
except: except:
pass pass
return str(ret) ret = self.filter_value(ret)
self.prev_value = ret
if ret is not None:
return str(ret)
@property @property
def name(self): def name(self):
n = super().name n = super().name
c = self.customize.get(CONF_NAME, {}) c = self.customize.get(CONF_NAME, {})
if isinstance(c, dict): if isinstance(c, dict):
c = c.get(self.key) try:
c = c.get(self.key)
except:
pass
return c or n return c or n

View File

@@ -57,7 +57,9 @@
Информация об обновлениях приходит с некоторым интервалом, чтобы вручную проверить наличие обновлений Информация об обновлениях приходит с некоторым интервалом, чтобы вручную проверить наличие обновлений
нажмите три точки возле интеграции в меню HACS и нажмите `обновить информацию` нажмите три точки возле интеграции в меню HACS и нажмите `обновить информацию`
Чтобы включить возможность использования бета-версий, зайдите в HACS, найдите интеграцию MegaD, нажмите три точки, ## Беты {: #beta }
Иногда я буду выпускать бета-версии, в стабильности которых пока не уверен и не готов раскатывать на всех, поэтому в целях тестирвоания
нужно принудительно включать поддержку бет, для этого зайдите в HACS, найдите интеграцию MegaD, нажмите три точки,
там кнопка "переустановить" или reinstall, дальше нужно нажать галку "показывать бета-версии" там кнопка "переустановить" или reinstall, дальше нужно нажать галку "показывать бета-версии"
## Зависимости {: #deps } ## Зависимости {: #deps }

View File

@@ -62,7 +62,7 @@ mega:
!!! note "" !!! note ""
- **smooth** (float, 0): программное плавное диммирование. Это поле отвечает за кол-во секунд, за которое яркость - **smooth** (float, 0): программное плавное диммирование. Это поле отвечает за кол-во секунд, за которое яркость
диммера набирает от 0 до 100% диммера набирает от 0 до 100%
- **limits** (list[int, int], [0, 255]), *начиная с версии 1.1.0*: границы диммирования в абсолютных единицах контроллера. При диммировании 1% - **range** (list[int, int], [0, 255]), *начиная с версии 1.1.0*: границы диммирования в абсолютных единицах 0..255. При диммировании 1%
будет равен левой границе, 100% - правой. будет равен левой границе, 100% - правой.
[Подробнее про плавное диммирование](smooth.md) [Подробнее про плавное диммирование](smooth.md)
### MegaD-16R-XT, MegaD-16PWM ### MegaD-16R-XT, MegaD-16PWM
@@ -159,6 +159,14 @@ some_led2:
- **value_template** (str): шаблон для конвертации, например `{{(value|float)/100}}` - **value_template** (str): шаблон для конвертации, например `{{(value|float)/100}}`
- **device_class** (str): класс устройства, [список доступных](https://developers.home-assistant.io/docs/core/entity/sensor#available-device-classes) - **device_class** (str): класс устройства, [список доступных](https://developers.home-assistant.io/docs/core/entity/sensor#available-device-classes)
- **hex_to_float** (bool, false): если ваш датчик возвращает float запакованный в HEX, интеграция его распакует (перед применением темплейта) - **hex_to_float** (bool, false): если ваш датчик возвращает float запакованный в HEX, интеграция его распакует (перед применением темплейта)
- **filter_high** (float, none): верхняя граница значений, выше нее значения будут считаться ошибочными и отбрасываться. [Доступно так же глобальное значение](#filter_high)
- **filter_low** (float, none): нижняя граница значений, ниже нее значения будут считаться ошибочными и отбрасываться. [Доступно так же глобальное значение](#filter_low)
- **filter_values** ([float], none): список значений, которые считаются ошибочными. [Доступно так же глобальное значение](#filter_values)
- **filter_scale** (float, none): значение отклонения от текущего значения, которое будет считаться выбросом и отфильтруется, например если
установить 1, то это означает, что при росте показателя сенсора на 100% и больше или падении на 100% и больше, такое значение не будет отображаться.
[Доступно так же глобальное значение](#filter_scale)
- **fill_na** (str, last): чем заполнять пропуски, по-умолчанию last, что означает последнее значение, можно так же поставить none-тогда будут пропуски (разрывы на графике).
При этом есть так же особенности адресации, так для сенсора на одном порте с одним значением: При этом есть так же особенности адресации, так для сенсора на одном порте с одним значением:
```yaml ```yaml
36: 36:
@@ -183,10 +191,10 @@ some_led2:
# конфиг # конфиг
``` ```
#### i2c {: #i2c} #### i2c {: #i2c}
Для сенсоров i2c адресация конфига такая: Для сенсоров i2c нужно так же указать id сенсора, который можно посмотреть в атрибутах объекта на [странице разработчика](https://my.home-assistant.io/redirect/developer_states/).
```yaml ```yaml
36: 36:
htu21d_humidity: # entity_id сенсора без приставки sensor., видно в интерфейсе HA htu21d_humidity: # i2c_id
# конфиг # конфиг
``` ```
[Подробнее про i2c](i2c.md) [Подробнее про i2c](i2c.md)
@@ -213,5 +221,27 @@ mega:
```yaml ```yaml
mega: mega:
allow_hosts: allow_hosts:
- 192.168.1.20 - 192.168.1.20
``` ```
### filter_high {: #filter_high}
Верхняя граница значений датчиков по-умолчанию, выше нее значения будут считаться ошибочными и отбрасываться
### filter_low {: #filter_low}
Нижняя граница значений датчиков по-умолчанию, ниже нее значения будут считаться ошибочными и отбрасываться
### filter_values {: #filter_values }
Список значений, которые считаются ошибочными, настройка по-умолчанию для всех датчиков. Удобно, если у вас много
однотипных датчиков
```yaml
mega:
filter_values: [-82, - 150]
```
### filter_scale {: #filter_scale }
Значение отклонения от текущего значения, которое будет считаться выбросом и отфильтруется, например если
установить 1, то это означает, что при росте показателя сенсора на 100% и больше или падении на 100% и больше, такое значение не будет отображаться.
```yaml
mega:
filter_scale: 1 # 100%
```