mirror of
https://github.com/home-assistant/core.git
synced 2026-09-03 10:05:58 -05:00
Don't create cpu temperature sensor when not supported in FRITZ!Box Tools (#167905)
This commit is contained in:
@@ -8,6 +8,7 @@ from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
from fritzconnection.lib.fritzstatus import FritzStatus
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
@@ -145,46 +146,65 @@ def _retrieve_link_attenuation_received_state(
|
||||
|
||||
def _retrieve_cpu_temperature_state(
|
||||
status: FritzStatus, last_value: float | None
|
||||
) -> float:
|
||||
) -> float | None:
|
||||
"""Return the first CPU temperature value."""
|
||||
return status.get_cpu_temperatures()[0] # type: ignore[no-any-return]
|
||||
try:
|
||||
return status.get_cpu_temperatures()[0] # type: ignore[no-any-return]
|
||||
except RequestException:
|
||||
return None
|
||||
|
||||
|
||||
def _is_suitable_cpu_temperature(status: FritzStatus) -> bool:
|
||||
"""Return whether the CPU temperature sensor is suitable."""
|
||||
try:
|
||||
cpu_temp = status.get_cpu_temperatures()[0]
|
||||
except RequestException, IndexError:
|
||||
_LOGGER.debug("CPU temperature not supported by the device")
|
||||
return False
|
||||
if cpu_temp == 0:
|
||||
_LOGGER.debug("CPU temperature returns 0°C, treating as not supported")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class FritzSensorEntityDescription(SensorEntityDescription, FritzEntityDescription):
|
||||
"""Describes Fritz sensor entity."""
|
||||
class FritzConnectionSensorEntityDescription(
|
||||
SensorEntityDescription, FritzEntityDescription
|
||||
):
|
||||
"""Describes Fritz connection sensor entity."""
|
||||
|
||||
is_suitable: Callable[[ConnectionInfo], bool] = lambda info: info.wan_enabled
|
||||
|
||||
|
||||
SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
FritzSensorEntityDescription(
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class FritzDeviceSensorEntityDescription(
|
||||
SensorEntityDescription, FritzEntityDescription
|
||||
):
|
||||
"""Describes Fritz device sensor entity."""
|
||||
|
||||
is_suitable: Callable[[FritzStatus], bool] = lambda status: True
|
||||
|
||||
|
||||
CONNECTION_SENSOR_TYPES: tuple[FritzConnectionSensorEntityDescription, ...] = (
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="external_ip",
|
||||
translation_key="external_ip",
|
||||
value_fn=_retrieve_external_ip_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="external_ipv6",
|
||||
translation_key="external_ipv6",
|
||||
value_fn=_retrieve_external_ipv6_state,
|
||||
is_suitable=lambda info: info.ipv6_active,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="device_uptime",
|
||||
translation_key="device_uptime",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=_retrieve_device_uptime_state,
|
||||
is_suitable=lambda info: True,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="connection_uptime",
|
||||
translation_key="connection_uptime",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=_retrieve_connection_uptime_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="kb_s_sent",
|
||||
translation_key="kb_s_sent",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -192,7 +212,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
value_fn=_retrieve_kb_s_sent_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="kb_s_received",
|
||||
translation_key="kb_s_received",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -200,21 +220,21 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
value_fn=_retrieve_kb_s_received_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="max_kb_s_sent",
|
||||
translation_key="max_kb_s_sent",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
value_fn=_retrieve_max_kb_s_sent_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="max_kb_s_received",
|
||||
translation_key="max_kb_s_received",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
value_fn=_retrieve_max_kb_s_received_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="gb_sent",
|
||||
translation_key="gb_sent",
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -222,7 +242,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.DATA_SIZE,
|
||||
value_fn=_retrieve_gb_sent_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="gb_received",
|
||||
translation_key="gb_received",
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -230,7 +250,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.DATA_SIZE,
|
||||
value_fn=_retrieve_gb_received_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="link_kb_s_sent",
|
||||
translation_key="link_kb_s_sent",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
@@ -238,7 +258,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=_retrieve_link_kb_s_sent_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="link_kb_s_received",
|
||||
translation_key="link_kb_s_received",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
@@ -246,7 +266,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=_retrieve_link_kb_s_received_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="link_noise_margin_sent",
|
||||
translation_key="link_noise_margin_sent",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
@@ -255,7 +275,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
value_fn=_retrieve_link_noise_margin_sent_state,
|
||||
is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="link_noise_margin_received",
|
||||
translation_key="link_noise_margin_received",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
@@ -264,7 +284,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
value_fn=_retrieve_link_noise_margin_received_state,
|
||||
is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="link_attenuation_sent",
|
||||
translation_key="link_attenuation_sent",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
@@ -273,7 +293,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
value_fn=_retrieve_link_attenuation_sent_state,
|
||||
is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
FritzConnectionSensorEntityDescription(
|
||||
key="link_attenuation_received",
|
||||
translation_key="link_attenuation_received",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
@@ -282,7 +302,17 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
value_fn=_retrieve_link_attenuation_received_state,
|
||||
is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
)
|
||||
|
||||
DEVICE_SENSOR_TYPES: tuple[FritzDeviceSensorEntityDescription, ...] = (
|
||||
FritzDeviceSensorEntityDescription(
|
||||
key="device_uptime",
|
||||
translation_key="device_uptime",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=_retrieve_device_uptime_state,
|
||||
),
|
||||
FritzDeviceSensorEntityDescription(
|
||||
key="cpu_temperature",
|
||||
translation_key="cpu_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
@@ -290,7 +320,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=_retrieve_cpu_temperature_state,
|
||||
is_suitable=lambda info: True,
|
||||
is_suitable=_is_suitable_cpu_temperature,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -305,20 +335,32 @@ async def async_setup_entry(
|
||||
avm_wrapper = entry.runtime_data
|
||||
|
||||
connection_info = await avm_wrapper.async_get_connection_info()
|
||||
|
||||
entities = [
|
||||
FritzBoxSensor(avm_wrapper, entry.title, description)
|
||||
for description in SENSOR_TYPES
|
||||
for description in CONNECTION_SENSOR_TYPES
|
||||
if description.is_suitable(connection_info)
|
||||
]
|
||||
|
||||
fritz_status = avm_wrapper.fritz_status
|
||||
|
||||
def _generate_device_sensors() -> list[FritzBoxSensor]:
|
||||
return [
|
||||
FritzBoxSensor(avm_wrapper, entry.title, description)
|
||||
for description in DEVICE_SENSOR_TYPES
|
||||
if description.is_suitable(fritz_status)
|
||||
]
|
||||
|
||||
entities += await hass.async_add_executor_job(_generate_device_sensors)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class FritzBoxSensor(FritzBoxBaseCoordinatorEntity, SensorEntity):
|
||||
"""Define FRITZ!Box connectivity class."""
|
||||
|
||||
entity_description: FritzSensorEntityDescription
|
||||
entity_description: (
|
||||
FritzConnectionSensorEntityDescription | FritzDeviceSensorEntityDescription
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ from unittest.mock import patch
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from fritzconnection.core.exceptions import FritzConnectionException
|
||||
import pytest
|
||||
from requests.exceptions import RequestException
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.fritz.const import DOMAIN, SCAN_INTERVAL, UPTIME_DEVIATION
|
||||
@@ -112,3 +113,41 @@ async def test_sensor_uptime_spike(
|
||||
|
||||
assert (new_state := hass.states.get(entity_id))
|
||||
assert new_state.state == "2026-01-16T06:00:21+00:00"
|
||||
|
||||
|
||||
@pytest.mark.freeze_time(datetime(2024, 9, 1, 20, tzinfo=UTC))
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "return_values"),
|
||||
[(RequestException("boom"), None), (None, [0, 0, 0]), (None, [])],
|
||||
)
|
||||
async def test_sensor_cpu_temp_not_supported(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
side_effect,
|
||||
return_values,
|
||||
fc_class_mock,
|
||||
fh_class_mock,
|
||||
fs_class_mock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test setup of Fritz!Tools sensors."""
|
||||
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with (
|
||||
patch("homeassistant.components.fritz.PLATFORMS", [Platform.SENSOR]),
|
||||
patch(
|
||||
"homeassistant.components.fritz.coordinator.FritzStatus", fs_class_mock
|
||||
) as mock_status,
|
||||
):
|
||||
mock_status.get_cpu_temperatures.side_effect = side_effect
|
||||
mock_status.get_cpu_temperatures.return_value = return_values
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
assert not entity_registry.async_is_registered(
|
||||
"sensor.mock_title_cpu_temperature"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user