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."""
|
"""Support for Homekit device discovery."""
|
||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
@ -74,6 +75,8 @@ class HKDevice():
|
||||||
self.configurator = hass.components.configurator
|
self.configurator = hass.components.configurator
|
||||||
self._connection_warning_logged = False
|
self._connection_warning_logged = False
|
||||||
|
|
||||||
|
self.pairing_lock = asyncio.Lock(loop=hass.loop)
|
||||||
|
|
||||||
self.pairing = self.controller.pairings.get(hkid)
|
self.pairing = self.controller.pairings.get(hkid)
|
||||||
|
|
||||||
if self.pairing is not None:
|
if self.pairing is not None:
|
||||||
|
@ -168,6 +171,32 @@ class HKDevice():
|
||||||
'name': 'HomeKit code',
|
'name': 'HomeKit code',
|
||||||
'type': 'string'}])
|
'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):
|
class HomeKitEntity(Entity):
|
||||||
"""Representation of a Home Assistant HomeKit device."""
|
"""Representation of a Home Assistant HomeKit device."""
|
||||||
|
@ -238,15 +267,15 @@ class HomeKitEntity(Entity):
|
||||||
# pylint: disable=not-callable
|
# pylint: disable=not-callable
|
||||||
setup_fn(char)
|
setup_fn(char)
|
||||||
|
|
||||||
def update(self):
|
async def async_update(self):
|
||||||
"""Obtain a HomeKit device's state."""
|
"""Obtain a HomeKit device's state."""
|
||||||
# pylint: disable=import-error
|
# pylint: disable=import-error
|
||||||
from homekit.exceptions import AccessoryDisconnectedError
|
from homekit.exceptions import AccessoryDisconnectedError
|
||||||
|
|
||||||
pairing = self._accessory.pairing
|
|
||||||
|
|
||||||
try:
|
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:
|
except AccessoryDisconnectedError:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -280,22 +309,6 @@ class HomeKitEntity(Entity):
|
||||||
"""Define the homekit characteristics the entity cares about."""
|
"""Define the homekit characteristics the entity cares about."""
|
||||||
raise NotImplementedError
|
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):
|
def setup(hass, config):
|
||||||
"""Set up for Homekit devices."""
|
"""Set up for Homekit devices."""
|
||||||
|
|
|
@ -74,28 +74,28 @@ class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel):
|
||||||
"""Return the state of the device."""
|
"""Return the state of the device."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def alarm_disarm(self, code=None):
|
async def async_alarm_disarm(self, code=None):
|
||||||
"""Send disarm command."""
|
"""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."""
|
"""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."""
|
"""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."""
|
"""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."""
|
"""Send state command."""
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['security-system-state.target'],
|
'iid': self._chars['security-system-state.target'],
|
||||||
'value': TARGET_STATE_MAP[state]}]
|
'value': TARGET_STATE_MAP[state]}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
|
|
|
@ -80,21 +80,21 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
|
||||||
def _update_temperature_target(self, value):
|
def _update_temperature_target(self, value):
|
||||||
self._target_temp = value
|
self._target_temp = value
|
||||||
|
|
||||||
def set_temperature(self, **kwargs):
|
async def async_set_temperature(self, **kwargs):
|
||||||
"""Set new target temperature."""
|
"""Set new target temperature."""
|
||||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||||
|
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['temperature.target'],
|
'iid': self._chars['temperature.target'],
|
||||||
'value': temp}]
|
'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."""
|
"""Set new target operation mode."""
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['heating-cooling.target'],
|
'iid': self._chars['heating-cooling.target'],
|
||||||
'value': MODE_HASS_TO_HOMEKIT[operation_mode]}]
|
'value': MODE_HASS_TO_HOMEKIT[operation_mode]}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
|
|
|
@ -114,20 +114,20 @@ class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice):
|
||||||
"""Return if the cover is opening or not."""
|
"""Return if the cover is opening or not."""
|
||||||
return self._state == STATE_OPENING
|
return self._state == STATE_OPENING
|
||||||
|
|
||||||
def open_cover(self, **kwargs):
|
async def async_open_cover(self, **kwargs):
|
||||||
"""Send open command."""
|
"""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."""
|
"""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."""
|
"""Send state command."""
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['door-state.target'],
|
'iid': self._chars['door-state.target'],
|
||||||
'value': TARGET_GARAGE_STATE_MAP[state]}]
|
'value': TARGET_GARAGE_STATE_MAP[state]}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
|
@ -232,41 +232,41 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice):
|
||||||
"""Return if the cover is opening or not."""
|
"""Return if the cover is opening or not."""
|
||||||
return self._state == STATE_OPENING
|
return self._state == STATE_OPENING
|
||||||
|
|
||||||
def open_cover(self, **kwargs):
|
async def async_open_cover(self, **kwargs):
|
||||||
"""Send open command."""
|
"""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."""
|
"""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."""
|
"""Send position command."""
|
||||||
position = kwargs[ATTR_POSITION]
|
position = kwargs[ATTR_POSITION]
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['position.target'],
|
'iid': self._chars['position.target'],
|
||||||
'value': position}]
|
'value': position}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_cover_tilt_position(self):
|
def current_cover_tilt_position(self):
|
||||||
"""Return current position of cover tilt."""
|
"""Return current position of cover tilt."""
|
||||||
return self._tilt_position
|
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."""
|
"""Move the cover tilt to a specific position."""
|
||||||
tilt_position = kwargs[ATTR_TILT_POSITION]
|
tilt_position = kwargs[ATTR_TILT_POSITION]
|
||||||
if 'vertical-tilt.target' in self._chars:
|
if 'vertical-tilt.target' in self._chars:
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['vertical-tilt.target'],
|
'iid': self._chars['vertical-tilt.target'],
|
||||||
'value': tilt_position}]
|
'value': tilt_position}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
elif 'horizontal-tilt.target' in self._chars:
|
elif 'horizontal-tilt.target' in self._chars:
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid':
|
'iid':
|
||||||
self._chars['horizontal-tilt.target'],
|
self._chars['horizontal-tilt.target'],
|
||||||
'value': tilt_position}]
|
'value': tilt_position}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
|
|
|
@ -101,7 +101,7 @@ class HomeKitLight(HomeKitEntity, Light):
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
return self._features
|
return self._features
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Turn the specified light on."""
|
"""Turn the specified light on."""
|
||||||
hs_color = kwargs.get(ATTR_HS_COLOR)
|
hs_color = kwargs.get(ATTR_HS_COLOR)
|
||||||
temperature = kwargs.get(ATTR_COLOR_TEMP)
|
temperature = kwargs.get(ATTR_COLOR_TEMP)
|
||||||
|
@ -127,11 +127,11 @@ class HomeKitLight(HomeKitEntity, Light):
|
||||||
characteristics.append({'aid': self._aid,
|
characteristics.append({'aid': self._aid,
|
||||||
'iid': self._chars['on'],
|
'iid': self._chars['on'],
|
||||||
'value': True})
|
'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."""
|
"""Turn the specified light off."""
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['on'],
|
'iid': self._chars['on'],
|
||||||
'value': False}]
|
'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 True if entity is available."""
|
||||||
return self._state is not None
|
return self._state is not None
|
||||||
|
|
||||||
def lock(self, **kwargs):
|
async def async_lock(self, **kwargs):
|
||||||
"""Lock the device."""
|
"""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."""
|
"""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."""
|
"""Send state command."""
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['lock-mechanism.target-state'],
|
'iid': self._chars['lock-mechanism.target-state'],
|
||||||
'value': TARGET_STATE_MAP[state]}]
|
'value': TARGET_STATE_MAP[state]}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
|
|
|
@ -48,20 +48,20 @@ class HomeKitSwitch(HomeKitEntity, SwitchDevice):
|
||||||
"""Return true if device is on."""
|
"""Return true if device is on."""
|
||||||
return self._on
|
return self._on
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Turn the specified switch on."""
|
"""Turn the specified switch on."""
|
||||||
self._on = True
|
self._on = True
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['on'],
|
'iid': self._chars['on'],
|
||||||
'value': True}]
|
'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."""
|
"""Turn the specified switch off."""
|
||||||
characteristics = [{'aid': self._aid,
|
characteristics = [{'aid': self._aid,
|
||||||
'iid': self._chars['on'],
|
'iid': self._chars['on'],
|
||||||
'value': False}]
|
'value': False}]
|
||||||
self.put_characteristics(characteristics)
|
await self._accessory.put_characteristics(characteristics)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue