Motion Blinds API lock (#68587)
This commit is contained in:
parent
cec3a08b95
commit
425b825ae9
3 changed files with 79 additions and 64 deletions
|
@ -1,4 +1,5 @@
|
|||
"""The motion_blinds component."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from socket import timeout
|
||||
|
@ -20,6 +21,7 @@ from .const import (
|
|||
DEFAULT_INTERFACE,
|
||||
DEFAULT_WAIT_FOR_PUSH,
|
||||
DOMAIN,
|
||||
KEY_API_LOCK,
|
||||
KEY_COORDINATOR,
|
||||
KEY_GATEWAY,
|
||||
KEY_MULTICAST_LISTENER,
|
||||
|
@ -56,6 +58,7 @@ class DataUpdateCoordinatorMotionBlinds(DataUpdateCoordinator):
|
|||
update_interval=update_interval,
|
||||
)
|
||||
|
||||
self.api_lock = coordinator_info[KEY_API_LOCK]
|
||||
self._gateway = coordinator_info[KEY_GATEWAY]
|
||||
self._wait_for_push = coordinator_info[CONF_WAIT_FOR_PUSH]
|
||||
|
||||
|
@ -88,7 +91,8 @@ class DataUpdateCoordinatorMotionBlinds(DataUpdateCoordinator):
|
|||
|
||||
async def _async_update_data(self):
|
||||
"""Fetch the latest data from the gateway and blinds."""
|
||||
data = await self.hass.async_add_executor_job(self.update_gateway)
|
||||
async with self.api_lock:
|
||||
data = await self.hass.async_add_executor_job(self.update_gateway)
|
||||
|
||||
all_available = all(device[ATTR_AVAILABLE] for device in data.values())
|
||||
if all_available:
|
||||
|
@ -130,8 +134,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
if not await connect_gateway_class.async_connect_gateway(host, key):
|
||||
raise ConfigEntryNotReady
|
||||
motion_gateway = connect_gateway_class.gateway_device
|
||||
api_lock = asyncio.Lock()
|
||||
coordinator_info = {
|
||||
KEY_GATEWAY: motion_gateway,
|
||||
KEY_API_LOCK: api_lock,
|
||||
CONF_WAIT_FOR_PUSH: wait_for_push,
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ DEFAULT_WAIT_FOR_PUSH = False
|
|||
DEFAULT_INTERFACE = "any"
|
||||
|
||||
KEY_GATEWAY = "gateway"
|
||||
KEY_API_LOCK = "api_lock"
|
||||
KEY_COORDINATOR = "coordinator"
|
||||
KEY_MULTICAST_LISTENER = "multicast_listener"
|
||||
KEY_VERSION = "version"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"""Support for Motion Blinds using their WLAN API."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from motionblinds import DEVICE_TYPES_WIFI, BlindType
|
||||
|
@ -20,9 +19,8 @@ from homeassistant.helpers import (
|
|||
)
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import async_track_point_in_time, track_point_in_time
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
ATTR_ABSOLUTE_POSITION,
|
||||
|
@ -94,7 +92,6 @@ async def async_setup_entry(
|
|||
coordinator,
|
||||
blind,
|
||||
POSITION_DEVICE_MAP[blind.type],
|
||||
config_entry,
|
||||
sw_version,
|
||||
)
|
||||
)
|
||||
|
@ -105,7 +102,6 @@ async def async_setup_entry(
|
|||
coordinator,
|
||||
blind,
|
||||
TILT_DEVICE_MAP[blind.type],
|
||||
config_entry,
|
||||
sw_version,
|
||||
)
|
||||
)
|
||||
|
@ -116,7 +112,6 @@ async def async_setup_entry(
|
|||
coordinator,
|
||||
blind,
|
||||
TDBU_DEVICE_MAP[blind.type],
|
||||
config_entry,
|
||||
sw_version,
|
||||
"Top",
|
||||
)
|
||||
|
@ -126,7 +121,6 @@ async def async_setup_entry(
|
|||
coordinator,
|
||||
blind,
|
||||
TDBU_DEVICE_MAP[blind.type],
|
||||
config_entry,
|
||||
sw_version,
|
||||
"Bottom",
|
||||
)
|
||||
|
@ -136,7 +130,6 @@ async def async_setup_entry(
|
|||
coordinator,
|
||||
blind,
|
||||
TDBU_DEVICE_MAP[blind.type],
|
||||
config_entry,
|
||||
sw_version,
|
||||
"Combined",
|
||||
)
|
||||
|
@ -152,7 +145,6 @@ async def async_setup_entry(
|
|||
coordinator,
|
||||
blind,
|
||||
POSITION_DEVICE_MAP[BlindType.RollerBlind],
|
||||
config_entry,
|
||||
sw_version,
|
||||
)
|
||||
)
|
||||
|
@ -170,12 +162,12 @@ async def async_setup_entry(
|
|||
class MotionPositionDevice(CoordinatorEntity, CoverEntity):
|
||||
"""Representation of a Motion Blind Device."""
|
||||
|
||||
def __init__(self, coordinator, blind, device_class, config_entry, sw_version):
|
||||
def __init__(self, coordinator, blind, device_class, sw_version):
|
||||
"""Initialize the blind."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._blind = blind
|
||||
self._config_entry = config_entry
|
||||
self._api_lock = coordinator.api_lock
|
||||
self._requesting_position = False
|
||||
self._previous_positions = []
|
||||
|
||||
|
@ -249,7 +241,9 @@ class MotionPositionDevice(CoordinatorEntity, CoverEntity):
|
|||
if len(self._previous_positions) > 2:
|
||||
del self._previous_positions[: len(self._previous_positions) - 2]
|
||||
|
||||
await self.hass.async_add_executor_job(self._blind.Update_trigger)
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Update_trigger)
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
if len(self._previous_positions) < 2 or not all(
|
||||
|
@ -257,53 +251,58 @@ class MotionPositionDevice(CoordinatorEntity, CoverEntity):
|
|||
for prev_position in self._previous_positions
|
||||
):
|
||||
# keep updating the position @UPDATE_INTERVAL_MOVING until the position does not change.
|
||||
async_track_point_in_time(
|
||||
self.hass,
|
||||
self.async_scheduled_update_request,
|
||||
dt_util.utcnow() + timedelta(seconds=UPDATE_INTERVAL_MOVING),
|
||||
async_call_later(
|
||||
self.hass, UPDATE_INTERVAL_MOVING, self.async_scheduled_update_request
|
||||
)
|
||||
else:
|
||||
self._previous_positions = []
|
||||
self._requesting_position = False
|
||||
|
||||
def request_position_till_stop(self):
|
||||
async def async_request_position_till_stop(self):
|
||||
"""Request the position of the blind every UPDATE_INTERVAL_MOVING seconds until it stops moving."""
|
||||
self._previous_positions = []
|
||||
if self._requesting_position or self.current_cover_position is None:
|
||||
return
|
||||
|
||||
self._requesting_position = True
|
||||
track_point_in_time(
|
||||
self.hass,
|
||||
self.async_scheduled_update_request,
|
||||
dt_util.utcnow() + timedelta(seconds=UPDATE_INTERVAL_MOVING),
|
||||
async_call_later(
|
||||
self.hass, UPDATE_INTERVAL_MOVING, self.async_scheduled_update_request
|
||||
)
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
self._blind.Open()
|
||||
self.request_position_till_stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Open)
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def close_cover(self, **kwargs):
|
||||
async def async_close_cover(self, **kwargs):
|
||||
"""Close cover."""
|
||||
self._blind.Close()
|
||||
self.request_position_till_stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Close)
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def set_cover_position(self, **kwargs):
|
||||
async def async_set_cover_position(self, **kwargs):
|
||||
"""Move the cover to a specific position."""
|
||||
position = kwargs[ATTR_POSITION]
|
||||
self._blind.Set_position(100 - position)
|
||||
self.request_position_till_stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(
|
||||
self._blind.Set_position, 100 - position
|
||||
)
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def set_absolute_position(self, **kwargs):
|
||||
async def async_set_absolute_position(self, **kwargs):
|
||||
"""Move the cover to a specific absolute position (see TDBU)."""
|
||||
position = kwargs[ATTR_ABSOLUTE_POSITION]
|
||||
self._blind.Set_position(100 - position)
|
||||
self.request_position_till_stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(
|
||||
self._blind.Set_position, 100 - position
|
||||
)
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def stop_cover(self, **kwargs):
|
||||
async def async_stop_cover(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
self._blind.Stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Stop)
|
||||
|
||||
|
||||
class MotionTiltDevice(MotionPositionDevice):
|
||||
|
@ -320,32 +319,34 @@ class MotionTiltDevice(MotionPositionDevice):
|
|||
return None
|
||||
return self._blind.angle * 100 / 180
|
||||
|
||||
def open_cover_tilt(self, **kwargs):
|
||||
async def async_open_cover_tilt(self, **kwargs):
|
||||
"""Open the cover tilt."""
|
||||
self._blind.Set_angle(180)
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Set_angle, 180)
|
||||
|
||||
def close_cover_tilt(self, **kwargs):
|
||||
async def async_close_cover_tilt(self, **kwargs):
|
||||
"""Close the cover tilt."""
|
||||
self._blind.Set_angle(0)
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Set_angle, 0)
|
||||
|
||||
def set_cover_tilt_position(self, **kwargs):
|
||||
async def async_set_cover_tilt_position(self, **kwargs):
|
||||
"""Move the cover tilt to a specific position."""
|
||||
angle = kwargs[ATTR_TILT_POSITION] * 180 / 100
|
||||
self._blind.Set_angle(angle)
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Set_angle, angle)
|
||||
|
||||
def stop_cover_tilt(self, **kwargs):
|
||||
async def async_stop_cover_tilt(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
self._blind.Stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Stop)
|
||||
|
||||
|
||||
class MotionTDBUDevice(MotionPositionDevice):
|
||||
"""Representation of a Motion Top Down Bottom Up blind Device."""
|
||||
|
||||
def __init__(
|
||||
self, coordinator, blind, device_class, config_entry, sw_version, motor
|
||||
):
|
||||
def __init__(self, coordinator, blind, device_class, sw_version, motor):
|
||||
"""Initialize the blind."""
|
||||
super().__init__(coordinator, blind, device_class, config_entry, sw_version)
|
||||
super().__init__(coordinator, blind, device_class, sw_version)
|
||||
self._motor = motor
|
||||
self._motor_key = motor[0]
|
||||
self._attr_name = f"{blind.blind_type}-{motor}-{blind.mac[12:]}"
|
||||
|
@ -389,33 +390,40 @@ class MotionTDBUDevice(MotionPositionDevice):
|
|||
attributes[ATTR_WIDTH] = self._blind.width
|
||||
return attributes
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
self._blind.Open(motor=self._motor_key)
|
||||
self.request_position_till_stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Open, self._motor_key)
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def close_cover(self, **kwargs):
|
||||
async def async_close_cover(self, **kwargs):
|
||||
"""Close cover."""
|
||||
self._blind.Close(motor=self._motor_key)
|
||||
self.request_position_till_stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Close, self._motor_key)
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def set_cover_position(self, **kwargs):
|
||||
async def async_set_cover_position(self, **kwargs):
|
||||
"""Move the cover to a specific scaled position."""
|
||||
position = kwargs[ATTR_POSITION]
|
||||
self._blind.Set_scaled_position(100 - position, motor=self._motor_key)
|
||||
self.request_position_till_stop()
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(
|
||||
self._blind.Set_scaled_position, 100 - position, self._motor_key
|
||||
)
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def set_absolute_position(self, **kwargs):
|
||||
async def async_set_absolute_position(self, **kwargs):
|
||||
"""Move the cover to a specific absolute position."""
|
||||
position = kwargs[ATTR_ABSOLUTE_POSITION]
|
||||
target_width = kwargs.get(ATTR_WIDTH, None)
|
||||
|
||||
self._blind.Set_position(
|
||||
100 - position, motor=self._motor_key, width=target_width
|
||||
)
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(
|
||||
self._blind.Set_position, 100 - position, self._motor_key, target_width
|
||||
)
|
||||
|
||||
self.request_position_till_stop()
|
||||
await self.async_request_position_till_stop()
|
||||
|
||||
def stop_cover(self, **kwargs):
|
||||
async def async_stop_cover(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
self._blind.Stop(motor=self._motor_key)
|
||||
async with self._api_lock:
|
||||
await self.hass.async_add_executor_job(self._blind.Stop, self._motor_key)
|
||||
|
|
Loading…
Add table
Reference in a new issue