[template][time] Add template time platform (#14838)

Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
This commit is contained in:
Pat Satyshur
2026-09-27 09:48:08 +00:00
committed by GitHub
co-authored by clydebarrow
parent b8af6cb005
commit ea4754e798
7 changed files with 174 additions and 1 deletions
+4
View File
@@ -3,6 +3,10 @@
time:
- platform: sntp # Required for datetime
id: sntp_time
- platform: template
id: template_offset_time
lambda: |-
return id(sntp_time).timestamp_now() + 3600;
wifi: # Required for sntp time
ap:
@@ -0,0 +1,47 @@
esphome:
name: template-time-test
host:
api:
logger:
time:
- platform: template
id: fixed_time
lambda: |-
return 1700000000;
# Derived from another time source, as when offsetting a real clock.
- platform: template
id: offset_time
lambda: |-
return id(fixed_time).timestamp_now() + 3600;
# A lambda that gives no value reports an invalid time.
- platform: template
id: empty_time
lambda: |-
return {};
text_sensor:
# Timestamps are exposed as text (rather than a 32-bit float sensor state,
# which cannot represent a UNIX epoch exactly) so the exact value can be checked.
- platform: template
name: "Fixed Timestamp"
id: fixed_timestamp
update_interval: 100ms
lambda: |-
return std::to_string(id(fixed_time).now().timestamp);
- platform: template
name: "Offset Timestamp"
id: offset_timestamp
update_interval: 100ms
lambda: |-
return std::to_string(id(offset_time).now().timestamp);
- platform: template
name: "Empty Time Valid"
id: empty_time_valid
update_interval: 100ms
lambda: |-
return std::string(id(empty_time).now().is_valid() ? "valid" : "invalid");
+49
View File
@@ -0,0 +1,49 @@
"""Integration test for the template time platform."""
from __future__ import annotations
import asyncio
from aioesphomeapi import EntityState, TextSensorState
import pytest
from .state_utils import InitialStateHelper, build_key_to_entity_mapping
from .types import APIClientConnectedFactory, RunCompiledFunction
# Timestamps are exposed as text sensors (rather than 32-bit float sensor states,
# which cannot represent a UNIX epoch exactly) so the exact value can be checked.
EXPECTED_STATES = {
"fixed_timestamp": "1700000000",
"offset_timestamp": "1700003600",
"empty_time_valid": "invalid",
}
@pytest.mark.asyncio
async def test_template_time(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Verify the template time platform evaluates its lambda on demand."""
async with run_compiled(yaml_config), api_client_connected() as client:
entities, _ = await client.list_entities_services()
key_to_name = build_key_to_entity_mapping(entities, list(EXPECTED_STATES))
events = {name: asyncio.Event() for name in EXPECTED_STATES}
def on_state(state: EntityState) -> None:
if isinstance(state, TextSensorState) and not state.missing_state:
name = key_to_name.get(state.key)
if name is not None and state.state == EXPECTED_STATES[name]:
events[name].set()
initial_state_helper = InitialStateHelper(entities)
client.subscribe_states(initial_state_helper.on_state_wrapper(on_state))
await initial_state_helper.wait_for_initial_states()
for name, event in events.items():
try:
await asyncio.wait_for(event.wait(), timeout=3.0)
except TimeoutError:
pytest.fail(f"Timeout waiting for {name} to report its expected value")