Fix next change (scheduler) sensors in AVM FRITZ!SmartHome (#126363)

This commit is contained in:
Michael 2024-09-23 10:11:27 +02:00 committed by Franck Nijhof
parent fba24b8ead
commit 4eb1fca68e
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
4 changed files with 85 additions and 10 deletions

View File

@ -83,20 +83,38 @@ def entity_category_temperature(device: FritzhomeDevice) -> EntityCategory | Non
return None
def value_nextchange_preset(device: FritzhomeDevice) -> str:
def value_nextchange_preset(device: FritzhomeDevice) -> str | None:
"""Return native value for next scheduled preset sensor."""
if not device.nextchange_endperiod:
return None
if device.nextchange_temperature == device.eco_temperature:
return PRESET_ECO
return PRESET_COMFORT
def value_scheduled_preset(device: FritzhomeDevice) -> str:
def value_scheduled_preset(device: FritzhomeDevice) -> str | None:
"""Return native value for current scheduled preset sensor."""
if not device.nextchange_endperiod:
return None
if device.nextchange_temperature == device.eco_temperature:
return PRESET_COMFORT
return PRESET_ECO
def value_nextchange_temperature(device: FritzhomeDevice) -> float | None:
"""Return native value for next scheduled temperature time sensor."""
if device.nextchange_endperiod and isinstance(device.nextchange_temperature, float):
return device.nextchange_temperature
return None
def value_nextchange_time(device: FritzhomeDevice) -> datetime | None:
"""Return native value for next scheduled changed time sensor."""
if device.nextchange_endperiod:
return utc_from_timestamp(device.nextchange_endperiod)
return None
SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
FritzSensorEntityDescription(
key="temperature",
@ -181,7 +199,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
suitable=suitable_nextchange_temperature,
native_value=lambda device: device.nextchange_temperature,
native_value=value_nextchange_temperature,
),
FritzSensorEntityDescription(
key="nextchange_time",
@ -189,7 +207,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
suitable=suitable_nextchange_time,
native_value=lambda device: utc_from_timestamp(device.nextchange_endperiod),
native_value=value_nextchange_time,
),
FritzSensorEntityDescription(
key="nextchange_preset",

View File

@ -5,7 +5,6 @@ from __future__ import annotations
from typing import Any
from unittest.mock import Mock
from homeassistant.components.climate import PRESET_COMFORT, PRESET_ECO
from homeassistant.components.fritzbox.const import DOMAIN
from homeassistant.core import HomeAssistant
@ -110,9 +109,7 @@ class FritzDeviceClimateMock(FritzEntityBaseMock):
target_temperature = 19.5
window_open = "fake_window"
nextchange_temperature = 22.0
nextchange_endperiod = 0
nextchange_preset = PRESET_COMFORT
scheduled_preset = PRESET_ECO
nextchange_endperiod = 1726855200
class FritzDeviceClimateWithoutTempSensorMock(FritzDeviceClimateMock):

View File

@ -123,7 +123,7 @@ async def test_setup(hass: HomeAssistant, fritz: Mock) -> None:
f"{SENSOR_DOMAIN}.{CONF_FAKE_NAME}_next_scheduled_change_time"
)
assert state
assert state.state == "1970-01-01T00:00:00+00:00"
assert state.state == "2024-09-20T18:00:00+00:00"
assert (
state.attributes[ATTR_FRIENDLY_NAME]
== f"{CONF_FAKE_NAME} Next scheduled change time"

View File

@ -3,8 +3,10 @@
from datetime import timedelta
from unittest.mock import Mock
import pytest
from requests.exceptions import HTTPError
from homeassistant.components.climate import PRESET_COMFORT, PRESET_ECO
from homeassistant.components.fritzbox.const import DOMAIN as FB_DOMAIN
from homeassistant.components.sensor import ATTR_STATE_CLASS, DOMAIN, SensorStateClass
from homeassistant.const import (
@ -12,6 +14,7 @@ from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
CONF_DEVICES,
PERCENTAGE,
STATE_UNKNOWN,
EntityCategory,
UnitOfTemperature,
)
@ -19,7 +22,12 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
import homeassistant.util.dt as dt_util
from . import FritzDeviceSensorMock, set_devices, setup_config_entry
from . import (
FritzDeviceClimateMock,
FritzDeviceSensorMock,
set_devices,
setup_config_entry,
)
from .const import CONF_FAKE_NAME, MOCK_CONFIG
from tests.common import async_fire_time_changed
@ -132,3 +140,55 @@ async def test_discover_new_device(hass: HomeAssistant, fritz: Mock) -> None:
state = hass.states.get(f"{DOMAIN}.new_device_temperature")
assert state
@pytest.mark.parametrize(
("next_changes", "expected_states"),
[
(
[0, 16],
[STATE_UNKNOWN, STATE_UNKNOWN, STATE_UNKNOWN, STATE_UNKNOWN],
),
(
[0, 22],
[STATE_UNKNOWN, STATE_UNKNOWN, STATE_UNKNOWN, STATE_UNKNOWN],
),
(
[1726855200, 16.0],
["2024-09-20T18:00:00+00:00", "16.0", PRESET_ECO, PRESET_COMFORT],
),
(
[1726855200, 22.0],
["2024-09-20T18:00:00+00:00", "22.0", PRESET_COMFORT, PRESET_ECO],
),
],
)
async def test_next_change_sensors(
hass: HomeAssistant, fritz: Mock, next_changes: list, expected_states: list
) -> None:
"""Test next change sensors."""
device = FritzDeviceClimateMock()
device.nextchange_endperiod = next_changes[0]
device.nextchange_temperature = next_changes[1]
assert await setup_config_entry(
hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz
)
base_name = f"{SENSOR_DOMAIN}.{CONF_FAKE_NAME}"
state = hass.states.get(f"{base_name}_next_scheduled_change_time")
assert state
assert state.state == expected_states[0]
state = hass.states.get(f"{base_name}_next_scheduled_temperature")
assert state
assert state.state == expected_states[1]
state = hass.states.get(f"{base_name}_next_scheduled_preset")
assert state
assert state.state == expected_states[2]
state = hass.states.get(f"{base_name}_current_scheduled_preset")
assert state
assert state.state == expected_states[3]