mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 03:37:07 +00:00
Add config flow for derivative sensor (#68268)
This commit is contained in:
parent
b284eddc63
commit
5bb271c9fb
@ -1 +1,23 @@
|
||||
"""The derivative component."""
|
||||
"""The Derivative integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Derivative from a config entry."""
|
||||
hass.config_entries.async_setup_platforms(entry, (Platform.SENSOR,))
|
||||
entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
|
||||
return True
|
||||
|
||||
|
||||
async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener, called when the config entry options are changed."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, (Platform.SENSOR,))
|
||||
|
93
homeassistant/components/derivative/config_flow.py
Normal file
93
homeassistant/components/derivative/config_flow.py
Normal file
@ -0,0 +1,93 @@
|
||||
"""Config flow for Derivative integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
CONF_SOURCE,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
TIME_DAYS,
|
||||
TIME_HOURS,
|
||||
TIME_MINUTES,
|
||||
TIME_SECONDS,
|
||||
)
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.helpers.helper_config_entry_flow import (
|
||||
HelperConfigFlowHandler,
|
||||
HelperFlowStep,
|
||||
)
|
||||
|
||||
from .const import (
|
||||
CONF_ROUND_DIGITS,
|
||||
CONF_TIME_WINDOW,
|
||||
CONF_UNIT_PREFIX,
|
||||
CONF_UNIT_TIME,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
UNIT_PREFIXES = [
|
||||
{"value": "none", "label": "none"},
|
||||
{"value": "n", "label": "n (nano)"},
|
||||
{"value": "µ", "label": "µ (micro)"},
|
||||
{"value": "m", "label": "m (milli)"},
|
||||
{"value": "k", "label": "k (kilo)"},
|
||||
{"value": "M", "label": "M (mega)"},
|
||||
{"value": "G", "label": "T (tera)"},
|
||||
{"value": "T", "label": "P (peta)"},
|
||||
]
|
||||
TIME_UNITS = [
|
||||
{"value": TIME_SECONDS, "label": "s (seconds)"},
|
||||
{"value": TIME_MINUTES, "label": "min (minutes)"},
|
||||
{"value": TIME_HOURS, "label": "h (hours)"},
|
||||
{"value": TIME_DAYS, "label": "d (days)"},
|
||||
]
|
||||
|
||||
OPTIONS_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ROUND_DIGITS, default=2): selector.selector(
|
||||
{
|
||||
"number": {
|
||||
"min": 0,
|
||||
"max": 6,
|
||||
"mode": "box",
|
||||
CONF_UNIT_OF_MEASUREMENT: "decimals",
|
||||
}
|
||||
}
|
||||
),
|
||||
vol.Required(CONF_TIME_WINDOW): selector.selector({"duration": {}}),
|
||||
vol.Required(CONF_UNIT_PREFIX, default="none"): selector.selector(
|
||||
{"select": {"options": UNIT_PREFIXES}}
|
||||
),
|
||||
vol.Required(CONF_UNIT_TIME, default=TIME_HOURS): selector.selector(
|
||||
{"select": {"options": TIME_UNITS}}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME): selector.selector({"text": {}}),
|
||||
vol.Required(CONF_SOURCE): selector.selector(
|
||||
{"entity": {"domain": "sensor"}},
|
||||
),
|
||||
}
|
||||
).extend(OPTIONS_SCHEMA.schema)
|
||||
|
||||
CONFIG_FLOW = {"user": HelperFlowStep(CONFIG_SCHEMA)}
|
||||
|
||||
OPTIONS_FLOW = {"init": HelperFlowStep(OPTIONS_SCHEMA)}
|
||||
|
||||
|
||||
class ConfigFlowHandler(HelperConfigFlowHandler, domain=DOMAIN):
|
||||
"""Handle a config or options flow for Derivative."""
|
||||
|
||||
config_flow = CONFIG_FLOW
|
||||
options_flow = OPTIONS_FLOW
|
||||
|
||||
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
||||
"""Return config entry title."""
|
||||
return cast(str, options[CONF_NAME])
|
9
homeassistant/components/derivative/const.py
Normal file
9
homeassistant/components/derivative/const.py
Normal file
@ -0,0 +1,9 @@
|
||||
"""Constants for the Derivative integration."""
|
||||
|
||||
DOMAIN = "derivative"
|
||||
|
||||
CONF_ROUND_DIGITS = "round"
|
||||
CONF_TIME_WINDOW = "time_window"
|
||||
CONF_UNIT = "unit"
|
||||
CONF_UNIT_PREFIX = "unit_prefix"
|
||||
CONF_UNIT_TIME = "unit_time"
|
@ -2,6 +2,9 @@
|
||||
"domain": "derivative",
|
||||
"name": "Derivative",
|
||||
"documentation": "https://www.home-assistant.io/integrations/derivative",
|
||||
"codeowners": ["@afaucogney"],
|
||||
"iot_class": "calculated"
|
||||
"codeowners": [
|
||||
"@afaucogney"
|
||||
],
|
||||
"iot_class": "calculated",
|
||||
"config_flow": true
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
CONF_NAME,
|
||||
@ -20,24 +21,26 @@ from homeassistant.const import (
|
||||
TIME_SECONDS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import async_track_state_change_event
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .const import (
|
||||
CONF_ROUND_DIGITS,
|
||||
CONF_TIME_WINDOW,
|
||||
CONF_UNIT,
|
||||
CONF_UNIT_PREFIX,
|
||||
CONF_UNIT_TIME,
|
||||
)
|
||||
|
||||
# mypy: allow-untyped-defs, no-check-untyped-defs
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_SOURCE_ID = "source"
|
||||
|
||||
CONF_ROUND_DIGITS = "round"
|
||||
CONF_UNIT_PREFIX = "unit_prefix"
|
||||
CONF_UNIT_TIME = "unit_time"
|
||||
CONF_UNIT = "unit"
|
||||
CONF_TIME_WINDOW = "time_window"
|
||||
|
||||
# SI Metric prefixes
|
||||
UNIT_PREFIXES = {
|
||||
None: 1,
|
||||
@ -76,6 +79,36 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Initialize Derivative config entry."""
|
||||
registry = er.async_get(hass)
|
||||
# Validate + resolve entity registry id to entity_id
|
||||
source_entity_id = er.async_validate_entity_id(
|
||||
registry, config_entry.options[CONF_SOURCE]
|
||||
)
|
||||
|
||||
unit_prefix = config_entry.options[CONF_UNIT_PREFIX]
|
||||
if unit_prefix == "none":
|
||||
unit_prefix = None
|
||||
|
||||
derivative_sensor = DerivativeSensor(
|
||||
name=config_entry.title,
|
||||
round_digits=int(config_entry.options[CONF_ROUND_DIGITS]),
|
||||
source_entity=source_entity_id,
|
||||
time_window=cv.time_period_dict(config_entry.options[CONF_TIME_WINDOW]),
|
||||
unique_id=config_entry.entry_id,
|
||||
unit_of_measurement=None,
|
||||
unit_prefix=unit_prefix,
|
||||
unit_time=config_entry.options[CONF_UNIT_TIME],
|
||||
)
|
||||
|
||||
async_add_entities([derivative_sensor])
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
@ -84,13 +117,14 @@ async def async_setup_platform(
|
||||
) -> None:
|
||||
"""Set up the derivative sensor."""
|
||||
derivative = DerivativeSensor(
|
||||
source_entity=config[CONF_SOURCE],
|
||||
name=config.get(CONF_NAME),
|
||||
round_digits=config[CONF_ROUND_DIGITS],
|
||||
source_entity=config[CONF_SOURCE],
|
||||
time_window=config[CONF_TIME_WINDOW],
|
||||
unit_of_measurement=config.get(CONF_UNIT),
|
||||
unit_prefix=config[CONF_UNIT_PREFIX],
|
||||
unit_time=config[CONF_UNIT_TIME],
|
||||
unit_of_measurement=config.get(CONF_UNIT),
|
||||
time_window=config[CONF_TIME_WINDOW],
|
||||
unique_id=None,
|
||||
)
|
||||
|
||||
async_add_entities([derivative])
|
||||
@ -101,15 +135,18 @@ class DerivativeSensor(RestoreEntity, SensorEntity):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
source_entity,
|
||||
*,
|
||||
name,
|
||||
round_digits,
|
||||
source_entity,
|
||||
time_window,
|
||||
unit_of_measurement,
|
||||
unit_prefix,
|
||||
unit_time,
|
||||
unit_of_measurement,
|
||||
time_window,
|
||||
unique_id,
|
||||
):
|
||||
"""Initialize the derivative sensor."""
|
||||
self._attr_unique_id = unique_id
|
||||
self._sensor_source_id = source_entity
|
||||
self._round_digits = round_digits
|
||||
self._state = 0
|
||||
@ -214,7 +251,7 @@ class DerivativeSensor(RestoreEntity, SensorEntity):
|
||||
self.async_write_ha_state()
|
||||
|
||||
async_track_state_change_event(
|
||||
self.hass, [self._sensor_source_id], calc_derivative
|
||||
self.hass, self._sensor_source_id, calc_derivative
|
||||
)
|
||||
|
||||
@property
|
||||
|
33
homeassistant/components/derivative/strings.json
Normal file
33
homeassistant/components/derivative/strings.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "New Derivative sensor",
|
||||
"description": "Precision controls the number of decimal digits in the output.\nIf the time window is not 0, the sensor's value is a time weighted moving average of derivatives within the window.\nThe derivative will be scaled according to the selected metric prefix and time unit of the derivative.",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"round": "Precision",
|
||||
"source": "Input sensor",
|
||||
"time_window": "Time window",
|
||||
"unit_prefix": "Metric prefix",
|
||||
"unit_time": "Time unit"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"options": {
|
||||
"description": "[%key:component::derivative::config::step::user::description%]",
|
||||
"data": {
|
||||
"name": "[%key:component::derivative::config::step::user::data::name%]",
|
||||
"round": "[%key:component::derivative::config::step::user::data::round%]",
|
||||
"source": "[%key:component::derivative::config::step::user::data::source%]",
|
||||
"time_window": "[%key:component::derivative::config::step::user::data::time_window%]",
|
||||
"unit_prefix": "[%key:component::derivative::config::step::user::data::unit_prefix%]",
|
||||
"unit_time": "[%key:component::derivative::config::step::user::data::unit_time%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
homeassistant/components/derivative/translations/en.json
Normal file
33
homeassistant/components/derivative/translations/en.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"round": "Precision",
|
||||
"source": "Input sensor",
|
||||
"time_window": "Time window",
|
||||
"unit_prefix": "Metric prefix",
|
||||
"unit_time": "Time unit"
|
||||
},
|
||||
"description": "Precision controls the number of decimal digits in the output.\nIf the time window is not 0, the sensor's value is a time weighted moving average of derivatives within the window.\nThe derivative will be scaled according to the selected metric prefix and time unit of the derivative.",
|
||||
"title": "New Derivative sensor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"options": {
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"round": "Precision",
|
||||
"source": "Input sensor",
|
||||
"time_window": "Time window",
|
||||
"unit_prefix": "Metric prefix",
|
||||
"unit_time": "Time unit"
|
||||
},
|
||||
"description": "Precision controls the number of decimal digits in the output.\nIf the time window is not 0, the sensor's value is a time weighted moving average of derivatives within the window.\nThe derivative will be scaled according to the selected metric prefix and time unit of the derivative."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -67,6 +67,7 @@ FLOWS = [
|
||||
"daikin",
|
||||
"deconz",
|
||||
"denonavr",
|
||||
"derivative",
|
||||
"devolo_home_control",
|
||||
"devolo_home_network",
|
||||
"dexcom",
|
||||
|
149
tests/components/derivative/test_config_flow.py
Normal file
149
tests/components/derivative/test_config_flow.py
Normal file
@ -0,0 +1,149 @@
|
||||
"""Test the Derivative config flow."""
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.derivative.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize("platform", ("sensor",))
|
||||
async def test_config_flow(hass: HomeAssistant, platform) -> None:
|
||||
"""Test the config flow."""
|
||||
input_sensor_entity_id = "sensor.input"
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.derivative.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"name": "My derivative",
|
||||
"round": 1,
|
||||
"source": input_sensor_entity_id,
|
||||
"time_window": {"seconds": 0},
|
||||
"unit_prefix": "none",
|
||||
"unit_time": "min",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "My derivative"
|
||||
assert result["data"] == {}
|
||||
assert result["options"] == {
|
||||
"name": "My derivative",
|
||||
"round": 1.0,
|
||||
"source": "sensor.input",
|
||||
"time_window": {"seconds": 0.0},
|
||||
"unit_prefix": "none",
|
||||
"unit_time": "min",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||
assert config_entry.data == {}
|
||||
assert config_entry.options == {
|
||||
"name": "My derivative",
|
||||
"round": 1.0,
|
||||
"source": "sensor.input",
|
||||
"time_window": {"seconds": 0.0},
|
||||
"unit_prefix": "none",
|
||||
"unit_time": "min",
|
||||
}
|
||||
assert config_entry.title == "My derivative"
|
||||
|
||||
|
||||
def get_suggested(schema, key):
|
||||
"""Get suggested value for key in voluptuous schema."""
|
||||
for k in schema.keys():
|
||||
if k == key:
|
||||
if k.description is None or "suggested_value" not in k.description:
|
||||
return None
|
||||
return k.description["suggested_value"]
|
||||
# Wanted key absent from schema
|
||||
raise Exception
|
||||
|
||||
|
||||
@pytest.mark.parametrize("platform", ("sensor",))
|
||||
async def test_options(hass: HomeAssistant, platform) -> None:
|
||||
"""Test reconfiguring."""
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
data={},
|
||||
domain=DOMAIN,
|
||||
options={
|
||||
"name": "My derivative",
|
||||
"round": 1.0,
|
||||
"source": "sensor.input",
|
||||
"time_window": {"seconds": 0.0},
|
||||
"unit_prefix": "k",
|
||||
"unit_time": "min",
|
||||
},
|
||||
title="My derivative",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
schema = result["data_schema"].schema
|
||||
assert get_suggested(schema, "round") == 1.0
|
||||
assert get_suggested(schema, "time_window") == {"seconds": 0.0}
|
||||
assert get_suggested(schema, "unit_prefix") == "k"
|
||||
assert get_suggested(schema, "unit_time") == "min"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"round": 2.0,
|
||||
"time_window": {"seconds": 10.0},
|
||||
"unit_prefix": "none",
|
||||
"unit_time": "h",
|
||||
},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"name": "My derivative",
|
||||
"round": 2.0,
|
||||
"source": "sensor.input",
|
||||
"time_window": {"seconds": 10.0},
|
||||
"unit_prefix": "none",
|
||||
"unit_time": "h",
|
||||
}
|
||||
assert config_entry.data == {}
|
||||
assert config_entry.options == {
|
||||
"name": "My derivative",
|
||||
"round": 2.0,
|
||||
"source": "sensor.input",
|
||||
"time_window": {"seconds": 10.0},
|
||||
"unit_prefix": "none",
|
||||
"unit_time": "h",
|
||||
}
|
||||
assert config_entry.title == "My derivative"
|
||||
|
||||
# Check config entry is reloaded with new options
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check the entity was updated, no new entity was created
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
||||
# Check the state of the entity has changed as expected
|
||||
hass.states.async_set("sensor.input", 10, {"unit_of_measurement": "cat"})
|
||||
hass.states.async_set("sensor.input", 11, {"unit_of_measurement": "cat"})
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(f"{platform}.my_derivative")
|
||||
assert state.attributes["unit_of_measurement"] == "cat/h"
|
61
tests/components/derivative/test_init.py
Normal file
61
tests/components/derivative/test_init.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Test the Derivative integration."""
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.derivative.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize("platform", ("sensor",))
|
||||
async def test_setup_and_remove_config_entry(
|
||||
hass: HomeAssistant,
|
||||
platform: str,
|
||||
) -> None:
|
||||
"""Test setting up and removing a config entry."""
|
||||
input_sensor_entity_id = "sensor.input"
|
||||
registry = er.async_get(hass)
|
||||
derivative_entity_id = f"{platform}.my_derivative"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
data={},
|
||||
domain=DOMAIN,
|
||||
options={
|
||||
"name": "My derivative",
|
||||
"round": 1.0,
|
||||
"source": "sensor.input",
|
||||
"time_window": {"seconds": 0.0},
|
||||
"unit_prefix": "k",
|
||||
"unit_time": "min",
|
||||
},
|
||||
title="My derivative",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check the entity is registered in the entity registry
|
||||
assert registry.async_get(derivative_entity_id) is not None
|
||||
|
||||
# Check the platform is setup correctly
|
||||
state = hass.states.get(derivative_entity_id)
|
||||
assert state.state == "0"
|
||||
assert "unit_of_measurement" not in state.attributes
|
||||
assert state.attributes["source"] == "sensor.input"
|
||||
|
||||
hass.states.async_set(input_sensor_entity_id, 10, {"unit_of_measurement": "dog"})
|
||||
hass.states.async_set(input_sensor_entity_id, 11, {"unit_of_measurement": "dog"})
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(derivative_entity_id)
|
||||
assert state.state != "0"
|
||||
assert state.attributes["unit_of_measurement"] == "kdog/min"
|
||||
|
||||
# Remove the config entry
|
||||
assert await hass.config_entries.async_remove(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check the state and entity registry entry are removed
|
||||
assert hass.states.get(derivative_entity_id) is None
|
||||
assert registry.async_get(derivative_entity_id) is None
|
Loading…
x
Reference in New Issue
Block a user