Compare commits

...

1 Commits

Author SHA1 Message Date
Andrey
49fcf880d9 add i2c sensors 2021-03-03 12:01:02 +03:00
11 changed files with 420 additions and 65 deletions

View File

@@ -1,43 +1,121 @@
import re from urllib.parse import urlparse, parse_qsl
from bs4 import BeautifulSoup
page = '''
<html><head></head><body><a href="/sec/?pt=33">Back</a><br>0x15 - <a href="/sec/?pt=33&amp;scl=32&amp;i2c_dev=t67xx">T67XX</a><br>0x40 - <a href="/sec/?pt=33&amp;scl=32&amp;i2c_dev=htu21d">HTU21D</a>/PCA9685/HM3301<br>0x4a - <a href="/sec/?pt=33&amp;scl=32&amp;i2c_dev=max44009">MAX44009</a><br>
</body></html>
'''
from urllib.parse import parse_qsl, urlparse
from bs4 import BeautifulSoup
from homeassistant.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_PRESSURE,
)
PATT_FW = re.compile(r'fw:\s(.+)\)') def parse_scan_page(page: str):
data = """ ret = []
<html><head></head><body>MegaD-2561 by <a href="http://ab-log.ru">ab-log.ru</a> (fw: 4.48b7)<br><a href="/sec/?cf=1">Config</a><br>-- MODS --<br><a href="/sec/?cf=3">XP1</a><br><a href="/sec/?cf=4">XP2</a><br>-- XT2 --<br><a href="/sec/?pt=30">P30 - OUT</a><br><a href="/sec/?pt=31">P31 - OUT</a><br><a href="/sec/?pt=32">P32 - IN</a><br><a href="/sec/?pt=33">P33 - I2C/SCL</a><br><a href="/sec/?pt=34">P34 - DS</a><br><a href="/sec/?pt=35">P35 - NC</a><br>-- XP5/6 --<br><a href="/sec/?pt=36">P36 - ADC</a><br><a href="/sec/?pt=37">P37 - NC</a></body></html> req = []
<head></head> page = BeautifulSoup(page, features="lxml")
<body>MegaD-2561 by <a href="http://ab-log.ru">ab-log.ru</a> (fw: 4.48b7)<br><a href="/sec/?cf=1">Config</a><br>-- MODS --<br><a href="/sec/?cf=3">XP1</a><br><a href="/sec/?cf=4">XP2</a><br>-- XT2 --<br><a href="/sec/?pt=30">P30 - OUT</a><br><a href="/sec/?pt=31">P31 - OUT</a><br><a href="/sec/?pt=32">P32 - IN</a><br><a href="/sec/?pt=33">P33 - I2C/SCL</a><br><a href="/sec/?pt=34">P34 - DS</a><br><a href="/sec/?pt=35">P35 - NC</a><br>-- XP5/6 --<br><a href="/sec/?pt=36">P36 - ADC</a><br><a href="/sec/?pt=37">P37 - NC</a></body> for x in page.find_all('a'):
MegaD-2561 by params = x.get('href')
<a href="http://ab-log.ru">ab-log.ru</a> if params is None:
(fw: 4.48b7) continue
<br> params = dict(parse_qsl(urlparse(params).query))
<a href="/sec/?cf=1">Config</a> if 'i2c_dev' in params:
<br> dev = params['i2c_dev']
-- MODS -- classes = i2c_classes.get(dev, [])
<br> for i, c in enumerate(classes):
<a href="/sec/?cf=3">XP1</a> if c is Skip:
<br> continue
<a href="/sec/?cf=4">XP2</a> elif c is Request:
<br> req.append(params)
-- XT2 -- continue
<br> elif isinstance(c, tuple):
<a href="/sec/?pt=30">P30 - OUT</a> suffix, c = c
<br> elif isinstance(c, str):
<a href="/sec/?pt=31">P31 - OUT</a> suffix = c
<br> else:
<a href="/sec/?pt=32">P32 - IN</a> suffix = ''
<br> if 'addr' in params:
<a href="/sec/?pt=33">P33 - I2C/SCL</a> suffix += f"_{params['addr']}" if suffix else str(params['addr'])
<br> if suffix:
<a href="/sec/?pt=34">P34 - DS</a> _dev = f'{dev}_{suffix}'
<br> else:
<a href="/sec/?pt=35">P35 - NC</a> _dev = dev
<br> params = params.copy()
-- XP5/6 -- if i > 0:
<br> params['i2c_par'] = i
<a href="/sec/?pt=36">P36 - ADC</a> ret.append({
<br> 'id_suffix': _dev,
<a href="/sec/?pt=37">P37 - NC</a> 'device_class': c,
<body>MegaD-2561 by <a href="http://ab-log.ru">ab-log.ru</a> (fw: 4.48b7)<br><a href="/sec/?cf=1">Config</a><br>-- MODS --<br><a href="/sec/?cf=3">XP1</a><br><a href="/sec/?cf=4">XP2</a><br>-- XT2 --<br><a href="/sec/?pt=30">P30 - OUT</a><br><a href="/sec/?pt=31">P31 - OUT</a><br><a href="/sec/?pt=32">P32 - IN</a><br><a href="/sec/?pt=33">P33 - I2C/SCL</a><br><a href="/sec/?pt=34">P34 - DS</a><br><a href="/sec/?pt=35">P35 - NC</a><br>-- XP5/6 --<br><a href="/sec/?pt=36">P36 - ADC</a><br><a href="/sec/?pt=37">P37 - NC</a></body> 'params': params,
<html><head></head><body>MegaD-2561 by <a href="http://ab-log.ru">ab-log.ru</a> (fw: 4.48b7)<br><a href="/sec/?cf=1">Config</a><br>-- MODS --<br><a href="/sec/?cf=3">XP1</a><br><a href="/sec/?cf=4">XP2</a><br>-- XT2 --<br><a href="/sec/?pt=30">P30 - OUT</a><br><a href="/sec/?pt=31">P31 - OUT</a><br><a href="/sec/?pt=32">P32 - IN</a><br><a href="/sec/?pt=33">P33 - I2C/SCL</a><br><a href="/sec/?pt=34">P34 - DS</a><br><a href="/sec/?pt=35">P35 - NC</a><br>-- XP5/6 --<br><a href="/sec/?pt=36">P36 - ADC</a><br><a href="/sec/?pt=37">P37 - NC</a></body></html> })
""" req.append(params)
print(PATT_FW.search(data).groups()[0]) return req, ret
class Skip:
pass
class Request:
pass
i2c_classes = {
'htu21d': [
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
],
'sht31': [
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
],
'max44009': [
DEVICE_CLASS_ILLUMINANCE
],
'bh1750': [
DEVICE_CLASS_ILLUMINANCE
],
'tsl2591': [
DEVICE_CLASS_ILLUMINANCE
],
'bmp180': [
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
],
'bmx280': [
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_HUMIDITY
],
'mlx90614': [
Skip,
('temp', DEVICE_CLASS_TEMPERATURE),
('object', DEVICE_CLASS_TEMPERATURE),
],
'ptsensor': [
Request, # запрос на измерение
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
],
'mcp9600': [
DEVICE_CLASS_TEMPERATURE, # термопара
DEVICE_CLASS_TEMPERATURE, # сенсор встроенный в микросхему
],
't67xx': [
None # для co2 нет класса в HA
],
'tmp117': [
DEVICE_CLASS_TEMPERATURE,
]
}
print(parse_scan_page(page))

View File

@@ -7,7 +7,7 @@ import voluptuous as vol
from homeassistant.const import ( from homeassistant.const import (
CONF_SCAN_INTERVAL, CONF_ID, CONF_NAME, CONF_DOMAIN, CONF_SCAN_INTERVAL, CONF_ID, CONF_NAME, CONF_DOMAIN,
CONF_UNIT_OF_MEASUREMENT, CONF_HOST, CONF_VALUE_TEMPLATE CONF_UNIT_OF_MEASUREMENT, CONF_HOST, CONF_VALUE_TEMPLATE, CONF_DEVICE_CLASS
) )
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers.service import bind_hass from homeassistant.helpers.service import bind_hass
@@ -34,6 +34,10 @@ CUSTOMIZE_PORT = {
vol.Any(str, { vol.Any(str, {
vol.Required(str): str vol.Required(str): str
}), }),
vol.Optional(CONF_DEVICE_CLASS):
vol.Any(str, {
vol.Required(str): str
}),
vol.Optional( vol.Optional(
CONF_RESPONSE_TEMPLATE, CONF_RESPONSE_TEMPLATE,
description='шаблон ответа когда на этот порт приходит' description='шаблон ответа когда на этот порт приходит'

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 = 17 VERSION = 18
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

@@ -25,6 +25,7 @@ class Config:
inta: str = field(compare=False, hash=False, default=None) inta: str = field(compare=False, hash=False, default=None)
misc: str = field(compare=False, hash=False, default=None) misc: str = field(compare=False, hash=False, default=None)
eact: str = field(compare=False, hash=False, default=None) eact: str = field(compare=False, hash=False, default=None)
src: BeautifulSoup = field(compare=False, hash=False, default=None)
def parse_config(page: str): def parse_config(page: str):
@@ -43,7 +44,7 @@ def parse_config(page: str):
v = page.find('input', attrs={'name': x}) v = page.find('input', attrs={'name': x})
if v: if v:
ret[x] = v['value'] ret[x] = v['value']
return Config(**ret) return Config(**ret, src=page)
DIGITAL_IN = Config(pty="0") DIGITAL_IN = Config(pty="0")

View File

@@ -318,7 +318,7 @@ class MegaOutPort(MegaPushEntity):
_cmd = {"cmd": f"{self.cmd_port}:{cmd}"} _cmd = {"cmd": f"{self.cmd_port}:{cmd}"}
if self.addr: if self.addr:
_cmd['addr'] = self.addr _cmd['addr'] = self.addr
await self.mega.request(**_cmd) await self.mega.request(**_cmd, priority=-1)
if self.index is not None: if self.index is not None:
# обновление текущего стейта для ds2413 # обновление текущего стейта для ds2413
await self.mega.get_port( await self.mega.get_port(
@@ -342,7 +342,7 @@ class MegaOutPort(MegaPushEntity):
_cmd = {"cmd": f"{self.cmd_port}:{cmd}"} _cmd = {"cmd": f"{self.cmd_port}:{cmd}"}
if self.addr: if self.addr:
_cmd['addr'] = self.addr _cmd['addr'] = self.addr
await self.mega.request(**_cmd) await self.mega.request(**_cmd, priority=-1)
if self.index is not None: if self.index is not None:
# обновление текущего стейта для ds2413 # обновление текущего стейта для ds2413
await self.mega.get_port( await self.mega.get_port(

View File

@@ -24,7 +24,8 @@ from .const import (
) )
from .entities import set_events_off, BaseMegaEntity, MegaOutPort from .entities import set_events_off, BaseMegaEntity, MegaOutPort
from .exceptions import CannotConnect, NoPort from .exceptions import CannotConnect, NoPort
from .tools import make_ints, int_ignore from .i2c import parse_scan_page
from .tools import make_ints, int_ignore, PriorityLock
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\.]+)')
@@ -83,6 +84,7 @@ class MegaD:
extenders=None, extenders=None,
ext_in=None, ext_in=None,
ext_acts=None, ext_acts=None,
i2c_sensors=None,
**kwargs, **kwargs,
): ):
"""Initialize.""" """Initialize."""
@@ -100,6 +102,7 @@ class MegaD:
self.extenders = extenders or [] self.extenders = extenders or []
self.ext_in = ext_in or {} self.ext_in = ext_in or {}
self.ext_act = ext_acts or {} self.ext_act = ext_acts or {}
self.i2c_sensors = i2c_sensors or []
self.poll_outs = poll_outs self.poll_outs = poll_outs
self.update_all = update_all if update_all is not None else True self.update_all = update_all if update_all is not None else True
self.nports = nports self.nports = nports
@@ -113,7 +116,7 @@ class MegaD:
self.id = id self.id = id
self.lck = asyncio.Lock() self.lck = asyncio.Lock()
self.last_long = {} self.last_long = {}
self._http_lck = asyncio.Lock() self._http_lck = PriorityLock()
self._notif_lck = asyncio.Lock() self._notif_lck = asyncio.Lock()
self.cnd = asyncio.Condition() self.cnd = asyncio.Condition()
self.online = True self.online = True
@@ -245,6 +248,12 @@ class MegaD:
Polling ports Polling ports
""" """
self.lg.debug('poll') self.lg.debug('poll')
for x in self.i2c_sensors:
if not isinstance(x, dict):
continue
ret = await self._update_i2c(x)
if isinstance(ret, dict):
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):
@@ -281,13 +290,13 @@ class MegaD:
async def send_command(self, port=None, cmd=None): async def send_command(self, port=None, cmd=None):
return await self.request(pt=port, cmd=cmd) return await self.request(pt=port, cmd=cmd)
async def request(self, **kwargs): async def request(self, priority=0, **kwargs):
cmd = '&'.join([f'{k}={v}' for k, v in kwargs.items() if v is not None]) cmd = '&'.join([f'{k}={v}' for k, v in kwargs.items() if v is not None])
url = f"http://{self.host}/{self.sec}" url = f"http://{self.host}/{self.sec}"
if cmd: if cmd:
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: async with self._http_lck(priority):
async with aiohttp.request("get", url=url) as req: async with aiohttp.request("get", url=url) as req:
if req.status != 200: if req.status != 200:
self.lg.warning('%s returned %s (%s)', url, req.status, await req.text()) self.lg.warning('%s returned %s (%s)', url, req.status, await req.text())
@@ -514,12 +523,24 @@ class MegaD:
ret[f'{port}e{i}'] = x ret[f'{port}e{i}'] = x
return ret return ret
async def _update_i2c(self, params):
"""
Обновление портов i2c
:param params: параметры url
:return:
"""
_params = tuple(params.items())
return {
_params: await self.request(**params)
}
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() ret['mqtt_id'] = await self.get_mqtt_id()
ret['extenders'] = extenders = [] ret['extenders'] = extenders = []
ret['ext_in'] = ext_int = {} ret['ext_in'] = ext_int = {}
ret['ext_acts'] = ext_acts = {} ret['ext_acts'] = ext_acts = {}
ret['i2c_sensors'] = i2c_sensors = []
async for port, cfg in self.scan_ports(nports): async for port, cfg in self.scan_ports(nports):
if cfg.pty == "0": if cfg.pty == "0":
ret['binary_sensor'][port].append({}) ret['binary_sensor'][port].append({})
@@ -559,6 +580,16 @@ class MegaD:
values = values.split(';') values = values.split(';')
for n in range(len(values)): for n in range(len(values)):
ret['light'][f'{port}e{n}'].append({'dimmer': True, 'dimmer_scale': 16}) ret['light'][f'{port}e{n}'].append({'dimmer': True, 'dimmer_scale': 16})
elif cfg.pty == '4' and cfg.gr == '0':
# i2c в режиме ANY
scan = cfg.src.find('a', text='I2C Scan')
self.lg.debug(f'find scan link: %s', scan)
if scan is not None:
page = await self.request(pt=port, cmd='scan')
req, parsed = parse_scan_page(page)
self.lg.debug(f'scan results: %s', (req, parsed))
ret['i2c'][port].append(parsed)
i2c_sensors.extend(req)
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

@@ -0,0 +1,108 @@
from urllib.parse import parse_qsl, urlparse
from bs4 import BeautifulSoup
from homeassistant.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_PRESSURE,
)
def parse_scan_page(page: str):
ret = []
req = []
page = BeautifulSoup(page, features="lxml")
for x in page.find_all('a'):
params = x.get('href')
if params is None:
continue
params = dict(parse_qsl(urlparse(params).query))
if 'i2c_dev' in params:
dev = params['i2c_dev']
classes = i2c_classes.get(dev, [])
for i, c in enumerate(classes):
if c is Skip:
continue
elif c is Request:
req.append(params)
continue
elif isinstance(c, tuple):
suffix, c = c
elif isinstance(c, str):
suffix = c
else:
suffix = ''
if 'addr' in params:
suffix += f"_{params['addr']}" if suffix else str(params['addr'])
if suffix:
_dev = f'{dev}_{suffix}'
else:
_dev = dev
params = params.copy()
if i > 0:
params['i2c_par'] = i
ret.append({
'id_suffix': _dev,
'device_class': c,
'params': params,
})
req.append(params)
return req, ret
class Skip:
pass
class Request:
pass
i2c_classes = {
'htu21d': [
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
],
'sht31': [
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
],
'max44009': [
DEVICE_CLASS_ILLUMINANCE
],
'bh1750': [
DEVICE_CLASS_ILLUMINANCE
],
'tsl2591': [
DEVICE_CLASS_ILLUMINANCE
],
'bmp180': [
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
],
'bmx280': [
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_HUMIDITY
],
'mlx90614': [
Skip,
('temp', DEVICE_CLASS_TEMPERATURE),
('object', DEVICE_CLASS_TEMPERATURE),
],
'ptsensor': [
Request, # запрос на измерение
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
],
'mcp9600': [
DEVICE_CLASS_TEMPERATURE, # термопара
DEVICE_CLASS_TEMPERATURE, # сенсор встроенный в микросхему
],
't67xx': [
None # для co2 нет класса в HA
],
'tmp117': [
DEVICE_CLASS_TEMPERATURE,
]
}

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.5.1b2" "version": "v0.6.1b1"
} }

View File

@@ -14,6 +14,7 @@ from homeassistant.const import (
CONF_UNIQUE_ID, CONF_UNIQUE_ID,
CONF_ID, CONF_ID,
CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE, CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE,
CONF_DEVICE_CLASS,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.template import Template from homeassistant.helpers.template import Template
@@ -82,21 +83,40 @@ 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 = []
for port, cfg in config_entry.data.get('sensor', {}).items(): for tp in ['sensor', 'i2c']:
port = int_ignore(port) for port, cfg in config_entry.data.get(tp, {}).items():
for data in cfg: port = int_ignore(port)
hub.lg.debug(f'add sensor on port %s with data %s', port, data) for data in cfg:
sensor = Mega1WSensor( hub.lg.debug(f'add sensor on port %s with data %s', port, data)
mega=hub, sensor = _constructors[tp](
port=port, mega=hub,
config_entry=config_entry, port=port,
**data, config_entry=config_entry,
) **data,
devices.append(sensor) )
devices.append(sensor)
async_add_devices(devices) async_add_devices(devices)
class MegaI2C(MegaPushEntity):
def __init__(self, *args, device_class: str, params: dict, **kwargs):
self._device_class = device_class
self._params = tuple(params.items())
super().__init__(*args, **kwargs)
def device_class(self):
return self._device_class
def state(self):
return self.mega.values[self._params]
@property
def device_class(self):
return self._device_class
class Mega1WSensor(MegaPushEntity): class Mega1WSensor(MegaPushEntity):
def __init__( def __init__(
@@ -141,7 +161,15 @@ class Mega1WSensor(MegaPushEntity):
@property @property
def device_class(self): def device_class(self):
return self._device_class _u = self.customize.get(CONF_DEVICE_CLASS, None)
if _u is None:
return self._device_class
elif isinstance(_u, str):
return _u
elif isinstance(_u, dict) and self.key in _u:
return _u[self.key]
else:
return self._device_class
@property @property
def state(self): def state(self):
@@ -177,4 +205,10 @@ class Mega1WSensor(MegaPushEntity):
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) c = c.get(self.key)
return c or n return c or n
_constructors = {
'sensor': Mega1WSensor,
'i2c': MegaI2C,
}

View File

@@ -1,3 +1,9 @@
import asyncio
import itertools
from heapq import heappush
from contextlib import asynccontextmanager
_params = ['m', 'click', 'cnt', 'pt'] _params = ['m', 'click', 'cnt', 'pt']
@@ -17,4 +23,95 @@ def int_ignore(x):
try: try:
return int(x) return int(x)
except (TypeError, ValueError): except (TypeError, ValueError):
return x return x
class PriorityLock(asyncio.Lock):
"""
You can acquire lock with some kind of priority in mind, so that locks with higher priority will be released first.
priority can be set with lck.acquire(1)
or by using context manager:
>>> lck = PriorityLock()
... async with lck(1):
... # do something
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._cnt = itertools.count()
def __call__(self, priority=0):
return self._with_priority(priority)
@asynccontextmanager
async def _with_priority(self, p):
await self.acquire(p)
try:
yield
finally:
self.release()
async def acquire(self, priority=0) -> bool:
"""Acquire a lock.
This method blocks until the lock is unlocked, then sets it to
locked and returns True.
"""
if (not self._locked and (self._waiters is None or
all(w.cancelled() for _, w in self._waiters))):
self._locked = True
return True
if self._waiters is None:
self._waiters = []
fut = self._loop.create_future()
cnt = next(self._cnt)
heappush(self._waiters, (priority, cnt, fut))
# Finally block should be called before the CancelledError
# handling as we don't want CancelledError to call
# _wake_up_first() and attempt to wake up itself.
try:
try:
await fut
finally:
self._waiters.remove((priority, cnt, fut))
except asyncio.exceptions.CancelledError:
if not self._locked:
self._wake_up_first()
raise
self._locked = True
return True
def release(self):
"""Release a lock.
When the lock is locked, reset it to unlocked, and return.
If any other coroutines are blocked waiting for the lock to become
unlocked, allow exactly one of them to proceed.
When invoked on an unlocked lock, a RuntimeError is raised.
There is no return value.
"""
if self._locked:
self._locked = False
self._wake_up_first()
else:
raise RuntimeError('Lock is not acquired.')
def _wake_up_first(self):
"""Wake up the first waiter if it isn't done."""
if not self._waiters:
return
try:
_, _, fut = self._waiters[0]
except IndexError:
return
# .done() necessarily means that a waiter will wake up later on and
# either take the lock, or, if it was cancelled and lock wasn't
# taken already, will hit this again and wake up a new waiter.
if not fut.done():
fut.set_result(True)

View File

@@ -30,6 +30,8 @@
выполнении предыдущей. выполнении предыдущей.
- поддержка [ds2413](https://www.ab-log.ru/smart-house/ethernet/megad-2w) (начиная с версии 0.4.1) - поддержка [ds2413](https://www.ab-log.ru/smart-house/ethernet/megad-2w) (начиная с версии 0.4.1)
- поддержка MCP23008/MCP23017/PCA9685 (начиная с версии 0.5.1) - поддержка MCP23008/MCP23017/PCA9685 (начиная с версии 0.5.1)
- поддержка всех возможных датчиков в режиме I2C-ANY, полный список поддерживаемых датчиков датчиков
[по ссылке](https://github.com/andvikt/mega_hacs/wiki/ш2с)
## Установка ## Установка
Рекомендованный способ с поддержкой обновлений - [HACS](https://hacs.xyz/docs/installation/installation): Рекомендованный способ с поддержкой обновлений - [HACS](https://hacs.xyz/docs/installation/installation):