diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96a9f8a..2996155 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,12 @@ jobs: - file: v25board_esp8266_d1_mini_lite_secplusv1.yaml name: V2.5 Board ESP8266 D1 Mini Lite Security+ 1.0 manifest_filename: v25board_esp8266_d1_mini_lite_secplusv1-manifest.json + - file: v25board_esp8266_d1_mini.yaml + name: V2.5 Board ESP8266 D1 Mini Security+ 2.0 + manifest_filename: v25board_esp8266_d1_mini-manifest.json + - file: v25board_esp8266_d1_mini_secplusv1.yaml + name: V2.5 Board ESP8266 D1 Mini Security+ 1.0 + manifest_filename: v25board_esp8266_d1_mini_secplusv1-manifest.json - file: v25board_esp32_d1_mini.yaml name: V2.5 Board ESP32 D1 Mini Security+ 2.0 manifest_filename: v25board_esp32_d1_mini-manifest.json @@ -46,12 +52,15 @@ jobs: - file: v25iboard_secplusv1.yaml name: V2.5i Board Security+ 1.0 manifest_filename: v25iboard_secplusv1-manifest.json + - file: v25iboard_drycontact.yaml + name: V2.5i Board Dry Contact + manifest_filename: v25iboard_drycontact-manifest.json fail-fast: false steps: - name: Checkout source code uses: actions/checkout@v3.3.0 - name: Build firmware - uses: esphome/build-action@v1.8.0 + uses: ratgdo/esphome-build-action@main id: esphome-build with: yaml_file: ${{ matrix.firmware.file }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 596558e..b1339ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,3 +7,12 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - id: check-added-large-files +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: v18.1.8 + hooks: + - id: clang-format + types_or: + - "c++" + - "c" + - "cuda" + args: [-style=Webkit, -i] diff --git a/README.md b/README.md index 9d87b6f..f71baa5 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ The ESPHome firmware will allow you to open the door to any position after calib - [Security+ 2.0 for v2.0 board with ESP8266 D1 Mini lite](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v2board_esp8266_d1_mini_lite.yaml) - [Security+ 2.0 for v2.0 board with ESP32 D1 Mini](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v2board_esp32_d1_mini.yaml) - [Security+ 2.0 for v2.0 board with ESP32 Lolin D2 Mini](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v2board_esp32_lolin_s2_mini.yaml) +- [Security+ 2.0 for v2.5 board with ESP8266 D1 Mini](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v25board_esp8266_d1_mini.yaml) +- [Security+ 1.0 for v2.5 board with ESP8266 D1 Mini](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v25board_esp8266_d1_mini_secplusv1.yaml) - [Security+ 2.0 for v2.5 board with ESP8266 D1 Mini lite](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v25board_esp8266_d1_mini_lite.yaml) - [Security+ 1.0 for v2.5 board with ESP8266 D1 Mini lite](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v25board_esp8266_d1_mini_lite_secplusv1.yaml) - [Security+ 2.0 for v2.5 board with ESP32 D1 Mini](https://github.com/RATGDO/esphome-ratgdo/blob/main/static/v25board_esp32_d1_mini.yaml) diff --git a/base.yaml b/base.yaml index fc6d0ba..5fccf26 100644 --- a/base.yaml +++ b/base.yaml @@ -6,9 +6,15 @@ external_components: url: https://github.com/ratgdo/esphome-ratgdo refresh: 1s +safe_mode: + preferences: flash_write_interval: 1min +text_sensor: + - platform: version + name: "Firmware Version" + ratgdo: id: ${id_prefix} input_gdo_pin: ${uart_rx_pin} @@ -41,6 +47,10 @@ api: id($id_prefix).clear_paired_devices(ratgdo::PairedDevice::ACCESSORY); } +ota: + - platform: esphome + id: ratgdo_ota + sensor: - platform: ratgdo id: ${id_prefix}_openings @@ -69,7 +79,7 @@ switch: id: "${id_prefix}_status_door" internal: true pin: - number: ${status_door_pin} # D0 output door status, HIGH for open, LOW for closed + number: ${status_door_pin} # output door status, HIGH for open, LOW for closed mode: output: true name: "Status door" @@ -78,7 +88,7 @@ switch: id: "${id_prefix}_status_obstruction" internal: true pin: - number: ${status_obstruction_pin} # D8 output for obstruction status, HIGH for obstructed, LOW for clear + number: ${status_obstruction_pin} # output for obstruction status, HIGH for obstructed, LOW for clear mode: output: true name: "Status obstruction" @@ -124,7 +134,7 @@ binary_sensor: - platform: gpio id: "${id_prefix}_dry_contact_open" pin: - number: ${dry_contact_open_pin} # D5 dry contact for opening door + number: ${dry_contact_open_pin} # dry contact for opening door inverted: true mode: input: true @@ -142,7 +152,7 @@ binary_sensor: - platform: gpio id: "${id_prefix}_dry_contact_close" pin: - number: ${dry_contact_close_pin} # D6 dry contact for closing door + number: ${dry_contact_close_pin} # dry contact for closing door inverted: true mode: input: true @@ -160,7 +170,7 @@ binary_sensor: - platform: gpio id: "${id_prefix}_dry_contact_light" pin: - number: ${dry_contact_light_pin} # D3 dry contact for triggering light (no discrete light commands, so toggle only) + number: ${dry_contact_light_pin} # dry contact for triggering light (no discrete light commands, so toggle only) inverted: true mode: input: true @@ -225,8 +235,10 @@ light: button: - platform: restart + id: ${id_prefix}_restart name: "Restart" - platform: safe_mode + id: ${id_prefix}_safe_mode name: "Safe mode boot" entity_category: diagnostic diff --git a/base_drycontact.yaml b/base_drycontact.yaml new file mode 100644 index 0000000..84b172b --- /dev/null +++ b/base_drycontact.yaml @@ -0,0 +1,105 @@ +--- + +external_components: + - source: + # type: local + # path: components + type: git + url: https://github.com/ratgdo/esphome-ratgdo + refresh: 1s + +safe_mode: + +preferences: + flash_write_interval: 1min + +text_sensor: + - platform: version + name: "Firmware Version" + +ratgdo: + id: ${id_prefix} + output_gdo_pin: ${uart_tx_pin} + input_obst_pin: ${input_obst_pin} + dry_contact_open_sensor: ${id_prefix}_dry_contact_open + dry_contact_close_sensor: ${id_prefix}_dry_contact_close + discrete_open_pin: ${discrete_open_pin} + discrete_close_pin: ${discrete_close_pin} + protocol: drycontact + +ota: + - platform: esphome + id: ratgdo_ota + +binary_sensor: + - platform: ratgdo + type: obstruction + id: ${id_prefix}_obstruction + ratgdo_id: ${id_prefix} + name: "Obstruction" + device_class: problem + - platform: gpio + id: "${id_prefix}_dry_contact_open" + pin: + number: ${dry_contact_open_pin} + inverted: true + mode: + input: true + pullup: true + name: "Open limit switch" + entity_category: diagnostic + filters: + - delayed_on_off: 500ms + - platform: gpio + id: "${id_prefix}_dry_contact_close" + pin: + number: ${dry_contact_close_pin} + inverted: true + mode: + input: true + pullup: true + name: "Close limit switch" + entity_category: diagnostic + filters: + - delayed_on_off: 500ms + +number: + - platform: ratgdo + id: ${id_prefix}_opening_duration + type: opening_duration + entity_category: config + ratgdo_id: ${id_prefix} + name: "Opening duration" + unit_of_measurement: "s" + + - platform: ratgdo + id: ${id_prefix}_closing_duration + type: closing_duration + entity_category: config + ratgdo_id: ${id_prefix} + name: "Closing duration" + unit_of_measurement: "s" + +cover: + - platform: ratgdo + id: ${id_prefix}_garage_door + device_class: garage + name: "Door" + ratgdo_id: ${id_prefix} + +button: + - platform: restart + id: ${id_prefix}_restart + name: "Restart" + - platform: safe_mode + id: ${id_prefix}_safe_mode + name: "Safe mode boot" + entity_category: diagnostic + + - platform: template + id: ${id_prefix}_toggle_door + name: "Toggle door" + on_press: + then: + lambda: !lambda |- + id($id_prefix).door_toggle(); diff --git a/base_secplusv1.yaml b/base_secplusv1.yaml index 18a61b6..aafdaa8 100644 --- a/base_secplusv1.yaml +++ b/base_secplusv1.yaml @@ -6,9 +6,15 @@ external_components: url: https://github.com/ratgdo/esphome-ratgdo refresh: 1s +safe_mode: + preferences: flash_write_interval: 1min +text_sensor: + - platform: version + name: "Firmware Version" + ratgdo: id: ${id_prefix} input_gdo_pin: ${uart_rx_pin} @@ -24,6 +30,10 @@ ratgdo: message: "Failed to communicate with garage opener on startup." notification_id: "esphome_ratgdo_${id_prefix}_sync_failed" +ota: + - platform: esphome + id: ratgdo_ota + lock: - platform: ratgdo id: ${id_prefix}_lock_remotes @@ -177,8 +187,10 @@ light: button: - platform: restart + id: ${id_prefix}_restart name: "Restart" - platform: safe_mode + id: ${id_prefix}_safe_mode name: "Safe mode boot" entity_category: diagnostic diff --git a/components/ratgdo/__init__.py b/components/ratgdo/__init__.py index 6b52ddc..5153cad 100644 --- a/components/ratgdo/__init__.py +++ b/components/ratgdo/__init__.py @@ -3,6 +3,7 @@ import esphome.config_validation as cv import voluptuous as vol from esphome import automation, pins from esphome.const import CONF_ID, CONF_TRIGGER_ID +from esphome.components import binary_sensor DEPENDENCIES = ["preferences"] MULTI_CONF = True @@ -25,6 +26,9 @@ DEFAULT_INPUT_GDO = ( CONF_INPUT_OBST = "input_obst_pin" DEFAULT_INPUT_OBST = "D7" # D7 black obstruction sensor terminal +CONF_DISCRETE_OPEN_PIN = "discrete_open_pin" +CONF_DISCRETE_CLOSE_PIN = "discrete_close_pin" + CONF_RATGDO_ID = "ratgdo_id" CONF_ON_SYNC_FAILED = "on_sync_failed" @@ -36,7 +40,21 @@ PROTOCOL_SECPLUSV2 = "secplusv2" PROTOCOL_DRYCONTACT = "drycontact" SUPPORTED_PROTOCOLS = [PROTOCOL_SECPLUSV1, PROTOCOL_SECPLUSV2, PROTOCOL_DRYCONTACT] -CONFIG_SCHEMA = cv.Schema( +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" + +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 + +CONFIG_SCHEMA = cv.All( + cv.Schema( { cv.GenerateID(): cv.declare_id(RATGDO), cv.Optional( @@ -48,16 +66,24 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_INPUT_OBST, default=DEFAULT_INPUT_OBST): cv.Any( cv.none, pins.gpio_input_pin_schema ), + 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), } ), - cv.Optional(CONF_PROTOCOL, default=PROTOCOL_SECPLUSV2): vol.In( + 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) + ).extend(cv.COMPONENT_SCHEMA), + validate_protocol, +) RATGDO_CLIENT_SCHMEA = cv.Schema( { @@ -82,6 +108,14 @@ async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_INPUT_OBST]) cg.add(var.set_input_obst_pin(pin)) + if CONF_DRY_CONTACT_OPEN_SENSOR in config and config[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 CONF_DRY_CONTACT_CLOSE_SENSOR in config and config[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) await automation.build_automation(trigger, [], conf) @@ -104,3 +138,10 @@ async def to_code(config): elif config[CONF_PROTOCOL] == PROTOCOL_DRYCONTACT: cg.add_define("PROTOCOL_DRYCONTACT") cg.add(var.init_protocol()) + + if CONF_DISCRETE_OPEN_PIN in config and config[CONF_DISCRETE_OPEN_PIN]: + pin = await cg.gpio_pin_expression(config[CONF_DISCRETE_OPEN_PIN]) + cg.add(var.set_discrete_open_pin(pin)) + if CONF_DISCRETE_CLOSE_PIN in config and config[CONF_DISCRETE_CLOSE_PIN]: + pin = await cg.gpio_pin_expression(config[CONF_DISCRETE_CLOSE_PIN]) + cg.add(var.set_discrete_close_pin(pin)) diff --git a/components/ratgdo/common.h b/components/ratgdo/common.h index e6571b5..3638cf4 100644 --- a/components/ratgdo/common.h +++ b/components/ratgdo/common.h @@ -1,4 +1,4 @@ #pragma once #define ESP_LOG1 ESP_LOGV -#define ESP_LOG2 ESP_LOGV \ No newline at end of file +#define ESP_LOG2 ESP_LOGV diff --git a/components/ratgdo/dry_contact.cpp b/components/ratgdo/dry_contact.cpp index 5dc5a9d..8589a7c 100644 --- a/components/ratgdo/dry_contact.cpp +++ b/components/ratgdo/dry_contact.cpp @@ -2,6 +2,7 @@ #include "dry_contact.h" #include "ratgdo.h" +#include "esphome/components/gpio/binary_sensor/gpio_binary_sensor.h" #include "esphome/core/gpio.h" #include "esphome/core/log.h" #include "esphome/core/scheduler.h" @@ -18,6 +19,12 @@ namespace ratgdo { this->scheduler_ = scheduler; this->tx_pin_ = tx_pin; this->rx_pin_ = rx_pin; + + this->open_limit_reached_ = 0; + this->last_open_limit_ = 0; + this->close_limit_reached_ = 0; + this->last_close_limit_ = 0; + this->door_state_ = DoorState::UNKNOWN; } void DryContact::loop() @@ -31,6 +38,44 @@ namespace ratgdo { void DryContact::sync() { + ESP_LOG1(TAG, "Ignoring sync action"); + } + + void DryContact::set_open_limit(bool state) + { + ESP_LOGD(TAG, "Set open_limit_reached to %d", state); + this->last_open_limit_ = this->open_limit_reached_; + this->last_close_limit_ = false; + this->open_limit_reached_ = state; + this->send_door_state(); + } + + void DryContact::set_close_limit(bool state) + { + ESP_LOGD(TAG, "Set close_limit_reached to %d", state); + this->last_close_limit_ = this->close_limit_reached_; + this->last_open_limit_ = false; + this->close_limit_reached_ = state; + this->send_door_state(); + } + + void DryContact::send_door_state() + { + if (this->open_limit_reached_) { + this->door_state_ = DoorState::OPEN; + } else if (this->close_limit_reached_) { + this->door_state_ = DoorState::CLOSED; + } else if (!this->close_limit_reached_ && !this->open_limit_reached_) { + if (this->last_close_limit_) { + this->door_state_ = DoorState::OPENING; + } + + if (this->last_open_limit_) { + this->door_state_ = DoorState::CLOSING; + } + } + + this->ratgdo_->received(this->door_state_); } void DryContact::light_action(LightAction action) @@ -47,14 +92,33 @@ namespace ratgdo { void DryContact::door_action(DoorAction action) { - if (action != DoorAction::TOGGLE) { - ESP_LOG1(TAG, "Ignoring door action: %s", DoorAction_to_string(action)); + if (action == DoorAction::OPEN && this->door_state_ != DoorState::CLOSED) { + ESP_LOGW(TAG, "The door is not closed. Ignoring door action: %s", DoorAction_to_string(action)); return; } + if (action == DoorAction::CLOSE && this->door_state_ != DoorState::OPEN) { + ESP_LOGW(TAG, "The door is not open. Ignoring door action: %s", DoorAction_to_string(action)); + return; + } + ESP_LOG1(TAG, "Door action: %s", DoorAction_to_string(action)); - this->tx_pin_->digital_write(1); - this->scheduler_->set_timeout(this->ratgdo_, "", 200, [=] { + if (action == DoorAction::OPEN) { + this->discrete_open_pin_->digital_write(1); + this->scheduler_->set_timeout(this->ratgdo_, "", 500, [=] { + this->discrete_open_pin_->digital_write(0); + }); + } + + if (action == DoorAction::CLOSE) { + this->discrete_close_pin_->digital_write(1); + this->scheduler_->set_timeout(this->ratgdo_, "", 500, [=] { + this->discrete_close_pin_->digital_write(0); + }); + } + + this->tx_pin_->digital_write(1); // Single button control + this->scheduler_->set_timeout(this->ratgdo_, "", 500, [=] { this->tx_pin_->digital_write(0); }); } diff --git a/components/ratgdo/dry_contact.h b/components/ratgdo/dry_contact.h index 99dd024..dee045d 100644 --- a/components/ratgdo/dry_contact.h +++ b/components/ratgdo/dry_contact.h @@ -1,6 +1,8 @@ #pragma once #include "SoftwareSerial.h" // Using espsoftwareserial https://github.com/plerup/espsoftwareserial +#include "esphome/components/gpio/binary_sensor/gpio_binary_sensor.h" +#include "esphome/core/gpio.h" #include "esphome/core/optional.h" #include "callbacks.h" @@ -17,6 +19,7 @@ namespace ratgdo { namespace dry_contact { using namespace esphome::ratgdo::protocol; + using namespace esphome::gpio; class DryContact : public Protocol { public: @@ -29,6 +32,23 @@ namespace ratgdo { void light_action(LightAction action); void lock_action(LockAction action); void door_action(DoorAction action); + void set_open_limit(bool state); + void set_close_limit(bool state); + void send_door_state(); + + void set_discrete_open_pin(InternalGPIOPin* pin) + { + this->discrete_open_pin_ = pin; + this->discrete_open_pin_->setup(); + this->discrete_open_pin_->pin_mode(gpio::FLAG_OUTPUT); + } + + void set_discrete_close_pin(InternalGPIOPin* pin) + { + this->discrete_close_pin_ = pin; + this->discrete_close_pin_->setup(); + this->discrete_close_pin_->pin_mode(gpio::FLAG_OUTPUT); + } Result call(Args args); @@ -39,9 +59,17 @@ namespace ratgdo { InternalGPIOPin* tx_pin_; InternalGPIOPin* rx_pin_; + InternalGPIOPin* discrete_open_pin_; + InternalGPIOPin* discrete_close_pin_; RATGDOComponent* ratgdo_; Scheduler* scheduler_; + + DoorState door_state_; + bool open_limit_reached_; + bool last_open_limit_; + bool close_limit_reached_; + bool last_close_limit_; }; } // namespace secplus1 diff --git a/components/ratgdo/protocol.h b/components/ratgdo/protocol.h index 8202079..ca49aeb 100644 --- a/components/ratgdo/protocol.h +++ b/components/ratgdo/protocol.h @@ -103,6 +103,12 @@ namespace ratgdo { 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); diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index f65ae25..af017d3 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -52,6 +52,11 @@ namespace ratgdo { // many things happening at startup, use some delay for sync set_timeout(SYNC_DELAY, [=] { this->sync(); }); + ESP_LOGD(TAG, " _____ _____ _____ _____ ____ _____ "); + ESP_LOGD(TAG, "| __ | _ |_ _| __| \\| |"); + ESP_LOGD(TAG, "| -| | | | | | | | | | |"); + ESP_LOGD(TAG, "|__|__|__|__| |_| |_____|____/|_____|"); + ESP_LOGD(TAG, "https://paulwieland.github.io/ratgdo/"); } // initializing protocol, this gets called before setup() because @@ -418,6 +423,14 @@ namespace ratgdo { 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::door_open() @@ -673,5 +686,22 @@ namespace ratgdo { this->learn_state.subscribe([=](LearnState state) { defer("learn_state", [=] { f(state); }); }); } + // dry contact methods + void RATGDOComponent::set_dry_contact_open_sensor(esphome::gpio::GPIOBinarySensor* 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); + }); + } + + void RATGDOComponent::set_dry_contact_close_sensor(esphome::gpio::GPIOBinarySensor* 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); + }); + } + } // namespace ratgdo } // namespace esphome diff --git a/components/ratgdo/ratgdo.h b/components/ratgdo/ratgdo.h index 510f636..fe7da6e 100644 --- a/components/ratgdo/ratgdo.h +++ b/components/ratgdo/ratgdo.h @@ -13,6 +13,7 @@ #pragma once +#include "esphome/components/gpio/binary_sensor/gpio_binary_sensor.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/core/preferences.h" @@ -91,6 +92,12 @@ namespace ratgdo { void set_input_gdo_pin(InternalGPIOPin* pin) { this->input_gdo_pin_ = pin; } void set_input_obst_pin(InternalGPIOPin* pin) { this->input_obst_pin_ = pin; } + // dry contact methods + void set_dry_contact_open_sensor(esphome::gpio::GPIOBinarySensor* dry_contact_open_sensor_); + void set_dry_contact_close_sensor(esphome::gpio::GPIOBinarySensor* 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); void received(const DoorState door_state); @@ -174,6 +181,8 @@ namespace ratgdo { InternalGPIOPin* output_gdo_pin_; InternalGPIOPin* input_gdo_pin_; InternalGPIOPin* input_obst_pin_; + esphome::gpio::GPIOBinarySensor* dry_contact_open_sensor_; + esphome::gpio::GPIOBinarySensor* dry_contact_close_sensor_; }; // RATGDOComponent } // namespace ratgdo diff --git a/components/ratgdo/secplus1.h b/components/ratgdo/secplus1.h index 42d83fd..76e8dc9 100644 --- a/components/ratgdo/secplus1.h +++ b/components/ratgdo/secplus1.h @@ -96,6 +96,12 @@ namespace ratgdo { const Traits& traits() const { return this->traits_; } + // methods not used by secplus1 + 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 wall_panel_emulation(size_t index = 0); diff --git a/components/ratgdo/secplus2.h b/components/ratgdo/secplus2.h index a30747d..bb79d91 100644 --- a/components/ratgdo/secplus2.h +++ b/components/ratgdo/secplus2.h @@ -101,6 +101,12 @@ namespace ratgdo { 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); diff --git a/static/index.html b/static/index.html index d57158d..77e016c 100644 --- a/static/index.html +++ b/static/index.html @@ -202,8 +202,8 @@ @@ -285,34 +285,31 @@