Add hot reload for derivative (#156898)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
karwosts
2025-11-22 11:23:29 -08:00
committed by GitHub
parent 01e38853c0
commit b27b357b91
6 changed files with 99 additions and 6 deletions

View File

@@ -5,5 +5,10 @@
"default": "mdi:chart-line"
}
}
},
"services": {
"reload": {
"service": "mdi:reload"
}
}
}

View File

@@ -23,6 +23,7 @@ from homeassistant.const import (
CONF_UNIQUE_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
Platform,
UnitOfTime,
)
from homeassistant.core import (
@@ -45,6 +46,7 @@ from homeassistant.helpers.event import (
async_track_state_change_event,
async_track_state_report_event,
)
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
@@ -54,6 +56,7 @@ from .const import (
CONF_UNIT,
CONF_UNIT_PREFIX,
CONF_UNIT_TIME,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
@@ -147,6 +150,8 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the derivative sensor."""
await async_setup_reload_service(hass, DOMAIN, [Platform.SENSOR])
derivative = DerivativeSensor(
hass,
name=config.get(CONF_NAME),
@@ -288,14 +293,14 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
)
self.async_write_ha_state()
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await super().async_added_to_hass()
async def _handle_restore(self) -> None:
restored_data = await self.async_get_last_sensor_data()
if restored_data:
self._attr_native_unit_of_measurement = (
restored_data.native_unit_of_measurement
)
if self._attr_native_unit_of_measurement is None:
# Only restore the unit if it's not assigned from YAML
self._attr_native_unit_of_measurement = (
restored_data.native_unit_of_measurement
)
try:
self._attr_native_value = round(
Decimal(restored_data.native_value), # type: ignore[arg-type]
@@ -304,6 +309,11 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
except (InvalidOperation, TypeError):
self._attr_native_value = None
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await super().async_added_to_hass()
await self._handle_restore()
source_state = self.hass.states.get(self._sensor_source_id)
self._derive_and_set_attributes_from_state(source_state)

View File

@@ -0,0 +1 @@
reload:

View File

@@ -58,5 +58,11 @@
}
}
},
"services": {
"reload": {
"description": "Reloads derivative sensors from the YAML-configuration.",
"name": "[%key:common::action::reload%]"
}
},
"title": "Derivative sensor"
}

View File

@@ -0,0 +1,9 @@
sensor:
- platform: derivative
name: derivative
source: "sensor.energy"
unit: "W"
- platform: derivative
name: derivative_new
source: "sensor.energy"
unit: "W"

View File

@@ -4,13 +4,16 @@ from datetime import timedelta
from math import sin
import random
from typing import Any
from unittest.mock import patch
from freezegun import freeze_time
import pytest
from homeassistant import config as hass_config, core as ha
from homeassistant.components.derivative.const import DOMAIN
from homeassistant.components.sensor import ATTR_STATE_CLASS, SensorStateClass
from homeassistant.const import (
SERVICE_RELOAD,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
UnitOfPower,
@@ -31,6 +34,7 @@ from homeassistant.util import dt as dt_util
from tests.common import (
MockConfigEntry,
async_fire_time_changed,
get_fixture_path,
mock_restore_cache_with_extra_data,
)
@@ -1000,6 +1004,64 @@ async def test_source_unit_change(
assert state.attributes.get("unit_of_measurement") == "dogs/s"
async def test_reload(hass: HomeAssistant) -> None:
"""Test hot-reloading derivative YAML sensors."""
hass.state = ha.CoreState.not_running
hass.states.async_set("sensor.energy", "0.0")
config = {
"sensor": [
{
"platform": "derivative",
"name": "derivative",
"source": "sensor.energy",
"unit": "kW",
},
{
"platform": "derivative",
"name": "derivative_remove",
"source": "sensor.energy",
"unit": "kW",
},
]
}
assert await async_setup_component(hass, "sensor", config)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 3
state = hass.states.get("sensor.derivative")
assert state is not None
assert state.attributes.get("unit_of_measurement") == "kW"
assert hass.states.get("sensor.derivative_remove")
yaml_path = get_fixture_path("configuration.yaml", "derivative")
with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
await hass.services.async_call(
DOMAIN,
SERVICE_RELOAD,
{},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 3
# Check that we can change the unit of an existing sensor
state = hass.states.get("sensor.derivative")
assert state is not None
assert state.attributes.get("unit_of_measurement") == "W"
# Check that we can remove a derivative sensor
assert hass.states.get("sensor.derivative_remove") is None
# Check that we can add a new derivative sensor
assert hass.states.get("sensor.derivative_new")
async def test_unique_id(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,