mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Move Fritzbox power, energy and temperature switch attributes to sensors (#52562)
* deprecate switch entity properties * Add last_reset to FritzBoxEnergySensor * Remove obsolet temperature attribute
This commit is contained in:
parent
dee5d8903c
commit
42e8a7c842
@ -13,9 +13,6 @@ ATTR_STATE_WINDOW_OPEN: Final = "window_open"
|
|||||||
|
|
||||||
ATTR_TEMPERATURE_UNIT: Final = "temperature_unit"
|
ATTR_TEMPERATURE_UNIT: Final = "temperature_unit"
|
||||||
|
|
||||||
ATTR_TOTAL_CONSUMPTION: Final = "total_consumption"
|
|
||||||
ATTR_TOTAL_CONSUMPTION_UNIT: Final = "total_consumption_unit"
|
|
||||||
|
|
||||||
CONF_CONNECTIONS: Final = "connections"
|
CONF_CONNECTIONS: Final = "connections"
|
||||||
CONF_COORDINATOR: Final = "coordinator"
|
CONF_COORDINATOR: Final = "coordinator"
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Support for AVM FRITZ!SmartHome temperature sensor only devices."""
|
"""Support for AVM FRITZ!SmartHome temperature sensor only devices."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
ATTR_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
@ -13,12 +15,17 @@ from homeassistant.const import (
|
|||||||
ATTR_NAME,
|
ATTR_NAME,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
|
POWER_WATT,
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
)
|
)
|
||||||
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.util.dt import utc_from_timestamp
|
||||||
|
|
||||||
from . import FritzBoxEntity
|
from . import FritzBoxEntity
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -68,11 +75,39 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if device.has_powermeter:
|
||||||
|
entities.append(
|
||||||
|
FritzBoxPowerSensor(
|
||||||
|
{
|
||||||
|
ATTR_NAME: f"{device.name} Power Consumption",
|
||||||
|
ATTR_ENTITY_ID: f"{device.ain}_power_consumption",
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: POWER_WATT,
|
||||||
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
},
|
||||||
|
coordinator,
|
||||||
|
ain,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
entities.append(
|
||||||
|
FritzBoxEnergySensor(
|
||||||
|
{
|
||||||
|
ATTR_NAME: f"{device.name} Total Energy",
|
||||||
|
ATTR_ENTITY_ID: f"{device.ain}_total_energy",
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||||
|
ATTR_STATE_CLASS: None,
|
||||||
|
},
|
||||||
|
coordinator,
|
||||||
|
ain,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class FritzBoxBatterySensor(FritzBoxEntity, SensorEntity):
|
class FritzBoxBatterySensor(FritzBoxEntity, SensorEntity):
|
||||||
"""The entity class for FRITZ!SmartHome sensors."""
|
"""The entity class for FRITZ!SmartHome battery sensors."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self) -> int | None:
|
def state(self) -> int | None:
|
||||||
@ -80,6 +115,30 @@ class FritzBoxBatterySensor(FritzBoxEntity, SensorEntity):
|
|||||||
return self.device.battery_level # type: ignore [no-any-return]
|
return self.device.battery_level # type: ignore [no-any-return]
|
||||||
|
|
||||||
|
|
||||||
|
class FritzBoxPowerSensor(FritzBoxEntity, SensorEntity):
|
||||||
|
"""The entity class for FRITZ!SmartHome power consumption sensors."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self) -> float | None:
|
||||||
|
"""Return the state of the sensor."""
|
||||||
|
return self.device.power / 1000 # type: ignore [no-any-return]
|
||||||
|
|
||||||
|
|
||||||
|
class FritzBoxEnergySensor(FritzBoxEntity, SensorEntity):
|
||||||
|
"""The entity class for FRITZ!SmartHome total energy sensors."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self) -> float | None:
|
||||||
|
"""Return the state of the sensor."""
|
||||||
|
return (self.device.energy or 0.0) / 1000
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_reset(self) -> datetime:
|
||||||
|
"""Return the time when the sensor was last reset, if any."""
|
||||||
|
# device does not provide timestamp of initialization
|
||||||
|
return utc_from_timestamp(0)
|
||||||
|
|
||||||
|
|
||||||
class FritzBoxTempSensor(FritzBoxEntity, SensorEntity):
|
class FritzBoxTempSensor(FritzBoxEntity, SensorEntity):
|
||||||
"""The entity class for FRITZ!SmartHome temperature sensors."""
|
"""The entity class for FRITZ!SmartHome temperature sensors."""
|
||||||
|
|
||||||
|
@ -10,10 +10,7 @@ from homeassistant.const import (
|
|||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_NAME,
|
ATTR_NAME,
|
||||||
ATTR_TEMPERATURE,
|
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
ENERGY_KILO_WATT_HOUR,
|
|
||||||
TEMP_CELSIUS,
|
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
@ -22,16 +19,11 @@ from . import FritzBoxEntity
|
|||||||
from .const import (
|
from .const import (
|
||||||
ATTR_STATE_DEVICE_LOCKED,
|
ATTR_STATE_DEVICE_LOCKED,
|
||||||
ATTR_STATE_LOCKED,
|
ATTR_STATE_LOCKED,
|
||||||
ATTR_TEMPERATURE_UNIT,
|
|
||||||
ATTR_TOTAL_CONSUMPTION,
|
|
||||||
ATTR_TOTAL_CONSUMPTION_UNIT,
|
|
||||||
CONF_COORDINATOR,
|
CONF_COORDINATOR,
|
||||||
DOMAIN as FRITZBOX_DOMAIN,
|
DOMAIN as FRITZBOX_DOMAIN,
|
||||||
)
|
)
|
||||||
from .model import SwitchExtraAttributes
|
from .model import SwitchExtraAttributes
|
||||||
|
|
||||||
ATTR_TOTAL_CONSUMPTION_UNIT_VALUE = ENERGY_KILO_WATT_HOUR
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
@ -91,22 +83,4 @@ class FritzboxSwitch(FritzBoxEntity, SwitchEntity):
|
|||||||
ATTR_STATE_DEVICE_LOCKED: self.device.device_lock,
|
ATTR_STATE_DEVICE_LOCKED: self.device.device_lock,
|
||||||
ATTR_STATE_LOCKED: self.device.lock,
|
ATTR_STATE_LOCKED: self.device.lock,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.device.has_powermeter:
|
|
||||||
attrs[
|
|
||||||
ATTR_TOTAL_CONSUMPTION
|
|
||||||
] = f"{((self.device.energy or 0.0) / 1000):.3f}"
|
|
||||||
attrs[ATTR_TOTAL_CONSUMPTION_UNIT] = ATTR_TOTAL_CONSUMPTION_UNIT_VALUE
|
|
||||||
if self.device.has_temperature_sensor:
|
|
||||||
attrs[ATTR_TEMPERATURE] = str(
|
|
||||||
self.hass.config.units.temperature(
|
|
||||||
self.device.temperature, TEMP_CELSIUS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attrs[ATTR_TEMPERATURE_UNIT] = self.hass.config.units.temperature_unit
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
@property
|
|
||||||
def current_power_w(self) -> float:
|
|
||||||
"""Return the current power usage in W."""
|
|
||||||
return self.device.power / 1000 # type: ignore [no-any-return]
|
|
||||||
|
@ -55,6 +55,7 @@ class FritzDeviceBinarySensorMock(FritzDeviceBaseMock):
|
|||||||
battery_level = 23
|
battery_level = 23
|
||||||
fw_version = "1.2.3"
|
fw_version = "1.2.3"
|
||||||
has_alarm = True
|
has_alarm = True
|
||||||
|
has_powermeter = False
|
||||||
has_switch = False
|
has_switch = False
|
||||||
has_temperature_sensor = False
|
has_temperature_sensor = False
|
||||||
has_thermostat = False
|
has_thermostat = False
|
||||||
@ -73,6 +74,7 @@ class FritzDeviceClimateMock(FritzDeviceBaseMock):
|
|||||||
eco_temperature = 16.0
|
eco_temperature = 16.0
|
||||||
fw_version = "1.2.3"
|
fw_version = "1.2.3"
|
||||||
has_alarm = False
|
has_alarm = False
|
||||||
|
has_powermeter = False
|
||||||
has_switch = False
|
has_switch = False
|
||||||
has_temperature_sensor = False
|
has_temperature_sensor = False
|
||||||
has_thermostat = True
|
has_thermostat = True
|
||||||
@ -91,6 +93,7 @@ class FritzDeviceSensorMock(FritzDeviceBaseMock):
|
|||||||
device_lock = "fake_locked_device"
|
device_lock = "fake_locked_device"
|
||||||
fw_version = "1.2.3"
|
fw_version = "1.2.3"
|
||||||
has_alarm = False
|
has_alarm = False
|
||||||
|
has_powermeter = False
|
||||||
has_switch = False
|
has_switch = False
|
||||||
has_temperature_sensor = True
|
has_temperature_sensor = True
|
||||||
has_thermostat = False
|
has_thermostat = False
|
||||||
@ -107,6 +110,7 @@ class FritzDeviceSwitchMock(FritzDeviceBaseMock):
|
|||||||
energy = 1234
|
energy = 1234
|
||||||
fw_version = "1.2.3"
|
fw_version = "1.2.3"
|
||||||
has_alarm = False
|
has_alarm = False
|
||||||
|
has_powermeter = True
|
||||||
has_switch = True
|
has_switch = True
|
||||||
has_temperature_sensor = True
|
has_temperature_sensor = True
|
||||||
has_thermostat = False
|
has_thermostat = False
|
||||||
|
@ -7,24 +7,22 @@ from requests.exceptions import HTTPError
|
|||||||
from homeassistant.components.fritzbox.const import (
|
from homeassistant.components.fritzbox.const import (
|
||||||
ATTR_STATE_DEVICE_LOCKED,
|
ATTR_STATE_DEVICE_LOCKED,
|
||||||
ATTR_STATE_LOCKED,
|
ATTR_STATE_LOCKED,
|
||||||
ATTR_TEMPERATURE_UNIT,
|
|
||||||
ATTR_TOTAL_CONSUMPTION,
|
|
||||||
ATTR_TOTAL_CONSUMPTION_UNIT,
|
|
||||||
DOMAIN as FB_DOMAIN,
|
DOMAIN as FB_DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
|
ATTR_LAST_RESET,
|
||||||
ATTR_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
DOMAIN as SENSOR_DOMAIN,
|
DOMAIN as SENSOR_DOMAIN,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
)
|
)
|
||||||
from homeassistant.components.switch import ATTR_CURRENT_POWER_W, DOMAIN
|
from homeassistant.components.switch import DOMAIN
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
ATTR_TEMPERATURE,
|
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
CONF_DEVICES,
|
CONF_DEVICES,
|
||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
POWER_WATT,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
@ -51,14 +49,9 @@ async def test_setup(hass: HomeAssistant, fritz: Mock):
|
|||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(ENTITY_ID)
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes[ATTR_CURRENT_POWER_W] == 5.678
|
|
||||||
assert state.attributes[ATTR_FRIENDLY_NAME] == CONF_FAKE_NAME
|
assert state.attributes[ATTR_FRIENDLY_NAME] == CONF_FAKE_NAME
|
||||||
assert state.attributes[ATTR_STATE_DEVICE_LOCKED] == "fake_locked_device"
|
assert state.attributes[ATTR_STATE_DEVICE_LOCKED] == "fake_locked_device"
|
||||||
assert state.attributes[ATTR_STATE_LOCKED] == "fake_locked"
|
assert state.attributes[ATTR_STATE_LOCKED] == "fake_locked"
|
||||||
assert state.attributes[ATTR_TEMPERATURE] == "1.23"
|
|
||||||
assert state.attributes[ATTR_TEMPERATURE_UNIT] == TEMP_CELSIUS
|
|
||||||
assert state.attributes[ATTR_TOTAL_CONSUMPTION] == "1.234"
|
|
||||||
assert state.attributes[ATTR_TOTAL_CONSUMPTION_UNIT] == ENERGY_KILO_WATT_HOUR
|
|
||||||
assert ATTR_STATE_CLASS not in state.attributes
|
assert ATTR_STATE_CLASS not in state.attributes
|
||||||
|
|
||||||
state = hass.states.get(f"{SENSOR_DOMAIN}.{CONF_FAKE_NAME}_temperature")
|
state = hass.states.get(f"{SENSOR_DOMAIN}.{CONF_FAKE_NAME}_temperature")
|
||||||
@ -70,6 +63,21 @@ async def test_setup(hass: HomeAssistant, fritz: Mock):
|
|||||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS
|
||||||
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_MEASUREMENT
|
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_MEASUREMENT
|
||||||
|
|
||||||
|
state = hass.states.get(f"{SENSOR_DOMAIN}.{CONF_FAKE_NAME}_power_consumption")
|
||||||
|
assert state
|
||||||
|
assert state.state == "5.678"
|
||||||
|
assert state.attributes[ATTR_FRIENDLY_NAME] == f"{CONF_FAKE_NAME} Power Consumption"
|
||||||
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == POWER_WATT
|
||||||
|
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_MEASUREMENT
|
||||||
|
|
||||||
|
state = hass.states.get(f"{SENSOR_DOMAIN}.{CONF_FAKE_NAME}_total_energy")
|
||||||
|
assert state
|
||||||
|
assert state.state == "1.234"
|
||||||
|
assert state.attributes[ATTR_LAST_RESET] == "1970-01-01T00:00:00+00:00"
|
||||||
|
assert state.attributes[ATTR_FRIENDLY_NAME] == f"{CONF_FAKE_NAME} Total Energy"
|
||||||
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == ENERGY_KILO_WATT_HOUR
|
||||||
|
assert ATTR_STATE_CLASS not in state.attributes
|
||||||
|
|
||||||
|
|
||||||
async def test_turn_on(hass: HomeAssistant, fritz: Mock):
|
async def test_turn_on(hass: HomeAssistant, fritz: Mock):
|
||||||
"""Test turn device on."""
|
"""Test turn device on."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user