From 647d137daaa2677ddec9c400a53f5d7bc3a53b0c Mon Sep 17 00:00:00 2001 From: Jc2k Date: Wed, 11 Mar 2020 11:40:47 +0000 Subject: [PATCH] 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 --- .../components/homekit_controller/__init__.py | 79 ++++----------- .../homekit_controller/air_quality.py | 14 +-- .../homekit_controller/alarm_control_panel.py | 25 ++--- .../homekit_controller/binary_sensor.py | 44 ++------- .../components/homekit_controller/climate.py | 53 +++------- .../components/homekit_controller/cover.py | 96 +++++++++---------- .../components/homekit_controller/fan.py | 49 ++++------ .../components/homekit_controller/light.py | 35 ++----- .../components/homekit_controller/lock.py | 24 ++--- .../homekit_controller/media_player.py | 8 +- .../components/homekit_controller/sensor.py | 70 ++++---------- .../components/homekit_controller/switch.py | 20 +--- 12 files changed, 159 insertions(+), 358 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index f697449bbdb..95224dfccbd 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -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) diff --git a/homeassistant/components/homekit_controller/air_quality.py b/homeassistant/components/homekit_controller/air_quality.py index b2145887cef..999980ad60c 100644 --- a/homeassistant/components/homekit_controller/air_quality.py +++ b/homeassistant/components/homekit_controller/air_quality.py @@ -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): diff --git a/homeassistant/components/homekit_controller/alarm_control_panel.py b/homeassistant/components/homekit_controller/alarm_control_panel.py index d0ddd8ae816..0a96d716c78 100644 --- a/homeassistant/components/homekit_controller/alarm_control_panel.py +++ b/homeassistant/components/homekit_controller/alarm_control_panel.py @@ -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 diff --git a/homeassistant/components/homekit_controller/binary_sensor.py b/homeassistant/components/homekit_controller/binary_sensor.py index 7ca7f7a5711..39d0e19ba40 100644 --- a/homeassistant/components/homekit_controller/binary_sensor.py +++ b/homeassistant/components/homekit_controller/binary_sensor.py @@ -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 = { diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index b294bb9bb71..e748ea430e5 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -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): diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index 2799d1d76a6..79682970496 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -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 diff --git a/homeassistant/components/homekit_controller/fan.py b/homeassistant/components/homekit_controller/fan.py index 24bb5b96503..25cfee73fb8 100644 --- a/homeassistant/components/homekit_controller/fan.py +++ b/homeassistant/components/homekit_controller/fan.py @@ -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 = { diff --git a/homeassistant/components/homekit_controller/light.py b/homeassistant/components/homekit_controller/light.py index 5978455cf6f..962cfcc466c 100644 --- a/homeassistant/components/homekit_controller/light.py +++ b/homeassistant/components/homekit_controller/light.py @@ -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): diff --git a/homeassistant/components/homekit_controller/lock.py b/homeassistant/components/homekit_controller/lock.py index fc046c704b9..b79c10e2ae0 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -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 diff --git a/homeassistant/components/homekit_controller/media_player.py b/homeassistant/components/homekit_controller/media_player.py index 09693f3e8a8..798931e2cb4 100644 --- a/homeassistant/components/homekit_controller/media_player.py +++ b/homeassistant/components/homekit_controller/media_player.py @@ -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] diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index 636c10dbe79..87f47e72023 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -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 = { diff --git a/homeassistant/components/homekit_controller/switch.py b/homeassistant/components/homekit_controller/switch.py index 9f12d59204d..2251062d99c 100644 --- a/homeassistant/components/homekit_controller/switch.py +++ b/homeassistant/components/homekit_controller/switch.py @@ -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}