Refactor homekit_controller entity update to work more like update coordinator (#32670)

* Clean up use of get_characteristic_types

* Get rid of get_hk_char_value helper

* Get rid of _update_fn callbacks

* Call async_write_has_state directly as async_state_changed doesnt do anything any more
This commit is contained in:
Jc2k 2020-03-11 11:40:47 +00:00 committed by GitHub
parent 4248893007
commit 647d137daa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 159 additions and 358 deletions

View file

@ -4,10 +4,9 @@ import os
import aiohomekit
from aiohomekit.model import Accessory
from aiohomekit.model.characteristics import CharacteristicsTypes
from aiohomekit.model.characteristics import Characteristic, CharacteristicsTypes
from aiohomekit.model.services import Service, ServicesTypes
from homeassistant.core import callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import Entity
@ -60,7 +59,7 @@ class HomeKitEntity(Entity):
"""Entity added to hass."""
self._signals.append(
self.hass.helpers.dispatcher.async_dispatcher_connect(
self._accessory.signal_state_updated, self.async_state_changed
self._accessory.signal_state_updated, self.async_write_ha_state
)
)
@ -86,93 +85,53 @@ class HomeKitEntity(Entity):
def setup(self):
"""Configure an entity baed on its HomeKit characteristics metadata."""
get_uuid = CharacteristicsTypes.get_uuid
characteristic_types = [get_uuid(c) for c in self.get_characteristic_types()]
self.pollable_characteristics = []
self.watchable_characteristics = []
self._chars = {}
self._char_names = {}
char_types = self.get_characteristic_types()
# Setup events and/or polling for characteristics directly attached to this entity
for char in self.service.characteristics:
if char.type not in characteristic_types:
continue
self._setup_characteristic(char.to_accessory_and_service_list())
for char in self.service.characteristics.filter(char_types=char_types):
self._setup_characteristic(char)
# Setup events and/or polling for characteristics attached to sub-services of this
# entity (like an INPUT_SOURCE).
for service in self.accessory.services.filter(parent_service=self.service):
for char in service.characteristics:
if char.type not in characteristic_types:
continue
self._setup_characteristic(char.to_accessory_and_service_list())
for char in service.characteristics.filter(char_types=char_types):
self._setup_characteristic(char)
def _setup_characteristic(self, char):
def _setup_characteristic(self, char: Characteristic):
"""Configure an entity based on a HomeKit characteristics metadata."""
# Build up a list of (aid, iid) tuples to poll on update()
if "pr" in char["perms"]:
self.pollable_characteristics.append((self._aid, char["iid"]))
if "pr" in char.perms:
self.pollable_characteristics.append((self._aid, char.iid))
# Build up a list of (aid, iid) tuples to subscribe to
if "ev" in char["perms"]:
self.watchable_characteristics.append((self._aid, char["iid"]))
if "ev" in char.perms:
self.watchable_characteristics.append((self._aid, char.iid))
# Build a map of ctype -> iid
short_name = CharacteristicsTypes.get_short(char["type"])
self._chars[short_name] = char["iid"]
self._char_names[char["iid"]] = short_name
self._chars[char.type_name] = char.iid
self._char_names[char.iid] = char.type_name
# Callback to allow entity to configure itself based on this
# characteristics metadata (valid values, value ranges, features, etc)
setup_fn_name = escape_characteristic_name(short_name)
setup_fn_name = escape_characteristic_name(char.type_name)
setup_fn = getattr(self, f"_setup_{setup_fn_name}", None)
if not setup_fn:
return
setup_fn(char)
def get_hk_char_value(self, characteristic_type):
"""Return the value for a given characteristic type enum."""
state = self._accessory.current_state.get(self._aid)
if not state:
return None
char = self._chars.get(CharacteristicsTypes.get_short(characteristic_type))
if not char:
return None
return state.get(char, {}).get("value")
@callback
def async_state_changed(self):
"""Collect new data from bridge and update the entity state in hass."""
accessory_state = self._accessory.current_state.get(self._aid, {})
for iid, result in accessory_state.items():
# No value so don't process this result
if "value" not in result:
continue
# Unknown iid - this is probably for a sibling service that is part
# of the same physical accessory. Ignore it.
if iid not in self._char_names:
continue
# Callback to update the entity with this characteristic value
char_name = escape_characteristic_name(self._char_names[iid])
update_fn = getattr(self, f"_update_{char_name}", None)
if not update_fn:
continue
update_fn(result["value"])
self.async_write_ha_state()
setup_fn(char.to_accessory_and_service_list())
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return the ID of this device."""
serial = self.accessory_info.value(CharacteristicsTypes.SERIAL_NUMBER)
return f"homekit-{serial}-{self._iid}"
@property
def name(self):
def name(self) -> str:
"""Return the name of the device if any."""
return self.accessory_info.value(CharacteristicsTypes.NAME)

View file

@ -34,38 +34,38 @@ class HomeAirQualitySensor(HomeKitEntity, AirQualityEntity):
@property
def particulate_matter_2_5(self):
"""Return the particulate matter 2.5 level."""
return self.get_hk_char_value(CharacteristicsTypes.DENSITY_PM25)
return self.service.value(CharacteristicsTypes.DENSITY_PM25)
@property
def particulate_matter_10(self):
"""Return the particulate matter 10 level."""
return self.get_hk_char_value(CharacteristicsTypes.DENSITY_PM10)
return self.service.value(CharacteristicsTypes.DENSITY_PM10)
@property
def ozone(self):
"""Return the O3 (ozone) level."""
return self.get_hk_char_value(CharacteristicsTypes.DENSITY_OZONE)
return self.service.value(CharacteristicsTypes.DENSITY_OZONE)
@property
def sulphur_dioxide(self):
"""Return the SO2 (sulphur dioxide) level."""
return self.get_hk_char_value(CharacteristicsTypes.DENSITY_SO2)
return self.service.value(CharacteristicsTypes.DENSITY_SO2)
@property
def nitrogen_dioxide(self):
"""Return the NO2 (nitrogen dioxide) level."""
return self.get_hk_char_value(CharacteristicsTypes.DENSITY_NO2)
return self.service.value(CharacteristicsTypes.DENSITY_NO2)
@property
def air_quality_text(self):
"""Return the Air Quality Index (AQI)."""
air_quality = self.get_hk_char_value(CharacteristicsTypes.AIR_QUALITY)
air_quality = self.service.value(CharacteristicsTypes.AIR_QUALITY)
return AIR_QUALITY_TEXT.get(air_quality, "unknown")
@property
def volatile_organic_compounds(self):
"""Return the volatile organic compounds (VOC) level."""
return self.get_hk_char_value(CharacteristicsTypes.DENSITY_VOC)
return self.service.value(CharacteristicsTypes.DENSITY_VOC)
@property
def device_state_attributes(self):

View file

@ -60,12 +60,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel):
"""Representation of a Homekit Alarm Control Panel."""
def __init__(self, *args):
"""Initialise the Alarm Control Panel."""
super().__init__(*args)
self._state = None
self._battery_level = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity cares about."""
return [
@ -74,12 +68,6 @@ class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel):
CharacteristicsTypes.BATTERY_LEVEL,
]
def _update_security_system_state_current(self, value):
self._state = CURRENT_STATE_MAP[value]
def _update_battery_level(self, value):
self._battery_level = value
@property
def icon(self):
"""Return icon."""
@ -88,7 +76,9 @@ class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel):
@property
def state(self):
"""Return the state of the device."""
return self._state
return CURRENT_STATE_MAP[
self.service.value(CharacteristicsTypes.SECURITY_SYSTEM_STATE_CURRENT)
]
@property
def supported_features(self) -> int:
@ -125,7 +115,10 @@ class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
if self._battery_level is None:
return None
attributes = {}
return {ATTR_BATTERY_LEVEL: self._battery_level}
battery_level = self.service.value(CharacteristicsTypes.BATTERY_LEVEL)
if battery_level:
attributes[ATTR_BATTERY_LEVEL] = battery_level
return attributes

View file

@ -20,18 +20,10 @@ _LOGGER = logging.getLogger(__name__)
class HomeKitMotionSensor(HomeKitEntity, BinarySensorDevice):
"""Representation of a Homekit motion sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._on = False
def get_characteristic_types(self):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.MOTION_DETECTED]
def _update_motion_detected(self, value):
self._on = value
@property
def device_class(self):
"""Define this binary_sensor as a motion sensor."""
@ -40,24 +32,16 @@ class HomeKitMotionSensor(HomeKitEntity, BinarySensorDevice):
@property
def is_on(self):
"""Has motion been detected."""
return self._on
return self.service.value(CharacteristicsTypes.MOTION_DETECTED)
class HomeKitContactSensor(HomeKitEntity, BinarySensorDevice):
"""Representation of a Homekit contact sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.CONTACT_STATE]
def _update_contact_state(self, value):
self._state = value
@property
def device_class(self):
"""Define this binary_sensor as a opening sensor."""
@ -66,17 +50,12 @@ class HomeKitContactSensor(HomeKitEntity, BinarySensorDevice):
@property
def is_on(self):
"""Return true if the binary sensor is on/open."""
return self._state == 1
return self.service.value(CharacteristicsTypes.CONTACT_STATE) == 1
class HomeKitSmokeSensor(HomeKitEntity, BinarySensorDevice):
"""Representation of a Homekit smoke sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
@ -86,22 +65,14 @@ class HomeKitSmokeSensor(HomeKitEntity, BinarySensorDevice):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.SMOKE_DETECTED]
def _update_smoke_detected(self, value):
self._state = value
@property
def is_on(self):
"""Return true if smoke is currently detected."""
return self._state == 1
return self.service.value(CharacteristicsTypes.SMOKE_DETECTED) == 1
class HomeKitOccupancySensor(HomeKitEntity, BinarySensorDevice):
"""Representation of a Homekit smoke sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
"""Representation of a Homekit occupancy sensor."""
@property
def device_class(self) -> str:
@ -112,13 +83,10 @@ class HomeKitOccupancySensor(HomeKitEntity, BinarySensorDevice):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.OCCUPANCY_DETECTED]
def _update_occupancy_detected(self, value):
self._state = value
@property
def is_on(self):
"""Return true if smoke is currently detected."""
return self._state == 1
"""Return true if occupancy is currently detected."""
return self.service.value(CharacteristicsTypes.OCCUPANCY_DETECTED) == 1
ENTITY_TYPES = {

View file

@ -67,14 +67,7 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
def __init__(self, *args):
"""Initialise the device."""
self._state = None
self._target_mode = None
self._current_mode = None
self._valid_modes = []
self._current_temp = None
self._target_temp = None
self._current_humidity = None
self._target_humidity = None
self._min_target_temp = None
self._max_target_temp = None
self._min_target_humidity = DEFAULT_MIN_HUMIDITY
@ -130,31 +123,6 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
if "maxValue" in characteristic:
self._max_target_humidity = characteristic["maxValue"]
def _update_heating_cooling_current(self, value):
# This characteristic describes the current mode of a device,
# e.g. a thermostat is "heating" a room to 75 degrees Fahrenheit.
# Can be 0 - 2 (Off, Heat, Cool)
self._current_mode = CURRENT_MODE_HOMEKIT_TO_HASS.get(value)
def _update_heating_cooling_target(self, value):
# This characteristic describes the target mode
# E.g. should the device start heating a room if the temperature
# falls below the target temperature.
# Can be 0 - 3 (Off, Heat, Cool, Auto)
self._target_mode = MODE_HOMEKIT_TO_HASS.get(value)
def _update_temperature_current(self, value):
self._current_temp = value
def _update_temperature_target(self, value):
self._target_temp = value
def _update_relative_humidity_current(self, value):
self._current_humidity = value
def _update_relative_humidity_target(self, value):
self._target_humidity = value
async def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)
@ -189,12 +157,12 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
@property
def current_temperature(self):
"""Return the current temperature."""
return self._current_temp
return self.service.value(CharacteristicsTypes.TEMPERATURE_CURRENT)
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self._target_temp
return self.service.value(CharacteristicsTypes.TEMPERATURE_TARGET)
@property
def min_temp(self):
@ -213,12 +181,12 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
@property
def current_humidity(self):
"""Return the current humidity."""
return self._current_humidity
return self.service.value(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT)
@property
def target_humidity(self):
"""Return the humidity we try to reach."""
return self._target_humidity
return self.service.value(CharacteristicsTypes.RELATIVE_HUMIDITY_TARGET)
@property
def min_humidity(self):
@ -233,12 +201,21 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
@property
def hvac_action(self):
"""Return the current running hvac operation."""
return self._current_mode
# This characteristic describes the current mode of a device,
# e.g. a thermostat is "heating" a room to 75 degrees Fahrenheit.
# Can be 0 - 2 (Off, Heat, Cool)
value = self.service.value(CharacteristicsTypes.HEATING_COOLING_CURRENT)
return CURRENT_MODE_HOMEKIT_TO_HASS.get(value)
@property
def hvac_mode(self):
"""Return hvac operation ie. heat, cool mode."""
return self._target_mode
# This characteristic describes the target mode
# E.g. should the device start heating a room if the temperature
# falls below the target temperature.
# Can be 0 - 3 (Off, Heat, Cool, Auto)
value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET)
return MODE_HOMEKIT_TO_HASS.get(value)
@property
def hvac_modes(self):

View file

@ -61,13 +61,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice):
"""Representation of a HomeKit Garage Door."""
def __init__(self, accessory, discovery_info):
"""Initialise the Cover."""
super().__init__(accessory, discovery_info)
self._state = None
self._obstruction_detected = None
self.lock_state = None
@property
def device_class(self):
"""Define this cover as a garage door."""
@ -81,31 +74,31 @@ class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice):
CharacteristicsTypes.OBSTRUCTION_DETECTED,
]
def _update_door_state_current(self, value):
self._state = CURRENT_GARAGE_STATE_MAP[value]
def _update_obstruction_detected(self, value):
self._obstruction_detected = value
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_OPEN | SUPPORT_CLOSE
@property
def state(self):
"""Return the current state of the garage door."""
value = self.service.value(CharacteristicsTypes.DOOR_STATE_CURRENT)
return CURRENT_GARAGE_STATE_MAP[value]
@property
def is_closed(self):
"""Return true if cover is closed, else False."""
return self._state == STATE_CLOSED
return self.state == STATE_CLOSED
@property
def is_closing(self):
"""Return if the cover is closing or not."""
return self._state == STATE_CLOSING
return self.state == STATE_CLOSING
@property
def is_opening(self):
"""Return if the cover is opening or not."""
return self._state == STATE_OPENING
return self.state == STATE_OPENING
async def async_open_cover(self, **kwargs):
"""Send open command."""
@ -129,10 +122,15 @@ class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
if self._obstruction_detected is None:
return None
attributes = {}
return {"obstruction-detected": self._obstruction_detected}
obstruction_detected = self.service.value(
CharacteristicsTypes.OBSTRUCTION_DETECTED
)
if obstruction_detected:
attributes["obstruction-detected"] = obstruction_detected
return attributes
class HomeKitWindowCover(HomeKitEntity, CoverDevice):
@ -141,11 +139,7 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice):
def __init__(self, accessory, discovery_info):
"""Initialise the Cover."""
super().__init__(accessory, discovery_info)
self._state = None
self._position = None
self._tilt_position = None
self._obstruction_detected = None
self.lock_state = None
self._features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION
def get_characteristic_types(self):
@ -175,21 +169,6 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice):
SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT | SUPPORT_SET_TILT_POSITION
)
def _update_position_state(self, value):
self._state = CURRENT_WINDOW_STATE_MAP[value]
def _update_position_current(self, value):
self._position = value
def _update_vertical_tilt_current(self, value):
self._tilt_position = value
def _update_horizontal_tilt_current(self, value):
self._tilt_position = value
def _update_obstruction_detected(self, value):
self._obstruction_detected = value
@property
def supported_features(self):
"""Flag supported features."""
@ -198,22 +177,36 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice):
@property
def current_cover_position(self):
"""Return the current position of cover."""
return self._position
return self.service.value(CharacteristicsTypes.POSITION_CURRENT)
@property
def is_closed(self):
"""Return true if cover is closed, else False."""
return self._position == 0
return self.current_cover_position == 0
@property
def is_closing(self):
"""Return if the cover is closing or not."""
return self._state == STATE_CLOSING
value = self.service.value(CharacteristicsTypes.POSITION_STATE)
state = CURRENT_WINDOW_STATE_MAP[value]
return state == STATE_CLOSING
@property
def is_opening(self):
"""Return if the cover is opening or not."""
return self._state == STATE_OPENING
value = self.service.value(CharacteristicsTypes.POSITION_STATE)
state = CURRENT_WINDOW_STATE_MAP[value]
return state == STATE_OPENING
@property
def current_cover_tilt_position(self):
"""Return current position of cover tilt."""
tilt_position = self.service.value(CharacteristicsTypes.VERTICAL_TILT_CURRENT)
if not tilt_position:
tilt_position = self.service.value(
CharacteristicsTypes.HORIZONTAL_TILT_CURRENT
)
return tilt_position
async def async_stop_cover(self, **kwargs):
"""Send hold command."""
@ -238,11 +231,6 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice):
]
await self._accessory.put_characteristics(characteristics)
@property
def current_cover_tilt_position(self):
"""Return current position of cover tilt."""
return self._tilt_position
async def async_set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
tilt_position = kwargs[ATTR_TILT_POSITION]
@ -268,8 +256,12 @@ class HomeKitWindowCover(HomeKitEntity, CoverDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
state_attributes = {}
if self._obstruction_detected is not None:
state_attributes["obstruction-detected"] = self._obstruction_detected
attributes = {}
return state_attributes
obstruction_detected = self.service.value(
CharacteristicsTypes.OBSTRUCTION_DETECTED
)
if obstruction_detected:
attributes["obstruction-detected"] = obstruction_detected
return attributes

View file

@ -46,12 +46,7 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity):
def __init__(self, *args):
"""Initialise the fan."""
self._on = None
self._features = 0
self._rotation_direction = 0
self._rotation_speed = 0
self._swing_mode = 0
super().__init__(*args)
def get_characteristic_types(self):
@ -71,31 +66,23 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity):
def _setup_swing_mode(self, char):
self._features |= SUPPORT_OSCILLATE
def _update_rotation_direction(self, value):
self._rotation_direction = value
def _update_rotation_speed(self, value):
self._rotation_speed = value
def _update_swing_mode(self, value):
self._swing_mode = value
@property
def is_on(self):
"""Return true if device is on."""
return self._on
@property
def speed(self):
"""Return the current speed."""
if not self.is_on:
return SPEED_OFF
if self._rotation_speed > SPEED_TO_PCNT[SPEED_MEDIUM]:
rotation_speed = self.service.value(CharacteristicsTypes.ROTATION_SPEED)
if rotation_speed > SPEED_TO_PCNT[SPEED_MEDIUM]:
return SPEED_HIGH
if self._rotation_speed > SPEED_TO_PCNT[SPEED_LOW]:
if rotation_speed > SPEED_TO_PCNT[SPEED_LOW]:
return SPEED_MEDIUM
if self._rotation_speed > SPEED_TO_PCNT[SPEED_OFF]:
if rotation_speed > SPEED_TO_PCNT[SPEED_OFF]:
return SPEED_LOW
return SPEED_OFF
@property
@ -108,12 +95,14 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity):
@property
def current_direction(self):
"""Return the current direction of the fan."""
return HK_DIRECTION_TO_HA[self._rotation_direction]
direction = self.service.value(CharacteristicsTypes.ROTATION_DIRECTION)
return HK_DIRECTION_TO_HA[direction]
@property
def oscillating(self):
"""Return whether or not the fan is currently oscillating."""
return self._swing_mode == 1
oscillating = self.service.value(CharacteristicsTypes.SWING_MODE)
return oscillating == 1
@property
def supported_features(self):
@ -208,8 +197,10 @@ class HomeKitFanV1(BaseHomeKitFan):
"""Define the homekit characteristics the entity cares about."""
return [CharacteristicsTypes.ON] + super().get_characteristic_types()
def _update_on(self, value):
self._on = value == 1
@property
def is_on(self):
"""Return true if device is on."""
return self.service.value(CharacteristicsTypes.ON) == 1
class HomeKitFanV2(BaseHomeKitFan):
@ -221,8 +212,10 @@ class HomeKitFanV2(BaseHomeKitFan):
"""Define the homekit characteristics the entity cares about."""
return [CharacteristicsTypes.ACTIVE] + super().get_characteristic_types()
def _update_active(self, value):
self._on = value == 1
@property
def is_on(self):
"""Return true if device is on."""
return self.service.value(CharacteristicsTypes.ACTIVE) == 1
ENTITY_TYPES = {

View file

@ -38,15 +38,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class HomeKitLight(HomeKitEntity, Light):
"""Representation of a Homekit light."""
def __init__(self, *args):
"""Initialise the light."""
super().__init__(*args)
self._on = False
self._brightness = 0
self._color_temperature = 0
self._hue = 0
self._saturation = 0
def get_characteristic_types(self):
"""Define the homekit characteristics the entity cares about."""
return [
@ -69,40 +60,28 @@ class HomeKitLight(HomeKitEntity, Light):
def _setup_saturation(self, char):
self._features |= SUPPORT_COLOR
def _update_on(self, value):
self._on = value
def _update_brightness(self, value):
self._brightness = value
def _update_color_temperature(self, value):
self._color_temperature = value
def _update_hue(self, value):
self._hue = value
def _update_saturation(self, value):
self._saturation = value
@property
def is_on(self):
"""Return true if device is on."""
return self._on
return self.service.value(CharacteristicsTypes.ON)
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
return self._brightness * 255 / 100
return self.service.value(CharacteristicsTypes.BRIGHTNESS) * 255 / 100
@property
def hs_color(self):
"""Return the color property."""
return (self._hue, self._saturation)
return (
self.service.value(CharacteristicsTypes.HUE),
self.service.value(CharacteristicsTypes.SATURATION),
)
@property
def color_temp(self):
"""Return the color temperature."""
return self._color_temperature
return self.service.value(CharacteristicsTypes.COLOR_TEMPERATURE)
@property
def supported_features(self):

View file

@ -37,12 +37,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class HomeKitLock(HomeKitEntity, LockDevice):
"""Representation of a HomeKit Controller Lock."""
def __init__(self, accessory, discovery_info):
"""Initialise the Lock."""
super().__init__(accessory, discovery_info)
self._state = None
self._battery_level = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity cares about."""
return [
@ -51,16 +45,11 @@ class HomeKitLock(HomeKitEntity, LockDevice):
CharacteristicsTypes.BATTERY_LEVEL,
]
def _update_lock_mechanism_current_state(self, value):
self._state = CURRENT_STATE_MAP[value]
def _update_battery_level(self, value):
self._battery_level = value
@property
def is_locked(self):
"""Return true if device is locked."""
return self._state == STATE_LOCKED
value = self.service.value(CharacteristicsTypes.LOCK_MECHANISM_CURRENT_STATE)
return CURRENT_STATE_MAP[value] == STATE_LOCKED
async def async_lock(self, **kwargs):
"""Lock the device."""
@ -84,7 +73,10 @@ class HomeKitLock(HomeKitEntity, LockDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
if self._battery_level is None:
return None
attributes = {}
return {ATTR_BATTERY_LEVEL: self._battery_level}
battery_level = self.service.value(CharacteristicsTypes.BATTERY_LEVEL)
if battery_level:
attributes[ATTR_BATTERY_LEVEL] = battery_level
return attributes

View file

@ -130,9 +130,7 @@ class HomeKitTelevision(HomeKitEntity, MediaPlayerDevice):
@property
def source(self):
"""Name of the current input source."""
active_identifier = self.get_hk_char_value(
CharacteristicsTypes.ACTIVE_IDENTIFIER
)
active_identifier = self.service.value(CharacteristicsTypes.ACTIVE_IDENTIFIER)
if not active_identifier:
return None
@ -150,11 +148,11 @@ class HomeKitTelevision(HomeKitEntity, MediaPlayerDevice):
@property
def state(self):
"""State of the tv."""
active = self.get_hk_char_value(CharacteristicsTypes.ACTIVE)
active = self.service.value(CharacteristicsTypes.ACTIVE)
if not active:
return STATE_PROBLEM
homekit_state = self.get_hk_char_value(CharacteristicsTypes.CURRENT_MEDIA_STATE)
homekit_state = self.service.value(CharacteristicsTypes.CURRENT_MEDIA_STATE)
if homekit_state is not None:
return HK_TO_HA_STATE[homekit_state]

View file

@ -25,11 +25,6 @@ UNIT_LUX = "lux"
class HomeKitHumiditySensor(HomeKitEntity):
"""Representation of a Homekit humidity sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT]
@ -54,23 +49,15 @@ class HomeKitHumiditySensor(HomeKitEntity):
"""Return units for the sensor."""
return UNIT_PERCENTAGE
def _update_relative_humidity_current(self, value):
self._state = value
@property
def state(self):
"""Return the current humidity."""
return self._state
return self.service.value(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT)
class HomeKitTemperatureSensor(HomeKitEntity):
"""Representation of a Homekit temperature sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.TEMPERATURE_CURRENT]
@ -95,23 +82,15 @@ class HomeKitTemperatureSensor(HomeKitEntity):
"""Return units for the sensor."""
return TEMP_CELSIUS
def _update_temperature_current(self, value):
self._state = value
@property
def state(self):
"""Return the current temperature in Celsius."""
return self._state
return self.service.value(CharacteristicsTypes.TEMPERATURE_CURRENT)
class HomeKitLightSensor(HomeKitEntity):
"""Representation of a Homekit light level sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.LIGHT_LEVEL_CURRENT]
@ -136,23 +115,15 @@ class HomeKitLightSensor(HomeKitEntity):
"""Return units for the sensor."""
return UNIT_LUX
def _update_light_level_current(self, value):
self._state = value
@property
def state(self):
"""Return the current light level in lux."""
return self._state
return self.service.value(CharacteristicsTypes.LIGHT_LEVEL_CURRENT)
class HomeKitCarbonDioxideSensor(HomeKitEntity):
"""Representation of a Homekit Carbon Dioxide sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity is tracking."""
return [CharacteristicsTypes.CARBON_DIOXIDE_LEVEL]
@ -172,25 +143,15 @@ class HomeKitCarbonDioxideSensor(HomeKitEntity):
"""Return units for the sensor."""
return CONCENTRATION_PARTS_PER_MILLION
def _update_carbon_dioxide_level(self, value):
self._state = value
@property
def state(self):
"""Return the current CO2 level in ppm."""
return self._state
return self.service.value(CharacteristicsTypes.CARBON_DIOXIDE_LEVEL)
class HomeKitBatterySensor(HomeKitEntity):
"""Representation of a Homekit battery sensor."""
def __init__(self, *args):
"""Initialise the entity."""
super().__init__(*args)
self._state = None
self._low_battery = False
self._charging = False
def get_characteristic_types(self):
"""Define the homekit characteristics the entity is tracking."""
return [
@ -218,12 +179,12 @@ class HomeKitBatterySensor(HomeKitEntity):
# This is similar to the logic in helpers.icon, but we have delegated the
# decision about what mdi:battery-alert is to the device.
icon = "mdi:battery"
if self._charging and self.state > 10:
if self.is_charging and self.state > 10:
percentage = int(round(self.state / 20 - 0.01)) * 20
icon += f"-charging-{percentage}"
elif self._charging:
elif self.is_charging:
icon += "-outline"
elif self._low_battery:
elif self.is_low_battery:
icon += "-alert"
elif self.state < 95:
percentage = max(int(round(self.state / 10 - 0.01)) * 10, 10)
@ -236,22 +197,23 @@ class HomeKitBatterySensor(HomeKitEntity):
"""Return units for the sensor."""
return UNIT_PERCENTAGE
def _update_battery_level(self, value):
self._state = value
@property
def is_low_battery(self):
"""Return true if battery level is low."""
return self.service.value(CharacteristicsTypes.STATUS_LO_BATT) == 1
def _update_status_lo_batt(self, value):
self._low_battery = value == 1
def _update_charging_state(self, value):
@property
def is_charging(self):
"""Return true if currently charing."""
# 0 = not charging
# 1 = charging
# 2 = not chargeable
self._charging = value == 1
return self.service.value(CharacteristicsTypes.CHARGING_STATE) == 1
@property
def state(self):
"""Return the current battery level percentage."""
return self._state
return self.service.value(CharacteristicsTypes.BATTERY_LEVEL)
ENTITY_TYPES = {

View file

@ -32,30 +32,17 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class HomeKitSwitch(HomeKitEntity, SwitchDevice):
"""Representation of a Homekit switch."""
def __init__(self, *args):
"""Initialise the switch."""
super().__init__(*args)
self._on = None
self._outlet_in_use = None
def get_characteristic_types(self):
"""Define the homekit characteristics the entity cares about."""
return [CharacteristicsTypes.ON, CharacteristicsTypes.OUTLET_IN_USE]
def _update_on(self, value):
self._on = value
def _update_outlet_in_use(self, value):
self._outlet_in_use = value
@property
def is_on(self):
"""Return true if device is on."""
return self._on
return self.service.value(CharacteristicsTypes.ON)
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}]
await self._accessory.put_characteristics(characteristics)
@ -67,5 +54,6 @@ class HomeKitSwitch(HomeKitEntity, SwitchDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
if self._outlet_in_use is not None:
return {OUTLET_IN_USE: self._outlet_in_use}
outlet_in_use = self.service.value(CharacteristicsTypes.OUTLET_IN_USE)
if outlet_in_use is not None:
return {OUTLET_IN_USE: outlet_in_use}