Add an asyncio Lock around pairing, which cant be used concurrently (#21933)
This commit is contained in:
parent
4f5446ff02
commit
5e2302e469
7 changed files with 76 additions and 63 deletions
|
@ -1,4 +1,5 @@
|
|||
"""Support for Homekit device discovery."""
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
@ -74,6 +75,8 @@ class HKDevice():
|
|||
self.configurator = hass.components.configurator
|
||||
self._connection_warning_logged = False
|
||||
|
||||
self.pairing_lock = asyncio.Lock(loop=hass.loop)
|
||||
|
||||
self.pairing = self.controller.pairings.get(hkid)
|
||||
|
||||
if self.pairing is not None:
|
||||
|
@ -168,6 +171,32 @@ class HKDevice():
|
|||
'name': 'HomeKit code',
|
||||
'type': 'string'}])
|
||||
|
||||
async def get_characteristics(self, *args, **kwargs):
|
||||
"""Read latest state from homekit accessory."""
|
||||
async with self.pairing_lock:
|
||||
chars = await self.hass.async_add_executor_job(
|
||||
self.pairing.get_characteristics,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
return chars
|
||||
|
||||
async def put_characteristics(self, characteristics):
|
||||
"""Control a HomeKit device state from Home Assistant."""
|
||||
chars = []
|
||||
for row in characteristics:
|
||||
chars.append((
|
||||
row['aid'],
|
||||
row['iid'],
|
||||
row['value'],
|
||||
))
|
||||
|
||||
async with self.pairing_lock:
|
||||
await self.hass.async_add_executor_job(
|
||||
self.pairing.put_characteristics,
|
||||
chars
|
||||
)
|
||||
|
||||
|
||||
class HomeKitEntity(Entity):
|
||||
"""Representation of a Home Assistant HomeKit device."""
|
||||
|
@ -238,15 +267,15 @@ class HomeKitEntity(Entity):
|
|||
# pylint: disable=not-callable
|
||||
setup_fn(char)
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Obtain a HomeKit device's state."""
|
||||
# pylint: disable=import-error
|
||||
from homekit.exceptions import AccessoryDisconnectedError
|
||||
|
||||
pairing = self._accessory.pairing
|
||||
|
||||
try:
|
||||
new_values_dict = pairing.get_characteristics(self._chars_to_poll)
|
||||
new_values_dict = await self._accessory.get_characteristics(
|
||||
self._chars_to_poll
|
||||
)
|
||||
except AccessoryDisconnectedError:
|
||||
return
|
||||
|
||||
|
@ -280,22 +309,6 @@ class HomeKitEntity(Entity):
|
|||
"""Define the homekit characteristics the entity cares about."""
|
||||
raise NotImplementedError
|
||||
|
||||
def update_characteristics(self, characteristics):
|
||||
"""Synchronise a HomeKit device state with Home Assistant."""
|
||||
pass
|
||||
|
||||
def put_characteristics(self, characteristics):
|
||||
"""Control a HomeKit device state from Home Assistant."""
|
||||
chars = []
|
||||
for row in characteristics:
|
||||
chars.append((
|
||||
row['aid'],
|
||||
row['iid'],
|
||||
row['value'],
|
||||
))
|
||||
|
||||
self._accessory.pairing.put_characteristics(chars)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up for Homekit devices."""
|
||||
|
|
|
@ -74,28 +74,28 @@ class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel):
|
|||
"""Return the state of the device."""
|
||||
return self._state
|
||||
|
||||
def alarm_disarm(self, code=None):
|
||||
async def async_alarm_disarm(self, code=None):
|
||||
"""Send disarm command."""
|
||||
self.set_alarm_state(STATE_ALARM_DISARMED, code)
|
||||
await self.set_alarm_state(STATE_ALARM_DISARMED, code)
|
||||
|
||||
def alarm_arm_away(self, code=None):
|
||||
async def async_alarm_arm_away(self, code=None):
|
||||
"""Send arm command."""
|
||||
self.set_alarm_state(STATE_ALARM_ARMED_AWAY, code)
|
||||
await self.set_alarm_state(STATE_ALARM_ARMED_AWAY, code)
|
||||
|
||||
def alarm_arm_home(self, code=None):
|
||||
async def async_alarm_arm_home(self, code=None):
|
||||
"""Send stay command."""
|
||||
self.set_alarm_state(STATE_ALARM_ARMED_HOME, code)
|
||||
await self.set_alarm_state(STATE_ALARM_ARMED_HOME, code)
|
||||
|
||||
def alarm_arm_night(self, code=None):
|
||||
async def async_alarm_arm_night(self, code=None):
|
||||
"""Send night command."""
|
||||
self.set_alarm_state(STATE_ALARM_ARMED_NIGHT, code)
|
||||
await self.set_alarm_state(STATE_ALARM_ARMED_NIGHT, code)
|
||||
|
||||
def set_alarm_state(self, state, code=None):
|
||||
async def set_alarm_state(self, state, code=None):
|
||||
"""Send state command."""
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['security-system-state.target'],
|
||||
'value': TARGET_STATE_MAP[state]}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
|
|
@ -80,21 +80,21 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
|
|||
def _update_temperature_target(self, value):
|
||||
self._target_temp = value
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
async def async_set_temperature(self, **kwargs):
|
||||
"""Set new target temperature."""
|
||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['temperature.target'],
|
||||
'value': temp}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
def set_operation_mode(self, operation_mode):
|
||||
async def async_set_operation_mode(self, operation_mode):
|
||||
"""Set new target operation mode."""
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['heating-cooling.target'],
|
||||
'value': MODE_HASS_TO_HOMEKIT[operation_mode]}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
|
|
|
@ -114,20 +114,20 @@ class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice):
|
|||
"""Return if the cover is opening or not."""
|
||||
return self._state == STATE_OPENING
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Send open command."""
|
||||
self.set_door_state(STATE_OPEN)
|
||||
await self.set_door_state(STATE_OPEN)
|
||||
|
||||
def close_cover(self, **kwargs):
|
||||
async def async_close_cover(self, **kwargs):
|
||||
"""Send close command."""
|
||||
self.set_door_state(STATE_CLOSED)
|
||||
await self.set_door_state(STATE_CLOSED)
|
||||
|
||||
def set_door_state(self, state):
|
||||
async def set_door_state(self, state):
|
||||
"""Send state command."""
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['door-state.target'],
|
||||
'value': TARGET_GARAGE_STATE_MAP[state]}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
@ -232,41 +232,41 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice):
|
|||
"""Return if the cover is opening or not."""
|
||||
return self._state == STATE_OPENING
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Send open command."""
|
||||
self.set_cover_position(position=100)
|
||||
await self.async_set_cover_position(position=100)
|
||||
|
||||
def close_cover(self, **kwargs):
|
||||
async def close_cover(self, **kwargs):
|
||||
"""Send close command."""
|
||||
self.set_cover_position(position=0)
|
||||
await self.async_set_cover_position(position=0)
|
||||
|
||||
def set_cover_position(self, **kwargs):
|
||||
async def async_set_cover_position(self, **kwargs):
|
||||
"""Send position command."""
|
||||
position = kwargs[ATTR_POSITION]
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['position.target'],
|
||||
'value': position}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
@property
|
||||
def current_cover_tilt_position(self):
|
||||
"""Return current position of cover tilt."""
|
||||
return self._tilt_position
|
||||
|
||||
def set_cover_tilt_position(self, **kwargs):
|
||||
async def async_set_cover_tilt_position(self, **kwargs):
|
||||
"""Move the cover tilt to a specific position."""
|
||||
tilt_position = kwargs[ATTR_TILT_POSITION]
|
||||
if 'vertical-tilt.target' in self._chars:
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['vertical-tilt.target'],
|
||||
'value': tilt_position}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
elif 'horizontal-tilt.target' in self._chars:
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid':
|
||||
self._chars['horizontal-tilt.target'],
|
||||
'value': tilt_position}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
|
|
@ -101,7 +101,7 @@ class HomeKitLight(HomeKitEntity, Light):
|
|||
"""Flag supported features."""
|
||||
return self._features
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Turn the specified light on."""
|
||||
hs_color = kwargs.get(ATTR_HS_COLOR)
|
||||
temperature = kwargs.get(ATTR_COLOR_TEMP)
|
||||
|
@ -127,11 +127,11 @@ class HomeKitLight(HomeKitEntity, Light):
|
|||
characteristics.append({'aid': self._aid,
|
||||
'iid': self._chars['on'],
|
||||
'value': True})
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn the specified light off."""
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['on'],
|
||||
'value': False}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
|
|
@ -75,20 +75,20 @@ class HomeKitLock(HomeKitEntity, LockDevice):
|
|||
"""Return True if entity is available."""
|
||||
return self._state is not None
|
||||
|
||||
def lock(self, **kwargs):
|
||||
async def async_lock(self, **kwargs):
|
||||
"""Lock the device."""
|
||||
self._set_lock_state(STATE_LOCKED)
|
||||
await self._set_lock_state(STATE_LOCKED)
|
||||
|
||||
def unlock(self, **kwargs):
|
||||
async def async_unlock(self, **kwargs):
|
||||
"""Unlock the device."""
|
||||
self._set_lock_state(STATE_UNLOCKED)
|
||||
await self._set_lock_state(STATE_UNLOCKED)
|
||||
|
||||
def _set_lock_state(self, state):
|
||||
async def _set_lock_state(self, state):
|
||||
"""Send state command."""
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['lock-mechanism.target-state'],
|
||||
'value': TARGET_STATE_MAP[state]}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
|
|
@ -48,20 +48,20 @@ class HomeKitSwitch(HomeKitEntity, SwitchDevice):
|
|||
"""Return true if device is on."""
|
||||
return self._on
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Turn the specified switch on."""
|
||||
self._on = True
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['on'],
|
||||
'value': True}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn the specified switch off."""
|
||||
characteristics = [{'aid': self._aid,
|
||||
'iid': self._chars['on'],
|
||||
'value': False}]
|
||||
self.put_characteristics(characteristics)
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
|
Loading…
Add table
Reference in a new issue