Update dyson for the new fan entity model (#45762)

* Update dyson for the new fan entity model

* Fix test

* tweak

* fix

* adj

* Update homeassistant/components/dyson/fan.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* move percentage is None block

* move percentage is None block

* no need to list comp

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
J. Nick Koston 2021-01-30 23:26:02 -10:00 committed by GitHub
parent 6b44636344
commit ea7aa6af59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 128 additions and 87 deletions

View File

@ -1,5 +1,6 @@
"""Support for Dyson Pure Cool link fan.""" """Support for Dyson Pure Cool link fan."""
import logging import logging
import math
from typing import Optional from typing import Optional
from libpurecool.const import FanMode, FanSpeed, NightMode, Oscillation from libpurecool.const import FanMode, FanSpeed, NightMode, Oscillation
@ -9,15 +10,12 @@ from libpurecool.dyson_pure_state import DysonPureCoolState
from libpurecool.dyson_pure_state_v2 import DysonPureCoolV2State from libpurecool.dyson_pure_state_v2 import DysonPureCoolV2State
import voluptuous as vol import voluptuous as vol
from homeassistant.components.fan import ( from homeassistant.components.fan import SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity
SPEED_HIGH,
SPEED_LOW,
SPEED_MEDIUM,
SUPPORT_OSCILLATE,
SUPPORT_SET_SPEED,
FanEntity,
)
from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.util.percentage import (
percentage_to_ranged_value,
ranged_value_to_percentage,
)
from . import DYSON_DEVICES, DysonEntity from . import DYSON_DEVICES, DysonEntity
@ -70,40 +68,30 @@ SET_DYSON_SPEED_SCHEMA = {
} }
SPEED_LIST_HA = [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] PRESET_MODE_AUTO = "auto"
PRESET_MODES = [PRESET_MODE_AUTO]
SPEED_LIST_DYSON = [ ORDERED_DYSON_SPEEDS = [
int(FanSpeed.FAN_SPEED_1.value), FanSpeed.FAN_SPEED_1,
int(FanSpeed.FAN_SPEED_2.value), FanSpeed.FAN_SPEED_2,
int(FanSpeed.FAN_SPEED_3.value), FanSpeed.FAN_SPEED_3,
int(FanSpeed.FAN_SPEED_4.value), FanSpeed.FAN_SPEED_4,
int(FanSpeed.FAN_SPEED_5.value), FanSpeed.FAN_SPEED_5,
int(FanSpeed.FAN_SPEED_6.value), FanSpeed.FAN_SPEED_6,
int(FanSpeed.FAN_SPEED_7.value), FanSpeed.FAN_SPEED_7,
int(FanSpeed.FAN_SPEED_8.value), FanSpeed.FAN_SPEED_8,
int(FanSpeed.FAN_SPEED_9.value), FanSpeed.FAN_SPEED_9,
int(FanSpeed.FAN_SPEED_10.value), FanSpeed.FAN_SPEED_10,
] ]
DYSON_SPEED_TO_INT_VALUE = {k: int(k.value) for k in ORDERED_DYSON_SPEEDS}
INT_VALUE_TO_DYSON_SPEED = {v: k for k, v in DYSON_SPEED_TO_INT_VALUE.items()}
SPEED_DYSON_TO_HA = { SPEED_LIST_DYSON = list(DYSON_SPEED_TO_INT_VALUE.values())
FanSpeed.FAN_SPEED_1.value: SPEED_LOW,
FanSpeed.FAN_SPEED_2.value: SPEED_LOW,
FanSpeed.FAN_SPEED_3.value: SPEED_LOW,
FanSpeed.FAN_SPEED_4.value: SPEED_LOW,
FanSpeed.FAN_SPEED_AUTO.value: SPEED_MEDIUM,
FanSpeed.FAN_SPEED_5.value: SPEED_MEDIUM,
FanSpeed.FAN_SPEED_6.value: SPEED_MEDIUM,
FanSpeed.FAN_SPEED_7.value: SPEED_MEDIUM,
FanSpeed.FAN_SPEED_8.value: SPEED_HIGH,
FanSpeed.FAN_SPEED_9.value: SPEED_HIGH,
FanSpeed.FAN_SPEED_10.value: SPEED_HIGH,
}
SPEED_HA_TO_DYSON = { SPEED_RANGE = (
SPEED_LOW: FanSpeed.FAN_SPEED_4, SPEED_LIST_DYSON[0],
SPEED_MEDIUM: FanSpeed.FAN_SPEED_7, SPEED_LIST_DYSON[-1],
SPEED_HIGH: FanSpeed.FAN_SPEED_10, ) # off is not included
}
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
@ -160,14 +148,23 @@ class DysonFanEntity(DysonEntity, FanEntity):
"""Representation of a Dyson fan.""" """Representation of a Dyson fan."""
@property @property
def speed(self): def percentage(self):
"""Return the current speed.""" """Return the current speed percentage."""
return SPEED_DYSON_TO_HA[self._device.state.speed] if self.auto_mode:
return None
return ranged_value_to_percentage(SPEED_RANGE, int(self._device.state.speed))
@property @property
def speed_list(self) -> list: def preset_modes(self):
"""Get the list of available speeds.""" """Return the available preset modes."""
return SPEED_LIST_HA return PRESET_MODES
@property
def preset_mode(self):
"""Return the current preset mode."""
if self.auto_mode:
return PRESET_MODE_AUTO
return None
@property @property
def dyson_speed(self): def dyson_speed(self):
@ -206,12 +203,25 @@ class DysonFanEntity(DysonEntity, FanEntity):
ATTR_DYSON_SPEED_LIST: self.dyson_speed_list, ATTR_DYSON_SPEED_LIST: self.dyson_speed_list,
} }
def set_speed(self, speed: str) -> None: def set_auto_mode(self, auto_mode: bool) -> None:
"""Set the speed of the fan.""" """Set auto mode."""
if speed not in SPEED_LIST_HA: raise NotImplementedError
raise ValueError(f'"{speed}" is not a valid speed')
_LOGGER.debug("Set fan speed to: %s", speed) def set_percentage(self, percentage: int) -> None:
self.set_dyson_speed(SPEED_HA_TO_DYSON[speed]) """Set the speed percentage of the fan."""
if percentage == 0:
self.turn_off()
return
dyson_speed = INT_VALUE_TO_DYSON_SPEED[
math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage))
]
self.set_dyson_speed(dyson_speed)
def set_preset_mode(self, preset_mode: str) -> None:
"""Set a preset mode on the fan."""
self._valid_preset_mode_or_raise(preset_mode)
# There currently is only one
self.set_auto_mode(True)
def set_dyson_speed(self, speed: FanSpeed) -> None: def set_dyson_speed(self, speed: FanSpeed) -> None:
"""Set the exact speed of the fan.""" """Set the exact speed of the fan."""
@ -225,21 +235,6 @@ class DysonFanEntity(DysonEntity, FanEntity):
speed = FanSpeed(f"{int(dyson_speed):04d}") speed = FanSpeed(f"{int(dyson_speed):04d}")
self.set_dyson_speed(speed) self.set_dyson_speed(speed)
class DysonPureCoolLinkEntity(DysonFanEntity):
"""Representation of a Dyson fan."""
def __init__(self, device):
"""Initialize the fan."""
super().__init__(device, DysonPureCoolState)
#
# The fan entity model has changed to use percentages and preset_modes
# instead of speeds.
#
# Please review
# https://developers.home-assistant.io/docs/core/entity/fan/
#
def turn_on( def turn_on(
self, self,
speed: Optional[str] = None, speed: Optional[str] = None,
@ -248,12 +243,22 @@ class DysonPureCoolLinkEntity(DysonFanEntity):
**kwargs, **kwargs,
) -> None: ) -> None:
"""Turn on the fan.""" """Turn on the fan."""
_LOGGER.debug("Turn on fan %s with speed %s", self.name, speed) _LOGGER.debug("Turn on fan %s with percentage %s", self.name, percentage)
if speed is not None: if preset_mode:
self.set_speed(speed) self.set_preset_mode(preset_mode)
else: elif percentage is None:
# Speed not set, just turn on # percentage not set, just turn on
self._device.set_configuration(fan_mode=FanMode.FAN) self._device.set_configuration(fan_mode=FanMode.FAN)
else:
self.set_percentage(percentage)
class DysonPureCoolLinkEntity(DysonFanEntity):
"""Representation of a Dyson fan."""
def __init__(self, device):
"""Initialize the fan."""
super().__init__(device, DysonPureCoolState)
def turn_off(self, **kwargs) -> None: def turn_off(self, **kwargs) -> None:
"""Turn off the fan.""" """Turn off the fan."""
@ -312,13 +317,6 @@ class DysonPureCoolEntity(DysonFanEntity):
"""Initialize the fan.""" """Initialize the fan."""
super().__init__(device, DysonPureCoolV2State) super().__init__(device, DysonPureCoolV2State)
#
# The fan entity model has changed to use percentages and preset_modes
# instead of speeds.
#
# Please review
# https://developers.home-assistant.io/docs/core/entity/fan/
#
def turn_on( def turn_on(
self, self,
speed: Optional[str] = None, speed: Optional[str] = None,
@ -327,12 +325,14 @@ class DysonPureCoolEntity(DysonFanEntity):
**kwargs, **kwargs,
) -> None: ) -> None:
"""Turn on the fan.""" """Turn on the fan."""
_LOGGER.debug("Turn on fan %s", self.name) _LOGGER.debug("Turn on fan %s with percentage %s", self.name, percentage)
if preset_mode:
if speed is not None: self.set_preset_mode(preset_mode)
self.set_speed(speed) elif percentage is None:
else: # percentage not set, just turn on
self._device.turn_on() self._device.turn_on()
else:
self.set_percentage(percentage)
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn off the fan.""" """Turn off the fan."""

View File

@ -19,16 +19,18 @@ from homeassistant.components.dyson.fan import (
ATTR_HEPA_FILTER, ATTR_HEPA_FILTER,
ATTR_NIGHT_MODE, ATTR_NIGHT_MODE,
ATTR_TIMER, ATTR_TIMER,
PRESET_MODE_AUTO,
SERVICE_SET_ANGLE, SERVICE_SET_ANGLE,
SERVICE_SET_AUTO_MODE, SERVICE_SET_AUTO_MODE,
SERVICE_SET_DYSON_SPEED, SERVICE_SET_DYSON_SPEED,
SERVICE_SET_FLOW_DIRECTION_FRONT, SERVICE_SET_FLOW_DIRECTION_FRONT,
SERVICE_SET_NIGHT_MODE, SERVICE_SET_NIGHT_MODE,
SERVICE_SET_TIMER, SERVICE_SET_TIMER,
SPEED_LOW,
) )
from homeassistant.components.fan import ( from homeassistant.components.fan import (
ATTR_OSCILLATING, ATTR_OSCILLATING,
ATTR_PERCENTAGE,
ATTR_PRESET_MODE,
ATTR_SPEED, ATTR_SPEED,
ATTR_SPEED_LIST, ATTR_SPEED_LIST,
DOMAIN as PLATFORM_DOMAIN, DOMAIN as PLATFORM_DOMAIN,
@ -37,7 +39,9 @@ from homeassistant.components.fan import (
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
SERVICE_TURN_ON, SERVICE_TURN_ON,
SPEED_HIGH, SPEED_HIGH,
SPEED_LOW,
SPEED_MEDIUM, SPEED_MEDIUM,
SPEED_OFF,
SUPPORT_OSCILLATE, SUPPORT_OSCILLATE,
SUPPORT_SET_SPEED, SUPPORT_SET_SPEED,
) )
@ -84,8 +88,16 @@ async def test_state_purecoollink(
attributes = state.attributes attributes = state.attributes
assert attributes[ATTR_NIGHT_MODE] is True assert attributes[ATTR_NIGHT_MODE] is True
assert attributes[ATTR_OSCILLATING] is True assert attributes[ATTR_OSCILLATING] is True
assert attributes[ATTR_PERCENTAGE] == 10
assert attributes[ATTR_PRESET_MODE] is None
assert attributes[ATTR_SPEED] == SPEED_LOW assert attributes[ATTR_SPEED] == SPEED_LOW
assert attributes[ATTR_SPEED_LIST] == [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] assert attributes[ATTR_SPEED_LIST] == [
SPEED_OFF,
SPEED_LOW,
SPEED_MEDIUM,
SPEED_HIGH,
PRESET_MODE_AUTO,
]
assert attributes[ATTR_DYSON_SPEED] == 1 assert attributes[ATTR_DYSON_SPEED] == 1
assert attributes[ATTR_DYSON_SPEED_LIST] == list(range(1, 11)) assert attributes[ATTR_DYSON_SPEED_LIST] == list(range(1, 11))
assert attributes[ATTR_AUTO_MODE] is False assert attributes[ATTR_AUTO_MODE] is False
@ -106,7 +118,9 @@ async def test_state_purecoollink(
attributes = state.attributes attributes = state.attributes
assert attributes[ATTR_NIGHT_MODE] is False assert attributes[ATTR_NIGHT_MODE] is False
assert attributes[ATTR_OSCILLATING] is False assert attributes[ATTR_OSCILLATING] is False
assert attributes[ATTR_SPEED] == SPEED_MEDIUM assert attributes[ATTR_PERCENTAGE] is None
assert attributes[ATTR_PRESET_MODE] == "auto"
assert attributes[ATTR_SPEED] == PRESET_MODE_AUTO
assert attributes[ATTR_DYSON_SPEED] == "AUTO" assert attributes[ATTR_DYSON_SPEED] == "AUTO"
assert attributes[ATTR_AUTO_MODE] is True assert attributes[ATTR_AUTO_MODE] is True
@ -125,8 +139,16 @@ async def test_state_purecool(hass: HomeAssistant, device: DysonPureCool) -> Non
assert attributes[ATTR_OSCILLATING] is True assert attributes[ATTR_OSCILLATING] is True
assert attributes[ATTR_ANGLE_LOW] == 24 assert attributes[ATTR_ANGLE_LOW] == 24
assert attributes[ATTR_ANGLE_HIGH] == 254 assert attributes[ATTR_ANGLE_HIGH] == 254
assert attributes[ATTR_PERCENTAGE] == 10
assert attributes[ATTR_PRESET_MODE] is None
assert attributes[ATTR_SPEED] == SPEED_LOW assert attributes[ATTR_SPEED] == SPEED_LOW
assert attributes[ATTR_SPEED_LIST] == [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] assert attributes[ATTR_SPEED_LIST] == [
SPEED_OFF,
SPEED_LOW,
SPEED_MEDIUM,
SPEED_HIGH,
PRESET_MODE_AUTO,
]
assert attributes[ATTR_DYSON_SPEED] == 1 assert attributes[ATTR_DYSON_SPEED] == 1
assert attributes[ATTR_DYSON_SPEED_LIST] == list(range(1, 11)) assert attributes[ATTR_DYSON_SPEED_LIST] == list(range(1, 11))
assert attributes[ATTR_AUTO_MODE] is False assert attributes[ATTR_AUTO_MODE] is False
@ -148,7 +170,9 @@ async def test_state_purecool(hass: HomeAssistant, device: DysonPureCool) -> Non
attributes = state.attributes attributes = state.attributes
assert attributes[ATTR_NIGHT_MODE] is False assert attributes[ATTR_NIGHT_MODE] is False
assert attributes[ATTR_OSCILLATING] is False assert attributes[ATTR_OSCILLATING] is False
assert attributes[ATTR_SPEED] == SPEED_MEDIUM assert attributes[ATTR_PERCENTAGE] is None
assert attributes[ATTR_PRESET_MODE] == "auto"
assert attributes[ATTR_SPEED] == PRESET_MODE_AUTO
assert attributes[ATTR_DYSON_SPEED] == "AUTO" assert attributes[ATTR_DYSON_SPEED] == "AUTO"
assert attributes[ATTR_AUTO_MODE] is True assert attributes[ATTR_AUTO_MODE] is True
assert attributes[ATTR_FLOW_DIRECTION_FRONT] is False assert attributes[ATTR_FLOW_DIRECTION_FRONT] is False
@ -170,6 +194,11 @@ async def test_state_purecool(hass: HomeAssistant, device: DysonPureCool) -> Non
{ATTR_SPEED: SPEED_LOW}, {ATTR_SPEED: SPEED_LOW},
{"fan_mode": FanMode.FAN, "fan_speed": FanSpeed.FAN_SPEED_4}, {"fan_mode": FanMode.FAN, "fan_speed": FanSpeed.FAN_SPEED_4},
), ),
(
SERVICE_TURN_ON,
{ATTR_PERCENTAGE: 40},
{"fan_mode": FanMode.FAN, "fan_speed": FanSpeed.FAN_SPEED_4},
),
(SERVICE_TURN_OFF, {}, {"fan_mode": FanMode.OFF}), (SERVICE_TURN_OFF, {}, {"fan_mode": FanMode.OFF}),
( (
SERVICE_OSCILLATE, SERVICE_OSCILLATE,
@ -229,6 +258,18 @@ async def test_commands_purecoollink(
"set_fan_speed", "set_fan_speed",
[FanSpeed.FAN_SPEED_4], [FanSpeed.FAN_SPEED_4],
), ),
(
SERVICE_TURN_ON,
{ATTR_PERCENTAGE: 40},
"set_fan_speed",
[FanSpeed.FAN_SPEED_4],
),
(
SERVICE_TURN_ON,
{ATTR_PRESET_MODE: "auto"},
"enable_auto_mode",
[],
),
(SERVICE_TURN_OFF, {}, "turn_off", []), (SERVICE_TURN_OFF, {}, "turn_off", []),
(SERVICE_OSCILLATE, {ATTR_OSCILLATING: True}, "enable_oscillation", []), (SERVICE_OSCILLATE, {ATTR_OSCILLATING: True}, "enable_oscillation", []),
(SERVICE_OSCILLATE, {ATTR_OSCILLATING: False}, "disable_oscillation", []), (SERVICE_OSCILLATE, {ATTR_OSCILLATING: False}, "disable_oscillation", []),