From 26341e075aab26e248bd8ce0d31e79b90b73ffca Mon Sep 17 00:00:00 2001 From: Nicolas Bachschwell Date: Mon, 29 Apr 2024 13:49:12 +0200 Subject: [PATCH] Now properly sends light state back to HA --- ...m_garage_component.h => cover_component.h} | 91 +++------------ .../hoermann_door/door_singleton.h | 79 +++++++++++++ .../hoermann_door/light_component.h | 106 ++++++++++++++++++ external_components/hoermann_door/output.py | 12 +- garage1.yaml | 26 ++--- 5 files changed, 214 insertions(+), 100 deletions(-) rename external_components/hoermann_door/{custom_garage_component.h => cover_component.h} (52%) create mode 100644 external_components/hoermann_door/door_singleton.h create mode 100644 external_components/hoermann_door/light_component.h diff --git a/external_components/hoermann_door/custom_garage_component.h b/external_components/hoermann_door/cover_component.h similarity index 52% rename from external_components/hoermann_door/custom_garage_component.h rename to external_components/hoermann_door/cover_component.h index 8a9640f..91ab121 100644 --- a/external_components/hoermann_door/custom_garage_component.h +++ b/external_components/hoermann_door/cover_component.h @@ -1,5 +1,9 @@ #pragma once +#include "door_singleton.h" + +#ifdef USE_COVER + #include "esphome.h" #include "Arduino.h" #include "hciemulator.h" @@ -14,22 +18,15 @@ namespace esphome { namespace hoermann_door { -HCIEmulator emulator; -TaskHandle_t modBusTask; void modBusPolling(void *parameter); class HoermanDoor : public Component, public cover::Cover { public: - HoermanDoor() - { - emulator; - } - cover::CoverTraits get_traits() override { auto traits = cover::CoverTraits(); traits.set_is_assumed_state(false); - traits.set_supports_position(true); + traits.set_supports_position(false); traits.set_supports_tilt(false); traits.set_supports_stop(true); traits.set_supports_toggle(true); @@ -51,11 +48,11 @@ public: if(pos == 1.0){ - emulator.openDoor(); + HoermannSingleton::getInstance()->getEmulator()->openDoor(); manual = false; } else if(pos == 0.0){ - emulator.closeDoor(); + HoermannSingleton::getInstance()->getEmulator()->closeDoor(); manual = false; } else{ @@ -64,7 +61,7 @@ public: } if (call.get_stop()) { - emulator.stopDoor(); + HoermannSingleton::getInstance()->getEmulator()->stopDoor(); } //if(call.get_close()) { //emulator.closeDoor(); @@ -91,25 +88,7 @@ void modBusPolling(void *parameter) void setup() override { - // setup modbus - RS485.begin(57600, SERIAL_8E1, 16, 17); - pinMode(TX_ON, OUTPUT); - digitalWrite(TX_ON, LOW); - - xTaskCreatePinnedToCore( - modBusPolling, // Function to implement the task - "ModBusTask", // Name of the task - 10000, // Stack size in words - NULL, // Task input parameter - // 1, // Priority of the task - configMAX_PRIORITIES - 1, - &modBusTask, // Task handle. - 1 // Core where the task should run - ); - - - - + HoermannSingleton::getInstance()->initializeEmulator(); } @@ -130,7 +109,7 @@ void modBusPolling(void *parameter) u_int8_t pos = 255; void loop() override { - u_int8_t position = emulator.getState().doorCurrentPosition; + u_int8_t position = HoermannSingleton::getInstance()->getEmulator()->getState().doorCurrentPosition; if(pos != position){ this->position = (float) position / 200.0; this->publish_state(); @@ -140,53 +119,9 @@ void modBusPolling(void *parameter) }; -// Custom binary output, for exposing binary states -class NbsLightOutput: public output::BinaryOutput, public Component { - public: - void setup() override { - } - - void write_state(bool state) override { - - } - - bool lastState = false; - bool firstState = false; - void loop() override { - if(!emulator.getState().valid) false; - if(!firstState || emulator.getState().lampOn != lastState){ - //ESP_LOGD("Test", "I have no idea"); - lastState = emulator.getState().lampOn; - this->set_state(lastState); - firstState = true; - } - } - - void turn_on() override { - if(!lastState) emulator.toggleLamp(); - } - void turn_off() override { - if(lastState) emulator.toggleLamp(); - } -}; - -volatile unsigned long lastCall = 0; -volatile unsigned long maxPeriod = 0; -void modBusPolling(void *parameter) -{ - while (true) - { - if (lastCall > 0) - { - maxPeriod = _max(micros() - lastCall, maxPeriod); - } - lastCall = micros(); - emulator.poll(); - vTaskDelay(1); - } - vTaskDelete(NULL); -} } // namespace hoermann -} // namespace esphome \ No newline at end of file +} // namespace esphome + +#endif \ No newline at end of file diff --git a/external_components/hoermann_door/door_singleton.h b/external_components/hoermann_door/door_singleton.h new file mode 100644 index 0000000..685e08c --- /dev/null +++ b/external_components/hoermann_door/door_singleton.h @@ -0,0 +1,79 @@ +#pragma once + +#include "esphome.h" +#include "Arduino.h" +#include "hciemulator.h" +//#include "cover.h" + +#define RS485 Serial2 +#define TX_ON 25 +//#define configMAX_PRIORITIES 25 + +#define TAG "hoermann_door" + + +void modBusPolling(void *parameter); + +class HoermannSingleton { + public: + HoermannSingleton() { + emulator; + } + static HoermannSingleton* instance_; + HCIEmulator emulator; + TaskHandle_t modBusTask; + bool hasBeenInitialized = false; + + public: + static HoermannSingleton* getInstance(){ + if(instance_ == nullptr){ + instance_ = new HoermannSingleton(); + } + return instance_; + } + + void initializeEmulator(){ + if(hasBeenInitialized) return; + hasBeenInitialized = true; + RS485.begin(57600, SERIAL_8E1, 16, 17); + pinMode(TX_ON, OUTPUT); + digitalWrite(TX_ON, LOW); + + xTaskCreatePinnedToCore( + modBusPolling, // Function to implement the task + "ModBusTask", // Name of the task + 10000, // Stack size in words + NULL, // Task input parameter + // 1, // Priority of the task + configMAX_PRIORITIES - 1, + &modBusTask, // Task handle. + 1 // Core where the task should run + ); + } + HCIEmulator *getEmulator(){ + return &emulator; + } +}; + +HoermannSingleton* HoermannSingleton::instance_ = nullptr; + +//DoorManager *DoorManager::getInstance() + + +volatile unsigned long lastCall = 0; +volatile unsigned long maxPeriod = 0; +void modBusPolling(void *parameter) +{ + auto emulator = HoermannSingleton::getInstance()->getEmulator(); + while (true) + { + if (lastCall > 0) + { + maxPeriod = _max(micros() - lastCall, maxPeriod); + } + lastCall = micros(); + emulator->poll(); + vTaskDelay(1); + } + vTaskDelete(NULL); +} \ No newline at end of file diff --git a/external_components/hoermann_door/light_component.h b/external_components/hoermann_door/light_component.h new file mode 100644 index 0000000..4928649 --- /dev/null +++ b/external_components/hoermann_door/light_component.h @@ -0,0 +1,106 @@ +#pragma once + +#ifdef USE_LIGHT + +#include "esphome.h" +#include "Arduino.h" +#include "hciemulator.h" +#include "door_singleton.h" +//#include "cover.h" + +#define RS485 Serial2 +#define TX_ON 25 +//#define configMAX_PRIORITIES 25 + +#define TAG "hoermann_door" + +namespace esphome { +namespace hoermann_door { + +// Custom binary output, for exposing binary states +class NbsLightManager: public binary::BinaryLightOutput, public light::LightState { + protected: + HoermannSingleton *single; + public: + void setup() override { + //single = HoermannSingleton::getInstance(); + //single->initializeGetInstance + } + + bool lastState = false; + bool firstState = false; + void loop() override { + //if(!DoorManager.getEmulator().getState().valid) false; + //if(!firstState || DoorManager.getEmulator().getState().lampOn != lastState){ + //ESP_LOGD("Test", "I have no idea"); + // lastState = DoorManager.getEmulator().getState().lampOn; + // this->write_state(lastState); + + // firstState = true; + //} + } +}; + +// Custom binary output, for exposing binary states +class NbsLightOutput: public output::BinaryOutput, public Component{ + + light::LightState *callback; + HoermannSingleton *single; + bool lastState = false; + bool firstState = true; + + public: + + virtual void write_state(bool state){ + if(lastState != state){ + single->getEmulator()->toggleLamp(); + //lastState = state; + } + } + + void set_state_callback(light::LightState *callback){ + if(callback == nullptr) ESP_LOGW("Hoermann_door(Light)", "Got Nullable callback"); + this->callback = callback; + } + + void loop() override { + if(!single->getEmulator()->getState().valid) false; + if(firstState || single->getEmulator()->getState().lampOn != lastState){ + //ESP_LOGD("Test", "I have no idea"); + lastState = single->getEmulator()->getState().lampOn; + if(lastState == true){ + ESP_LOGD("Hoermann_door(Light)", "Light State ON"); + + auto call = callback->make_call(); + call.set_state(true); + call.perform(); + } + else { + ESP_LOGD("Hoermann_door(Light)", "Light State OFF"); + auto call = callback->make_call(); + call.set_state(false); + call.perform(); + } + + firstState = false; + } + } + + void setup() override { + single = HoermannSingleton::getInstance(); + single->initializeEmulator(); + } + + void turn_on() override { + this->write_state(true); + } + void turn_off() override { + this->write_state(false); + } +}; + + +} // namespace hoermann +} // namespace esphome + +#endif \ No newline at end of file diff --git a/external_components/hoermann_door/output.py b/external_components/hoermann_door/output.py index 055f383..ed3ce0f 100644 --- a/external_components/hoermann_door/output.py +++ b/external_components/hoermann_door/output.py @@ -1,18 +1,22 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation -from esphome.components import output +from esphome.components import output, light from esphome.const import CONF_ID hoermann_cover_ns = cg.esphome_ns.namespace('hoermann_door') HoermannCoverLight = hoermann_cover_ns.class_('NbsLightOutput', output.BinaryOutput, cg.Component) +CONF_STATE_CALLBACK = "state_callback" CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(HoermannCoverLight) + cv.GenerateID(CONF_ID): cv.declare_id(HoermannCoverLight), + cv.Required(CONF_STATE_CALLBACK): cv.use_id(light.LightState) }).extend(cv.COMPONENT_SCHEMA) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) - yield output.register_output(var, config) \ No newline at end of file + yield output.register_output(var, config) + + callback = yield cg.get_variable(config[CONF_STATE_CALLBACK]) + cg.add(var.set_state_callback(callback)) \ No newline at end of file diff --git a/garage1.yaml b/garage1.yaml index 16ed17c..c68b8a0 100644 --- a/garage1.yaml +++ b/garage1.yaml @@ -9,19 +9,11 @@ esphome: libraries: - plerup/EspSoftwareSerial -# custom_component: -# - lambda: |- -# auto door = new HoermanDoor(); -# App.register_component(door); -# return {door}; -# components: -# - id: door_${garageSide} -# - lambda: |- -# auto light = new NbsLightOutput(); -# App.register_component(light); -# return {light}; -# components: -# - id: door_${garageSide}_lamp +ota: + password: !secret gd_passwd +api: + encryption: + key: !secret gd_key wifi: ssid: !secret wifi_ssid @@ -39,18 +31,16 @@ external_components: cover: - platform: hoermann_door name: door_${garageSide} + light: - platform: binary name: door_${garageSide}_lamp + id: lamp_id_${garageSide} output: light_out output: - id: light_out platform: hoermann_door + state_callback: lamp_id_${garageSide} -ota: - password: !secret gd_passwd -api: - encryption: - key: !secret gd_key \ No newline at end of file