mirror of
https://github.com/esphome/esphome.git
synced 2026-10-25 15:35:34 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9218e79806 | ||
|
|
49929479b9 | ||
|
|
5794bfd555 | ||
|
|
e7a2a833d9 | ||
|
|
17a9b53f62 |
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in new issue
Block a user