Compare commits

...
4 changed files with 78 additions and 1 deletions

No files matched your search

+5
View File
@@ -124,6 +124,7 @@ from esphome.core import (
)
from esphome.core.config import UNIT_OF_MEASUREMENT_MAX_LENGTH
from esphome.core.entity_helpers import (
FollowEntity,
SubEntities,
entity_duplicate_validator,
lazy_load_validator,
@@ -1109,6 +1110,10 @@ def sub_sensors(
return SubEntities(new_sensor, config, parent)
# Makes a plain sensor follow another sensor: follow_sensor(entity, source)
follow_sensor = FollowEntity("add_on_state_callback", ((cg.float_, "x"),), "x")
SENSOR_IN_RANGE_CONDITION_SCHEMA = cv.All(
{
cv.Required(CONF_ID): cv.use_id(Sensor),
@@ -22,6 +22,7 @@ from esphome.const import (
)
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import (
FollowEntity,
SubEntities,
entity_duplicate_validator,
queue_entity_register,
@@ -238,6 +239,12 @@ def sub_text_sensors(
return SubEntities(new_text_sensor, config, parent)
# Makes a plain text sensor follow another text sensor: follow_text_sensor(entity, source)
follow_text_sensor = FollowEntity(
"add_on_state_callback", ((cg.std_string_ref.operator("const"), "x"),), "x"
)
@coroutine_with_priority(CoroPriority.CORE)
async def to_code(config):
cg.add_global(text_sensor_ns.using)
+18
View File
@@ -45,6 +45,7 @@ from esphome.core.config import (
UNIT_OF_MEASUREMENT_MAX_LENGTH,
)
from esphome.cpp_generator import (
LambdaExpression,
MockObj,
MockObjClass,
RawExpression,
@@ -519,6 +520,23 @@ class ApplyEntity:
_APPLY_DOMAIN = "entity_apply"
@dataclass(frozen=True, slots=True)
class FollowEntity:
"""A state callback, its parameters and the value an entity republishes from it."""
callback: str
params: tuple[tuple[SafeExpType, str], ...]
value: str
def __call__(self, entity: MockObj, source: MockObj, *args: SafeExpType) -> None:
"""Make ``entity`` publish every state of ``source``; ``args`` precede the callback."""
publish = f"{parent_ref(entity).publish_state(RawExpression(self.value))};"
callback = LambdaExpression(
[publish], list(self.params), capture="", return_type=cg.void
)
add(getattr(source, self.callback)(*args, callback))
@dataclass(frozen=True, slots=True)
class SubEntities:
"""Create the child entity configured under key, set parent (if bound) and pass it to setter.
+48 -1
View File
@@ -11,7 +11,7 @@ import pytest
from esphome import final_validate as fv
import esphome.codegen as cg
from esphome.components import button, number, select, switch
from esphome.components import button, number, select, sensor, switch, text_sensor
from esphome.config import Config
import esphome.config_validation as cv
from esphome.config_validation import Invalid
@@ -1883,6 +1883,53 @@ async def test_sub_entities_create_from_whole_config(
assert setup_test_environment == ["hub->set_rf_switch(rf_sw)"]
@pytest.mark.parametrize(
("follow", "expected"),
[
(
sensor.follow_sensor,
[
(
"src->add_on_state_callback([](float x) -> void {\n"
" ::dst->publish_state(x);\n})"
)
],
),
(
text_sensor.follow_text_sensor,
[
(
"src->add_on_state_callback([](const std::string & x) -> void {\n"
" ::dst->publish_state(x);\n})"
)
],
),
],
)
def test_follow_entity(
setup_test_environment: list[str],
follow: Callable[[MockObj, MockObj], None],
expected: list[str],
) -> None:
"""The entity follows its source through a lambda that captures nothing."""
follow(MockObj("dst", "->"), MockObj("src", "->"))
assert setup_test_environment == expected
def test_follow_entity_hub_callback(setup_test_environment: list[str]) -> None:
"""A hub callback takes leading arguments and the published value is an expression."""
follow = entity_helpers.FollowEntity(
"register_listener", ((cg.uint8, "x"),), "x == 3"
)
follow(MockObj("dst", "->"), MockObj("hub", "->"), 7)
assert setup_test_environment == [
(
"hub->register_listener(7, [](uint8_t x) -> void {\n"
" ::dst->publish_state(x == 3);\n})"
)
]
def test_entity_duplicate_validator_without_name() -> None:
"""A config with no name is returned untouched and not tracked."""
validator = entity_duplicate_validator("sensor")