diff --git a/homeassistant/components/fully_kiosk/__init__.py b/homeassistant/components/fully_kiosk/__init__.py index 5a3d6078004..ebd9af1134a 100644 --- a/homeassistant/components/fully_kiosk/__init__.py +++ b/homeassistant/components/fully_kiosk/__init__.py @@ -6,7 +6,13 @@ from homeassistant.core import HomeAssistant from .const import DOMAIN from .coordinator import FullyKioskDataUpdateCoordinator -PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [ + Platform.BINARY_SENSOR, + Platform.BUTTON, + Platform.NUMBER, + Platform.SENSOR, + Platform.SWITCH, +] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/fully_kiosk/number.py b/homeassistant/components/fully_kiosk/number.py new file mode 100644 index 00000000000..d39f3f6391d --- /dev/null +++ b/homeassistant/components/fully_kiosk/number.py @@ -0,0 +1,99 @@ +"""Fully Kiosk Browser number entity.""" +from __future__ import annotations + +from contextlib import suppress + +from homeassistant.components.number import NumberEntity, NumberEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import TIME_SECONDS +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import FullyKioskDataUpdateCoordinator +from .entity import FullyKioskEntity + +ENTITY_TYPES: tuple[NumberEntityDescription, ...] = ( + NumberEntityDescription( + key="timeToScreensaverV2", + name="Screensaver timer", + native_max_value=9999, + native_step=1, + native_min_value=0, + native_unit_of_measurement=TIME_SECONDS, + entity_category=EntityCategory.CONFIG, + ), + NumberEntityDescription( + key="screensaverBrightness", + name="Screensaver brightness", + native_max_value=255, + native_step=1, + native_min_value=0, + entity_category=EntityCategory.CONFIG, + ), + NumberEntityDescription( + key="timeToScreenOffV2", + name="Screen off timer", + native_max_value=9999, + native_step=1, + native_min_value=0, + native_unit_of_measurement=TIME_SECONDS, + entity_category=EntityCategory.CONFIG, + ), + NumberEntityDescription( + key="screenBrightness", + name="Screen brightness", + native_max_value=255, + native_step=1, + native_min_value=0, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Fully Kiosk Browser number entities.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id] + + async_add_entities( + FullyNumberEntity(coordinator, entity) + for entity in ENTITY_TYPES + if entity.key in coordinator.data["settings"] + ) + + +class FullyNumberEntity(FullyKioskEntity, NumberEntity): + """Representation of a Fully Kiosk Browser entity.""" + + def __init__( + self, + coordinator: FullyKioskDataUpdateCoordinator, + description: NumberEntityDescription, + ) -> None: + """Initialize the number entity.""" + super().__init__(coordinator) + self.entity_description = description + self._attr_unique_id = f"{coordinator.data['deviceID']}-{description.key}" + + @property + def native_value(self) -> int | None: + """Return the state of the number entity.""" + if ( + value := self.coordinator.data["settings"].get(self.entity_description.key) + ) is None: + return None + + with suppress(ValueError): + return int(value) + + return None + + async def async_set_native_value(self, value: float) -> None: + """Set the value of the entity.""" + await self.coordinator.fully.setConfigurationString( + self.entity_description.key, int(value) + ) diff --git a/tests/components/fully_kiosk/test_number.py b/tests/components/fully_kiosk/test_number.py new file mode 100644 index 00000000000..968faa3f0b4 --- /dev/null +++ b/tests/components/fully_kiosk/test_number.py @@ -0,0 +1,91 @@ +"""Test the Fully Kiosk Browser number entities.""" +from unittest.mock import MagicMock + +from homeassistant.components.fully_kiosk.const import DOMAIN, UPDATE_INTERVAL +import homeassistant.components.number as number +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.util import dt + +from tests.common import MockConfigEntry, async_fire_time_changed + + +async def test_numbers( + hass: HomeAssistant, + mock_fully_kiosk: MagicMock, + init_integration: MockConfigEntry, +) -> None: + """Test standard Fully Kiosk numbers.""" + entity_registry = er.async_get(hass) + device_registry = dr.async_get(hass) + + state = hass.states.get("number.amazon_fire_screensaver_timer") + assert state + assert state.state == "900" + entry = entity_registry.async_get("number.amazon_fire_screensaver_timer") + assert entry + assert entry.unique_id == "abcdef-123456-timeToScreensaverV2" + await set_value(hass, "number.amazon_fire_screensaver_timer", 600) + assert len(mock_fully_kiosk.setConfigurationString.mock_calls) == 1 + + state = hass.states.get("number.amazon_fire_screensaver_brightness") + assert state + assert state.state == "0" + entry = entity_registry.async_get("number.amazon_fire_screensaver_brightness") + assert entry + assert entry.unique_id == "abcdef-123456-screensaverBrightness" + + state = hass.states.get("number.amazon_fire_screen_off_timer") + assert state + assert state.state == "0" + entry = entity_registry.async_get("number.amazon_fire_screen_off_timer") + assert entry + assert entry.unique_id == "abcdef-123456-timeToScreenOffV2" + + state = hass.states.get("number.amazon_fire_screen_brightness") + assert state + assert state.state == "9" + entry = entity_registry.async_get("number.amazon_fire_screen_brightness") + assert entry + assert entry.unique_id == "abcdef-123456-screenBrightness" + + # Test invalid numeric data + mock_fully_kiosk.getSettings.return_value = {"screenBrightness": "invalid"} + async_fire_time_changed(hass, dt.utcnow() + UPDATE_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get("number.amazon_fire_screen_brightness") + assert state + assert state.state == STATE_UNKNOWN + + # Test unknown/missing data + mock_fully_kiosk.getSettings.return_value = {} + async_fire_time_changed(hass, dt.utcnow() + UPDATE_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get("number.amazon_fire_screensaver_timer") + assert state + assert state.state == STATE_UNKNOWN + + assert entry.device_id + device_entry = device_registry.async_get(entry.device_id) + assert device_entry + assert device_entry.configuration_url == "http://192.168.1.234:2323" + assert device_entry.entry_type is None + assert device_entry.hw_version is None + assert device_entry.identifiers == {(DOMAIN, "abcdef-123456")} + assert device_entry.manufacturer == "amzn" + assert device_entry.model == "KFDOWI" + assert device_entry.name == "Amazon Fire" + assert device_entry.sw_version == "1.42.5" + + +def set_value(hass, entity_id, value): + """Set the value of a number entity.""" + return hass.services.async_call( + number.DOMAIN, + "set_value", + {ATTR_ENTITY_ID: entity_id, number.ATTR_VALUE: value}, + blocking=True, + )