mirror of
https://github.com/esphome/esphome.git
synced 2026-02-18 15:35:59 -07:00
Merge branch 'integration' into memory_api
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
17
tests/integration/fixtures/lock_automations.yaml
Normal file
17
tests/integration/fixtures/lock_automations.yaml
Normal 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"
|
||||
58
tests/integration/test_lock_automations.py
Normal file
58
tests/integration/test_lock_automations.py
Normal 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")
|
||||
Reference in New Issue
Block a user