diff --git a/.coveragerc b/.coveragerc index a7baa17e852..b592f1c416d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -975,6 +975,7 @@ omit = homeassistant/components/senseme/switch.py homeassistant/components/sensibo/__init__.py homeassistant/components/sensibo/climate.py + homeassistant/components/sensibo/coordinator.py homeassistant/components/serial/sensor.py homeassistant/components/serial_pm/sensor.py homeassistant/components/sesame/lock.py diff --git a/homeassistant/components/sensibo/__init__.py b/homeassistant/components/sensibo/__init__.py index 7401a8c2150..b62482b60b5 100644 --- a/homeassistant/components/sensibo/__init__.py +++ b/homeassistant/components/sensibo/__init__.py @@ -1,53 +1,22 @@ """The sensibo component.""" from __future__ import annotations -import asyncio -import logging - -import aiohttp -import async_timeout -import pysensibo - from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_API_KEY from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import _INITIAL_FETCH_FIELDS, DOMAIN, PLATFORMS, TIMEOUT - -_LOGGER = logging.getLogger(__name__) +from .const import DOMAIN, PLATFORMS +from .coordinator import SensiboDataUpdateCoordinator async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Sensibo from a config entry.""" - client = pysensibo.SensiboClient( - entry.data[CONF_API_KEY], session=async_get_clientsession(hass), timeout=TIMEOUT - ) - devices = [] - try: - async with async_timeout.timeout(TIMEOUT): - for dev in await client.async_get_devices(_INITIAL_FETCH_FIELDS): - devices.append(dev) - except ( - aiohttp.client_exceptions.ClientConnectorError, - asyncio.TimeoutError, - pysensibo.SensiboError, - ) as err: - raise ConfigEntryNotReady( - f"Failed to get devices from Sensibo servers: {err}" - ) from err - if not devices: - return False - - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { - "devices": devices, - "client": client, - } + coordinator = SensiboDataUpdateCoordinator(hass, entry) + await coordinator.async_config_entry_first_refresh() + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) - _LOGGER.debug("Loaded entry for %s", entry.title) + return True @@ -57,6 +26,5 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: del hass.data[DOMAIN][entry.entry_id] if not hass.data[DOMAIN]: del hass.data[DOMAIN] - _LOGGER.debug("Unloaded entry for %s", entry.title) return True return False diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 648e05dbe79..b711bca5ac3 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -2,12 +2,11 @@ from __future__ import annotations import asyncio -import logging from typing import Any -import aiohttp +from aiohttp.client_exceptions import ClientConnectionError import async_timeout -from pysensibo import SensiboClient, SensiboError +from pysensibo import SensiboError import voluptuous as vol from homeassistant.components.climate import ( @@ -32,20 +31,20 @@ from homeassistant.const import ( ATTR_TEMPERATURE, CONF_API_KEY, CONF_ID, - STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT, ) from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util.temperature import convert as convert_temperature -from .const import _FETCH_FIELDS, ALL, DOMAIN, TIMEOUT - -_LOGGER = logging.getLogger(__name__) +from .const import ALL, DOMAIN, LOGGER, TIMEOUT +from .coordinator import SensiboDataUpdateCoordinator SERVICE_ASSUME_STATE = "assume_state" @@ -72,7 +71,7 @@ SENSIBO_TO_HA = { "fan": HVAC_MODE_FAN_ONLY, "auto": HVAC_MODE_HEAT_COOL, "dry": HVAC_MODE_DRY, - "": HVAC_MODE_OFF, + "off": HVAC_MODE_OFF, } HA_TO_SENSIBO = {value: key for key, value in SENSIBO_TO_HA.items()} @@ -85,7 +84,7 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up Sensibo devices.""" - _LOGGER.warning( + LOGGER.warning( "Loading Sensibo via platform setup is deprecated; Please remove it from your configuration" ) hass.async_create_task( @@ -102,13 +101,13 @@ async def async_setup_entry( ) -> None: """Set up the Sensibo climate entry.""" - data = hass.data[DOMAIN][entry.entry_id] - client = data["client"] - devices = data["devices"] + coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] entities = [ - SensiboClimate(client, dev, hass.config.units.temperature_unit) - for dev in devices + SensiboClimate(coordinator, device_id, hass.config.units.temperature_unit) + for device_id, device_data in coordinator.data.items() + # Remove none climate devices + if device_data["hvac_modes"] and device_data["temp"] ] async_add_entities(entities) @@ -138,208 +137,234 @@ async def async_setup_entry( ) -class SensiboClimate(ClimateEntity): +class SensiboClimate(CoordinatorEntity, ClimateEntity): """Representation of a Sensibo device.""" - def __init__(self, client: SensiboClient, data: dict[str, Any], units: str) -> None: + coordinator: SensiboDataUpdateCoordinator + + def __init__( + self, + coordinator: SensiboDataUpdateCoordinator, + device_id: str, + temp_unit: str, + ) -> None: """Initiate SensiboClimate.""" - self._client = client - self._id = data["id"] - self._external_state = None - self._units = units - self._failed_update = False - self._attr_available = False - self._attr_unique_id = self._id + super().__init__(coordinator) + self._client = coordinator.client + self._attr_unique_id = device_id + self._attr_name = coordinator.data[device_id]["name"] self._attr_temperature_unit = ( - TEMP_CELSIUS if data["temperatureUnit"] == "C" else TEMP_FAHRENHEIT - ) - self._do_update(data) - self._attr_target_temperature_step = ( - 1 if self.temperature_unit == units else None + TEMP_CELSIUS + if coordinator.data[device_id]["temp_unit"] == "C" + else TEMP_FAHRENHEIT ) + self._attr_supported_features = self.get_features() self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, self._id)}, - name=self._attr_name, + identifiers={(DOMAIN, coordinator.data[device_id]["id"])}, + name=coordinator.data[device_id]["name"], manufacturer="Sensibo", configuration_url="https://home.sensibo.com/", - model=data["productModel"], - sw_version=data["firmwareVersion"], - hw_version=data["firmwareType"], - suggested_area=self._attr_name, + model=coordinator.data[device_id]["model"], + sw_version=coordinator.data[device_id]["fw_ver"], + hw_version=coordinator.data[device_id]["fw_type"], + suggested_area=coordinator.data[device_id]["name"], ) - def _do_update(self, data) -> None: - self._attr_name = data["room"]["name"] - self._ac_states = data["acState"] - self._attr_extra_state_attributes = { - "battery": data["measurements"].get("batteryVoltage") - } - self._attr_current_temperature = convert_temperature( - data["measurements"].get("temperature"), - TEMP_CELSIUS, - self._attr_temperature_unit, - ) - self._attr_current_humidity = data["measurements"].get("humidity") - - self._attr_target_temperature = self._ac_states.get("targetTemperature") - if self._ac_states["on"]: - self._attr_hvac_mode = SENSIBO_TO_HA.get(self._ac_states["mode"], "") - else: - self._attr_hvac_mode = HVAC_MODE_OFF - self._attr_fan_mode = self._ac_states.get("fanLevel") - self._attr_swing_mode = self._ac_states.get("swing") - - self._attr_available = data["connectionStatus"].get("isAlive") - capabilities = data["remoteCapabilities"] - self._attr_hvac_modes = [SENSIBO_TO_HA[mode] for mode in capabilities["modes"]] - self._attr_hvac_modes.append(HVAC_MODE_OFF) - - current_capabilities = capabilities["modes"][self._ac_states.get("mode")] - self._attr_fan_modes = current_capabilities.get("fanLevels") - self._attr_swing_modes = current_capabilities.get("swing") - - temperature_unit_key = data.get("temperatureUnit") or self._ac_states.get( - "temperatureUnit" - ) - if temperature_unit_key: - self._temperature_unit = ( - TEMP_CELSIUS if temperature_unit_key == "C" else TEMP_FAHRENHEIT - ) - self._temperatures_list = ( - current_capabilities["temperatures"] - .get(temperature_unit_key, {}) - .get("values", []) - ) - else: - self._temperature_unit = self._units - self._temperatures_list = [] - self._attr_min_temp = ( - self._temperatures_list[0] if self._temperatures_list else super().min_temp - ) - self._attr_max_temp = ( - self._temperatures_list[-1] if self._temperatures_list else super().max_temp - ) - self._attr_temperature_unit = self._temperature_unit - - self._attr_supported_features = 0 - for key in self._ac_states: + def get_features(self) -> int: + """Get supported features.""" + features = 0 + for key in self.coordinator.data[self.unique_id]["features"]: if key in FIELD_TO_FLAG: - self._attr_supported_features |= FIELD_TO_FLAG[key] + features |= FIELD_TO_FLAG[key] + return features - self._attr_state = self._external_state or super().state + @property + def current_humidity(self) -> int: + """Return the current humidity.""" + return self.coordinator.data[self.unique_id]["humidity"] + + @property + def hvac_mode(self) -> str: + """Return hvac operation.""" + return ( + SENSIBO_TO_HA[self.coordinator.data[self.unique_id]["hvac_mode"]] + if self.coordinator.data[self.unique_id]["on"] + else HVAC_MODE_OFF + ) + + @property + def hvac_modes(self) -> list[str]: + """Return the list of available hvac operation modes.""" + return [ + SENSIBO_TO_HA[mode] + for mode in self.coordinator.data[self.unique_id]["hvac_modes"] + ] + + @property + def current_temperature(self) -> float: + """Return the current temperature.""" + return convert_temperature( + self.coordinator.data[self.unique_id]["temp"], + TEMP_CELSIUS, + self.temperature_unit, + ) + + @property + def target_temperature(self) -> float | None: + """Return the temperature we try to reach.""" + return self.coordinator.data[self.unique_id]["target_temp"] + + @property + def target_temperature_step(self) -> float | None: + """Return the supported step of target temperature.""" + return self.coordinator.data[self.unique_id]["temp_step"] + + @property + def fan_mode(self) -> str | None: + """Return the fan setting.""" + return self.coordinator.data[self.unique_id]["fan_mode"] + + @property + def fan_modes(self) -> list[str] | None: + """Return the list of available fan modes.""" + return self.coordinator.data[self.unique_id]["fan_modes"] + + @property + def swing_mode(self) -> str | None: + """Return the swing setting.""" + return self.coordinator.data[self.unique_id]["swing_mode"] + + @property + def swing_modes(self) -> list[str] | None: + """Return the list of available swing modes.""" + return self.coordinator.data[self.unique_id]["swing_modes"] + + @property + def min_temp(self) -> float: + """Return the minimum temperature.""" + return self.coordinator.data[self.unique_id]["temp_list"][0] + + @property + def max_temp(self) -> float: + """Return the maximum temperature.""" + return self.coordinator.data[self.unique_id]["temp_list"][-1] + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self.coordinator.data[self.unique_id]["available"] and super().available async def async_set_temperature(self, **kwargs) -> None: """Set new target temperature.""" if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: return - temperature = int(temperature) - if temperature not in self._temperatures_list: + + if temperature == self.target_temperature: + return + + if temperature not in self.coordinator.data[self.unique_id]["temp_list"]: # Requested temperature is not supported. - if temperature == self.target_temperature: - return - index = self._temperatures_list.index(self.target_temperature) - if ( - temperature > self.target_temperature - and index < len(self._temperatures_list) - 1 - ): - temperature = self._temperatures_list[index + 1] - elif temperature < self.target_temperature and index > 0: - temperature = self._temperatures_list[index - 1] + if temperature > self.coordinator.data[self.unique_id]["temp_list"][-1]: + temperature = self.coordinator.data[self.unique_id]["temp_list"][-1] + + elif temperature < self.coordinator.data[self.unique_id]["temp_list"][0]: + temperature = self.coordinator.data[self.unique_id]["temp_list"][0] + else: return - await self._async_set_ac_state_property("targetTemperature", temperature) + result = await self._async_set_ac_state_property( + "targetTemperature", int(temperature) + ) + if result: + self.coordinator.data[self.unique_id]["target_temp"] = int(temperature) + self.async_write_ha_state() - async def async_set_fan_mode(self, fan_mode) -> None: + async def async_set_fan_mode(self, fan_mode: str) -> None: """Set new target fan mode.""" - await self._async_set_ac_state_property("fanLevel", fan_mode) + result = await self._async_set_ac_state_property("fanLevel", fan_mode) + if result: + self.coordinator.data[self.unique_id]["fan_mode"] = fan_mode + self.async_write_ha_state() - async def async_set_hvac_mode(self, hvac_mode) -> None: + async def async_set_hvac_mode(self, hvac_mode: str) -> None: """Set new target operation mode.""" if hvac_mode == HVAC_MODE_OFF: - await self._async_set_ac_state_property("on", False) + result = await self._async_set_ac_state_property("on", False) + if result: + self.coordinator.data[self.unique_id]["on"] = False + self.async_write_ha_state() return # Turn on if not currently on. - if not self._ac_states["on"]: - await self._async_set_ac_state_property("on", True) + if not self.coordinator.data[self.unique_id]["on"]: + result = await self._async_set_ac_state_property("on", True) + if result: + self.coordinator.data[self.unique_id]["on"] = True - await self._async_set_ac_state_property("mode", HA_TO_SENSIBO[hvac_mode]) + result = await self._async_set_ac_state_property( + "mode", HA_TO_SENSIBO[hvac_mode] + ) + if result: + self.coordinator.data[self.unique_id]["hvac_mode"] = HA_TO_SENSIBO[ + hvac_mode + ] + self.async_write_ha_state() - async def async_set_swing_mode(self, swing_mode) -> None: + async def async_set_swing_mode(self, swing_mode: str) -> None: """Set new target swing operation.""" - await self._async_set_ac_state_property("swing", swing_mode) + result = await self._async_set_ac_state_property("swing", swing_mode) + if result: + self.coordinator.data[self.unique_id]["swing_mode"] = swing_mode + self.async_write_ha_state() async def async_turn_on(self) -> None: """Turn Sensibo unit on.""" - await self._async_set_ac_state_property("on", True) + result = await self._async_set_ac_state_property("on", True) + if result: + self.coordinator.data[self.unique_id]["on"] = True + self.async_write_ha_state() async def async_turn_off(self) -> None: """Turn Sensibo unit on.""" - await self._async_set_ac_state_property("on", False) - - async def async_assume_state(self, state) -> None: - """Set external state.""" - change_needed = (state != HVAC_MODE_OFF and not self._ac_states["on"]) or ( - state == HVAC_MODE_OFF and self._ac_states["on"] - ) - - if change_needed: - await self._async_set_ac_state_property("on", state != HVAC_MODE_OFF, True) - - if state in (STATE_ON, HVAC_MODE_OFF): - self._external_state = None - else: - self._external_state = state - - async def async_update(self) -> None: - """Retrieve latest state.""" - try: - async with async_timeout.timeout(TIMEOUT): - data = await self._client.async_get_device(self._id, _FETCH_FIELDS) - except ( - aiohttp.client_exceptions.ClientError, - asyncio.TimeoutError, - SensiboError, - ) as err: - if self._failed_update: - _LOGGER.warning( - "Failed to update data for device '%s' from Sensibo servers with error %s", - self._attr_name, - err, - ) - self._attr_available = False - self.async_write_ha_state() - return - - _LOGGER.debug("First failed update data for device '%s'", self._attr_name) - self._failed_update = True - return - - if self.temperature_unit == self.hass.config.units.temperature_unit: - self._attr_target_temperature_step = 1 - else: - self._attr_target_temperature_step = None - - self._failed_update = False - self._do_update(data) + result = await self._async_set_ac_state_property("on", False) + if result: + self.coordinator.data[self.unique_id]["on"] = False + self.async_write_ha_state() async def _async_set_ac_state_property( - self, name, value, assumed_state=False - ) -> None: + self, name: str, value: Any, assumed_state: bool = False + ) -> bool: """Set AC state.""" + result = {} try: async with async_timeout.timeout(TIMEOUT): - await self._client.async_set_ac_state_property( - self._id, name, value, self._ac_states, assumed_state + result = await self._client.async_set_ac_state_property( + self.unique_id, + name, + value, + self.coordinator.data[self.unique_id]["ac_states"], + assumed_state, ) except ( - aiohttp.client_exceptions.ClientError, + ClientConnectionError, asyncio.TimeoutError, SensiboError, ) as err: - self._attr_available = False - self.async_write_ha_state() - raise Exception( - f"Failed to set AC state for device {self._attr_name} to Sensibo servers" + raise HomeAssistantError( + f"Failed to set AC state for device {self.name} to Sensibo servers: {err}" ) from err + LOGGER.debug("Result: %s", result) + if result["status"] == "Success": + return True + failure = result["failureReason"] + raise HomeAssistantError( + f"Could not set state for device {self.name} due to reason {failure}" + ) + + async def async_assume_state(self, state) -> None: + """Sync state with api.""" + if state == self.state or (state == "on" and self.state != HVAC_MODE_OFF): + return + await self._async_set_ac_state_property("on", state != HVAC_MODE_OFF, True) + await self.coordinator.async_refresh() diff --git a/homeassistant/components/sensibo/config_flow.py b/homeassistant/components/sensibo/config_flow.py index 77f1049d8d2..8544972baee 100644 --- a/homeassistant/components/sensibo/config_flow.py +++ b/homeassistant/components/sensibo/config_flow.py @@ -16,7 +16,7 @@ from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from .const import _INITIAL_FETCH_FIELDS, DEFAULT_NAME, DOMAIN, TIMEOUT +from .const import DEFAULT_NAME, DOMAIN, TIMEOUT _LOGGER = logging.getLogger(__name__) @@ -37,7 +37,7 @@ async def async_validate_api(hass: HomeAssistant, api_key: str) -> bool: try: async with async_timeout.timeout(TIMEOUT): - if await client.async_get_devices(_INITIAL_FETCH_FIELDS): + if await client.async_get_devices(): return True except ( aiohttp.ClientConnectionError, diff --git a/homeassistant/components/sensibo/const.py b/homeassistant/components/sensibo/const.py index fb387e64a1a..e6d5bebff42 100644 --- a/homeassistant/components/sensibo/const.py +++ b/homeassistant/components/sensibo/const.py @@ -1,20 +1,14 @@ """Constants for Sensibo.""" +import logging + from homeassistant.const import Platform +LOGGER = logging.getLogger(__package__) + +DEFAULT_SCAN_INTERVAL = 60 DOMAIN = "sensibo" PLATFORMS = [Platform.CLIMATE] ALL = ["all"] DEFAULT_NAME = "Sensibo" TIMEOUT = 8 -_FETCH_FIELDS = ",".join( - [ - "room{name}", - "measurements", - "remoteCapabilities", - "acState", - "connectionStatus{isAlive}", - "temperatureUnit", - ] -) -_INITIAL_FETCH_FIELDS = f"id,firmwareVersion,firmwareType,productModel,{_FETCH_FIELDS}" diff --git a/homeassistant/components/sensibo/coordinator.py b/homeassistant/components/sensibo/coordinator.py new file mode 100644 index 00000000000..4781250874f --- /dev/null +++ b/homeassistant/components/sensibo/coordinator.py @@ -0,0 +1,111 @@ +"""DataUpdateCoordinator for the Sensibo integration.""" +from __future__ import annotations + +from datetime import timedelta +from typing import Any + +import pysensibo + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_KEY +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, TIMEOUT + + +class SensiboDataUpdateCoordinator(DataUpdateCoordinator): + """A Sensibo Data Update Coordinator.""" + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: + """Initialize the Sensibo coordinator.""" + self.client = pysensibo.SensiboClient( + entry.data[CONF_API_KEY], + session=async_get_clientsession(hass), + timeout=TIMEOUT, + ) + super().__init__( + hass, + LOGGER, + name=DOMAIN, + update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL), + ) + + async def _async_update_data(self) -> dict[str, dict[str, Any]]: + """Fetch data from Sensibo.""" + + devices = [] + try: + for dev in await self.client.async_get_devices(): + devices.append(dev) + except (pysensibo.SensiboError) as error: + raise UpdateFailed from error + + device_data: dict[str, dict[str, Any]] = {} + for dev in devices: + unique_id = dev["id"] + name = dev["room"]["name"] + temperature = dev["measurements"].get("temperature", 0.0) + humidity = dev["measurements"].get("humidity", 0) + ac_states = dev["acState"] + target_temperature = ac_states.get("targetTemperature") + hvac_mode = ac_states.get("mode") + running = ac_states.get("on") + fan_mode = ac_states.get("fanLevel") + swing_mode = ac_states.get("swing") + available = dev["connectionStatus"].get("isAlive", True) + capabilities = dev["remoteCapabilities"] + hvac_modes = list(capabilities["modes"]) + if hvac_modes: + hvac_modes.append("off") + current_capabilities = capabilities["modes"][ac_states.get("mode")] + fan_modes = current_capabilities.get("fanLevels") + swing_modes = current_capabilities.get("swing") + temperature_unit_key = dev.get("temperatureUnit") or ac_states.get( + "temperatureUnit" + ) + temperatures_list = ( + current_capabilities["temperatures"] + .get(temperature_unit_key, {}) + .get("values", [0]) + ) + if temperatures_list: + temperature_step = temperatures_list[1] - temperatures_list[0] + features = list(ac_states) + state = hvac_mode if hvac_mode else "off" + + fw_ver = dev["firmwareVersion"] + fw_type = dev["firmwareType"] + model = dev["productModel"] + + calibration_temp = dev["sensorsCalibration"].get("temperature", 0.0) + calibration_hum = dev["sensorsCalibration"].get("humidity", 0.0) + + device_data[unique_id] = { + "id": unique_id, + "name": name, + "ac_states": ac_states, + "temp": temperature, + "humidity": humidity, + "target_temp": target_temperature, + "hvac_mode": hvac_mode, + "on": running, + "fan_mode": fan_mode, + "swing_mode": swing_mode, + "available": available, + "hvac_modes": hvac_modes, + "fan_modes": fan_modes, + "swing_modes": swing_modes, + "temp_unit": temperature_unit_key, + "temp_list": temperatures_list, + "temp_step": temperature_step, + "features": features, + "state": state, + "fw_ver": fw_ver, + "fw_type": fw_type, + "model": model, + "calibration_temp": calibration_temp, + "calibration_hum": calibration_hum, + } + return device_data diff --git a/homeassistant/components/sensibo/services.yaml b/homeassistant/components/sensibo/services.yaml index 586ad3b4168..6438ea37e18 100644 --- a/homeassistant/components/sensibo/services.yaml +++ b/homeassistant/components/sensibo/services.yaml @@ -13,6 +13,9 @@ assume_state: name: State description: State to set. required: true - example: "idle" + example: "on" selector: - text: + select: + options: + - "on" + - "off"