Strip unused sensors and their implementation

This commit is contained in:
2026-05-12 16:26:24 +00:00
parent 19d7a1532e
commit dac704f372
11 changed files with 7 additions and 373 deletions
+2 -74
View File
@@ -84,13 +84,6 @@ CONF_INPUT_GDO = "input_gdo_pin"
DEFAULT_INPUT_GDO = (
"D2" # D2 red control terminal / GarageDoorOpener (UART1 RX) pin is D2 on D1 Mini
)
CONF_INPUT_OBST = "input_obst_pin"
DEFAULT_INPUT_OBST = "D7" # D7 black obstruction sensor terminal
CONF_OBST_SLEEP_LOW = "obst_sleep_low"
CONF_DISCRETE_OPEN_PIN = "discrete_open_pin"
CONF_DISCRETE_CLOSE_PIN = "discrete_close_pin"
CONF_RATGDO_ID = "ratgdo_id"
@@ -98,33 +91,11 @@ CONF_ON_SYNC_FAILED = "on_sync_failed"
CONF_PROTOCOL = "protocol"
PROTOCOL_SECPLUSV1 = "secplusv1"
PROTOCOL_SECPLUSV2 = "secplusv2"
PROTOCOL_DRYCONTACT = "drycontact"
SUPPORTED_PROTOCOLS = [PROTOCOL_SECPLUSV1, PROTOCOL_SECPLUSV2, PROTOCOL_DRYCONTACT]
CONF_DRY_CONTACT_OPEN_SENSOR = "dry_contact_open_sensor"
CONF_DRY_CONTACT_CLOSE_SENSOR = "dry_contact_close_sensor"
CONF_DRY_CONTACT_SENSOR_GROUP = "dry_contact_sensor_group"
SUPPORTED_PROTOCOLS = [PROTOCOL_SECPLUSV2]
def validate_protocol(config):
if config.get(CONF_PROTOCOL, None) == PROTOCOL_DRYCONTACT and (
CONF_DRY_CONTACT_CLOSE_SENSOR not in config
or CONF_DRY_CONTACT_OPEN_SENSOR not in config
):
raise cv.Invalid(
"dry_contact_close_sensor and dry_contact_open_sensor are required when using protocol drycontact"
)
if config.get(CONF_PROTOCOL, None) != PROTOCOL_DRYCONTACT and (
CONF_DRY_CONTACT_CLOSE_SENSOR in config
or CONF_DRY_CONTACT_OPEN_SENSOR in config
):
raise cv.Invalid(
"dry_contact_close_sensor and dry_contact_open_sensor are only valid when using protocol drycontact"
)
# if config.get(CONF_PROTOCOL, None) == PROTOCOL_DRYCONTACT and CONF_DRY_CONTACT_OPEN_SENSOR not in config:
# raise cv.Invalid("dry_contact_open_sensor is required when using protocol drycontact")
return config
@@ -138,12 +109,6 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(
CONF_INPUT_GDO, default=DEFAULT_INPUT_GDO
): pins.gpio_input_pin_schema,
cv.Optional(CONF_INPUT_OBST, default=DEFAULT_INPUT_OBST): cv.Any(
cv.none, pins.gpio_input_pin_schema
),
cv.SplitDefault(CONF_OBST_SLEEP_LOW, esp32=False, esp8266=True): cv.boolean,
cv.Optional(CONF_DISCRETE_OPEN_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_DISCRETE_CLOSE_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_ON_SYNC_FAILED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SyncFailed),
@@ -152,14 +117,6 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_PROTOCOL, default=PROTOCOL_SECPLUSV2): cv.All(
vol.In(SUPPORTED_PROTOCOLS)
),
# cv.Inclusive(CONF_DRY_CONTACT_OPEN_SENSOR,CONF_DRY_CONTACT_SENSOR_GROUP): cv.use_id(binary_sensor.BinarySensor),
# cv.Inclusive(CONF_DRY_CONTACT_CLOSE_SENSOR,CONF_DRY_CONTACT_SENSOR_GROUP): cv.use_id(binary_sensor.BinarySensor),
cv.Optional(CONF_DRY_CONTACT_OPEN_SENSOR): cv.use_id(
binary_sensor.BinarySensor
),
cv.Optional(CONF_DRY_CONTACT_CLOSE_SENSOR): cv.use_id(
binary_sensor.BinarySensor
),
}
).extend(cv.COMPONENT_SCHEMA),
validate_protocol,
@@ -184,23 +141,6 @@ async def to_code(config):
cg.add(var.set_output_gdo_pin(pin))
pin = await cg.gpio_pin_expression(config[CONF_INPUT_GDO])
cg.add(var.set_input_gdo_pin(pin))
if config.get(CONF_INPUT_OBST):
pin = await cg.gpio_pin_expression(config[CONF_INPUT_OBST])
cg.add(var.set_input_obst_pin(pin))
cg.add(var.set_obst_sleep_low(config[CONF_OBST_SLEEP_LOW]))
if config.get(CONF_DRY_CONTACT_OPEN_SENSOR):
dry_contact_open_sensor = await cg.get_variable(
config[CONF_DRY_CONTACT_OPEN_SENSOR]
)
cg.add(var.set_dry_contact_open_sensor(dry_contact_open_sensor))
if config.get(CONF_DRY_CONTACT_CLOSE_SENSOR):
dry_contact_close_sensor = await cg.get_variable(
config[CONF_DRY_CONTACT_CLOSE_SENSOR]
)
cg.add(var.set_dry_contact_close_sensor(dry_contact_close_sensor))
for conf in config.get(CONF_ON_SYNC_FAILED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
@@ -228,12 +168,7 @@ async def to_code(config):
version=None,
)
if config[CONF_PROTOCOL] == PROTOCOL_SECPLUSV1:
cg.add_build_flag("-DPROTOCOL_SECPLUSV1")
elif config[CONF_PROTOCOL] == PROTOCOL_SECPLUSV2:
cg.add_build_flag("-DPROTOCOL_SECPLUSV2")
elif config[CONF_PROTOCOL] == PROTOCOL_DRYCONTACT:
cg.add_build_flag("-DPROTOCOL_DRYCONTACT")
cg.add_define("PROTOCOL_SECPLUSV2")
cg.add(var.init_protocol())
# RATGDOComponent::setup() subscribes to door_state
@@ -241,10 +176,3 @@ async def to_code(config):
# Emit observable subscriber count defines after all children register
CORE.add_job(_emit_subscriber_defines)
if config.get(CONF_DISCRETE_OPEN_PIN):
pin = await cg.gpio_pin_expression(config[CONF_DISCRETE_OPEN_PIN])
cg.add(var.set_discrete_open_pin(pin))
if config.get(CONF_DISCRETE_CLOSE_PIN):
pin = await cg.gpio_pin_expression(config[CONF_DISCRETE_CLOSE_PIN])
cg.add(var.set_discrete_close_pin(pin))
@@ -24,7 +24,6 @@ SensorType = ratgdo_ns.enum("SensorType")
CONF_TYPE = "type"
TYPES = {
"button": SensorType.RATGDO_SENSOR_BUTTON,
"vehicle_detected": SensorType.RATGDO_SENSOR_VEHICLE_DETECTED,
"vehicle_arriving": SensorType.RATGDO_SENSOR_VEHICLE_ARRIVING,
"vehicle_leaving": SensorType.RATGDO_SENSOR_VEHICLE_LEAVING,
@@ -12,11 +12,6 @@ void RATGDOBinarySensor::setup()
this->publish_initial_state(false);
switch (this->binary_sensor_type_) {
case SensorType::RATGDO_SENSOR_BUTTON:
this->parent_->subscribe_button_state([this](ButtonState state) {
this->publish_state(state == ButtonState::PRESSED);
});
break;
#ifdef RATGDO_USE_VEHICLE_SENSORS
case SensorType::RATGDO_SENSOR_VEHICLE_DETECTED:
this->parent_->subscribe_vehicle_detected_state([this](VehicleDetectedState state) {
@@ -44,9 +39,6 @@ void RATGDOBinarySensor::dump_config()
{
LOG_BINARY_SENSOR("", "RATGDO BinarySensor", this);
switch (this->binary_sensor_type_) {
case SensorType::RATGDO_SENSOR_BUTTON:
ESP_LOGCONFIG(TAG, " Type: Button");
break;
#ifdef RATGDO_USE_VEHICLE_SENSORS
case SensorType::RATGDO_SENSOR_VEHICLE_DETECTED:
ESP_LOGCONFIG(TAG, " Type: VehicleDetected");
@@ -9,9 +9,8 @@
namespace esphome::ratgdo {
enum SensorType : uint8_t {
RATGDO_SENSOR_BUTTON = 3,
#ifdef RATGDO_USE_VEHICLE_SENSORS
RATGDO_SENSOR_VEHICLE_DETECTED,
RATGDO_SENSOR_VEHICLE_DETECTED = 4,
RATGDO_SENSOR_VEHICLE_ARRIVING,
RATGDO_SENSOR_VEHICLE_LEAVING,
#endif
-6
View File
@@ -90,12 +90,6 @@ namespace protocol {
virtual void sync();
// dry contact methods
virtual void set_open_limit(bool);
virtual void set_close_limit(bool);
virtual void set_discrete_open_pin(InternalGPIOPin* pin);
virtual void set_discrete_close_pin(InternalGPIOPin* pin);
virtual const Traits& traits() const;
virtual void light_action(LightAction action);
+1 -88
View File
@@ -15,15 +15,7 @@
#include "common.h"
#include "ratgdo_state.h"
#ifdef PROTOCOL_DRYCONTACT
#include "dry_contact.h"
#endif
#ifdef PROTOCOL_SECPLUSV1
#include "secplus1.h"
#endif
#ifdef PROTOCOL_SECPLUSV2
#include "secplus2.h"
#endif
#include "esphome/core/application.h"
#include "esphome/core/gpio.h"
@@ -80,40 +72,13 @@ void RATGDOComponent::setup()
ESP_LOGD(TAG, "| -| | | | | | | | | | |");
ESP_LOGD(TAG, "|__|__|__|__| |_| |_____|____/|_____|");
ESP_LOGD(TAG, "https://paulwieland.github.io/ratgdo/");
this->subscribe_door_state([this](DoorState state, float position) {
#ifdef RATGDO_USE_VEHICLE_SENSORS
if (this->last_door_state_for_presence_ != DoorState::UNKNOWN && state != DoorState::CLOSED && !this->flags_.presence_detect_window_active) {
this->flags_.presence_detect_window_active = true;
this->set_timeout(
TIMEOUT_PRESENCE_DETECT_WINDOW, PRESENCE_DETECT_WINDOW,
[this] { this->flags_.presence_detect_window_active = false; });
}
if (state == DoorState::CLOSED) {
this->set_timeout(
TIMEOUT_PRESENCE_DETECT_WINDOW, PRESENCE_DETECT_WINDOW_AFTER_CLOSE,
[this] { this->flags_.presence_detect_window_active = false; });
}
this->last_door_state_for_presence_ = state;
#endif
});
}
// initializing protocol, this gets called before setup() because
// its children components might require that
void RATGDOComponent::init_protocol()
{
#ifdef PROTOCOL_SECPLUSV2
this->protocol_ = new secplus2::Secplus2();
#endif
#ifdef PROTOCOL_SECPLUSV1
this->protocol_ = new secplus1::Secplus1();
#endif
#ifdef PROTOCOL_DRYCONTACT
this->protocol_ = new dry_contact::DryContact();
#endif
}
void RATGDOComponent::loop()
@@ -239,13 +204,6 @@ void RATGDOComponent::received(const LockState lock_state)
this->lock_state = lock_state;
}
void RATGDOComponent::received(const ButtonState button_state)
{
ESP_LOGD(TAG, "Button state=%s",
LOG_STR_ARG(ButtonState_to_string(*this->button_state)));
this->button_state = button_state;
}
void RATGDOComponent::received(const LightAction light_action)
{
ESP_LOGD(TAG, "Light cmd=%s state=%s",
@@ -270,17 +228,6 @@ void RATGDOComponent::received(const Openings openings)
}
}
void RATGDOComponent::received(const TimeToClose ttc)
{
ESP_LOGD(TAG, "Time to close (TTC): %ds", ttc.seconds);
}
void RATGDOComponent::received(const BatteryState battery_state)
{
ESP_LOGD(TAG, "Battery state=%s",
LOG_STR_ARG(BatteryState_to_string(battery_state)));
}
void RATGDOComponent::schedule_door_position_sync(float update_period)
{
ESP_LOG1(
@@ -423,14 +370,6 @@ void RATGDOComponent::query_openings()
void RATGDOComponent::sync()
{
this->protocol_->sync();
// dry contact protocol:
// needed to trigger the intial state of the limit switch sensors
// ideally this would be in drycontact::sync
#ifdef PROTOCOL_DRYCONTACT
this->protocol_->set_open_limit(this->dry_contact_open_sensor_->state);
this->protocol_->set_close_limit(this->dry_contact_close_sensor_->state);
#endif
}
void RATGDOComponent::set_door_state_expiry()
@@ -488,12 +427,7 @@ void RATGDOComponent::door_close()
return;
}
if (this->flags_.obstruction_sensor_detected) {
this->door_action(DoorAction::CLOSE);
} else if (*this->door_state == DoorState::OPEN) {
ESP_LOGD(TAG, "No obstruction sensors detected. Close using TOGGLE.");
this->door_action(DoorAction::TOGGLE);
}
this->door_action(DoorAction::CLOSE);
if (*this->closing_duration > 0) {
// query state in case we don't get a status message
@@ -628,25 +562,4 @@ void RATGDOComponent::lock_toggle()
// Subscribe implementations are now templates in ratgdo.h
// dry contact methods
void RATGDOComponent::set_dry_contact_open_sensor(
esphome::binary_sensor::BinarySensor* dry_contact_open_sensor)
{
dry_contact_open_sensor_ = dry_contact_open_sensor;
dry_contact_open_sensor_->add_on_state_callback([this](bool sensor_value) {
this->protocol_->set_open_limit(sensor_value);
this->door_position = 1.0;
});
}
void RATGDOComponent::set_dry_contact_close_sensor(
esphome::binary_sensor::BinarySensor* dry_contact_close_sensor)
{
dry_contact_close_sensor_ = dry_contact_close_sensor;
dry_contact_close_sensor_->add_on_state_callback([this](bool sensor_value) {
this->protocol_->set_close_limit(sensor_value);
this->door_position = 0.0;
});
}
} // namespace esphome::ratgdo
+2 -47
View File
@@ -62,15 +62,6 @@ typedef Parented<RATGDOComponent> RATGDOClient;
const float DOOR_POSITION_UNKNOWN = -1.0;
const float DOOR_DELTA_UNKNOWN = -2.0;
struct RATGDOStore {
volatile uint32_t obstruction_low_count = 0; // count obstruction low pulses
static void IRAM_ATTR HOT isr_obstruction(RATGDOStore* arg)
{
arg->obstruction_low_count++;
}
};
using protocol::Args;
using protocol::Result;
@@ -114,7 +105,6 @@ public:
single_observable<LightState> light_state { LightState::UNKNOWN };
single_observable<LockState> lock_state { LockState::UNKNOWN };
single_observable<ButtonState> button_state { ButtonState::UNKNOWN };
#ifdef RATGDO_USE_VEHICLE_SENSORS
observable<VehicleDetectedState, RATGDO_MAX_VEHICLE_DETECTED_SUBSCRIBERS> vehicle_detected_state { VehicleDetectedState::NO };
observable<VehicleArrivingState, RATGDO_MAX_VEHICLE_ARRIVING_SUBSCRIBERS> vehicle_arriving_state { VehicleArrivingState::NO };
@@ -127,14 +117,6 @@ public:
void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; }
void set_input_gdo_pin(InternalGPIOPin* pin) { this->input_gdo_pin_ = pin; }
void set_input_obst_pin(InternalGPIOPin* pin) { this->input_obst_pin_ = pin; }
void set_obst_sleep_low(bool low) { this->flags_.obst_sleep_low = low; }
// dry contact methods
void set_dry_contact_open_sensor(esphome::binary_sensor::BinarySensor* dry_contact_open_sensor_);
void set_dry_contact_close_sensor(esphome::binary_sensor::BinarySensor* dry_contact_close_sensor_);
void set_discrete_open_pin(InternalGPIOPin* pin) { this->protocol_->set_discrete_open_pin(pin); }
void set_discrete_close_pin(InternalGPIOPin* pin) { this->protocol_->set_discrete_close_pin(pin); }
Result call_protocol(Args args);
@@ -142,10 +124,7 @@ public:
void received(const LightState light_state);
void received(const LockState lock_state);
void received(const LightAction light_action);
void received(const ButtonState button_state);
void received(const Openings openings);
void received(const TimeToClose ttc);
void received(const BatteryState pdc);
// door
void door_toggle();
@@ -154,7 +133,6 @@ public:
void door_stop();
void door_action(DoorAction action);
void ensure_door_action(DoorAction action, uint32_t delay = 1500);
void door_move_to_position(float position);
void set_door_position(float door_position) { this->door_position = door_position; }
void set_opening_duration(float duration);
@@ -254,8 +232,6 @@ public:
template <typename F>
void subscribe_lock_state(F&& f);
template <typename F>
void subscribe_button_state(F&& f);
template <typename F>
void subscribe_sync_failed(F&& f);
template <typename F>
void subscribe_door_action_delayed(F&& f);
@@ -277,22 +253,14 @@ protected:
protocol::Protocol* protocol_;
InternalGPIOPin* output_gdo_pin_;
InternalGPIOPin* input_gdo_pin_;
InternalGPIOPin* input_obst_pin_;
esphome::binary_sensor::BinarySensor* dry_contact_open_sensor_;
esphome::binary_sensor::BinarySensor* dry_contact_close_sensor_;
// 4-byte members
RATGDOStore isr_store_ { };
// Bool members packed into bitfield
struct {
uint8_t obstruction_sensor_detected : 1;
uint8_t obst_sleep_low : 1;
#ifdef RATGDO_USE_VEHICLE_SENSORS
uint8_t presence_detect_window_active : 1;
uint8_t reserved : 5; // Reserved for future use
uint8_t reserved : 7; // Reserved for future use
#else
uint8_t reserved : 6; // Reserved for future use
uint8_t reserved : 8; // Reserved for future use
#endif
} flags_ { 0 };
@@ -368,19 +336,14 @@ namespace scheduler_ids {
DEFER_OPENINGS,
DEFER_LIGHT_STATE,
DEFER_LOCK_STATE,
DEFER_BUTTON_STATE,
// Named timeout IDs (replacing string-based names)
TIMEOUT_DOOR_QUERY_STATE,
TIMEOUT_DOOR_ACTION,
TIMEOUT_MOVE_TO_POSITION,
// Shared by RATGDOComponent and Secplus1 — safe because only one
// protocol is compiled at a time (#ifdef PROTOCOL_SECPLUSV1) and
// both use ratgdo_ as the scheduler owner.
TIMEOUT_DOOR_STATE_EXPIRY,
TIMEOUT_PRESENCE_DETECT_WINDOW,
TIMEOUT_CLEAR_PRESENCE,
TIMEOUT_WALL_PANEL_EMULATION,
TIMEOUT_SYNC,
};
} // namespace scheduler_ids
@@ -466,14 +429,6 @@ void RATGDOComponent::subscribe_lock_state(F&& f)
});
}
template <typename F>
void RATGDOComponent::subscribe_button_state(F&& f)
{
this->button_state.subscribe([this, f](ButtonState state) {
defer(scheduler_ids::DEFER_BUTTON_STATE, [f, state] { f(state); });
});
}
template <typename F>
void RATGDOComponent::subscribe_sync_failed(F&& f)
{
-15
View File
@@ -44,17 +44,6 @@ ENUM(LockState, uint8_t,
(UNKNOWN, 2))
LockState lock_state_toggle(LockState state);
/// Enum for all states the button can be in.
ENUM(ButtonState, uint8_t,
(PRESSED, 0),
(RELEASED, 1),
(UNKNOWN, 2))
ENUM_SPARSE(BatteryState, uint8_t,
(UNKNOWN, 0),
(CHARGING, 0x6),
(FULL, 0x8))
// actions
ENUM(LightAction, uint8_t,
(OFF, 0),
@@ -94,8 +83,4 @@ struct Openings {
uint8_t flag;
};
struct TimeToClose {
uint16_t seconds;
};
} // namespace esphome::ratgdo
-11
View File
@@ -1,6 +1,4 @@
#ifdef PROTOCOL_SECPLUSV2
#include "secplus2.h"
#include "ratgdo.h"
@@ -281,15 +279,8 @@ namespace secplus2 {
this->ratgdo_->received(to_LockState((cmd.byte2 & 1), LockState::UNKNOWN));
} else if (cmd.type == CommandType::LIGHT) {
this->ratgdo_->received(to_LightAction(cmd.nibble, LightAction::UNKNOWN));
} else if (cmd.type == CommandType::DOOR_ACTION) {
auto button_state = (cmd.byte1 & 1) == 1 ? ButtonState::PRESSED : ButtonState::RELEASED;
this->ratgdo_->received(button_state);
} else if (cmd.type == CommandType::OPENINGS) {
this->ratgdo_->received(Openings { static_cast<uint16_t>((cmd.byte1 << 8) | cmd.byte2), cmd.nibble });
} else if (cmd.type == CommandType::SET_TTC) {
this->ratgdo_->received(TimeToClose { static_cast<uint16_t>((cmd.byte1 << 8) | cmd.byte2) });
} else if (cmd.type == CommandType::BATTERY_STATUS) {
this->ratgdo_->received(to_BatteryState(cmd.byte1, BatteryState::UNKNOWN));
}
ESP_LOG1(TAG, "Done handle command: %s", LOG_STR_ARG(CommandType_to_string(cmd.type)));
@@ -377,5 +368,3 @@ namespace secplus2 {
} // namespace secplus2
} // namespace esphome::ratgdo
#endif // PROTOCOL_SECPLUSV2
-31
View File
@@ -1,7 +1,5 @@
#pragma once
#ifdef PROTOCOL_SECPLUSV2
#include "esphome/core/optional.h"
#include "ratgdo_uart.h"
@@ -32,32 +30,11 @@ namespace secplus2 {
(UNKNOWN, 0x000),
(GET_STATUS, 0x080),
(STATUS, 0x081),
(OBST_1, 0x084), // sent when an obstruction happens?
(OBST_2, 0x085), // sent when an obstruction happens?
(BATTERY_STATUS, 0x09d),
(PAIR_3, 0x0a0),
(PAIR_3_RESP, 0x0a1),
(LEARN, 0x181),
(LOCK, 0x18c),
(DOOR_ACTION, 0x280),
(LIGHT, 0x281),
(MOTOR_ON, 0x284),
(MOTION, 0x285),
(GET_PAIRED_DEVICES, 0x307), // nibble 0 for total, 1 wireless, 2 keypads, 3 wall, 4 accessories.
(PAIRED_DEVICES, 0x308), // byte2 holds number of paired devices
(CLEAR_PAIRED_DEVICES, 0x30D), // nibble 0 to clear remotes, 1 keypads, 2 wall, 3 accessories (offset from above)
(LEARN_1, 0x391),
(PING, 0x392),
(PING_RESP, 0x393),
(PAIR_2, 0x400),
(PAIR_2_RESP, 0x401),
(SET_TTC, 0x402), // ttc_in_seconds = (byte1<<8)+byte2
(CANCEL_TTC, 0x408), // ?
(TTC, 0x40a), // Time to close
(GET_OPENINGS, 0x48b),
(OPENINGS, 0x48c), // openings = (byte1<<8)+byte2
)
@@ -106,12 +83,6 @@ namespace secplus2 {
const Traits& traits() const { return this->traits_; }
// methods not used by secplus2
void set_open_limit(bool state) { }
void set_close_limit(bool state) { }
void set_discrete_open_pin(InternalGPIOPin* pin) { }
void set_discrete_close_pin(InternalGPIOPin* pin) { }
protected:
void increment_rolling_code_counter(int delta = 1);
void set_rolling_code_counter(uint32_t counter);
@@ -185,5 +156,3 @@ namespace secplus2 {
};
} // namespace secplus2
} // namespace esphome::ratgdo
#endif // PROTOCOL_SECPLUSV2