Xiaomi miio add coordinator to fan platform (#54366)
* init coordinator for airpurifiers and airfresh * Update fan entities with coordinator * cache mode and fan_level at user update * pylint define attributes in _init * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> * cleanup code * Set hass.data[DATA_KEY] to enable * rename to filtered_entities in service handler * Update homeassistant/components/xiaomi_miio/fan.py Co-authored-by: Joakim Sørensen <hi@ludeeus.dev> * flake Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> Co-authored-by: Joakim Sørensen <hi@ludeeus.dev>
This commit is contained in:
parent
a7c08fff81
commit
5de1adacf7
2 changed files with 176 additions and 198 deletions
|
@ -3,7 +3,15 @@ from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from miio import AirHumidifier, AirHumidifierMiot, AirHumidifierMjjsq, DeviceException
|
from miio import (
|
||||||
|
AirFresh,
|
||||||
|
AirHumidifier,
|
||||||
|
AirHumidifierMiot,
|
||||||
|
AirHumidifierMjjsq,
|
||||||
|
AirPurifier,
|
||||||
|
AirPurifierMiot,
|
||||||
|
DeviceException,
|
||||||
|
)
|
||||||
from miio.gateway.gateway import GatewayException
|
from miio.gateway.gateway import GatewayException
|
||||||
|
|
||||||
from homeassistant import config_entries, core
|
from homeassistant import config_entries, core
|
||||||
|
@ -23,10 +31,13 @@ from .const import (
|
||||||
KEY_DEVICE,
|
KEY_DEVICE,
|
||||||
MODELS_AIR_MONITOR,
|
MODELS_AIR_MONITOR,
|
||||||
MODELS_FAN,
|
MODELS_FAN,
|
||||||
|
MODELS_FAN_MIIO,
|
||||||
MODELS_HUMIDIFIER,
|
MODELS_HUMIDIFIER,
|
||||||
|
MODELS_HUMIDIFIER_MIIO,
|
||||||
MODELS_HUMIDIFIER_MIOT,
|
MODELS_HUMIDIFIER_MIOT,
|
||||||
MODELS_HUMIDIFIER_MJJSQ,
|
MODELS_HUMIDIFIER_MJJSQ,
|
||||||
MODELS_LIGHT,
|
MODELS_LIGHT,
|
||||||
|
MODELS_PURIFIER_MIOT,
|
||||||
MODELS_SWITCH,
|
MODELS_SWITCH,
|
||||||
MODELS_VACUUM,
|
MODELS_VACUUM,
|
||||||
)
|
)
|
||||||
|
@ -107,19 +118,44 @@ async def async_create_miio_device_and_coordinator(
|
||||||
token = entry.data[CONF_TOKEN]
|
token = entry.data[CONF_TOKEN]
|
||||||
name = entry.title
|
name = entry.title
|
||||||
device = None
|
device = None
|
||||||
|
migrate = False
|
||||||
|
|
||||||
if model not in MODELS_HUMIDIFIER:
|
if (
|
||||||
|
model not in MODELS_HUMIDIFIER
|
||||||
|
and model not in MODELS_PURIFIER_MIOT
|
||||||
|
and model not in MODELS_FAN_MIIO
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
|
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
|
||||||
|
|
||||||
|
# Humidifiers
|
||||||
if model in MODELS_HUMIDIFIER_MIOT:
|
if model in MODELS_HUMIDIFIER_MIOT:
|
||||||
device = AirHumidifierMiot(host, token)
|
device = AirHumidifierMiot(host, token)
|
||||||
|
migrate = True
|
||||||
elif model in MODELS_HUMIDIFIER_MJJSQ:
|
elif model in MODELS_HUMIDIFIER_MJJSQ:
|
||||||
device = AirHumidifierMjjsq(host, token, model=model)
|
device = AirHumidifierMjjsq(host, token, model=model)
|
||||||
else:
|
migrate = True
|
||||||
|
elif model in MODELS_HUMIDIFIER_MIIO:
|
||||||
device = AirHumidifier(host, token, model=model)
|
device = AirHumidifier(host, token, model=model)
|
||||||
|
migrate = True
|
||||||
|
# Airpurifiers and Airfresh
|
||||||
|
elif model in MODELS_PURIFIER_MIOT:
|
||||||
|
device = AirPurifierMiot(host, token)
|
||||||
|
elif model.startswith("zhimi.airpurifier."):
|
||||||
|
device = AirPurifier(host, token)
|
||||||
|
elif model.startswith("zhimi.airfresh."):
|
||||||
|
device = AirFresh(host, token)
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Unsupported device found! Please create an issue at "
|
||||||
|
"https://github.com/syssi/xiaomi_airpurifier/issues "
|
||||||
|
"and provide the following data: %s",
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if migrate:
|
||||||
# Removing fan platform entity for humidifiers and migrate the name to the config entry for migration
|
# Removing fan platform entity for humidifiers and migrate the name to the config entry for migration
|
||||||
entity_registry = er.async_get(hass)
|
entity_registry = er.async_get(hass)
|
||||||
entity_id = entity_registry.async_get_entity_id("fan", DOMAIN, entry.unique_id)
|
entity_id = entity_registry.async_get_entity_id("fan", DOMAIN, entry.unique_id)
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
"""Support for Xiaomi Mi Air Purifier and Xiaomi Mi Air Humidifier."""
|
"""Support for Xiaomi Mi Air Purifier and Xiaomi Mi Air Humidifier."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import partial
|
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from miio import AirFresh, AirPurifier, AirPurifierMiot, DeviceException
|
|
||||||
from miio.airfresh import (
|
from miio.airfresh import (
|
||||||
LedBrightness as AirfreshLedBrightness,
|
LedBrightness as AirfreshLedBrightness,
|
||||||
OperationMode as AirfreshOperationMode,
|
OperationMode as AirfreshOperationMode,
|
||||||
|
@ -35,6 +33,7 @@ from homeassistant.const import (
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_TOKEN,
|
CONF_TOKEN,
|
||||||
)
|
)
|
||||||
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util.percentage import (
|
from homeassistant.util.percentage import (
|
||||||
percentage_to_ranged_value,
|
percentage_to_ranged_value,
|
||||||
|
@ -56,6 +55,8 @@ from .const import (
|
||||||
FEATURE_SET_LED,
|
FEATURE_SET_LED,
|
||||||
FEATURE_SET_LED_BRIGHTNESS,
|
FEATURE_SET_LED_BRIGHTNESS,
|
||||||
FEATURE_SET_VOLUME,
|
FEATURE_SET_VOLUME,
|
||||||
|
KEY_COORDINATOR,
|
||||||
|
KEY_DEVICE,
|
||||||
MODEL_AIRPURIFIER_2H,
|
MODEL_AIRPURIFIER_2H,
|
||||||
MODEL_AIRPURIFIER_2S,
|
MODEL_AIRPURIFIER_2S,
|
||||||
MODEL_AIRPURIFIER_PRO,
|
MODEL_AIRPURIFIER_PRO,
|
||||||
|
@ -79,9 +80,8 @@ from .const import (
|
||||||
SERVICE_SET_LEARN_MODE_ON,
|
SERVICE_SET_LEARN_MODE_ON,
|
||||||
SERVICE_SET_LED_BRIGHTNESS,
|
SERVICE_SET_LED_BRIGHTNESS,
|
||||||
SERVICE_SET_VOLUME,
|
SERVICE_SET_VOLUME,
|
||||||
SUCCESS,
|
|
||||||
)
|
)
|
||||||
from .device import XiaomiMiioEntity
|
from .device import XiaomiCoordinatedMiioEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -430,62 +430,55 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the Fan from a config entry."""
|
"""Set up the Fan from a config entry."""
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
if config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
|
if not config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
|
||||||
if DATA_KEY not in hass.data:
|
return
|
||||||
hass.data[DATA_KEY] = {}
|
|
||||||
|
hass.data.setdefault(DATA_KEY, {})
|
||||||
|
|
||||||
host = config_entry.data[CONF_HOST]
|
|
||||||
token = config_entry.data[CONF_TOKEN]
|
|
||||||
name = config_entry.title
|
name = config_entry.title
|
||||||
model = config_entry.data[CONF_MODEL]
|
model = config_entry.data[CONF_MODEL]
|
||||||
unique_id = config_entry.unique_id
|
unique_id = config_entry.unique_id
|
||||||
|
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
|
||||||
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
|
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
|
||||||
|
|
||||||
if model in MODELS_PURIFIER_MIOT:
|
if model in MODELS_PURIFIER_MIOT:
|
||||||
air_purifier = AirPurifierMiot(host, token)
|
|
||||||
entity = XiaomiAirPurifierMiot(
|
entity = XiaomiAirPurifierMiot(
|
||||||
name, air_purifier, config_entry, unique_id, allowed_failures=2
|
name,
|
||||||
|
device,
|
||||||
|
config_entry,
|
||||||
|
unique_id,
|
||||||
|
coordinator,
|
||||||
)
|
)
|
||||||
elif model.startswith("zhimi.airpurifier."):
|
elif model.startswith("zhimi.airpurifier."):
|
||||||
air_purifier = AirPurifier(host, token)
|
entity = XiaomiAirPurifier(name, device, config_entry, unique_id, coordinator)
|
||||||
entity = XiaomiAirPurifier(name, air_purifier, config_entry, unique_id)
|
|
||||||
elif model.startswith("zhimi.airfresh."):
|
elif model.startswith("zhimi.airfresh."):
|
||||||
air_fresh = AirFresh(host, token)
|
entity = XiaomiAirFresh(name, device, config_entry, unique_id, coordinator)
|
||||||
entity = XiaomiAirFresh(name, air_fresh, config_entry, unique_id)
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.error(
|
|
||||||
"Unsupported device found! Please create an issue at "
|
|
||||||
"https://github.com/syssi/xiaomi_airpurifier/issues "
|
|
||||||
"and provide the following data: %s",
|
|
||||||
model,
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
hass.data[DATA_KEY][host] = entity
|
hass.data[DATA_KEY][unique_id] = entity
|
||||||
|
|
||||||
entities.append(entity)
|
entities.append(entity)
|
||||||
|
|
||||||
async def async_service_handler(service):
|
async def async_service_handler(service):
|
||||||
"""Map services to methods on XiaomiAirPurifier."""
|
"""Map services to methods on XiaomiAirPurifier."""
|
||||||
method = SERVICE_TO_METHOD[service.service]
|
method = SERVICE_TO_METHOD[service.service]
|
||||||
params = {
|
params = {
|
||||||
key: value
|
key: value for key, value in service.data.items() if key != ATTR_ENTITY_ID
|
||||||
for key, value in service.data.items()
|
|
||||||
if key != ATTR_ENTITY_ID
|
|
||||||
}
|
}
|
||||||
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
||||||
if entity_ids:
|
if entity_ids:
|
||||||
entities = [
|
filtered_entities = [
|
||||||
entity
|
entity
|
||||||
for entity in hass.data[DATA_KEY].values()
|
for entity in hass.data[DATA_KEY].values()
|
||||||
if entity.entity_id in entity_ids
|
if entity.entity_id in entity_ids
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
entities = hass.data[DATA_KEY].values()
|
filtered_entities = hass.data[DATA_KEY].values()
|
||||||
|
|
||||||
update_tasks = []
|
update_tasks = []
|
||||||
|
|
||||||
for entity in entities:
|
for entity in filtered_entities:
|
||||||
entity_method = getattr(entity, method["method"], None)
|
entity_method = getattr(entity, method["method"], None)
|
||||||
if not entity_method:
|
if not entity_method:
|
||||||
continue
|
continue
|
||||||
|
@ -503,21 +496,23 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
DOMAIN, air_purifier_service, async_service_handler, schema=schema
|
DOMAIN, air_purifier_service, async_service_handler, schema=schema
|
||||||
)
|
)
|
||||||
|
|
||||||
async_add_entities(entities, update_before_add=True)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
|
||||||
"""Representation of a generic Xiaomi device."""
|
"""Representation of a generic Xiaomi device."""
|
||||||
|
|
||||||
def __init__(self, name, device, entry, unique_id):
|
def __init__(self, name, device, entry, unique_id, coordinator):
|
||||||
"""Initialize the generic Xiaomi device."""
|
"""Initialize the generic Xiaomi device."""
|
||||||
super().__init__(name, device, entry, unique_id)
|
super().__init__(name, device, entry, unique_id, coordinator)
|
||||||
|
|
||||||
self._available = False
|
self._available = False
|
||||||
|
self._available_attributes = {}
|
||||||
self._state = None
|
self._state = None
|
||||||
|
self._mode = None
|
||||||
|
self._fan_level = None
|
||||||
self._state_attrs = {ATTR_MODEL: self._model}
|
self._state_attrs = {ATTR_MODEL: self._model}
|
||||||
self._device_features = FEATURE_SET_CHILD_LOCK
|
self._device_features = FEATURE_SET_CHILD_LOCK
|
||||||
self._skip_update = False
|
|
||||||
self._supported_features = 0
|
self._supported_features = 0
|
||||||
self._speed_count = 100
|
self._speed_count = 100
|
||||||
self._preset_modes = []
|
self._preset_modes = []
|
||||||
|
@ -583,22 +578,20 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
async def _try_command(self, mask_error, func, *args, **kwargs):
|
@callback
|
||||||
"""Call a miio device command handling error messages."""
|
def _handle_coordinator_update(self):
|
||||||
try:
|
"""Fetch state from the device."""
|
||||||
result = await self.hass.async_add_executor_job(
|
self._available = True
|
||||||
partial(func, *args, **kwargs)
|
self._state = self.coordinator.data.is_on
|
||||||
|
self._state_attrs.update(
|
||||||
|
{
|
||||||
|
key: self._extract_value_from_attribute(self.coordinator.data, value)
|
||||||
|
for key, value in self._available_attributes.items()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
self._mode = self._state_attrs.get(ATTR_MODE)
|
||||||
_LOGGER.debug("Response received from miio device: %s", result)
|
self._fan_level = self._state_attrs.get(ATTR_FAN_LEVEL)
|
||||||
|
self.async_write_ha_state()
|
||||||
return result == SUCCESS
|
|
||||||
except DeviceException as exc:
|
|
||||||
if self._available:
|
|
||||||
_LOGGER.error(mask_error, exc)
|
|
||||||
self._available = False
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# The fan entity model has changed to use percentages and preset_modes
|
# The fan entity model has changed to use percentages and preset_modes
|
||||||
|
@ -630,7 +623,7 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
self._state = True
|
self._state = True
|
||||||
self._skip_update = True
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs) -> None:
|
async def async_turn_off(self, **kwargs) -> None:
|
||||||
"""Turn the device off."""
|
"""Turn the device off."""
|
||||||
|
@ -640,7 +633,7 @@ class XiaomiGenericDevice(XiaomiMiioEntity, FanEntity):
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
self._state = False
|
self._state = False
|
||||||
self._skip_update = True
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_set_buzzer_on(self):
|
async def async_set_buzzer_on(self):
|
||||||
"""Turn the buzzer on."""
|
"""Turn the buzzer on."""
|
||||||
|
@ -706,11 +699,9 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
|
||||||
|
|
||||||
REVERSE_SPEED_MODE_MAPPING = {v: k for k, v in SPEED_MODE_MAPPING.items()}
|
REVERSE_SPEED_MODE_MAPPING = {v: k for k, v in SPEED_MODE_MAPPING.items()}
|
||||||
|
|
||||||
def __init__(self, name, device, entry, unique_id, allowed_failures=0):
|
def __init__(self, name, device, entry, unique_id, coordinator):
|
||||||
"""Initialize the plug switch."""
|
"""Initialize the plug switch."""
|
||||||
super().__init__(name, device, entry, unique_id)
|
super().__init__(name, device, entry, unique_id, coordinator)
|
||||||
self._allowed_failures = allowed_failures
|
|
||||||
self._failure = 0
|
|
||||||
|
|
||||||
if self._model == MODEL_AIRPURIFIER_PRO:
|
if self._model == MODEL_AIRPURIFIER_PRO:
|
||||||
self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO
|
self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO
|
||||||
|
@ -774,45 +765,8 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
|
||||||
self._state_attrs.update(
|
self._state_attrs.update(
|
||||||
{attribute: None for attribute in self._available_attributes}
|
{attribute: None for attribute in self._available_attributes}
|
||||||
)
|
)
|
||||||
|
self._mode = self._state_attrs.get(ATTR_MODE)
|
||||||
async def async_update(self):
|
self._fan_level = self._state_attrs.get(ATTR_FAN_LEVEL)
|
||||||
"""Fetch state from the device."""
|
|
||||||
# On state change the device doesn't provide the new state immediately.
|
|
||||||
if self._skip_update:
|
|
||||||
self._skip_update = False
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
state = await self.hass.async_add_executor_job(self._device.status)
|
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.is_on
|
|
||||||
self._state_attrs.update(
|
|
||||||
{
|
|
||||||
key: self._extract_value_from_attribute(state, value)
|
|
||||||
for key, value in self._available_attributes.items()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self._failure = 0
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
|
||||||
self._failure += 1
|
|
||||||
if self._failure < self._allowed_failures:
|
|
||||||
_LOGGER.info(
|
|
||||||
"Got exception while fetching the state: %s, failure: %d",
|
|
||||||
ex,
|
|
||||||
self._failure,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if self._available:
|
|
||||||
self._available = False
|
|
||||||
_LOGGER.error(
|
|
||||||
"Got exception while fetching the state: %s, failure: %d",
|
|
||||||
ex,
|
|
||||||
self._failure,
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_mode(self):
|
def preset_mode(self):
|
||||||
|
@ -1032,8 +986,7 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
||||||
def percentage(self):
|
def percentage(self):
|
||||||
"""Return the current percentage based speed."""
|
"""Return the current percentage based speed."""
|
||||||
if self._state:
|
if self._state:
|
||||||
fan_level = self._state_attrs[ATTR_FAN_LEVEL]
|
return ranged_value_to_percentage((1, 3), self._fan_level)
|
||||||
return ranged_value_to_percentage((1, 3), fan_level)
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -1041,9 +994,7 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
||||||
def preset_mode(self):
|
def preset_mode(self):
|
||||||
"""Get the active preset mode."""
|
"""Get the active preset mode."""
|
||||||
if self._state:
|
if self._state:
|
||||||
preset_mode = AirpurifierMiotOperationMode(
|
preset_mode = AirpurifierMiotOperationMode(self._mode).name
|
||||||
self._state_attrs[ATTR_MODE]
|
|
||||||
).name
|
|
||||||
return preset_mode if preset_mode in self._preset_modes else None
|
return preset_mode if preset_mode in self._preset_modes else None
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
@ -1053,7 +1004,7 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
||||||
def speed(self):
|
def speed(self):
|
||||||
"""Return the current speed."""
|
"""Return the current speed."""
|
||||||
if self._state:
|
if self._state:
|
||||||
return AirpurifierMiotOperationMode(self._state_attrs[ATTR_MODE]).name
|
return AirpurifierMiotOperationMode(self._mode).name
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -1063,12 +1014,15 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
"""
|
"""
|
||||||
fan_level = math.ceil(percentage_to_ranged_value((1, 3), percentage))
|
fan_level = math.ceil(percentage_to_ranged_value((1, 3), percentage))
|
||||||
if fan_level:
|
if not fan_level:
|
||||||
await self._try_command(
|
return
|
||||||
|
if await self._try_command(
|
||||||
"Setting fan level of the miio device failed.",
|
"Setting fan level of the miio device failed.",
|
||||||
self._device.set_fan_level,
|
self._device.set_fan_level,
|
||||||
fan_level,
|
fan_level,
|
||||||
)
|
):
|
||||||
|
self._fan_level = fan_level
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||||
"""Set the preset mode of the fan.
|
"""Set the preset mode of the fan.
|
||||||
|
@ -1078,11 +1032,13 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
||||||
if preset_mode not in self.preset_modes:
|
if preset_mode not in self.preset_modes:
|
||||||
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
|
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
|
||||||
return
|
return
|
||||||
await self._try_command(
|
if await self._try_command(
|
||||||
"Setting operation mode of the miio device failed.",
|
"Setting operation mode of the miio device failed.",
|
||||||
self._device.set_mode,
|
self._device.set_mode,
|
||||||
self.PRESET_MODE_MAPPING[preset_mode],
|
self.PRESET_MODE_MAPPING[preset_mode],
|
||||||
)
|
):
|
||||||
|
self._mode = self.PRESET_MODE_MAPPING[preset_mode].value
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
# the async_set_speed function is deprecated, support will end with release 2021.7
|
# the async_set_speed function is deprecated, support will end with release 2021.7
|
||||||
# it is added here only for compatibility with legacy speeds
|
# it is added here only for compatibility with legacy speeds
|
||||||
|
@ -1093,11 +1049,13 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
|
||||||
|
|
||||||
_LOGGER.debug("Setting the operation mode to: %s", speed)
|
_LOGGER.debug("Setting the operation mode to: %s", speed)
|
||||||
|
|
||||||
await self._try_command(
|
if await self._try_command(
|
||||||
"Setting operation mode of the miio device failed.",
|
"Setting operation mode of the miio device failed.",
|
||||||
self._device.set_mode,
|
self._device.set_mode,
|
||||||
AirpurifierMiotOperationMode[speed.title()],
|
AirpurifierMiotOperationMode[speed.title()],
|
||||||
)
|
):
|
||||||
|
self._mode = AirpurifierMiotOperationMode[speed.title()].value
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_set_led_brightness(self, brightness: int = 2):
|
async def async_set_led_brightness(self, brightness: int = 2):
|
||||||
"""Set the led brightness."""
|
"""Set the led brightness."""
|
||||||
|
@ -1128,9 +1086,9 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
||||||
"Interval": AirfreshOperationMode.Interval,
|
"Interval": AirfreshOperationMode.Interval,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, name, device, entry, unique_id):
|
def __init__(self, name, device, entry, unique_id, coordinator):
|
||||||
"""Initialize the miio device."""
|
"""Initialize the miio device."""
|
||||||
super().__init__(name, device, entry, unique_id)
|
super().__init__(name, device, entry, unique_id, coordinator)
|
||||||
|
|
||||||
self._device_features = FEATURE_FLAGS_AIRFRESH
|
self._device_features = FEATURE_FLAGS_AIRFRESH
|
||||||
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRFRESH
|
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRFRESH
|
||||||
|
@ -1142,37 +1100,13 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
||||||
self._state_attrs.update(
|
self._state_attrs.update(
|
||||||
{attribute: None for attribute in self._available_attributes}
|
{attribute: None for attribute in self._available_attributes}
|
||||||
)
|
)
|
||||||
|
self._mode = self._state_attrs.get(ATTR_MODE)
|
||||||
async def async_update(self):
|
|
||||||
"""Fetch state from the device."""
|
|
||||||
# On state change the device doesn't provide the new state immediately.
|
|
||||||
if self._skip_update:
|
|
||||||
self._skip_update = False
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
state = await self.hass.async_add_executor_job(self._device.status)
|
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.is_on
|
|
||||||
self._state_attrs.update(
|
|
||||||
{
|
|
||||||
key: self._extract_value_from_attribute(state, value)
|
|
||||||
for key, value in self._available_attributes.items()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
|
||||||
if self._available:
|
|
||||||
self._available = False
|
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_mode(self):
|
def preset_mode(self):
|
||||||
"""Get the active preset mode."""
|
"""Get the active preset mode."""
|
||||||
if self._state:
|
if self._state:
|
||||||
preset_mode = AirfreshOperationMode(self._state_attrs[ATTR_MODE]).name
|
preset_mode = AirfreshOperationMode(self._mode).name
|
||||||
return preset_mode if preset_mode in self._preset_modes else None
|
return preset_mode if preset_mode in self._preset_modes else None
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
@ -1181,7 +1115,7 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
||||||
def percentage(self):
|
def percentage(self):
|
||||||
"""Return the current percentage based speed."""
|
"""Return the current percentage based speed."""
|
||||||
if self._state:
|
if self._state:
|
||||||
mode = AirfreshOperationMode(self._state_attrs[ATTR_MODE])
|
mode = AirfreshOperationMode(self._mode)
|
||||||
if mode in self.REVERSE_SPEED_MODE_MAPPING:
|
if mode in self.REVERSE_SPEED_MODE_MAPPING:
|
||||||
return ranged_value_to_percentage(
|
return ranged_value_to_percentage(
|
||||||
(1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode]
|
(1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode]
|
||||||
|
@ -1194,7 +1128,7 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
||||||
def speed(self):
|
def speed(self):
|
||||||
"""Return the current speed."""
|
"""Return the current speed."""
|
||||||
if self._state:
|
if self._state:
|
||||||
return AirfreshOperationMode(self._state_attrs[ATTR_MODE]).name
|
return AirfreshOperationMode(self._mode).name
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -1207,11 +1141,15 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
||||||
percentage_to_ranged_value((1, self._speed_count), percentage)
|
percentage_to_ranged_value((1, self._speed_count), percentage)
|
||||||
)
|
)
|
||||||
if speed_mode:
|
if speed_mode:
|
||||||
await self._try_command(
|
if await self._try_command(
|
||||||
"Setting operation mode of the miio device failed.",
|
"Setting operation mode of the miio device failed.",
|
||||||
self._device.set_mode,
|
self._device.set_mode,
|
||||||
AirfreshOperationMode(self.SPEED_MODE_MAPPING[speed_mode]),
|
AirfreshOperationMode(self.SPEED_MODE_MAPPING[speed_mode]),
|
||||||
)
|
):
|
||||||
|
self._mode = AirfreshOperationMode(
|
||||||
|
self.SPEED_MODE_MAPPING[speed_mode]
|
||||||
|
).value
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||||
"""Set the preset mode of the fan.
|
"""Set the preset mode of the fan.
|
||||||
|
@ -1221,11 +1159,13 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
||||||
if preset_mode not in self.preset_modes:
|
if preset_mode not in self.preset_modes:
|
||||||
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
|
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
|
||||||
return
|
return
|
||||||
await self._try_command(
|
if await self._try_command(
|
||||||
"Setting operation mode of the miio device failed.",
|
"Setting operation mode of the miio device failed.",
|
||||||
self._device.set_mode,
|
self._device.set_mode,
|
||||||
self.PRESET_MODE_MAPPING[preset_mode],
|
self.PRESET_MODE_MAPPING[preset_mode],
|
||||||
)
|
):
|
||||||
|
self._mode = self.PRESET_MODE_MAPPING[preset_mode].value
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
# the async_set_speed function is deprecated, support will end with release 2021.7
|
# the async_set_speed function is deprecated, support will end with release 2021.7
|
||||||
# it is added here only for compatibility with legacy speeds
|
# it is added here only for compatibility with legacy speeds
|
||||||
|
@ -1236,11 +1176,13 @@ class XiaomiAirFresh(XiaomiGenericDevice):
|
||||||
|
|
||||||
_LOGGER.debug("Setting the operation mode to: %s", speed)
|
_LOGGER.debug("Setting the operation mode to: %s", speed)
|
||||||
|
|
||||||
await self._try_command(
|
if await self._try_command(
|
||||||
"Setting operation mode of the miio device failed.",
|
"Setting operation mode of the miio device failed.",
|
||||||
self._device.set_mode,
|
self._device.set_mode,
|
||||||
AirfreshOperationMode[speed.title()],
|
AirfreshOperationMode[speed.title()],
|
||||||
)
|
):
|
||||||
|
self._mode = AirfreshOperationMode[speed.title()].value
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_set_led_on(self):
|
async def async_set_led_on(self):
|
||||||
"""Turn the led on."""
|
"""Turn the led on."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue