From 47312793f86cdf2af872dae65902ea2d80a0dfdd Mon Sep 17 00:00:00 2001 From: LG-ThinQ-Integration Date: Tue, 8 Oct 2024 19:22:08 +0900 Subject: [PATCH] Add select platform to LG ThinQ integration (#125709) * Add select platform to LG ThinQ integration * Update file mode * Add select platform to LG ThinQ integration * Update strings.json * Update select.py * Update __init__.py * Move entities from select to switch * Dedup translations --------- Co-authored-by: jangwon.lee Co-authored-by: Joost Lekkerkerker --- homeassistant/components/lg_thinq/__init__.py | 1 + homeassistant/components/lg_thinq/icons.json | 41 ++++ homeassistant/components/lg_thinq/select.py | 202 ++++++++++++++++++ .../components/lg_thinq/strings.json | 158 +++++++++++++- 4 files changed, 398 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/lg_thinq/select.py diff --git a/homeassistant/components/lg_thinq/__init__.py b/homeassistant/components/lg_thinq/__init__.py index 6ca74cda8c0..a8d3fe175ef 100644 --- a/homeassistant/components/lg_thinq/__init__.py +++ b/homeassistant/components/lg_thinq/__init__.py @@ -43,6 +43,7 @@ PLATFORMS = [ Platform.EVENT, Platform.FAN, Platform.NUMBER, + Platform.SELECT, Platform.SENSOR, Platform.SWITCH, Platform.VACUUM, diff --git a/homeassistant/components/lg_thinq/icons.json b/homeassistant/components/lg_thinq/icons.json index 4e681c286ac..9a54ff73a73 100644 --- a/homeassistant/components/lg_thinq/icons.json +++ b/homeassistant/components/lg_thinq/icons.json @@ -131,6 +131,47 @@ "default": "mdi:bed-clock" } }, + "select": { + "wind_strength": { + "default": "mdi:wind-power-outline" + }, + "monitoring_enabled": { + "default": "mdi:monitor-eye" + }, + "current_job_mode": { + "default": "mdi:format-list-bulleted" + }, + "operation_mode": { + "default": "mdi:gesture-tap-button" + }, + "operation_mode_for_location": { + "default": "mdi:gesture-tap-button" + }, + "air_clean_operation_mode": { + "default": "mdi:air-filter" + }, + "cook_mode": { + "default": "mdi:chef-hat" + }, + "cook_mode_for_location": { + "default": "mdi:chef-hat" + }, + "light_brightness": { + "default": "mdi:list-status" + }, + "wind_angle": { + "default": "mdi:rotate-360" + }, + "display_light": { + "default": "mdi:brightness-6" + }, + "fresh_air_filter": { + "default": "mdi:air-filter" + }, + "hygiene_dry_mode": { + "default": "mdi:format-list-bulleted" + } + }, "sensor": { "error": { "default": "mdi:alert-circle-outline" diff --git a/homeassistant/components/lg_thinq/select.py b/homeassistant/components/lg_thinq/select.py new file mode 100644 index 00000000000..45f1b5cbfa4 --- /dev/null +++ b/homeassistant/components/lg_thinq/select.py @@ -0,0 +1,202 @@ +"""Support for select entities.""" + +from __future__ import annotations + +import logging + +from thinqconnect import DeviceType +from thinqconnect.devices.const import Property as ThinQProperty +from thinqconnect.integration import ActiveMode + +from homeassistant.components.select import SelectEntity, SelectEntityDescription +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ThinqConfigEntry +from .coordinator import DeviceDataUpdateCoordinator +from .entity import ThinQEntity + +SELECT_DESC: dict[ThinQProperty, SelectEntityDescription] = { + ThinQProperty.MONITORING_ENABLED: SelectEntityDescription( + key=ThinQProperty.MONITORING_ENABLED, + translation_key=ThinQProperty.MONITORING_ENABLED, + ), + ThinQProperty.COOK_MODE: SelectEntityDescription( + key=ThinQProperty.COOK_MODE, + translation_key=ThinQProperty.COOK_MODE, + ), + ThinQProperty.DISPLAY_LIGHT: SelectEntityDescription( + key=ThinQProperty.DISPLAY_LIGHT, + translation_key=ThinQProperty.DISPLAY_LIGHT, + ), + ThinQProperty.CURRENT_JOB_MODE: SelectEntityDescription( + key=ThinQProperty.CURRENT_JOB_MODE, + translation_key=ThinQProperty.CURRENT_JOB_MODE, + ), +} +AIR_FLOW_SELECT_DESC: dict[ThinQProperty, SelectEntityDescription] = { + ThinQProperty.WIND_STRENGTH: SelectEntityDescription( + key=ThinQProperty.WIND_STRENGTH, + translation_key=ThinQProperty.WIND_STRENGTH, + ), + ThinQProperty.WIND_ANGLE: SelectEntityDescription( + key=ThinQProperty.WIND_ANGLE, + translation_key=ThinQProperty.WIND_ANGLE, + ), +} +OPERATION_SELECT_DESC: dict[ThinQProperty, SelectEntityDescription] = { + ThinQProperty.AIR_CLEAN_OPERATION_MODE: SelectEntityDescription( + key=ThinQProperty.AIR_CLEAN_OPERATION_MODE, + translation_key="air_clean_operation_mode", + ), + ThinQProperty.DISH_WASHER_OPERATION_MODE: SelectEntityDescription( + key=ThinQProperty.DISH_WASHER_OPERATION_MODE, + translation_key="operation_mode", + ), + ThinQProperty.DRYER_OPERATION_MODE: SelectEntityDescription( + key=ThinQProperty.DRYER_OPERATION_MODE, + translation_key="operation_mode", + ), + ThinQProperty.HYGIENE_DRY_MODE: SelectEntityDescription( + key=ThinQProperty.HYGIENE_DRY_MODE, + translation_key=ThinQProperty.HYGIENE_DRY_MODE, + ), + ThinQProperty.LIGHT_BRIGHTNESS: SelectEntityDescription( + key=ThinQProperty.LIGHT_BRIGHTNESS, + translation_key=ThinQProperty.LIGHT_BRIGHTNESS, + ), + ThinQProperty.OVEN_OPERATION_MODE: SelectEntityDescription( + key=ThinQProperty.OVEN_OPERATION_MODE, + translation_key="operation_mode", + ), + ThinQProperty.STYLER_OPERATION_MODE: SelectEntityDescription( + key=ThinQProperty.STYLER_OPERATION_MODE, + translation_key="operation_mode", + ), + ThinQProperty.WASHER_OPERATION_MODE: SelectEntityDescription( + key=ThinQProperty.WASHER_OPERATION_MODE, + translation_key="operation_mode", + ), +} + +DEVICE_TYPE_SELECT_MAP: dict[DeviceType, tuple[SelectEntityDescription, ...]] = { + DeviceType.AIR_CONDITIONER: ( + SELECT_DESC[ThinQProperty.MONITORING_ENABLED], + OPERATION_SELECT_DESC[ThinQProperty.AIR_CLEAN_OPERATION_MODE], + ), + DeviceType.AIR_PURIFIER_FAN: ( + AIR_FLOW_SELECT_DESC[ThinQProperty.WIND_STRENGTH], + AIR_FLOW_SELECT_DESC[ThinQProperty.WIND_ANGLE], + SELECT_DESC[ThinQProperty.DISPLAY_LIGHT], + SELECT_DESC[ThinQProperty.CURRENT_JOB_MODE], + ), + DeviceType.AIR_PURIFIER: ( + AIR_FLOW_SELECT_DESC[ThinQProperty.WIND_STRENGTH], + SELECT_DESC[ThinQProperty.CURRENT_JOB_MODE], + ), + DeviceType.DEHUMIDIFIER: (AIR_FLOW_SELECT_DESC[ThinQProperty.WIND_STRENGTH],), + DeviceType.DISH_WASHER: ( + OPERATION_SELECT_DESC[ThinQProperty.DISH_WASHER_OPERATION_MODE], + ), + DeviceType.DRYER: (OPERATION_SELECT_DESC[ThinQProperty.DRYER_OPERATION_MODE],), + DeviceType.HUMIDIFIER: ( + AIR_FLOW_SELECT_DESC[ThinQProperty.WIND_STRENGTH], + SELECT_DESC[ThinQProperty.DISPLAY_LIGHT], + SELECT_DESC[ThinQProperty.CURRENT_JOB_MODE], + OPERATION_SELECT_DESC[ThinQProperty.HYGIENE_DRY_MODE], + ), + DeviceType.OVEN: ( + SELECT_DESC[ThinQProperty.COOK_MODE], + OPERATION_SELECT_DESC[ThinQProperty.OVEN_OPERATION_MODE], + ), + DeviceType.STYLER: (OPERATION_SELECT_DESC[ThinQProperty.STYLER_OPERATION_MODE],), + DeviceType.WASHCOMBO_MAIN: ( + OPERATION_SELECT_DESC[ThinQProperty.WASHER_OPERATION_MODE], + ), + DeviceType.WASHCOMBO_MINI: ( + OPERATION_SELECT_DESC[ThinQProperty.WASHER_OPERATION_MODE], + ), + DeviceType.WASHER: (OPERATION_SELECT_DESC[ThinQProperty.WASHER_OPERATION_MODE],), + DeviceType.WASHTOWER_DRYER: ( + OPERATION_SELECT_DESC[ThinQProperty.WASHER_OPERATION_MODE], + ), + DeviceType.WASHTOWER: ( + OPERATION_SELECT_DESC[ThinQProperty.DRYER_OPERATION_MODE], + OPERATION_SELECT_DESC[ThinQProperty.WASHER_OPERATION_MODE], + ), + DeviceType.WASHTOWER_WASHER: ( + OPERATION_SELECT_DESC[ThinQProperty.WASHER_OPERATION_MODE], + ), + DeviceType.WATER_HEATER: (SELECT_DESC[ThinQProperty.CURRENT_JOB_MODE],), + DeviceType.WINE_CELLAR: (OPERATION_SELECT_DESC[ThinQProperty.LIGHT_BRIGHTNESS],), +} + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ThinqConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up an entry for select platform.""" + entities: list[ThinQSelectEntity] = [] + for coordinator in entry.runtime_data.coordinators.values(): + if ( + descriptions := DEVICE_TYPE_SELECT_MAP.get( + coordinator.api.device.device_type + ) + ) is not None: + for description in descriptions: + entities.extend( + ThinQSelectEntity(coordinator, description, property_id) + for property_id in coordinator.api.get_active_idx( + description.key, ActiveMode.WRITABLE + ) + ) + + if entities: + async_add_entities(entities) + + +class ThinQSelectEntity(ThinQEntity, SelectEntity): + """Represent a thinq select platform.""" + + def __init__( + self, + coordinator: DeviceDataUpdateCoordinator, + entity_description: SelectEntityDescription, + property_id: str, + ) -> None: + """Initialize a select entity.""" + super().__init__(coordinator, entity_description, property_id) + + self._attr_options = self.data.options if self.data.options is not None else [] + + def _update_status(self) -> None: + """Update status itself.""" + super()._update_status() + + if self.data.value: + self._attr_current_option = str(self.data.value) + else: + self._attr_current_option = None + + _LOGGER.debug( + "[%s:%s] update status: %s -> %s, options:%s", + self.coordinator.device_name, + self.property_id, + self.data.value, + self.current_option, + self.options, + ) + + async def async_select_option(self, option: str) -> None: + """Change the selected option.""" + _LOGGER.debug( + "[%s:%s] async_select_option: %s", + self.coordinator.device_name, + self.property_id, + option, + ) + await self.async_call_api(self.coordinator.api.post(self.property_id, option)) diff --git a/homeassistant/components/lg_thinq/strings.json b/homeassistant/components/lg_thinq/strings.json index e9afc8c091b..c47c128fcfb 100644 --- a/homeassistant/components/lg_thinq/strings.json +++ b/homeassistant/components/lg_thinq/strings.json @@ -547,7 +547,7 @@ "select": "My space", "smart_humidity": "Smart", "spot": "Spiral spot mode", - "turbo": "Turbo", + "turbo": "[%key:component::lg_thinq::entity::select::wind_strength::state::power%]", "vacation": "Vacation", "zigzag": "Zigzag" } @@ -560,13 +560,13 @@ "mop": "[%key:component::lg_thinq::entity::sensor::current_job_mode::state::mop%]", "normal": "[%key:component::lg_thinq::entity::sensor::current_job_mode::state::normal%]", "off": "[%key:common::state::off%]", - "turbo": "[%key:component::lg_thinq::entity::sensor::current_job_mode::state::turbo%]" + "turbo": "[%key:component::lg_thinq::entity::select::wind_strength::state::power%]" } }, "personalization_mode": { "name": "Personal mode", "state": { - "auto_inside": "Auto mode", + "auto_inside": "[%key:component::lg_thinq::entity::switch::auto_mode::name%]", "sleep": "Sleep mode", "baby": "Baby care mode", "sick_house": "New Home mode", @@ -579,7 +579,7 @@ } }, "cock_state": { - "name": "UVnano", + "name": "[%key:component::lg_thinq::entity::switch::uv_nano::name%]", "state": { "cleaning": "[%key:component::lg_thinq::entity::sensor::current_state::state::cleaning%]", "normal": "[%key:common::state::standby%]" @@ -624,6 +624,156 @@ "temperature_number": "[%key:component::lg_thinq::entity::sensor::target_temperature::state::temperature_number%]" } } + }, + "select": { + "wind_strength": { + "name": "Speed", + "state": { + "slow": "[%key:component::lg_thinq::entity::climate::climate_air_conditioner::state_attributes::fan_mode::state::slow%]", + "low": "Low", + "mid": "Medium", + "high": "High", + "power": "Turbo", + "turbo": "[%key:component::lg_thinq::entity::select::wind_strength::state::power%]", + "auto": "Auto", + "wind_1": "Step 1", + "wind_2": "Step 2", + "wind_3": "Step 3", + "wind_4": "Step 4", + "wind_5": "Step 5", + "wind_6": "Step 6", + "wind_7": "Step 7", + "wind_8": "Step 8", + "wind_9": "Step 9", + "wind_10": "Step 10" + } + }, + "monitoring_enabled": { + "name": "[%key:component::lg_thinq::entity::sensor::monitoring_enabled::name%]", + "state": { + "on_working": "[%key:component::lg_thinq::entity::sensor::monitoring_enabled::state::on_working%]", + "always": "[%key:component::lg_thinq::entity::sensor::monitoring_enabled::state::always%]" + } + }, + "current_job_mode": { + "name": "Operating mode", + "state": { + "air_clean": "Purifying", + "auto": "[%key:component::lg_thinq::entity::select::wind_strength::state::auto%]", + "baby_care": "[%key:component::lg_thinq::entity::sensor::personalization_mode::state::baby%]", + "circulator": "Booster", + "clean": "Single", + "direct_clean": "Direct mode", + "dual_clean": "Dual", + "fast": "[%key:component::lg_thinq::entity::select::wind_strength::state::power%]", + "heat_pump": "[%key:component::lg_thinq::entity::sensor::current_job_mode::state::heat_pump%]", + "humidify": "Mist", + "humidify_and_air_clean": "Mist & purifying", + "humidity": "Humid", + "nature_clean": "Natural mode", + "pet_clean": "[%key:component::lg_thinq::entity::sensor::personalization_mode::state::pet%]", + "silent": "Silent", + "sleep": "Sleep", + "smart": "Smart mode", + "space_clean": "Diffusion mode", + "spot_clean": "Wide mode", + "turbo": "[%key:component::lg_thinq::entity::select::wind_strength::state::power%]", + "up_feature": "Additional mode", + "vacation": "Vacation" + } + }, + "operation_mode": { + "name": "Operation", + "state": { + "cancel": "[%key:component::lg_thinq::entity::sensor::current_state::state::cancel%]", + "power_off": "Power off", + "preheating": "Preheating", + "start": "[%key:common::action::start%]", + "stop": "[%key:common::action::stop%]", + "wake_up": "Sleep mode off" + } + }, + "operation_mode_for_location": { + "name": "{location} operation", + "state": { + "cancel": "[%key:component::lg_thinq::entity::sensor::current_state::state::cancel%]", + "power_off": "[%key:component::lg_thinq::entity::select::operation_mode::state::power_off%]", + "preheating": "[%key:component::lg_thinq::entity::select::operation_mode::state::preheating%]", + "start": "[%key:common::action::start%]", + "stop": "[%key:common::action::stop%]", + "wake_up": "[%key:component::lg_thinq::entity::select::operation_mode::state::wake_up%]" + } + }, + "air_clean_operation_mode": { + "name": "[%key:component::lg_thinq::entity::climate::climate_air_conditioner::state_attributes::preset_mode::state::air_clean%]", + "state": { + "start": "[%key:common::action::start%]", + "stop": "[%key:common::action::stop%]" + } + }, + "cook_mode": { + "name": "Cook mode", + "state": { + "bake": "Bake", + "convection_bake": "Convection bake", + "convection_roast": "Convection roast", + "roast": "Roast", + "crisp_convection": "Crisp convection" + } + }, + "cook_mode_for_location": { + "name": "{location} cook mode", + "state": { + "bake": "[%key:component::lg_thinq::entity::select::cook_mode::state::bake%]", + "convection_bake": "[%key:component::lg_thinq::entity::select::cook_mode::state::convection_bake%]", + "convection_roast": "[%key:component::lg_thinq::entity::select::cook_mode::state::convection_roast%]", + "roast": "[%key:component::lg_thinq::entity::select::cook_mode::state::roast%]", + "crisp_convection": "[%key:component::lg_thinq::entity::select::cook_mode::state::crisp_convection%]" + } + }, + "light_brightness": { + "name": "Light" + }, + "wind_angle": { + "name": "Rotation", + "state": { + "off": "[%key:common::state::off%]", + "angle_45": "45°", + "angle_60": "60°", + "angle_90": "90°", + "angle_140": "140°" + } + }, + "display_light": { + "name": "Display brightness", + "state": { + "off": "[%key:common::state::off%]", + "level_1": "Brightness 1", + "level_2": "Brightness 2", + "level_3": "Brightness 3" + } + }, + "fresh_air_filter": { + "name": "[%key:component::lg_thinq::entity::binary_sensor::one_touch_filter::name%]", + "state": { + "off": "[%key:common::state::off%]", + "auto": "[%key:component::lg_thinq::entity::select::wind_strength::state::auto%]", + "power": "Power", + "replace": "[%key:component::lg_thinq::entity::sensor::fresh_air_filter::state::replace%]", + "smart_power": "[%key:component::lg_thinq::entity::sensor::fresh_air_filter::state::smart_power%]", + "smart_off": "[%key:common::state::off%]", + "smart_on": "[%key:component::lg_thinq::entity::sensor::fresh_air_filter::state::smart_power%]" + } + }, + "hygiene_dry_mode": { + "name": "[%key:component::lg_thinq::entity::switch::hygiene_dry_mode::name%]", + "state": { + "off": "[%key:common::state::off%]", + "fast": "Fast", + "silent": "Silent", + "normal": "Delicate" + } + } } } }