diff --git a/homeassistant/components/steamist/__init__.py b/homeassistant/components/steamist/__init__.py index fd4191d70e2..e5a0b84189f 100644 --- a/homeassistant/components/steamist/__init__.py +++ b/homeassistant/components/steamist/__init__.py @@ -11,7 +11,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DOMAIN from .coordinator import SteamistDataUpdateCoordinator -PLATFORMS: list[str] = [Platform.SWITCH] +PLATFORMS: list[str] = [Platform.SENSOR, Platform.SWITCH] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/steamist/entity.py b/homeassistant/components/steamist/entity.py index c375ba8c6d0..17898984288 100644 --- a/homeassistant/components/steamist/entity.py +++ b/homeassistant/components/steamist/entity.py @@ -5,10 +5,7 @@ from aiosteamist import SteamistStatus from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.entity import Entity, EntityDescription -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity from .coordinator import SteamistDataUpdateCoordinator @@ -20,7 +17,7 @@ class SteamistEntity(CoordinatorEntity, Entity): def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: SteamistDataUpdateCoordinator, entry: ConfigEntry, description: EntityDescription, ) -> None: diff --git a/homeassistant/components/steamist/sensor.py b/homeassistant/components/steamist/sensor.py new file mode 100644 index 00000000000..0adbfa9c2b2 --- /dev/null +++ b/homeassistant/components/steamist/sensor.py @@ -0,0 +1,80 @@ +"""Support for Steamist sensors.""" +from __future__ import annotations + +from typing import cast + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, TIME_MINUTES +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import SteamistDataUpdateCoordinator +from .entity import SteamistEntity + +_KEY_MINUTES_REMAIN = "minutes_remain" +_KEY_TEMP = "temp" + +UNIT_MAPPINGS = { + "C": TEMP_CELSIUS, + "F": TEMP_FAHRENHEIT, +} + +STEAMIST_SENSORS = ( + SensorEntityDescription( + key=_KEY_MINUTES_REMAIN, + name="Steam Minutes Remain", + native_unit_of_measurement=TIME_MINUTES, + ), + SensorEntityDescription( + key=_KEY_TEMP, + name="Steam Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up sensors.""" + coordinator: SteamistDataUpdateCoordinator = hass.data[DOMAIN][ + config_entry.entry_id + ] + async_add_entities( + [ + SteamistSensorEntity(coordinator, config_entry, description) + for description in STEAMIST_SENSORS + ] + ) + + +class SteamistSensorEntity(SteamistEntity, SensorEntity): + """Representation of an Steamist steam switch.""" + + def __init__( + self, + coordinator: SteamistDataUpdateCoordinator, + entry: ConfigEntry, + description: SensorEntityDescription, + ) -> None: + """Initialize the sensor entity.""" + super().__init__(coordinator, entry, description) + if description.key == _KEY_TEMP: + self._attr_native_unit_of_measurement = UNIT_MAPPINGS[ + self._status.temp_units + ] + + @property + def native_value(self) -> int: + """Return the native value of the sensor.""" + return cast(int, getattr(self._status, self.entity_description.key)) diff --git a/homeassistant/components/steamist/switch.py b/homeassistant/components/steamist/switch.py index 1a8ef7f64de..af9e894b70d 100644 --- a/homeassistant/components/steamist/switch.py +++ b/homeassistant/components/steamist/switch.py @@ -7,9 +7,9 @@ from homeassistant.components.switch import SwitchEntity, SwitchEntityDescriptio from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import DOMAIN +from .coordinator import SteamistDataUpdateCoordinator from .entity import SteamistEntity ACTIVE_SWITCH = SwitchEntityDescription( @@ -23,7 +23,9 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up sensors.""" - coordinator: DataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + coordinator: SteamistDataUpdateCoordinator = hass.data[DOMAIN][ + config_entry.entry_id + ] async_add_entities([SteamistSwitchEntity(coordinator, config_entry, ACTIVE_SWITCH)]) diff --git a/tests/components/steamist/__init__.py b/tests/components/steamist/__init__.py index 5de5a586280..dfe64eb8006 100644 --- a/tests/components/steamist/__init__.py +++ b/tests/components/steamist/__init__.py @@ -1,9 +1,20 @@ """Tests for the Steamist integration.""" +from __future__ import annotations + from contextlib import contextmanager from unittest.mock import AsyncMock, MagicMock, patch from aiosteamist import Steamist, SteamistStatus +from homeassistant.components import steamist +from homeassistant.components.steamist.const import DOMAIN +from homeassistant.config_entries import ConfigEntry, ConfigEntryState +from homeassistant.const import CONF_HOST +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry + MOCK_ASYNC_GET_STATUS_INACTIVE = SteamistStatus( temp=70, temp_units="F", minutes_remain=0, active=False ) @@ -12,6 +23,23 @@ MOCK_ASYNC_GET_STATUS_ACTIVE = SteamistStatus( ) +async def _async_setup_entry_with_status( + hass: HomeAssistant, status: SteamistStatus +) -> tuple[Steamist, ConfigEntry]: + config_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: "127.0.0.1"}, + ) + config_entry.add_to_hass(hass) + client = _mocked_steamist() + client.async_get_status = AsyncMock(return_value=status) + with _patch_status(status, client): + await async_setup_component(hass, steamist.DOMAIN, {steamist.DOMAIN: {}}) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.LOADED + return client, config_entry + + def _mocked_steamist() -> Steamist: client = MagicMock(auto_spec=Steamist) client.async_turn_on_steam = AsyncMock() @@ -20,23 +48,10 @@ def _mocked_steamist() -> Steamist: return client -def _patch_status_active(client=None): +def _patch_status(status: SteamistStatus, client: Steamist | None = None): if client is None: client = _mocked_steamist() - client.async_get_status = AsyncMock(return_value=MOCK_ASYNC_GET_STATUS_ACTIVE) - - @contextmanager - def _patcher(): - with patch("homeassistant.components.steamist.Steamist", return_value=client): - yield - - return _patcher() - - -def _patch_status_inactive(client=None): - if client is None: - client = _mocked_steamist() - client.async_get_status = AsyncMock(return_value=MOCK_ASYNC_GET_STATUS_INACTIVE) + client.async_get_status = AsyncMock(return_value=status) @contextmanager def _patcher(): diff --git a/tests/components/steamist/test_init.py b/tests/components/steamist/test_init.py index 65f2b8bb2e1..2af62fc24e5 100644 --- a/tests/components/steamist/test_init.py +++ b/tests/components/steamist/test_init.py @@ -11,22 +11,16 @@ from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -from . import _patch_status_active +from . import MOCK_ASYNC_GET_STATUS_ACTIVE, _async_setup_entry_with_status from tests.common import MockConfigEntry async def test_config_entry_reload(hass: HomeAssistant) -> None: """Test that a config entry can be reloaded.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "127.0.0.1"}, + _, config_entry = await _async_setup_entry_with_status( + hass, MOCK_ASYNC_GET_STATUS_ACTIVE ) - config_entry.add_to_hass(hass) - with _patch_status_active(): - await async_setup_component(hass, steamist.DOMAIN, {steamist.DOMAIN: {}}) - await hass.async_block_till_done() - assert config_entry.state == ConfigEntryState.LOADED await hass.config_entries.async_unload(config_entry.entry_id) await hass.async_block_till_done() assert config_entry.state == ConfigEntryState.NOT_LOADED diff --git a/tests/components/steamist/test_sensor.py b/tests/components/steamist/test_sensor.py new file mode 100644 index 00000000000..4d83a0eb80d --- /dev/null +++ b/tests/components/steamist/test_sensor.py @@ -0,0 +1,33 @@ +"""Tests for the steamist sensos.""" +from __future__ import annotations + +from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS, TIME_MINUTES +from homeassistant.core import HomeAssistant + +from . import ( + MOCK_ASYNC_GET_STATUS_ACTIVE, + MOCK_ASYNC_GET_STATUS_INACTIVE, + _async_setup_entry_with_status, +) + + +async def test_steam_active(hass: HomeAssistant) -> None: + """Test that the sensors are setup with the expected values when steam is active.""" + await _async_setup_entry_with_status(hass, MOCK_ASYNC_GET_STATUS_ACTIVE) + state = hass.states.get("sensor.steam_temperature") + assert state.state == "39" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + state = hass.states.get("sensor.steam_minutes_remain") + assert state.state == "14" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TIME_MINUTES + + +async def test_steam_inactive(hass: HomeAssistant) -> None: + """Test that the sensors are setup with the expected values when steam is not active.""" + await _async_setup_entry_with_status(hass, MOCK_ASYNC_GET_STATUS_INACTIVE) + state = hass.states.get("sensor.steam_temperature") + assert state.state == "21" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + state = hass.states.get("sensor.steam_minutes_remain") + assert state.state == "0" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TIME_MINUTES diff --git a/tests/components/steamist/test_switch.py b/tests/components/steamist/test_switch.py index 072513eaa83..47a9cbf6708 100644 --- a/tests/components/steamist/test_switch.py +++ b/tests/components/steamist/test_switch.py @@ -4,40 +4,23 @@ from __future__ import annotations from datetime import timedelta from unittest.mock import AsyncMock -from homeassistant.components import steamist -from homeassistant.components.steamist.const import DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN -from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, STATE_OFF, STATE_ON +from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from . import ( MOCK_ASYNC_GET_STATUS_ACTIVE, MOCK_ASYNC_GET_STATUS_INACTIVE, - _mocked_steamist, - _patch_status_active, - _patch_status_inactive, + _async_setup_entry_with_status, ) -from tests.common import MockConfigEntry, async_fire_time_changed +from tests.common import async_fire_time_changed async def test_steam_active(hass: HomeAssistant) -> None: - """Test that the binary sensors are setup with the expected values when steam is active.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "127.0.0.1"}, - ) - config_entry.add_to_hass(hass) - client = _mocked_steamist() - client.async_get_status = AsyncMock(return_value=MOCK_ASYNC_GET_STATUS_ACTIVE) - with _patch_status_active(client): - await async_setup_component(hass, steamist.DOMAIN, {steamist.DOMAIN: {}}) - await hass.async_block_till_done() - assert config_entry.state == ConfigEntryState.LOADED - + """Test that the switches are setup with the expected values when steam is active.""" + client, _ = await _async_setup_entry_with_status(hass, MOCK_ASYNC_GET_STATUS_ACTIVE) assert len(hass.states.async_all("switch")) == 1 assert hass.states.get("switch.steam_active").state == STATE_ON @@ -55,18 +38,10 @@ async def test_steam_active(hass: HomeAssistant) -> None: async def test_steam_inactive(hass: HomeAssistant) -> None: - """Test that the binary sensors are setup with the expected values when steam is not active.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "127.0.0.1"}, + """Test that the switches are setup with the expected values when steam is not active.""" + client, _ = await _async_setup_entry_with_status( + hass, MOCK_ASYNC_GET_STATUS_INACTIVE ) - config_entry.add_to_hass(hass) - client = _mocked_steamist() - client.async_get_status = AsyncMock(return_value=MOCK_ASYNC_GET_STATUS_INACTIVE) - with _patch_status_inactive(client): - await async_setup_component(hass, steamist.DOMAIN, {steamist.DOMAIN: {}}) - await hass.async_block_till_done() - assert config_entry.state == ConfigEntryState.LOADED assert len(hass.states.async_all("switch")) == 1 assert hass.states.get("switch.steam_active").state == STATE_OFF