diff --git a/components/secplus_gdo/binary_sensor/__init__.py b/components/secplus_gdo/binary_sensor/__init__.py index 19bac01..a285b92 100644 --- a/components/secplus_gdo/binary_sensor/__init__.py +++ b/components/secplus_gdo/binary_sensor/__init__.py @@ -36,6 +36,7 @@ "obstruction": "register_obstruction", "motor": "register_motor", "button": "register_button", + "sync": "register_sync", } diff --git a/components/secplus_gdo/cover/gdo_door.cpp b/components/secplus_gdo/cover/gdo_door.cpp index a8dcdaa..4717683 100644 --- a/components/secplus_gdo/cover/gdo_door.cpp +++ b/components/secplus_gdo/cover/gdo_door.cpp @@ -23,6 +23,12 @@ void GDODoor::set_state(gdo_door_state_t state, float position) { } } + if (this->state_ == state && this->position == position) { + return; + } + + ESP_LOGI(TAG, "Door state: %s, position: %.0f%%", gdo_door_state_to_string(state), position * 100.0f); + switch (state) { case GDO_DOOR_STATE_OPEN: this->position = COVER_OPEN; @@ -141,6 +147,11 @@ void GDODoor::do_action(const cover::CoverCall& call) { } void GDODoor::control(const cover::CoverCall& call) { + if (!this->synced_) { + this->publish_state(false); + return; + } + if (call.get_stop()) { ESP_LOGD(TAG, "Stop command received"); if (this->pre_close_active_) { diff --git a/components/secplus_gdo/cover/gdo_door.h b/components/secplus_gdo/cover/gdo_door.h index 3f23580..7522670 100644 --- a/components/secplus_gdo/cover/gdo_door.h +++ b/components/secplus_gdo/cover/gdo_door.h @@ -44,6 +44,10 @@ using namespace esphome::cover; this->pre_close_end_trigger = trigger; } + void set_sync_state(bool synced) { + this->synced_ = synced; + } + void do_action(const cover::CoverCall& call); void do_action_after_warning(const cover::CoverCall& call); void set_pre_close_warning_duration(uint32_t ms) { this->pre_close_duration_ = ms; } @@ -61,6 +65,7 @@ using namespace esphome::cover; optional target_position_{0}; CoverOperation prev_operation{COVER_OPERATION_IDLE}; gdo_door_state_t state_{GDO_DOOR_STATE_MAX}; + bool synced_{false}; }; } // namespace secplus_gdo } // namespace esphome diff --git a/components/secplus_gdo/light/__init__.py b/components/secplus_gdo/light/__init__.py index 85f7910..7708659 100644 --- a/components/secplus_gdo/light/__init__.py +++ b/components/secplus_gdo/light/__init__.py @@ -41,5 +41,4 @@ async def to_code(config): await cg.register_component(var, config) await light.register_light(var, config) parent = await cg.get_variable(config[CONF_SECPLUS_GDO_ID]) - text = "std::bind(&" + str(GDOLight) + "::set_state," + str(config[CONF_OUTPUT_ID]) + ",std::placeholders::_1)" - cg.add(parent.register_light(cg.RawExpression(text))) \ No newline at end of file + cg.add(parent.register_light(var)) diff --git a/components/secplus_gdo/light/gdo_light.h b/components/secplus_gdo/light/gdo_light.h index 9431610..aa85d3b 100644 --- a/components/secplus_gdo/light/gdo_light.h +++ b/components/secplus_gdo/light/gdo_light.h @@ -29,6 +29,10 @@ class GDOLight : public binary::BinaryLightOutput, public Component { void setup_state(light::LightState *state) override { this->state_ = state; } void write_state(light::LightState *state) override { + if (!this->synced_) { + return; + } + bool binary; state->current_values_as_binary(&binary); if (binary) @@ -38,14 +42,27 @@ class GDOLight : public binary::BinaryLightOutput, public Component { } void set_state(gdo_light_state_t state) { + if (state == this->light_state_) { + return; + } + + this->light_state_ = state; + ESP_LOGI(TAG, "Light state: %s", gdo_light_state_to_string(state)); bool is_on = state == GDO_LIGHT_STATE_ON; this->state_->current_values.set_state(is_on); this->state_->remote_values.set_state(is_on); this->state_->publish_state(); } + void set_sync_state(bool synced) { + this->synced_ = synced; + } + private: light::LightState *state_{nullptr}; + gdo_light_state_t light_state_{GDO_LIGHT_STATE_MAX}; + static constexpr auto TAG{"GDOLight"}; + bool synced_{false}; }; // GDOLight } // namespace secplus_gdo } // namespace esphome diff --git a/components/secplus_gdo/lock/__init__.py b/components/secplus_gdo/lock/__init__.py index b836923..f2b2583 100644 --- a/components/secplus_gdo/lock/__init__.py +++ b/components/secplus_gdo/lock/__init__.py @@ -41,5 +41,4 @@ async def to_code(config): await lock.register_lock(var, config) await cg.register_component(var, config) parent = await cg.get_variable(config[CONF_SECPLUS_GDO_ID]) - text = "std::bind(&" + str(GDOLock) + "::set_state," + str(config[CONF_ID]) + ",std::placeholders::_1)" - cg.add(parent.register_lock(cg.RawExpression(text))) + cg.add(parent.register_lock(var)) diff --git a/components/secplus_gdo/lock/gdo_lock.h b/components/secplus_gdo/lock/gdo_lock.h index 39d998a..ce58f85 100644 --- a/components/secplus_gdo/lock/gdo_lock.h +++ b/components/secplus_gdo/lock/gdo_lock.h @@ -27,23 +27,22 @@ namespace secplus_gdo { class GDOLock : public lock::Lock, public Component { public: void set_state(gdo_lock_state_t state) { - if (state == GDO_LOCK_STATE_LOCKED && this->state == lock::LockState::LOCK_STATE_LOCKED) { - return; - } - if (state == GDO_LOCK_STATE_UNLOCKED && this->state == lock::LockState::LOCK_STATE_UNLOCKED) { + if (state == this->lock_state_) { return; } - auto call = this->make_call(); - if (state == GDO_LOCK_STATE_LOCKED) { - call.set_state(lock::LockState::LOCK_STATE_LOCKED); - } else if (state == GDO_LOCK_STATE_UNLOCKED) { - call.set_state(lock::LockState::LOCK_STATE_UNLOCKED); - } - this->control(call); + this->lock_state_ = state; + ESP_LOGI(TAG, "Lock state: %s", gdo_lock_state_to_string(state)); + this->publish_state(state == GDO_LOCK_STATE_LOCKED ? + lock::LockState::LOCK_STATE_LOCKED : + lock::LockState::LOCK_STATE_UNLOCKED); } void control(const lock::LockCall& call) override { + if (!this->synced_) { + return; + } + auto state = *call.get_state(); if (state == lock::LockState::LOCK_STATE_LOCKED) { @@ -51,9 +50,16 @@ namespace secplus_gdo { } else if (state == lock::LockState::LOCK_STATE_UNLOCKED) { gdo_unlock(); } + } - this->publish_state(state); + void set_sync_state(bool synced) { + this->synced_ = synced; } + + private: + gdo_lock_state_t lock_state_{GDO_LOCK_STATE_MAX}; + bool synced_{false}; + static constexpr const char* TAG = "GDOLock"; }; } // namespace secplus_gdo diff --git a/components/secplus_gdo/secplus_gdo.cpp b/components/secplus_gdo/secplus_gdo.cpp index c0d8566..a11ad93 100644 --- a/components/secplus_gdo/secplus_gdo.cpp +++ b/components/secplus_gdo/secplus_gdo.cpp @@ -32,8 +32,11 @@ namespace secplus_gdo { ESP_LOGI(TAG, "Synced: %s, protocol: %s", status->synced ? "true" : "false", gdo_protocol_type_to_string(status->protocol)); if (status->protocol == GDO_PROTOCOL_SEC_PLUS_V2) { ESP_LOGI(TAG, "Client ID: %" PRIu32 ", Rolling code: %" PRIu32, status->client_id, status->rolling_code); - gdo->set_client_id(status->client_id); - gdo->set_rolling_code(status->rolling_code); + if (status->synced) { + // Save the last successful ClientID rolling code value to NVS for use on reboot + gdo->set_client_id(status->client_id); + gdo->set_rolling_code(status->rolling_code); + } } if (!status->synced) { @@ -47,20 +50,16 @@ namespace secplus_gdo { gdo->set_protocol_state(status->protocol); } + gdo->set_sync_state(status->synced); break; case GDO_CB_EVENT_LIGHT: - ESP_LOGI(TAG, "Light: %s", gdo_light_state_to_string(status->light)); gdo->set_light_state(status->light); break; case GDO_CB_EVENT_LOCK: - ESP_LOGI(TAG, "Lock: %s", gdo_lock_state_to_string(status->lock)); gdo->set_lock_state(status->lock); break; case GDO_CB_EVENT_DOOR_POSITION: { float position = (float)(10000 - status->door_position)/10000.0f; - float target = (float)(10000 - status->door_target)/10000.0f; - ESP_LOGI(TAG, "Door: %s, %.0f%%, target: %.0f%%", gdo_door_state_to_string(status->door), - position*100, target*100); gdo->set_door_state(status->door, position); if (status->door != GDO_DOOR_STATE_OPENING && status->door != GDO_DOOR_STATE_CLOSING) { gdo->set_motor_state(GDO_MOTOR_STATE_OFF); @@ -68,7 +67,7 @@ namespace secplus_gdo { break; } case GDO_CB_EVENT_LEARN: - ESP_LOGI(TAG, "Learn: %s", gdo_learn_state_to_string(status->learn)); + //ESP_LOGI(TAG, "Learn: %s", gdo_learn_state_to_string(status->learn)); break; case GDO_CB_EVENT_OBSTRUCTION: ESP_LOGI(TAG, "Obstruction: %s", gdo_obstruction_state_to_string(status->obstruction)); @@ -153,5 +152,23 @@ namespace secplus_gdo { ESP_LOGCONFIG(TAG, "Setting up secplus GDO ..."); } + void GDOComponent::set_sync_state(bool synced) { + if (this->door_) { + this->door_->set_sync_state(synced); + } + + if (this->light_) { + this->light_->set_sync_state(synced); + } + + if (this->lock_) { + this->lock_->set_sync_state(synced); + } + + if (this->f_sync) { + this->f_sync(synced); + } + } + } // namespace secplus_gdo } // namespace esphome \ No newline at end of file diff --git a/components/secplus_gdo/secplus_gdo.h b/components/secplus_gdo/secplus_gdo.h index 03f2065..e976b05 100644 --- a/components/secplus_gdo/secplus_gdo.h +++ b/components/secplus_gdo/secplus_gdo.h @@ -23,6 +23,8 @@ #include "select/gdo_select.h" #include "switch/gdo_switch.h" #include "cover/gdo_door.h" +#include "light/gdo_light.h" +#include "lock/gdo_lock.h" #include "gdo.h" namespace esphome { @@ -57,17 +59,19 @@ namespace secplus_gdo { void register_motor(std::function f) { f_motor = f; } void set_motor_state(gdo_motor_state_t state) { if (f_motor) { f_motor(state == GDO_MOTOR_STATE_ON); } } + void register_sync(std::function f) { f_sync = f; } + void register_openings(std::function f) { f_openings = f; } void set_openings(uint16_t openings) { if (f_openings) { f_openings(openings); } } void register_door(GDODoor *door) { this->door_ = door; } void set_door_state(gdo_door_state_t state, float position) { if (this->door_) { this->door_->set_state(state, position); } } - void register_light(std::function f) { f_light = f; } - void set_light_state(gdo_light_state_t state) { if (f_light) { f_light(state); } } + void register_light(GDOLight *light) { this->light_ = light; } + void set_light_state(gdo_light_state_t state) { if (this->light_) { this->light_->set_state(state); } } - void register_lock(std::function f) { f_lock = f; } - void set_lock_state(gdo_lock_state_t state) { if (f_lock) { f_lock(state); } } + void register_lock(GDOLock *lock) { this->lock_ = lock; } + void set_lock_state(gdo_lock_state_t state) { if (this->lock_) { this->lock_->set_state(state); } } void register_learn(GDOSwitch *sw) { this->learn_switch_ = sw; } void set_learn_state(gdo_learn_state_t state) { if (this->learn_switch_) { @@ -88,16 +92,19 @@ namespace secplus_gdo { void register_toggle_only(GDOSwitch *sw) { this->toggle_only_switch_ = sw; } + void set_sync_state(bool synced); + protected: gdo_status_t status_{}; - std::function f_lock{nullptr}; - std::function f_light{nullptr}; std::function f_openings{nullptr}; std::function f_motion{nullptr}; std::function f_obstruction{nullptr}; std::function f_button{nullptr}; std::function f_motor{nullptr}; + std::function f_sync{nullptr}; GDODoor* door_{nullptr}; + GDOLight* light_{nullptr}; + GDOLock* lock_{nullptr}; GDONumber* open_duration_{nullptr}; GDONumber* close_duration_{nullptr}; GDONumber* client_id_{nullptr}; diff --git a/garage-door-GDOv2-Q.yaml b/garage-door-GDOv2-Q.yaml index 2fd6cd5..e6ff1f7 100644 --- a/garage-door-GDOv2-Q.yaml +++ b/garage-door-GDOv2-Q.yaml @@ -57,7 +57,7 @@ substitutions: name: konnected friendly_name: GDO blaQ project_name: konnected.garage-door-gdov2-q - project_version: "1.2.5" + project_version: "1.3.0" garage_door_cover_name: Garage Door garage_light_name: Garage Light garage_openings_name: Garage Openings @@ -66,6 +66,7 @@ substitutions: garage_obstruction_name: Obstruction garage_motor_name: Motor garage_button_name: Wall Button + garage_sync_name: Synced #### @@ -185,6 +186,6 @@ web_server: esphome: platformio_options: lib_deps: - - https://github.com/konnected-io/gdolib#4e6f493 + - https://github.com/konnected-io/gdolib#76ba232 build_flags: - -DUART_SCLK_DEFAULT=UART_SCLK_APB \ No newline at end of file diff --git a/packages/secplus-gdo.yaml b/packages/secplus-gdo.yaml index a9744ce..c70be3f 100644 --- a/packages/secplus-gdo.yaml +++ b/packages/secplus-gdo.yaml @@ -66,6 +66,12 @@ binary_sensor: id: gdo_button secplus_gdo_id: gdo_blaq type: button + - platform: secplus_gdo + name: $garage_sync_name + id: gdo_synced + secplus_gdo_id: gdo_blaq + type: sync + device_class: connectivity switch: - platform: secplus_gdo