mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[hoermann_hcp] Add connectivity binary sensor (#18189)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
co-authored by
J. Nick Koston
parent
04dd6b3a55
commit
069f40f653
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import DEVICE_CLASS_CONNECTIVITY, ENTITY_CATEGORY_DIAGNOSTIC
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_HOERMANN_HCP_ID, HoermannHcp, hoermann_hcp_ns
|
||||
|
||||
DEPENDENCIES = ["hoermann_hcp"]
|
||||
|
||||
CONF_IS_CONNECTED = "is_connected"
|
||||
|
||||
HoermannHcpConnectedBinarySensor = hoermann_hcp_ns.class_(
|
||||
"HoermannHcpConnectedBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HOERMANN_HCP_ID): cv.use_id(HoermannHcp),
|
||||
cv.Optional(CONF_IS_CONNECTED): binary_sensor.binary_sensor_schema(
|
||||
HoermannHcpConnectedBinarySensor,
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
}
|
||||
),
|
||||
cv.has_at_least_one_key(CONF_IS_CONNECTED),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
if (conf := config.get(CONF_IS_CONNECTED)) is not None:
|
||||
parent = await cg.get_variable(config[CONF_HOERMANN_HCP_ID])
|
||||
var = await binary_sensor.new_binary_sensor(conf, parent)
|
||||
await cg.register_component(var, conf)
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "hoermann_hcp_binary_sensor.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::hoermann_hcp {
|
||||
|
||||
static const char *const TAG = "hoermann_hcp.binary_sensor";
|
||||
|
||||
void HoermannHcpConnectedBinarySensor::setup() {
|
||||
// Publishing unconditionally is deliberate: the base class dedupes, and filters need every input to drive
|
||||
// their timers.
|
||||
this->parent_->add_on_state_callback([this]() { this->publish_state(this->parent_->is_valid()); });
|
||||
this->publish_initial_state(this->parent_->is_valid());
|
||||
}
|
||||
|
||||
void HoermannHcpConnectedBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Hoermann HCP Connected", this); }
|
||||
|
||||
} // namespace esphome::hoermann_hcp
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "../hoermann_hcp.h"
|
||||
|
||||
namespace esphome::hoermann_hcp {
|
||||
|
||||
class HoermannHcpConnectedBinarySensor : public binary_sensor::BinarySensor, public Component {
|
||||
public:
|
||||
explicit HoermannHcpConnectedBinarySensor(HoermannHcp *parent) : parent_(parent) {}
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
HoermannHcp *const parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::hoermann_hcp
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "esphome/components/hoermann_hcp/binary_sensor/hoermann_hcp_binary_sensor.h"
|
||||
|
||||
namespace esphome::hoermann_hcp {
|
||||
|
||||
using modbus::RegisterValues;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint16_t COMMAND_REG = 0x9C41;
|
||||
constexpr uint16_t BROADCAST_REG = 0x9D31;
|
||||
|
||||
RegisterValues make_registers(std::initializer_list<uint16_t> values) {
|
||||
RegisterValues registers;
|
||||
for (uint16_t value : values)
|
||||
registers.push_back(value);
|
||||
return registers;
|
||||
}
|
||||
|
||||
// Exposes the connection bookkeeping so a drop can be driven without waiting one out.
|
||||
class TestableHoermannHcp : public HoermannHcp {
|
||||
public:
|
||||
using HoermannHcp::set_valid_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Nothing has been heard from the bus controller yet, so the sensor starts out seeded as disconnected.
|
||||
TEST(HoermannHcpBinarySensorTest, StartsDisconnected) {
|
||||
HoermannHcp door;
|
||||
HoermannHcpConnectedBinarySensor sensor(&door);
|
||||
sensor.setup();
|
||||
EXPECT_TRUE(sensor.has_state());
|
||||
EXPECT_FALSE(sensor.state);
|
||||
}
|
||||
|
||||
// The connection flag follows the bus controller in both directions.
|
||||
TEST(HoermannHcpBinarySensorTest, FollowsTheConnectionState) {
|
||||
TestableHoermannHcp door;
|
||||
HoermannHcpConnectedBinarySensor sensor(&door);
|
||||
sensor.setup();
|
||||
ASSERT_FALSE(sensor.state);
|
||||
|
||||
door.on_write_registers(COMMAND_REG, make_registers({0x0000, 0x0000}));
|
||||
door.update();
|
||||
EXPECT_TRUE(sensor.state);
|
||||
|
||||
door.set_valid_(false);
|
||||
door.update();
|
||||
EXPECT_FALSE(sensor.state);
|
||||
}
|
||||
|
||||
// Any hub change re-runs the publish path, so an unchanged connection must not be reported twice.
|
||||
TEST(HoermannHcpBinarySensorTest, UnchangedConnectionIsPublishedOnce) {
|
||||
HoermannHcp door;
|
||||
HoermannHcpConnectedBinarySensor sensor(&door);
|
||||
sensor.setup();
|
||||
int publishes = 0;
|
||||
sensor.add_on_state_callback([&publishes](bool /*state*/) { publishes++; });
|
||||
|
||||
door.on_write_registers(COMMAND_REG, make_registers({0x0000, 0x0000}));
|
||||
door.update();
|
||||
ASSERT_EQ(publishes, 1);
|
||||
|
||||
// A status broadcast changes the door state without touching the connection.
|
||||
door.on_write_registers(BROADCAST_REG, make_registers({0x0000, 0x0064, 0x0100}));
|
||||
door.update();
|
||||
EXPECT_EQ(publishes, 1);
|
||||
}
|
||||
|
||||
} // namespace esphome::hoermann_hcp
|
||||
@@ -6,3 +6,8 @@ cover:
|
||||
- platform: hoermann_hcp
|
||||
name: Garage Door
|
||||
device_class: garage
|
||||
|
||||
binary_sensor:
|
||||
- platform: hoermann_hcp
|
||||
is_connected:
|
||||
name: Garage Connected
|
||||
|
||||
Reference in New Issue
Block a user