Merge branch 'integration' into memory_api

This commit is contained in:
J. Nick Koston
2025-11-29 18:21:24 -06:00
6 changed files with 88 additions and 18 deletions

View File

@@ -21,6 +21,7 @@ esphome/components/adc128s102/* @DeerMaximum
esphome/components/addressable_light/* @justfalter
esphome/components/ade7880/* @kpfleming
esphome/components/ade7953/* @angelnu
esphome/components/ade7953_base/* @angelnu
esphome/components/ade7953_i2c/* @angelnu
esphome/components/ade7953_spi/* @angelnu
esphome/components/ads1118/* @solomondg1

View File

@@ -24,6 +24,8 @@ from esphome.const import (
UNIT_WATT,
)
CODEOWNERS = ["@angelnu"]
CONF_CURRENT_A = "current_a"
CONF_CURRENT_B = "current_b"
CONF_ACTIVE_POWER_A = "active_power_a"

View File

@@ -189,7 +189,7 @@ template<typename... Ts> class EnrollmentAction : public Action<Ts...>, public P
TEMPLATABLE_VALUE(std::string, name)
TEMPLATABLE_VALUE(uint8_t, direction)
void play(Ts... x) override {
void play(const Ts &...x) override {
auto name = this->name_.value(x...);
auto direction = (HlkFm22xFaceDirection) this->direction_.value(x...);
this->parent_->enroll_face(name, direction);
@@ -200,7 +200,7 @@ template<typename... Ts> class DeleteAction : public Action<Ts...>, public Paren
public:
TEMPLATABLE_VALUE(int16_t, face_id)
void play(Ts... x) override {
void play(const Ts &...x) override {
auto face_id = this->face_id_.value(x...);
this->parent_->delete_face(face_id);
}
@@ -208,17 +208,17 @@ template<typename... Ts> class DeleteAction : public Action<Ts...>, public Paren
template<typename... Ts> class DeleteAllAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
void play(Ts... x) override { this->parent_->delete_all_faces(); }
void play(const Ts &...x) override { this->parent_->delete_all_faces(); }
};
template<typename... Ts> class ScanAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
void play(Ts... x) override { this->parent_->scan_face(); }
void play(const Ts &...x) override { this->parent_->scan_face(); }
};
template<typename... Ts> class ResetAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
void play(Ts... x) override { this->parent_->reset(); }
void play(const Ts &...x) override { this->parent_->reset(); }
};
} // namespace esphome::hlk_fm22x

View File

@@ -49,26 +49,18 @@ template<typename... Ts> class LockCondition : public Condition<Ts...> {
bool state_;
};
class LockLockTrigger : public Trigger<> {
template<LockState State> class LockStateTrigger : public Trigger<> {
public:
LockLockTrigger(Lock *a_lock) {
explicit LockStateTrigger(Lock *a_lock) {
a_lock->add_on_state_callback([this, a_lock]() {
if (a_lock->state == LockState::LOCK_STATE_LOCKED) {
if (a_lock->state == State) {
this->trigger();
}
});
}
};
class LockUnlockTrigger : public Trigger<> {
public:
LockUnlockTrigger(Lock *a_lock) {
a_lock->add_on_state_callback([this, a_lock]() {
if (a_lock->state == LockState::LOCK_STATE_UNLOCKED) {
this->trigger();
}
});
}
};
using LockLockTrigger = LockStateTrigger<LockState::LOCK_STATE_LOCKED>;
using LockUnlockTrigger = LockStateTrigger<LockState::LOCK_STATE_UNLOCKED>;
} // namespace esphome::lock

View File

@@ -0,0 +1,17 @@
esphome:
name: lock-automations-test
host:
api: # Port will be automatically injected
logger:
level: DEBUG
lock:
- platform: template
id: test_lock
name: "Test Lock"
optimistic: true
on_lock:
- logger.log: "TRIGGER: on_lock fired"
on_unlock:
- logger.log: "TRIGGER: on_unlock fired"

View File

@@ -0,0 +1,58 @@
"""Integration test for lock automation triggers.
Tests that on_lock and on_unlock triggers work correctly.
"""
import asyncio
import pytest
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_lock_automations(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test lock on_lock and on_unlock triggers."""
loop = asyncio.get_running_loop()
# Futures for log line detection
on_lock_future: asyncio.Future[bool] = loop.create_future()
on_unlock_future: asyncio.Future[bool] = loop.create_future()
def check_output(line: str) -> None:
"""Check log output for trigger messages."""
if "TRIGGER: on_lock fired" in line and not on_lock_future.done():
on_lock_future.set_result(True)
elif "TRIGGER: on_unlock fired" in line and not on_unlock_future.done():
on_unlock_future.set_result(True)
async with (
run_compiled(yaml_config, line_callback=check_output),
api_client_connected() as client,
):
# Import here to avoid import errors when aioesphomeapi is not installed
from aioesphomeapi import LockCommand
# Get entities
entities = await client.list_entities_services()
lock = next(e for e in entities[0] if e.object_id == "test_lock")
# Test 1: Lock - should trigger on_lock
client.lock_command(key=lock.key, command=LockCommand.LOCK)
try:
await asyncio.wait_for(on_lock_future, timeout=5.0)
except TimeoutError:
pytest.fail("on_lock trigger did not fire")
# Test 2: Unlock - should trigger on_unlock
client.lock_command(key=lock.key, command=LockCommand.UNLOCK)
try:
await asyncio.wait_for(on_unlock_future, timeout=5.0)
except TimeoutError:
pytest.fail("on_unlock trigger did not fire")