From eee27761b222b72933b0cd920c84f06890185903 Mon Sep 17 00:00:00 2001 From: Marius Muja Date: Sat, 1 Jul 2023 07:13:38 -0700 Subject: [PATCH] Refactor children state update mechanism (#24) Co-authored-by: J. Nick Koston --- base.yaml | 1 + components/ratgdo/__init__.py | 2 +- .../binary_sensor/ratgdo_binary_sensor.cpp | 42 ++- .../binary_sensor/ratgdo_binary_sensor.h | 6 - components/ratgdo/cover/ratgdo_cover.cpp | 8 + components/ratgdo/cover/ratgdo_cover.h | 5 +- .../ratgdo/light/ratgdo_light_output.cpp | 8 + components/ratgdo/light/ratgdo_light_output.h | 4 +- components/ratgdo/number/ratgdo_number.cpp | 41 +-- components/ratgdo/number/ratgdo_number.h | 6 +- components/ratgdo/observable.h | 46 +++ components/ratgdo/ratgdo.cpp | 299 +++++++----------- components/ratgdo/ratgdo.h | 65 ++-- components/ratgdo/ratgdo_child.cpp | 22 -- components/ratgdo/ratgdo_child.h | 33 -- components/ratgdo/sensor/ratgdo_sensor.cpp | 7 +- components/ratgdo/sensor/ratgdo_sensor.h | 4 +- components/ratgdo/switch/ratgdo_switch.cpp | 7 + components/ratgdo/switch/ratgdo_switch.h | 4 +- static/v2board_esp8266_d1_mini_lite.yaml | 1 + 20 files changed, 270 insertions(+), 341 deletions(-) create mode 100644 components/ratgdo/observable.h delete mode 100644 components/ratgdo/ratgdo_child.cpp delete mode 100644 components/ratgdo/ratgdo_child.h diff --git a/base.yaml b/base.yaml index e1354ad..e2208ab 100644 --- a/base.yaml +++ b/base.yaml @@ -4,6 +4,7 @@ external_components: - source: type: git url: https://github.com/esphome-ratgdo/esphome-ratgdo + ref: observer_callbacks refresh: 1s preferences: diff --git a/components/ratgdo/__init__.py b/components/ratgdo/__init__.py index f59ebcb..4322293 100644 --- a/components/ratgdo/__init__.py +++ b/components/ratgdo/__init__.py @@ -54,7 +54,7 @@ RATGDO_CLIENT_SCHMEA = cv.Schema( async def register_ratgdo_child(var, config): parent = await cg.get_variable(config[CONF_RATGDO_ID]) - cg.add(parent.register_child(var)) + cg.add(var.set_parent(parent)) async def to_code(config): diff --git a/components/ratgdo/binary_sensor/ratgdo_binary_sensor.cpp b/components/ratgdo/binary_sensor/ratgdo_binary_sensor.cpp index 8736e90..1a156af 100644 --- a/components/ratgdo/binary_sensor/ratgdo_binary_sensor.cpp +++ b/components/ratgdo/binary_sensor/ratgdo_binary_sensor.cpp @@ -11,6 +11,24 @@ namespace ratgdo { { if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_MOTION || this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_OBSTRUCTION || this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_BUTTON) this->publish_state(false); + + if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_MOTION) { + this->parent_->subscribe_motion_state([=](MotionState state) { + this->publish_state(state == MotionState::MOTION_STATE_DETECTED); + }); + } else if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_OBSTRUCTION) { + this->parent_->subscribe_obstruction_state([=](ObstructionState state) { + this->publish_state(state == ObstructionState::OBSTRUCTION_STATE_OBSTRUCTED); + }); + } else if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_MOTOR) { + this->parent_->subscribe_motor_state([=](MotorState state) { + this->publish_state(state == MotorState::MOTOR_STATE_ON); + }); + } else if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_BUTTON) { + this->parent_->subscribe_button_state([=](ButtonState state) { + this->publish_state(state == ButtonState::BUTTON_STATE_PRESSED); + }); + } } void RATGDOBinarySensor::dump_config() @@ -26,30 +44,6 @@ namespace ratgdo { ESP_LOGCONFIG(TAG, " Type: Button"); } } - void RATGDOBinarySensor::on_motion_state(MotionState state) - { - if (this->binary_sensor_type_ != SensorType::RATGDO_SENSOR_MOTION) - return; - this->publish_state(state == MotionState::MOTION_STATE_DETECTED); - } - void RATGDOBinarySensor::on_obstruction_state(ObstructionState state) - { - if (this->binary_sensor_type_ != SensorType::RATGDO_SENSOR_OBSTRUCTION) - return; - this->publish_state(state == ObstructionState::OBSTRUCTION_STATE_OBSTRUCTED); - } - void RATGDOBinarySensor::on_motor_state(MotorState state) - { - if (this->binary_sensor_type_ != SensorType::RATGDO_SENSOR_MOTOR) - return; - this->publish_state(state == MotorState::MOTOR_STATE_ON); - } - void RATGDOBinarySensor::on_button_state(ButtonState state) - { - if (this->binary_sensor_type_ != SensorType::RATGDO_SENSOR_BUTTON) - return; - this->publish_state(state == ButtonState::BUTTON_STATE_PRESSED); - } } // namespace ratgdo } // namespace esphome diff --git a/components/ratgdo/binary_sensor/ratgdo_binary_sensor.h b/components/ratgdo/binary_sensor/ratgdo_binary_sensor.h index 7358daa..3460641 100644 --- a/components/ratgdo/binary_sensor/ratgdo_binary_sensor.h +++ b/components/ratgdo/binary_sensor/ratgdo_binary_sensor.h @@ -1,7 +1,6 @@ #pragma once #include "../ratgdo.h" -#include "../ratgdo_child.h" #include "../ratgdo_state.h" #include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/core/component.h" @@ -22,11 +21,6 @@ namespace ratgdo { void dump_config() override; void set_binary_sensor_type(SensorType binary_sensor_type_) { this->binary_sensor_type_ = binary_sensor_type_; } - void on_motion_state(MotionState state) override; - void on_obstruction_state(ObstructionState state) override; - void on_motor_state(MotorState state) override; - void on_button_state(ButtonState state) override; - protected: SensorType binary_sensor_type_; }; diff --git a/components/ratgdo/cover/ratgdo_cover.cpp b/components/ratgdo/cover/ratgdo_cover.cpp index 9245da8..c084d87 100644 --- a/components/ratgdo/cover/ratgdo_cover.cpp +++ b/components/ratgdo/cover/ratgdo_cover.cpp @@ -13,6 +13,14 @@ namespace ratgdo { { LOG_COVER("", "RATGDO Cover", this); } + + void RATGDOCover::setup() + { + this->parent_->subscribe_door_state([=](DoorState state, float position) { + this->on_door_state(state, position); + }); + } + void RATGDOCover::on_door_state(DoorState state, float position) { switch (state) { diff --git a/components/ratgdo/cover/ratgdo_cover.h b/components/ratgdo/cover/ratgdo_cover.h index ded08d2..2399371 100644 --- a/components/ratgdo/cover/ratgdo_cover.h +++ b/components/ratgdo/cover/ratgdo_cover.h @@ -1,7 +1,6 @@ #pragma once #include "../ratgdo.h" -#include "../ratgdo_child.h" #include "../ratgdo_state.h" #include "esphome/components/cover/cover.h" #include "esphome/core/component.h" @@ -12,8 +11,10 @@ namespace ratgdo { class RATGDOCover : public cover::Cover, public RATGDOClient, public Component { public: void dump_config() override; + void setup() override; + cover::CoverTraits get_traits() override; - void on_door_state(DoorState state, float position) override; + void on_door_state(DoorState state, float position); protected: void control(const cover::CoverCall& call) override; diff --git a/components/ratgdo/light/ratgdo_light_output.cpp b/components/ratgdo/light/ratgdo_light_output.cpp index c6365ac..337abea 100644 --- a/components/ratgdo/light/ratgdo_light_output.cpp +++ b/components/ratgdo/light/ratgdo_light_output.cpp @@ -13,6 +13,14 @@ namespace ratgdo { { ESP_LOGCONFIG("", "RATGDO Light"); } + + void RATGDOLightOutput::setup() + { + this->parent_->subscribe_light_state([=](LightState s) { + this->on_light_state(s); + }); + } + void RATGDOLightOutput::on_light_state(esphome::ratgdo::LightState state) { if (this->light_state_) { diff --git a/components/ratgdo/light/ratgdo_light_output.h b/components/ratgdo/light/ratgdo_light_output.h index 9404ac9..d5d59f2 100644 --- a/components/ratgdo/light/ratgdo_light_output.h +++ b/components/ratgdo/light/ratgdo_light_output.h @@ -1,7 +1,6 @@ #pragma once #include "../ratgdo.h" -#include "../ratgdo_child.h" #include "../ratgdo_state.h" #include "esphome/components/light/light_output.h" #include "esphome/core/component.h" @@ -12,13 +11,14 @@ namespace ratgdo { class RATGDOLightOutput : public light::LightOutput, public RATGDOClient, public Component { public: void dump_config() override; + void setup() override; light::LightTraits get_traits() override; void write_state(light::LightState* state) override; void setup_state(light::LightState* state) override; void set_state(esphome::ratgdo::LightState state); light::LightState* get_state() { return this->light_state_; } - void on_light_state(esphome::ratgdo::LightState state) override; + void on_light_state(esphome::ratgdo::LightState state); protected: light::LightState* light_state_; diff --git a/components/ratgdo/number/ratgdo_number.cpp b/components/ratgdo/number/ratgdo_number.cpp index 6250f60..1838afd 100644 --- a/components/ratgdo/number/ratgdo_number.cpp +++ b/components/ratgdo/number/ratgdo_number.cpp @@ -19,6 +19,23 @@ namespace ratgdo { } } + void RATGDONumber::setup() + { + if (this->number_type_ == RATGDO_ROLLING_CODE_COUNTER) { + this->parent_->subscribe_rolling_code_counter([=](uint32_t value) { + this->publish_state(value); + }); + } else if (this->number_type_ == RATGDO_OPENING_DURATION) { + this->parent_->subscribe_opening_duration([=](float value) { + this->publish_state(value); + }); + } else if (this->number_type_ == RATGDO_CLOSING_DURATION) { + this->parent_->subscribe_closing_duration([=](float value) { + this->publish_state(value); + }); + } + } + void RATGDONumber::set_number_type(NumberType number_type_) { this->number_type_ = number_type_; @@ -27,30 +44,6 @@ namespace ratgdo { } } - void RATGDONumber::on_rolling_code_change(uint32_t rollingCodeCounter) - { - if (this->number_type_ != RATGDO_ROLLING_CODE_COUNTER) { - return; - } - this->publish_state(rollingCodeCounter); - } - - void RATGDONumber::on_opening_duration_change(float duration) - { - if (this->number_type_ != RATGDO_OPENING_DURATION) { - return; - } - this->publish_state(duration); - } - - void RATGDONumber::on_closing_duration_change(float duration) - { - if (this->number_type_ != RATGDO_CLOSING_DURATION) { - return; - } - this->publish_state(duration); - } - void RATGDONumber::control(float value) { if (this->number_type_ == RATGDO_ROLLING_CODE_COUNTER) { diff --git a/components/ratgdo/number/ratgdo_number.h b/components/ratgdo/number/ratgdo_number.h index 419800c..5b9b55f 100644 --- a/components/ratgdo/number/ratgdo_number.h +++ b/components/ratgdo/number/ratgdo_number.h @@ -1,7 +1,6 @@ #pragma once #include "../ratgdo.h" -#include "../ratgdo_child.h" #include "../ratgdo_state.h" #include "esphome/components/number/number.h" #include "esphome/core/component.h" @@ -18,12 +17,9 @@ namespace ratgdo { class RATGDONumber : public number::Number, public RATGDOClient, public Component { public: void dump_config() override; + void setup() override; void set_number_type(NumberType number_type_); - void on_rolling_code_change(uint32_t rollingCodeCounter) override; - void on_opening_duration_change(float duration) override; - void on_closing_duration_change(float duration) override; - void control(float value) override; protected: diff --git a/components/ratgdo/observable.h b/components/ratgdo/observable.h new file mode 100644 index 0000000..0f14235 --- /dev/null +++ b/components/ratgdo/observable.h @@ -0,0 +1,46 @@ +#pragma once + +namespace esphome { +namespace ratgdo { + + template + class observable { + public: + observable(const T& value) + : value_(value) + { + } + + template + observable& operator=(U value) + { + if (value != this->value_) { + this->value_ = value; + this->notify(); + } + return *this; + } + + T const* operator&() const { return &this->value_; } + T const& operator*() const { return this->value_; } + + template + void subscribe(Observer&& observer) + { + this->observers_.push_back(std::forward(observer)); + } + + void notify() const + { + for (const auto& observer : this->observers_) { + observer(this->value_); + } + } + + private: + T value_; + std::vector> observers_; + }; + +} // namespace ratgdo +} // namespace esphome diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index fabab1c..a4f5133 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -12,7 +12,6 @@ ************************************/ #include "ratgdo.h" -#include "ratgdo_child.h" #include "ratgdo_state.h" #include "esphome/core/log.h" @@ -46,22 +45,23 @@ namespace ratgdo { void RATGDOComponent::setup() { this->rollingCodePref_ = global_preferences->make_preference(734874333U); - if (!this->rollingCodePref_.load(&this->rollingCodeCounter)) { - this->rollingCodeCounter = 0; - } + uint32_t rolling_code_counter = 0; + this->rollingCodePref_.load(&rolling_code_counter); + this->rollingCodeCounter = rolling_code_counter; + // observers are subscribed in the setup() of children defer notify until after setup() + defer([=] { this->rollingCodeCounter.notify(); }); + this->openingDurationPref_ = global_preferences->make_preference(734874334U); - if (!this->openingDurationPref_.load(&this->openingDuration)) { - this->setOpeningDuration(0); - } else { - this->sendOpeningDuration(); - } + float opening_duration = 0; + this->openingDurationPref_.load(&opening_duration); + this->setOpeningDuration(opening_duration); + defer([=] { this->openingDuration.notify(); }); this->closingDurationPref_ = global_preferences->make_preference(734874335U); - if (!this->closingDurationPref_.load(&this->closingDuration)) { - this->setClosingDuration(0.f); - } else { - this->sendClosingDuration(); - } + float closing_duration = 0; + this->closingDurationPref_.load(&closing_duration); + this->setClosingDuration(closing_duration); + defer([=] { this->closingDuration.notify(); }); this->output_gdo_pin_->setup(); this->input_gdo_pin_->setup(); @@ -87,7 +87,6 @@ namespace ratgdo { { obstructionLoop(); gdoStateLoop(); - statusUpdateLoop(); } void RATGDOComponent::dump_config() @@ -96,7 +95,7 @@ namespace ratgdo { LOG_PIN(" Output GDO Pin: ", this->output_gdo_pin_); LOG_PIN(" Input GDO Pin: ", this->input_gdo_pin_); LOG_PIN(" Input Obstruction Pin: ", this->input_obst_pin_); - ESP_LOGCONFIG(TAG, " Rolling Code Counter: %d", this->rollingCodeCounter); + ESP_LOGCONFIG(TAG, " Rolling Code Counter: %d", *this->rollingCodeCounter); ESP_LOGCONFIG(TAG, " Remote ID: %d", this->remote_id); } @@ -183,53 +182,53 @@ namespace ratgdo { if (cmd == command::STATUS) { - this->doorState = static_cast(nibble); + auto doorState = static_cast(nibble); - if (this->doorState == DoorState::DOOR_STATE_OPENING && this->previousDoorState == DoorState::DOOR_STATE_CLOSED) { + if (doorState == DoorState::DOOR_STATE_OPENING && *this->doorState == DoorState::DOOR_STATE_CLOSED) { this->startOpening = millis(); } - if (this->doorState == DoorState::DOOR_STATE_OPEN && this->previousDoorState == DoorState::DOOR_STATE_OPENING) { + if (doorState == DoorState::DOOR_STATE_OPEN && *this->doorState == DoorState::DOOR_STATE_OPENING) { if (this->startOpening > 0) { auto duration = (millis() - this->startOpening) / 1000; - duration = this->openingDuration > 0 ? (duration + this->openingDuration) / 2 : duration; + duration = *this->openingDuration > 0 ? (duration + *this->openingDuration) / 2 : duration; this->setOpeningDuration(round(duration * 10) / 10); } } - if (this->doorState == DoorState::DOOR_STATE_CLOSING && this->previousDoorState == DoorState::DOOR_STATE_OPEN) { + if (doorState == DoorState::DOOR_STATE_CLOSING && *this->doorState == DoorState::DOOR_STATE_OPEN) { this->startClosing = millis(); } - if (this->doorState == DoorState::DOOR_STATE_CLOSED && this->previousDoorState == DoorState::DOOR_STATE_CLOSING) { + if (doorState == DoorState::DOOR_STATE_CLOSED && *this->doorState == DoorState::DOOR_STATE_CLOSING) { if (this->startClosing > 0) { auto duration = (millis() - this->startClosing) / 1000; - duration = this->closingDuration > 0 ? (duration + this->closingDuration) / 2 : duration; + duration = *this->closingDuration > 0 ? (duration + *this->closingDuration) / 2 : duration; this->setClosingDuration(round(duration * 10) / 10); } } - if (this->doorState == DoorState::DOOR_STATE_STOPPED) { + if (doorState == DoorState::DOOR_STATE_STOPPED) { this->startOpening = -1; this->startClosing = -1; } - if (this->doorState == DoorState::DOOR_STATE_OPEN) { + if (doorState == DoorState::DOOR_STATE_OPEN) { this->doorPosition = 1.0; - } else if (this->doorState == DoorState::DOOR_STATE_CLOSED) { + } else if (doorState == DoorState::DOOR_STATE_CLOSED) { this->doorPosition = 0.0; } else { - if (this->closingDuration == 0 || this->openingDuration == 0 || this->doorPosition == DOOR_POSITION_UNKNOWN) { + if (*this->closingDuration == 0 || *this->openingDuration == 0 || *this->doorPosition == DOOR_POSITION_UNKNOWN) { this->doorPosition = 0.5; // best guess } } - if (this->doorState == DoorState::DOOR_STATE_OPENING && !this->movingToPosition) { - this->positionSyncWhileOpening(1.0 - this->doorPosition); + if (doorState == DoorState::DOOR_STATE_OPENING && !this->movingToPosition) { + this->positionSyncWhileOpening(1.0 - *this->doorPosition); this->movingToPosition = true; } - if (this->doorState == DoorState::DOOR_STATE_CLOSING && !this->movingToPosition) { - this->positionSyncWhileClosing(this->doorPosition); + if (doorState == DoorState::DOOR_STATE_CLOSING && !this->movingToPosition) { + this->positionSyncWhileClosing(*this->doorPosition); this->movingToPosition = true; } - if (this->doorState == DoorState::DOOR_STATE_OPEN || this->doorState == DoorState::DOOR_STATE_CLOSED || this->doorState == DoorState::DOOR_STATE_STOPPED) { + if (doorState == DoorState::DOOR_STATE_OPEN || doorState == DoorState::DOOR_STATE_CLOSED || doorState == DoorState::DOOR_STATE_STOPPED) { this->cancelPositionSyncCallbacks(); } @@ -239,41 +238,43 @@ namespace ratgdo { this->motorState = MotorState::MOTOR_STATE_OFF; // when the status message is read, reset motor state to 0|off // this->obstructionState = static_cast((byte1 >> 6) & 1); - if (this->doorState == DoorState::DOOR_STATE_CLOSED && this->doorState != this->previousDoorState) { + if (doorState == DoorState::DOOR_STATE_CLOSED && doorState != *this->doorState) { transmit(command::GET_OPENINGS); } + this->doorState = doorState; + ESP_LOGD(TAG, "Status: door=%s light=%s lock=%s", - door_state_to_string(this->doorState), - light_state_to_string(this->lightState), - lock_state_to_string(this->lockState)); + door_state_to_string(*this->doorState), + light_state_to_string(*this->lightState), + lock_state_to_string(*this->lockState)); } else if (cmd == command::LIGHT) { if (nibble == 0) { this->lightState = LightState::LIGHT_STATE_OFF; } else if (nibble == 1) { this->lightState = LightState::LIGHT_STATE_ON; } else if (nibble == 2) { // toggle - this->lightState = light_state_toggle(this->lightState); + this->lightState = light_state_toggle(*this->lightState); } ESP_LOGD(TAG, "Light: action=%s state=%s", nibble == 0 ? "OFF" : nibble == 1 ? "ON" : "TOGGLE", - light_state_to_string(this->lightState)); + light_state_to_string(*this->lightState)); } else if (cmd == command::MOTOR_ON) { this->motorState = MotorState::MOTOR_STATE_ON; - ESP_LOGD(TAG, "Motor: state=%s", motor_state_to_string(this->motorState)); + ESP_LOGD(TAG, "Motor: state=%s", motor_state_to_string(*this->motorState)); } else if (cmd == command::OPEN) { this->buttonState = (byte1 & 1) == 1 ? ButtonState::BUTTON_STATE_PRESSED : ButtonState::BUTTON_STATE_RELEASED; - ESP_LOGD(TAG, "Open: button=%s", button_state_to_string(this->buttonState)); + ESP_LOGD(TAG, "Open: button=%s", button_state_to_string(*this->buttonState)); } else if (cmd == command::OPENINGS) { this->openings = (byte1 << 8) | byte2; - ESP_LOGD(TAG, "Openings: %d", this->openings); + ESP_LOGD(TAG, "Openings: %d", *this->openings); } else if (cmd == command::MOTION) { this->motionState = MotionState::MOTION_STATE_DETECTED; - if (this->lightState == LightState::LIGHT_STATE_OFF) { + if (*this->lightState == LightState::LIGHT_STATE_OFF) { transmit(command::GET_STATUS); } - ESP_LOGD(TAG, "Motion: %s", motion_state_to_string(this->motionState)); + ESP_LOGD(TAG, "Motion: %s", motion_state_to_string(*this->motionState)); } else { ESP_LOGD(TAG, "Unhandled command: cmd=%03x nibble=%02x byte1=%02x byte2=%02x fixed=%010" PRIx64 " data=%08" PRIx32, cmd, nibble, byte1, byte2, fixed, data); } @@ -285,8 +286,8 @@ namespace ratgdo { uint64_t fixed = ((command & ~0xff) << 24) | this->remote_id; uint32_t send_data = (data << 8) | (command & 0xff); - ESP_LOGD(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), this->rollingCodeCounter, fixed, send_data); - encode_wireline(this->rollingCodeCounter, fixed, send_data, this->txRollingCode); + ESP_LOGD(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), *this->rollingCodeCounter, fixed, send_data); + encode_wireline(*this->rollingCodeCounter, fixed, send_data, this->txRollingCode); printRollingCode(); if (increment) { @@ -300,68 +301,38 @@ namespace ratgdo { this->openingDuration = duration; this->openingDurationPref_.save(&this->openingDuration); - sendOpeningDuration(); - - if (this->closingDuration == 0 && duration != 0) { + if (*this->closingDuration == 0 && duration != 0) { this->setClosingDuration(duration); } } - void RATGDOComponent::sendOpeningDuration() - { - for (auto* child : this->children_) { - child->on_opening_duration_change(this->openingDuration); - } - } - void RATGDOComponent::setClosingDuration(float duration) { ESP_LOGD(TAG, "Set closing duration: %.1fs", duration); this->closingDuration = duration; this->closingDurationPref_.save(&this->closingDuration); - sendClosingDuration(); - - if (this->openingDuration == 0 && duration != 0) { + if (*this->openingDuration == 0 && duration != 0) { this->setOpeningDuration(duration); } } - void RATGDOComponent::sendClosingDuration() - { - for (auto* child : this->children_) { - child->on_closing_duration_change(this->closingDuration); - } - } - void RATGDOComponent::setRollingCodeCounter(uint32_t counter) { ESP_LOGV(TAG, "Set rolling code counter to %d", counter); this->rollingCodeCounter = counter; this->rollingCodePref_.save(&this->rollingCodeCounter); - sendRollingCodeChanged(); } void RATGDOComponent::incrementRollingCodeCounter(int delta) { - this->rollingCodeCounter = (this->rollingCodeCounter + delta) & 0xfffffff; - sendRollingCodeChanged(); - } - - void RATGDOComponent::sendRollingCodeChanged() - { - if (!this->rollingCodeUpdatesEnabled_) { - return; - } - for (auto* child : this->children_) { - child->on_rolling_code_change(this->rollingCodeCounter); - } + this->rollingCodeCounter = (*this->rollingCodeCounter + delta) & 0xfffffff; } void RATGDOComponent::printRollingCode() { ESP_LOGV(TAG, "Counter: %d Send code: [%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X]", - this->rollingCodeCounter, + *this->rollingCodeCounter, this->txRollingCode[0], this->txRollingCode[1], this->txRollingCode[2], @@ -455,88 +426,15 @@ namespace ratgdo { if (byte_count == CODE_LENGTH) { reading_msg = false; byte_count = 0; - if (readRollingCode() == command::STATUS && this->forceUpdate_) { - this->forceUpdate_ = false; - this->previousDoorState = DoorState::DOOR_STATE_UNKNOWN; - this->previousLightState = LightState::LIGHT_STATE_UNKNOWN; - this->previousLockState = LockState::LOCK_STATE_UNKNOWN; - } + readRollingCode(); return; } } } } - void RATGDOComponent::statusUpdateLoop() - { - if (this->doorState != this->previousDoorState) { - ESP_LOGV(TAG, "Door state: %s", door_state_to_string(this->doorState)); - for (auto* child : this->children_) { - child->on_door_state(this->doorState, this->doorPosition); - } - this->previousDoorState = this->doorState; - } - if (this->doorPosition != this->previousDoorPosition) { - ESP_LOGV(TAG, "Door position: %f", this->doorPosition); - for (auto* child : this->children_) { - child->on_door_state(this->doorState, this->doorPosition); - } - this->previousDoorPosition = this->doorPosition; - } - if (this->lightState != this->previousLightState) { - ESP_LOGV(TAG, "Light state %s (%d)", light_state_to_string(this->lightState), this->lightState); - for (auto* child : this->children_) { - child->on_light_state(this->lightState); - } - this->previousLightState = this->lightState; - } - if (this->lockState != this->previousLockState) { - ESP_LOGV(TAG, "Lock state %s", lock_state_to_string(this->lockState)); - for (auto* child : this->children_) { - child->on_lock_state(this->lockState); - } - this->previousLockState = this->lockState; - } - if (this->obstructionState != this->previousObstructionState) { - ESP_LOGV(TAG, "Obstruction state %s", obstruction_state_to_string(this->obstructionState)); - for (auto* child : this->children_) { - child->on_obstruction_state(this->obstructionState); - } - this->previousObstructionState = this->obstructionState; - } - if (this->motorState != this->previousMotorState) { - ESP_LOGV(TAG, "Motor state %s", motor_state_to_string(this->motorState)); - for (auto* child : this->children_) { - child->on_motor_state(this->motorState); - } - this->previousMotorState = this->motorState; - } - if (this->motionState != this->previousMotionState) { - ESP_LOGV(TAG, "Motion state %s", motion_state_to_string(this->motionState)); - for (auto* child : this->children_) { - child->on_motion_state(this->motionState); - } - this->previousMotionState = this->motionState; - } - if (this->buttonState != this->previousButtonState) { - ESP_LOGV(TAG, "Button state %s", button_state_to_string(this->buttonState)); - for (auto* child : this->children_) { - child->on_button_state(this->buttonState); - } - this->previousButtonState = this->buttonState; - } - if (this->openings != this->previousOpenings) { - ESP_LOGV(TAG, "Openings: %d", this->openings); - for (auto* child : this->children_) { - child->on_openings_change(this->openings); - } - this->previousOpenings = this->openings; - } - } - void RATGDOComponent::query_status() { - this->forceUpdate_ = true; transmit(command::GET_STATUS); } @@ -574,9 +472,9 @@ namespace ratgdo { this->incrementRollingCodeCounter(MAX_CODES_WITHOUT_FLASH_WRITE); set_retry( - 300, 10, [=](auto r) { - if (this->doorState != DoorState::DOOR_STATE_UNKNOWN) { // have status - if (this->openings != 0) { // have openings + 300, 10, [=](uint8_t r) { + if (*this->doorState != DoorState::DOOR_STATE_UNKNOWN) { // have status + if (*this->openings != 0) { // have openings return RetryResult::DONE; } else { transmit(command::GET_OPENINGS); @@ -592,7 +490,7 @@ namespace ratgdo { void RATGDOComponent::openDoor() { - if (this->doorState == DoorState::DOOR_STATE_OPENING) { + if (*this->doorState == DoorState::DOOR_STATE_OPENING) { return; // gets ignored by opener } this->cancelPositionSyncCallbacks(); @@ -602,7 +500,7 @@ namespace ratgdo { void RATGDOComponent::closeDoor() { - if (this->doorState == DoorState::DOOR_STATE_CLOSING || this->doorState == DoorState::DOOR_STATE_OPENING) { + if (*this->doorState == DoorState::DOOR_STATE_CLOSING || *this->doorState == DoorState::DOOR_STATE_OPENING) { return; // gets ignored by opener } this->cancelPositionSyncCallbacks(); @@ -612,7 +510,7 @@ namespace ratgdo { void RATGDOComponent::stopDoor() { - if (this->doorState != DoorState::DOOR_STATE_OPENING && this->doorState != DoorState::DOOR_STATE_CLOSING) { + if (*this->doorState != DoorState::DOOR_STATE_OPENING && *this->doorState != DoorState::DOOR_STATE_CLOSING) { ESP_LOGW(TAG, "The door is not moving."); return; } @@ -621,7 +519,7 @@ namespace ratgdo { void RATGDOComponent::toggleDoor() { - if (this->doorState == DoorState::DOOR_STATE_OPENING) { + if (*this->doorState == DoorState::DOOR_STATE_OPENING) { return; // gets ignored by opener } this->cancelPositionSyncCallbacks(); @@ -631,54 +529,54 @@ namespace ratgdo { void RATGDOComponent::positionSyncWhileOpening(float delta, float update_period) { - if (this->openingDuration == 0) { + if (*this->openingDuration == 0) { ESP_LOGW(TAG, "I don't know opening duration, ignoring position sync"); return; } - auto updates = this->openingDuration * 1000 * delta / update_period; + auto updates = *this->openingDuration * 1000 * delta / update_period; auto position_update = delta / updates; auto count = int(updates); ESP_LOGD(TAG, "[Opening] Position sync %d times: ", count); // try to keep position in sync while door is moving set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) { ESP_LOGD(TAG, "[Opening] Position sync: %d: ", r); - this->doorPosition += position_update; + this->doorPosition = *this->doorPosition + position_update; return RetryResult::RETRY; }); } void RATGDOComponent::positionSyncWhileClosing(float delta, float update_period) { - if (this->closingDuration == 0) { + if (*this->closingDuration == 0) { ESP_LOGW(TAG, "I don't know closing duration, ignoring position sync"); return; } - auto updates = this->closingDuration * 1000 * delta / update_period; + auto updates = *this->closingDuration * 1000 * delta / update_period; auto position_update = delta / updates; auto count = int(updates); ESP_LOGD(TAG, "[Closing] Position sync %d times: ", count); // try to keep position in sync while door is moving set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) { ESP_LOGD(TAG, "[Closing] Position sync: %d: ", r); - this->doorPosition -= position_update; + this->doorPosition = *this->doorPosition - position_update; return RetryResult::RETRY; }); } void RATGDOComponent::setDoorPosition(float position) { - if (this->doorState == DoorState::DOOR_STATE_OPENING || this->doorState == DoorState::DOOR_STATE_CLOSING) { + if (*this->doorState == DoorState::DOOR_STATE_OPENING || *this->doorState == DoorState::DOOR_STATE_CLOSING) { ESP_LOGW(TAG, "The door is moving, ignoring."); return; } - auto delta = position - this->doorPosition; + auto delta = position - *this->doorPosition; if (delta == 0) { ESP_LOGD(TAG, "Door is already at position %.2f", position); return; } - auto duration = delta > 0 ? this->openingDuration : this->closingDuration; + auto duration = delta > 0 ? *this->openingDuration : *this->closingDuration; if (duration == 0) { ESP_LOGW(TAG, "I don't know duration, ignoring move to position"); return; @@ -738,7 +636,7 @@ namespace ratgdo { void RATGDOComponent::toggleLight() { - this->lightState = light_state_toggle(this->lightState); + this->lightState = light_state_toggle(*this->lightState); transmit(command::LIGHT, data::LIGHT_TOGGLE); } @@ -756,7 +654,7 @@ namespace ratgdo { void RATGDOComponent::toggleLock() { - this->lockState = lock_state_toggle(this->lockState); + this->lockState = lock_state_toggle(*this->lockState); transmit(command::LOCK, data::LOCK_TOGGLE); } @@ -768,14 +666,61 @@ namespace ratgdo { // we have configured preferences to write every 5s } - void RATGDOComponent::register_child(RATGDOClient* obj) - { - this->children_.push_back(obj); - obj->set_parent(this); - } LightState RATGDOComponent::getLightState() { - return this->lightState; + return *this->lightState; + } + + void RATGDOComponent::subscribe_rolling_code_counter(std::function&& f) + { + // change update to children is defered until after component loop + // if multiple changes occur during component loop, only the last one is notified + this->rollingCodeCounter.subscribe([=](uint32_t state) { defer("rolling_code_counter", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_opening_duration(std::function&& f) + { + this->openingDuration.subscribe([=](float state) { defer("opening_duration", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_closing_duration(std::function&& f) + { + this->closingDuration.subscribe([=](float state) { defer("closing_duration", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_openings(std::function&& f) + { + this->openings.subscribe([=](uint16_t state) { defer("openings", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_door_state(std::function&& f) + { + this->doorState.subscribe([=](DoorState state) { + defer("door_state", [=] { f(state, *this->doorPosition); }); + }); + this->doorPosition.subscribe([=](float position) { + defer("door_state", [=] { f(*this->doorState, position); }); + }); + } + void RATGDOComponent::subscribe_light_state(std::function&& f) + { + this->lightState.subscribe([=](LightState state) { defer("light_state", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_lock_state(std::function&& f) + { + this->lockState.subscribe([=](LockState state) { defer("lock_state", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_obstruction_state(std::function&& f) + { + this->obstructionState.subscribe([=](ObstructionState state) { defer("obstruction_state", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_motor_state(std::function&& f) + { + this->motorState.subscribe([=](MotorState state) { defer("motor_state", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_button_state(std::function&& f) + { + this->buttonState.subscribe([=](ButtonState state) { defer("button_state", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_motion_state(std::function&& f) + { + this->motionState.subscribe([=](MotionState state) { defer("motion_state", [=] { f(state); }); }); } } // namespace ratgdo diff --git a/components/ratgdo/ratgdo.h b/components/ratgdo/ratgdo.h index 0b649f7..aa68a94 100644 --- a/components/ratgdo/ratgdo.h +++ b/components/ratgdo/ratgdo.h @@ -17,19 +17,19 @@ #include "esphome/core/gpio.h" #include "esphome/core/log.h" #include "esphome/core/preferences.h" +#include "observable.h" extern "C" { #include "secplus.h" } -#include "ratgdo_child.h" #include "ratgdo_state.h" namespace esphome { namespace ratgdo { - // Forward declare RATGDOClient - class RATGDOClient; + class RATGDOComponent; + typedef Parented RATGDOClient; static const uint8_t CODE_LENGTH = 19; @@ -128,44 +128,29 @@ namespace ratgdo { EspSoftwareSerial::UART swSerial; - uint32_t rollingCodeCounter { 0 }; + observable rollingCodeCounter { 0 }; uint32_t lastSyncedRollingCodeCounter { 0 }; float startOpening { -1 }; - float openingDuration { 0 }; + observable openingDuration { 0 }; float startClosing { -1 }; - float closingDuration { 0 }; + observable closingDuration { 0 }; uint8_t txRollingCode[CODE_LENGTH]; uint8_t rxRollingCode[CODE_LENGTH]; - uint16_t previousOpenings { 0 }; // number of times the door has been opened - uint16_t openings { 0 }; // number of times the door has been opened + observable openings { 0 }; // number of times the door has been opened - DoorState previousDoorState { DoorState::DOOR_STATE_UNKNOWN }; - DoorState doorState { DoorState::DOOR_STATE_UNKNOWN }; - - float doorPosition { DOOR_POSITION_UNKNOWN }; - float previousDoorPosition { DOOR_POSITION_UNKNOWN }; + observable doorState { DoorState::DOOR_STATE_UNKNOWN }; + observable doorPosition { DOOR_POSITION_UNKNOWN }; bool movingToPosition { false }; - LightState previousLightState { LightState::LIGHT_STATE_UNKNOWN }; - LightState lightState { LightState::LIGHT_STATE_UNKNOWN }; - - LockState previousLockState { LockState::LOCK_STATE_UNKNOWN }; - LockState lockState { LockState::LOCK_STATE_UNKNOWN }; - - ObstructionState previousObstructionState { ObstructionState::OBSTRUCTION_STATE_UNKNOWN }; - ObstructionState obstructionState { ObstructionState::OBSTRUCTION_STATE_UNKNOWN }; - - MotorState previousMotorState { MotorState::MOTOR_STATE_UNKNOWN }; - MotorState motorState { MotorState::MOTOR_STATE_UNKNOWN }; - - ButtonState previousButtonState { ButtonState::BUTTON_STATE_UNKNOWN }; - ButtonState buttonState { ButtonState::BUTTON_STATE_UNKNOWN }; - - MotionState previousMotionState { MotionState::MOTION_STATE_UNKNOWN }; - MotionState motionState { MotionState::MOTION_STATE_UNKNOWN }; + observable lightState { LightState::LIGHT_STATE_UNKNOWN }; + observable lockState { LockState::LOCK_STATE_UNKNOWN }; + observable obstructionState { ObstructionState::OBSTRUCTION_STATE_UNKNOWN }; + observable motorState { MotorState::MOTOR_STATE_UNKNOWN }; + observable buttonState { ButtonState::BUTTON_STATE_UNKNOWN }; + observable motionState { MotionState::MOTION_STATE_UNKNOWN }; void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; }; void set_input_gdo_pin(InternalGPIOPin* pin) { this->input_gdo_pin_ = pin; }; @@ -180,7 +165,6 @@ namespace ratgdo { void gdoStateLoop(); void obstructionLoop(); - void statusUpdateLoop(); void saveCounter(); @@ -210,25 +194,30 @@ namespace ratgdo { void getRollingCode(command::cmd command, uint32_t data, bool increment); uint16_t readRollingCode(); void incrementRollingCodeCounter(int delta = 1); - void sendRollingCodeChanged(); void setRollingCodeCounter(uint32_t counter); void setOpeningDuration(float duration); - void sendOpeningDuration(); void setClosingDuration(float duration); - void sendClosingDuration(); LightState getLightState(); - /** Register a child component. */ - void register_child(RATGDOClient* obj); + + void subscribe_rolling_code_counter(std::function&& f); + void subscribe_opening_duration(std::function&& f); + void subscribe_closing_duration(std::function&& f); + void subscribe_openings(std::function&& f); + void subscribe_door_state(std::function&& f); + void subscribe_light_state(std::function&& f); + void subscribe_lock_state(std::function&& f); + void subscribe_obstruction_state(std::function&& f); + void subscribe_motor_state(std::function&& f); + void subscribe_button_state(std::function&& f); + void subscribe_motion_state(std::function&& f); protected: ESPPreferenceObject rollingCodePref_; ESPPreferenceObject openingDurationPref_; ESPPreferenceObject closingDurationPref_; - std::vector children_; bool rollingCodeUpdatesEnabled_ { true }; - bool forceUpdate_ { false }; RATGDOStore store_ {}; InternalGPIOPin* output_gdo_pin_; diff --git a/components/ratgdo/ratgdo_child.cpp b/components/ratgdo/ratgdo_child.cpp deleted file mode 100644 index 3cbc91b..0000000 --- a/components/ratgdo/ratgdo_child.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "esphome/core/helpers.h" - -#include "ratgdo.h" -#include "ratgdo_state.h" - -namespace esphome { -namespace ratgdo { - - void RATGDOClient::on_door_state(DoorState state, float position) {}; - void RATGDOClient::on_light_state(LightState state) {}; - void RATGDOClient::on_lock_state(LockState state) {}; - void RATGDOClient::on_motion_state(MotionState state) {}; - void RATGDOClient::on_obstruction_state(ObstructionState state) {}; - void RATGDOClient::on_motor_state(MotorState state) {}; - void RATGDOClient::on_rolling_code_change(uint32_t rollingCodeCounter) {}; - void RATGDOClient::on_opening_duration_change(float duration) {}; - void RATGDOClient::on_closing_duration_change(float duration) {}; - void RATGDOClient::on_openings_change(uint32_t openings) {}; - void RATGDOClient::on_button_state(ButtonState state) {}; - -} // namespace ratgdo -} // namespace esphome diff --git a/components/ratgdo/ratgdo_child.h b/components/ratgdo/ratgdo_child.h deleted file mode 100644 index 61e0e71..0000000 --- a/components/ratgdo/ratgdo_child.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "esphome/core/helpers.h" - -#include "ratgdo.h" -#include "ratgdo_state.h" - -namespace esphome { -namespace ratgdo { - - // Forward declare RATGDOComponent - class RATGDOComponent; - - class RATGDOClient : public Parented { - public: - virtual void on_door_state(DoorState state, float position); - virtual void on_light_state(LightState state); - virtual void on_lock_state(LockState state); - virtual void on_motion_state(MotionState state); - virtual void on_obstruction_state(ObstructionState state); - virtual void on_motor_state(MotorState state); - virtual void on_rolling_code_change(uint32_t rollingCodeCounter); - virtual void on_opening_duration_change(float duration); - virtual void on_closing_duration_change(float duration); - virtual void on_openings_change(uint32_t openings); - virtual void on_button_state(ButtonState state); - - protected: - friend RATGDOComponent; - }; - -} // namespace ratgdo -} // namespace esphome diff --git a/components/ratgdo/sensor/ratgdo_sensor.cpp b/components/ratgdo/sensor/ratgdo_sensor.cpp index b26f92a..c2312b9 100644 --- a/components/ratgdo/sensor/ratgdo_sensor.cpp +++ b/components/ratgdo/sensor/ratgdo_sensor.cpp @@ -12,9 +12,12 @@ namespace ratgdo { LOG_SENSOR("", "RATGDO Sensor", this); ESP_LOGCONFIG(TAG, " Type: Openings"); } - void RATGDOSensor::on_openings_change(uint32_t openings) + + void RATGDOSensor::setup() { - this->publish_state(openings); + this->parent_->subscribe_openings([=](uint16_t value) { + this->publish_state(value); + }); } } // namespace ratgdo diff --git a/components/ratgdo/sensor/ratgdo_sensor.h b/components/ratgdo/sensor/ratgdo_sensor.h index f999e29..26f2618 100644 --- a/components/ratgdo/sensor/ratgdo_sensor.h +++ b/components/ratgdo/sensor/ratgdo_sensor.h @@ -1,7 +1,6 @@ #pragma once #include "../ratgdo.h" -#include "../ratgdo_child.h" #include "../ratgdo_state.h" #include "esphome/components/sensor/sensor.h" #include "esphome/core/component.h" @@ -16,10 +15,9 @@ namespace ratgdo { class RATGDOSensor : public sensor::Sensor, public RATGDOClient, public Component { public: void dump_config() override; + void setup() override; void set_ratgdo_sensor_type(RATGDOSensorType ratgdo_sensor_type_) { this->ratgdo_sensor_type_ = ratgdo_sensor_type_; } - void on_openings_change(uint32_t openings) override; - protected: RATGDOSensorType ratgdo_sensor_type_; }; diff --git a/components/ratgdo/switch/ratgdo_switch.cpp b/components/ratgdo/switch/ratgdo_switch.cpp index 4ee98e1..9d36bf9 100644 --- a/components/ratgdo/switch/ratgdo_switch.cpp +++ b/components/ratgdo/switch/ratgdo_switch.cpp @@ -13,6 +13,13 @@ namespace ratgdo { ESP_LOGCONFIG(TAG, " Type: Lock"); } + void RATGDOSwitch::setup() + { + this->parent_->subscribe_lock_state([=](LockState state) { + this->on_lock_state(state); + }); + } + void RATGDOSwitch::on_lock_state(LockState state) { bool value = state == LockState::LOCK_STATE_LOCKED; diff --git a/components/ratgdo/switch/ratgdo_switch.h b/components/ratgdo/switch/ratgdo_switch.h index aa6bbcd..e29ad99 100644 --- a/components/ratgdo/switch/ratgdo_switch.h +++ b/components/ratgdo/switch/ratgdo_switch.h @@ -1,7 +1,6 @@ #pragma once #include "../ratgdo.h" -#include "../ratgdo_child.h" #include "../ratgdo_state.h" #include "esphome/components/switch/switch.h" #include "esphome/core/component.h" @@ -16,9 +15,10 @@ namespace ratgdo { class RATGDOSwitch : public switch_::Switch, public RATGDOClient, public Component { public: void dump_config() override; + void setup() override; void set_switch_type(SwitchType switch_type_) { this->switch_type_ = switch_type_; } - void on_lock_state(LockState state) override; + void on_lock_state(LockState state); void write_state(bool state) override; protected: diff --git a/static/v2board_esp8266_d1_mini_lite.yaml b/static/v2board_esp8266_d1_mini_lite.yaml index 939c174..9103098 100644 --- a/static/v2board_esp8266_d1_mini_lite.yaml +++ b/static/v2board_esp8266_d1_mini_lite.yaml @@ -33,6 +33,7 @@ packages: url: https://github.com/esphome-ratgdo/esphome-ratgdo files: [base.yaml] refresh: 1s + ref: observer_callbacks # Sync time with Home Assistant. time: