Add temp and minutes remaining sensors to steamist (#63653)

This commit is contained in:
J. Nick Koston 2022-01-07 21:02:12 -10:00 committed by GitHub
parent 9dd09f66e2
commit ba402237c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 161 additions and 65 deletions

View File

@ -11,7 +11,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import DOMAIN
from .coordinator import SteamistDataUpdateCoordinator 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: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

View File

@ -5,10 +5,7 @@ from aiosteamist import SteamistStatus
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import CoordinatorEntity
CoordinatorEntity,
DataUpdateCoordinator,
)
from .coordinator import SteamistDataUpdateCoordinator from .coordinator import SteamistDataUpdateCoordinator
@ -20,7 +17,7 @@ class SteamistEntity(CoordinatorEntity, Entity):
def __init__( def __init__(
self, self,
coordinator: DataUpdateCoordinator, coordinator: SteamistDataUpdateCoordinator,
entry: ConfigEntry, entry: ConfigEntry,
description: EntityDescription, description: EntityDescription,
) -> None: ) -> None:

View File

@ -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))

View File

@ -7,9 +7,9 @@ from homeassistant.components.switch import SwitchEntity, SwitchEntityDescriptio
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN from .const import DOMAIN
from .coordinator import SteamistDataUpdateCoordinator
from .entity import SteamistEntity from .entity import SteamistEntity
ACTIVE_SWITCH = SwitchEntityDescription( ACTIVE_SWITCH = SwitchEntityDescription(
@ -23,7 +23,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up sensors.""" """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)]) async_add_entities([SteamistSwitchEntity(coordinator, config_entry, ACTIVE_SWITCH)])

View File

@ -1,9 +1,20 @@
"""Tests for the Steamist integration.""" """Tests for the Steamist integration."""
from __future__ import annotations
from contextlib import contextmanager from contextlib import contextmanager
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from aiosteamist import Steamist, SteamistStatus 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( MOCK_ASYNC_GET_STATUS_INACTIVE = SteamistStatus(
temp=70, temp_units="F", minutes_remain=0, active=False 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: def _mocked_steamist() -> Steamist:
client = MagicMock(auto_spec=Steamist) client = MagicMock(auto_spec=Steamist)
client.async_turn_on_steam = AsyncMock() client.async_turn_on_steam = AsyncMock()
@ -20,23 +48,10 @@ def _mocked_steamist() -> Steamist:
return client return client
def _patch_status_active(client=None): def _patch_status(status: SteamistStatus, client: Steamist | None = None):
if client is None: if client is None:
client = _mocked_steamist() client = _mocked_steamist()
client.async_get_status = AsyncMock(return_value=MOCK_ASYNC_GET_STATUS_ACTIVE) client.async_get_status = AsyncMock(return_value=status)
@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)
@contextmanager @contextmanager
def _patcher(): def _patcher():

View File

@ -11,22 +11,16 @@ from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component 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 from tests.common import MockConfigEntry
async def test_config_entry_reload(hass: HomeAssistant) -> None: async def test_config_entry_reload(hass: HomeAssistant) -> None:
"""Test that a config entry can be reloaded.""" """Test that a config entry can be reloaded."""
config_entry = MockConfigEntry( _, config_entry = await _async_setup_entry_with_status(
domain=DOMAIN, hass, MOCK_ASYNC_GET_STATUS_ACTIVE
data={CONF_HOST: "127.0.0.1"},
) )
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.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert config_entry.state == ConfigEntryState.NOT_LOADED assert config_entry.state == ConfigEntryState.NOT_LOADED

View File

@ -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

View File

@ -4,40 +4,23 @@ from __future__ import annotations
from datetime import timedelta from datetime import timedelta
from unittest.mock import AsyncMock 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.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import ( from . import (
MOCK_ASYNC_GET_STATUS_ACTIVE, MOCK_ASYNC_GET_STATUS_ACTIVE,
MOCK_ASYNC_GET_STATUS_INACTIVE, MOCK_ASYNC_GET_STATUS_INACTIVE,
_mocked_steamist, _async_setup_entry_with_status,
_patch_status_active,
_patch_status_inactive,
) )
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: async def test_steam_active(hass: HomeAssistant) -> None:
"""Test that the binary sensors are setup with the expected values when steam is active.""" """Test that the switches are setup with the expected values when steam is active."""
config_entry = MockConfigEntry( client, _ = await _async_setup_entry_with_status(hass, MOCK_ASYNC_GET_STATUS_ACTIVE)
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
assert len(hass.states.async_all("switch")) == 1 assert len(hass.states.async_all("switch")) == 1
assert hass.states.get("switch.steam_active").state == STATE_ON 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: async def test_steam_inactive(hass: HomeAssistant) -> None:
"""Test that the binary sensors are setup with the expected values when steam is not active.""" """Test that the switches are setup with the expected values when steam is not active."""
config_entry = MockConfigEntry( client, _ = await _async_setup_entry_with_status(
domain=DOMAIN, hass, MOCK_ASYNC_GET_STATUS_INACTIVE
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_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 len(hass.states.async_all("switch")) == 1
assert hass.states.get("switch.steam_active").state == STATE_OFF assert hass.states.get("switch.steam_active").state == STATE_OFF