async def test_ordered_list_percentage_round_trip(): """Test we can round trip.""" for ordered_list in (SMALL_ORDERED_LIST, LARGE_ORDERED_LIST): for i in range(1, 100): ordered_list_item_to_percentage( ordered_list, percentage_to_ordered_list_item(ordered_list, i)) == i
def turn_on(self, speed=None, percentage=None, preset_mode=None, **kwargs): ret = False if not self.is_on: ret = self.set_property(self._prop_power, True) if self._prop_percentage: if not percentage and speed: percentage = ordered_list_item_to_percentage(self.speed_list, speed) if percentage: ret = self.set_property(self._prop_percentage, percentage) elif percentage is not None: _LOGGER.warning('Set fan speed percentage to %s failed: %s', self.name, { 'speed': speed, 'percentage': percentage, }) elif self._prop_speed: if not speed and percentage: speed = percentage_to_ordered_list_item(self.speed_list, percentage) val = self._prop_speed.list_first(speed) if speed else None if val is None and self._prop_speed.value_range: if speed is not None: val = int(speed) if val is not None: ret = self.set_property(self._prop_speed, val) elif speed is not None: _LOGGER.warning('Set fan speed level to %s failed: %s', self.name, { 'speed': speed, 'percentage': percentage, 'value': val, }) if preset_mode and self._prop_mode: val = self._prop_mode.list_first(preset_mode) if val is not None: ret = self.set_property(self._prop_mode, val) return ret
def percentage(self): """Return the current speed percentage.""" if self._state is None: return None if self._state == 0: return 0 return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, self._state)
async def async_turn_on(self, speed: str = None, **kwargs): """Turn on the fan.""" if speed is None: speed = FAN_SPEEDS[0] await self.async_set_percentage( ordered_list_item_to_percentage(FAN_SPEEDS, speed))
def percentage(self) -> Optional[int]: for speed, status_pattern in self._available_speeds.items(): for k, v in status_pattern.items(): if self._device_status.get(k) != v: break else: return ordered_list_item_to_percentage(self._speeds, speed)
def speed_received(msg): """Handle new received MQTT message for the speed.""" speed_payload = self._value_templates[ATTR_SPEED](msg.payload) if speed_payload == self._payload["SPEED_LOW"]: speed = SPEED_LOW elif speed_payload == self._payload["SPEED_MEDIUM"]: speed = SPEED_MEDIUM elif speed_payload == self._payload["SPEED_HIGH"]: speed = SPEED_HIGH elif speed_payload == self._payload["SPEED_OFF"]: speed = SPEED_OFF else: speed = None if speed and speed in self._legacy_speeds_list: self._speed = speed else: _LOGGER.warning( "'%s' received on topic %s is not a valid speed", msg.payload, msg.topic, ) return if not self._implemented_percentage: if speed in self._speeds_list: self._percentage = ordered_list_item_to_percentage( self._speeds_list, speed) elif speed == SPEED_OFF: self._percentage = 0 self.async_write_ha_state()
async def set_state(self, data: RequestData, state: dict[str, Any]): """Set device state.""" yandex_mode = state['value'] if self.modes_map: ha_modes = self.modes_map.get(yandex_mode) if not ha_modes: raise SmartHomeError( ERR_INVALID_VALUE, f'Unsupported mode "{yandex_mode}" for {self.instance} instance of {self.state.entity_id}. ' f'Check \"modes\" setting for this entity') ha_mode = self._convert_mapping_speed_value(ha_modes[0]) else: ha_mode = ordered_list_item_to_percentage(self.supported_ha_modes, yandex_mode) await self.hass.services.async_call( fan.DOMAIN, fan.SERVICE_SET_PERCENTAGE, { ATTR_ENTITY_ID: self.state.entity_id, fan.ATTR_PERCENTAGE: ha_mode }, blocking=True, context=data.context)
class WiLightFan(WiLightDevice, FanEntity): """Representation of a WiLights fan.""" _attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.DIRECTION def __init__(self, api_device, index, item_name): """Initialize the device.""" super().__init__(api_device, index, item_name) # Initialize the WiLights fan. self._direction = WL_DIRECTION_FORWARD @property def icon(self): """Return the icon of device based on its type.""" return "mdi:fan" @property def is_on(self): """Return true if device is on.""" return self._status.get("direction", WL_DIRECTION_OFF) != WL_DIRECTION_OFF @property def percentage(self) -> int | None: """Return the current speed percentage.""" if ("direction" in self._status and self._status["direction"] == WL_DIRECTION_OFF): return 0 if (wl_speed := self._status.get("speed")) is None: return None return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, wl_speed)
def cvt_battery(val: int | None) -> int | None: """Convert battery to percentage.""" if val is None: return None if val > 0: return percentage.ordered_list_item_to_percentage([1, 2, 3, 4], val) return 0
async def async_set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode of the fan. This method is a coroutine. """ if preset_mode not in self.preset_modes: _LOGGER.warning("'%s'is not a valid preset mode", preset_mode) return # Legacy are deprecated in the schema, support will be removed after a quarter (2021.7) if preset_mode in self._legacy_speeds_list: await self.async_set_speed(speed=preset_mode) if not self._implemented_percentage and preset_mode in self.speed_list: self._percentage = ordered_list_item_to_percentage( self.speed_list, preset_mode) mqtt_payload = self._command_templates[ATTR_PRESET_MODE](preset_mode) mqtt.async_publish( self.hass, self._topic[CONF_PRESET_MODE_COMMAND_TOPIC], mqtt_payload, self._config[CONF_QOS], self._config[CONF_RETAIN], ) if self._optimistic_preset_mode: self._preset_mode = preset_mode self.async_write_ha_state()
def speed_to_percentage(self, speed: str) -> int: """ Map a speed to a percentage. Officially this should only have to deal with the 4 pre-defined speeds: return { SPEED_OFF: 0, SPEED_LOW: 33, SPEED_MEDIUM: 66, SPEED_HIGH: 100, }[speed] Unfortunately lots of fans make up their own speeds. So the default mapping is more dynamic. """ if speed in OFF_SPEED_VALUES: return 0 speed_list = self._speed_list_without_preset_modes if speed_list and speed not in speed_list: raise NotValidSpeedError( f"The speed {speed} is not a valid speed.") try: return ordered_list_item_to_percentage(speed_list, speed) except ValueError as ex: raise NoValidSpeedsError( f"The speed_list {speed_list} does not contain any valid speeds." ) from ex
def percentage(self) -> int: """Return the current percentage.""" if self._fan_speed != "0": percentage = ordered_list_item_to_percentage( self._speed_names, self._fan_speed) return percentage
def percentage(self) -> Optional[int]: """Return the current speed.""" if not self.is_on: return 0 if self.speeds is None: return None return ordered_list_item_to_percentage(self.speeds, self._tuya.speed())
async def async_turn_on(self, percentage: int = None, **kwargs): """Turn on the fan.""" if percentage is None: percentage = ordered_list_item_to_percentage( self._speed_list, self._last_on_speed or self._speed_list[0]) await self.async_set_percentage(percentage)
def percentage(self) -> int | None: """Return the current speed percentage.""" if self._device.speed == 0: return 0 if self._device.speed not in ORDERED_NAMED_FAN_SPEEDS: return None return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, self._device.speed)
def speed_to_percentage(self, speed: str) -> int: # pylint: disable=no-self-use """Map a legacy speed to a percentage.""" if speed in OFF_SPEED_VALUES: return 0 if speed not in LEGACY_SPEED_LIST: raise NotValidSpeedError( f"The speed {speed} is not a valid speed.") return ordered_list_item_to_percentage(LEGACY_SPEED_LIST, speed)
def percentage(self) -> Optional[int]: """Return the current speed percentage.""" if self._device["fan_speed"] is None: return None if self._device["fan_speed"] == FAN_OFF: return 0 return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, self._device["fan_speed"])
async def async_turn_on(self, speed=None, percentage=None, preset_mode=None, **kwargs): """Turn the fan on.""" # Tasmota does not support turning a fan on with implicit speed await self.async_set_percentage( percentage or ordered_list_item_to_percentage( ORDERED_NAMED_FAN_SPEEDS, tasmota_const.FAN_SPEED_MEDIUM))
def percentage(self) -> str: """Return the current speed percentage.""" if "direction" in self._status: if self._status["direction"] == WL_DIRECTION_OFF: return 0 wl_speed = self._status.get("speed") if wl_speed is None: return None return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, wl_speed)
def percentage(self) -> int | None: """Return the current speed percentage.""" if not self._api.state.is_on: return 0 if self.speed_count == 0: return 100 if self._api.state.fan_speed is None: return None return ordered_list_item_to_percentage(self._device.fan_speeds, self._api.state.fan_speed)
def percentage(self): """Return the current speed as a percentage.""" if self._prop_percentage: val = self._prop_percentage.from_dict(self._state_attrs) if val is not None: return val lst = [v for v in self.speed_list if v not in OFF_SPEED_VALUES] try: return ordered_list_item_to_percentage(lst, self.speed) except ValueError: return None
def percentage(self) -> int | None: """Return the current speed percentage.""" if not self._static_info.supports_speed: return None if not self._supports_speed_levels: return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, self._state.speed) return ranged_value_to_percentage( (1, self._static_info.supported_speed_levels), self._state.speed_level)
def percentage(self) -> Union[int, None]: """Return the current speed percentage.""" state = self._wrapper.get_state() if state is None: return None elif self._wrapper.is_sleep or self._wrapper.is_auto: return None elif state.get(ATTR_AIRFLOW) is None: return None else: return ordered_list_item_to_percentage( ORDERED_NAMED_FAN_SPEEDS, state.get(ATTR_AIRFLOW) )
def speed_to_percentage(self, speed: str) -> int: """Convert speed to percentage. Legacy fan support. """ if speed == SPEED_OFF: return 0 if speed not in LEGACY_SPEED_TO_DECONZ: speed = SPEED_MEDIUM return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, LEGACY_SPEED_TO_DECONZ[speed])
def percentage(self) -> int: """Return the current speed.""" if not self.is_on: return 0 if self.tuya_device.category == "kj": if self.air_purifier_speed_range_len > 1: if not self.air_purifier_speed_range_enum: # if air-purifier speed enumeration is supported we will prefer it. return ordered_list_item_to_percentage( self.air_purifier_speed_range_enum, self.tuya_device.status.get(DPCODE_AP_FAN_SPEED_ENUM, 0)) return self.tuya_device.status.get(DPCODE_FAN_SPEED, 0)
def percentage(self) -> int | None: """Return the current speed.""" if not self.is_on: return 0 if (self.tuya_device.category == "kj" and self.air_purifier_speed_range_len > 1 and not self.air_purifier_speed_range_enum and DPCODE_AP_FAN_SPEED_ENUM in self.tuya_device.status): # if air-purifier speed enumeration is supported we will prefer it. return ordered_list_item_to_percentage( self.air_purifier_speed_range_enum, self.tuya_device.status[DPCODE_AP_FAN_SPEED_ENUM], ) # some type may not have the fan_speed_percent key return self.tuya_device.status.get(DPCODE_FAN_SPEED)
def preset_mode_received(msg): """Handle new received MQTT message for preset mode.""" preset_mode = self._value_templates[ATTR_PRESET_MODE](msg.payload) if preset_mode not in self.preset_modes: _LOGGER.warning( "'%s' received on topic %s is not a valid preset mode", msg.payload, msg.topic, ) return self._preset_mode = preset_mode if not self._implemented_percentage and (preset_mode in self.speed_list): self._percentage = ordered_list_item_to_percentage( self.speed_list, preset_mode) self.async_write_ha_state()
def _update_from_device_data(self, data: State | None) -> None: """Handle data update.""" if not data: self._percentage = 0 return if data.fan_speed: self._percentage = ordered_list_item_to_percentage( ORDERED_NAMED_FAN_SPEEDS, str(data.fan_speed)) else: self._percentage = 0 if data.after_cooking_on: if data.after_cooking_fan_speed: self._preset_mode = PRESET_MODE_AFTER_COOKING_MANUAL else: self._preset_mode = PRESET_MODE_AFTER_COOKING_AUTO else: self._preset_mode = PRESET_MODE_NORMAL
def turn_on(self, speed=None, percentage=None, preset_mode=None, **kwargs): ret = False if not self.is_on: ret = self.set_property(self._prop_power.full_name, True) if self._prop_percentage: if not percentage and speed: percentage = ordered_list_item_to_percentage(self.speed_list, speed) if percentage: ret = self.set_property(self._prop_percentage.full_name, percentage) elif self._prop_speed: if not speed and percentage: speed = percentage_to_ordered_list_item(self.speed_list, percentage) val = self._prop_speed.list_first(speed) if speed else None if val is not None: ret = self.set_property(self._prop_speed.full_name, val) if preset_mode and self._prop_mode: val = self._prop_mode.list_first(preset_mode) if val is not None: ret = self.set_property(self._prop_mode.full_name, val) return ret
def status_updated(self): """Get state of Tuya fan.""" self._is_on = self.dps(self._dp_id) current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) if self._use_ordered_list: _LOGGER.debug( "Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list, ) if current_speed is not None: self._percentage = ordered_list_item_to_percentage( self._ordered_list, current_speed) else: _LOGGER.debug( "Fan current_speed ranged_value_to_percentage: %s from %s", current_speed, self._speed_range, ) if current_speed is not None: self._percentage = ranged_value_to_percentage( self._speed_range, int(current_speed)) _LOGGER.debug("Fan current_percentage: %s", self._percentage) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) _LOGGER.debug("Fan current_oscillating : %s", self._oscillating) if self.has_config(CONF_FAN_DIRECTION): value = self.dps_conf(CONF_FAN_DIRECTION) if value is not None: if value == self._config.get(CONF_FAN_DIRECTION_FWD): self._direction = DIRECTION_FORWARD if value == self._config.get(CONF_FAN_DIRECTION_REV): self._direction = DIRECTION_REVERSE _LOGGER.debug("Fan current_direction : %s > %s", value, self._direction)