Add support for v1 and v2 HomeKit fans. (#30503)
* Add support for v1 and v2 HomeKit fans. * Lint fix
This commit is contained in:
parent
8625e962ce
commit
fffc5a5fbb
7 changed files with 1856 additions and 0 deletions
254
homeassistant/components/homekit_controller/fan.py
Normal file
254
homeassistant/components/homekit_controller/fan.py
Normal file
|
@ -0,0 +1,254 @@
|
|||
"""Support for Homekit fans."""
|
||||
import logging
|
||||
|
||||
from homekit.model.characteristics import CharacteristicsTypes
|
||||
|
||||
from homeassistant.components.fan import (
|
||||
DIRECTION_FORWARD,
|
||||
DIRECTION_REVERSE,
|
||||
SPEED_HIGH,
|
||||
SPEED_LOW,
|
||||
SPEED_MEDIUM,
|
||||
SPEED_OFF,
|
||||
SUPPORT_DIRECTION,
|
||||
SUPPORT_OSCILLATE,
|
||||
SUPPORT_SET_SPEED,
|
||||
FanEntity,
|
||||
)
|
||||
|
||||
from . import KNOWN_DEVICES, HomeKitEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# 0 is clockwise, 1 is counter-clockwise. The match to forward and reverse is so that
|
||||
# its consistent with homeassistant.components.homekit.
|
||||
DIRECTION_TO_HK = {
|
||||
DIRECTION_REVERSE: 1,
|
||||
DIRECTION_FORWARD: 0,
|
||||
}
|
||||
HK_DIRECTION_TO_HA = {v: k for (k, v) in DIRECTION_TO_HK.items()}
|
||||
|
||||
SPEED_TO_PCNT = {
|
||||
SPEED_HIGH: 100,
|
||||
SPEED_MEDIUM: 50,
|
||||
SPEED_LOW: 25,
|
||||
SPEED_OFF: 0,
|
||||
}
|
||||
|
||||
|
||||
class BaseHomeKitFan(HomeKitEntity, FanEntity):
|
||||
"""Representation of a Homekit fan."""
|
||||
|
||||
# This must be set in subclasses to the name of a boolean characteristic
|
||||
# that controls whether the fan is on or off.
|
||||
on_characteristic = None
|
||||
|
||||
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):
|
||||
"""Define the homekit characteristics the entity cares about."""
|
||||
return [
|
||||
CharacteristicsTypes.SWING_MODE,
|
||||
CharacteristicsTypes.ROTATION_DIRECTION,
|
||||
CharacteristicsTypes.ROTATION_SPEED,
|
||||
]
|
||||
|
||||
def _setup_rotation_direction(self, char):
|
||||
self._features |= SUPPORT_DIRECTION
|
||||
|
||||
def _setup_rotation_speed(self, char):
|
||||
self._features |= SUPPORT_SET_SPEED
|
||||
|
||||
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]:
|
||||
return SPEED_HIGH
|
||||
if self._rotation_speed > SPEED_TO_PCNT[SPEED_LOW]:
|
||||
return SPEED_MEDIUM
|
||||
if self._rotation_speed > SPEED_TO_PCNT[SPEED_OFF]:
|
||||
return SPEED_LOW
|
||||
return SPEED_OFF
|
||||
|
||||
@property
|
||||
def speed_list(self):
|
||||
"""Get the list of available speeds."""
|
||||
if self.supported_features & SUPPORT_SET_SPEED:
|
||||
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
||||
return []
|
||||
|
||||
@property
|
||||
def current_direction(self):
|
||||
"""Return the current direction of the fan."""
|
||||
return HK_DIRECTION_TO_HA[self._rotation_direction]
|
||||
|
||||
@property
|
||||
def oscillating(self):
|
||||
"""Return whether or not the fan is currently oscillating."""
|
||||
return self._swing_mode == 1
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
return self._features
|
||||
|
||||
async def async_set_direction(self, direction):
|
||||
"""Set the direction of the fan."""
|
||||
if self.supported_features & SUPPORT_DIRECTION:
|
||||
await self._accessory.put_characteristics(
|
||||
[
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["rotation.direction"],
|
||||
"value": DIRECTION_TO_HK[direction],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
async def async_set_speed(self, speed):
|
||||
"""Set the speed of the fan."""
|
||||
if speed == SPEED_OFF:
|
||||
return await self.async_turn_off()
|
||||
|
||||
if self.supported_features & SUPPORT_SET_SPEED:
|
||||
await self._accessory.put_characteristics(
|
||||
[
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["rotation.speed"],
|
||||
"value": SPEED_TO_PCNT[speed],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
async def async_oscillate(self, oscillating: bool):
|
||||
"""Oscillate the fan."""
|
||||
if self.supported_features & SUPPORT_OSCILLATE:
|
||||
await self._accessory.put_characteristics(
|
||||
[
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["swing-mode"],
|
||||
"value": 1 if oscillating else 0,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
async def async_turn_on(self, speed=None, **kwargs):
|
||||
"""Turn the specified fan on."""
|
||||
|
||||
characteristics = []
|
||||
|
||||
if not self.is_on:
|
||||
characteristics.append(
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars[self.on_characteristic],
|
||||
"value": True,
|
||||
}
|
||||
)
|
||||
|
||||
if self.supported_features & SUPPORT_SET_SPEED and speed:
|
||||
characteristics.append(
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["rotation.speed"],
|
||||
"value": SPEED_TO_PCNT[speed],
|
||||
},
|
||||
)
|
||||
|
||||
if not characteristics:
|
||||
return
|
||||
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn the specified fan off."""
|
||||
characteristics = [
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars[self.on_characteristic],
|
||||
"value": False,
|
||||
}
|
||||
]
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
|
||||
class HomeKitFanV1(BaseHomeKitFan):
|
||||
"""Implement fan support for public.hap.service.fan."""
|
||||
|
||||
on_characteristic = "on"
|
||||
|
||||
def get_characteristic_types(self):
|
||||
"""Define the homekit characteristics the entity cares about."""
|
||||
return [CharacteristicsTypes.ON] + super().get_characteristic_types()
|
||||
|
||||
def _update_on(self, value):
|
||||
self._on = value == 1
|
||||
|
||||
|
||||
class HomeKitFanV2(BaseHomeKitFan):
|
||||
"""Implement fan support for public.hap.service.fanv2."""
|
||||
|
||||
on_characteristic = "active"
|
||||
|
||||
def get_characteristic_types(self):
|
||||
"""Define the homekit characteristics the entity cares about."""
|
||||
return [CharacteristicsTypes.ACTIVE] + super().get_characteristic_types()
|
||||
|
||||
def _update_active(self, value):
|
||||
self._on = value == 1
|
||||
|
||||
|
||||
ENTITY_TYPES = {
|
||||
"fan": HomeKitFanV1,
|
||||
"fanv2": HomeKitFanV2,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Legacy set up platform."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up Homekit fans."""
|
||||
hkid = config_entry.data["AccessoryPairingID"]
|
||||
conn = hass.data[KNOWN_DEVICES][hkid]
|
||||
|
||||
def async_add_service(aid, service):
|
||||
entity_class = ENTITY_TYPES.get(service["stype"])
|
||||
if not entity_class:
|
||||
return False
|
||||
info = {"aid": aid, "iid": service["iid"]}
|
||||
async_add_entities([entity_class(conn, info)], True)
|
||||
return True
|
||||
|
||||
conn.add_listener(async_add_service)
|
Loading…
Add table
Add a link
Reference in a new issue