Format all files
This commit is contained in:
+7
-7
@@ -8,22 +8,22 @@ esphome:
|
|||||||
version: "1.0"
|
version: "1.0"
|
||||||
|
|
||||||
external_components:
|
external_components:
|
||||||
- source: components
|
- source: components
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
device_base: !include templates/esp8266.yaml
|
device_base: !include templates/esp8266.yaml
|
||||||
sensor: !include templates/esp8266-vcc.yaml
|
sensor: !include templates/esp8266-vcc.yaml
|
||||||
|
|
||||||
one_wire:
|
one_wire:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: D1
|
pin: D1
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: dallas_temp
|
- platform: dallas_temp
|
||||||
address: 0x0401212E565C7428
|
address: 0x0401212E565C7428
|
||||||
id: bathroom_heater_temperature
|
id: bathroom_heater_temperature
|
||||||
|
|
||||||
- platform: duty_time
|
- platform: duty_time
|
||||||
id: continuous_on_time
|
id: continuous_on_time
|
||||||
name: Continuous on time
|
name: Continuous on time
|
||||||
accuracy_decimals: 0
|
accuracy_decimals: 0
|
||||||
@@ -34,7 +34,7 @@ remote_transmitter:
|
|||||||
id: heater_ir_transmitter
|
id: heater_ir_transmitter
|
||||||
|
|
||||||
climate:
|
climate:
|
||||||
- platform: zehnder
|
- platform: zehnder
|
||||||
name: None
|
name: None
|
||||||
id: bathroom_heater
|
id: bathroom_heater
|
||||||
transmitter_id: heater_ir_transmitter
|
transmitter_id: heater_ir_transmitter
|
||||||
@@ -42,7 +42,7 @@ climate:
|
|||||||
on_state:
|
on_state:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return x.mode == climate::CLIMATE_MODE_HEAT;'
|
lambda: "return x.mode == climate::CLIMATE_MODE_HEAT;"
|
||||||
then:
|
then:
|
||||||
- sensor.duty_time.start: continuous_on_time
|
- sensor.duty_time.start: continuous_on_time
|
||||||
else:
|
else:
|
||||||
@@ -50,7 +50,7 @@ climate:
|
|||||||
- sensor.duty_time.reset: continuous_on_time
|
- sensor.duty_time.reset: continuous_on_time
|
||||||
|
|
||||||
interval:
|
interval:
|
||||||
- interval: 1min
|
- interval: 1min
|
||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ switch:
|
|||||||
optimistic: true
|
optimistic: true
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: sntp
|
- platform: sntp
|
||||||
id: sntp_time
|
id: sntp_time
|
||||||
servers: !secret sntp_servers
|
servers: !secret sntp_servers
|
||||||
timezone: Europe/Zurich
|
timezone: Europe/Zurich
|
||||||
|
|||||||
+3
-3
@@ -14,7 +14,7 @@ packages:
|
|||||||
psram:
|
psram:
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
- id: camera_i2c
|
- id: camera_i2c
|
||||||
sda: GPIO26
|
sda: GPIO26
|
||||||
scl: GPIO27
|
scl: GPIO27
|
||||||
|
|
||||||
@@ -34,12 +34,12 @@ esp32_camera: # Ai-Thinker
|
|||||||
idle_framerate: 0.1fps
|
idle_framerate: 0.1fps
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: ledc
|
- platform: ledc
|
||||||
pin: GPIO4
|
pin: GPIO4
|
||||||
channel: 2 # PWM channel 1 is used for esp32_camera.
|
channel: 2 # PWM channel 1 is used for esp32_camera.
|
||||||
id: led
|
id: led
|
||||||
|
|
||||||
light:
|
light:
|
||||||
- platform: monochromatic
|
- platform: monochromatic
|
||||||
output: led
|
output: led
|
||||||
name: Flash
|
name: Flash
|
||||||
|
|||||||
@@ -8,12 +8,10 @@
|
|||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
class SyncFailed : public Trigger<> {
|
class SyncFailed : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
explicit SyncFailed(RATGDOComponent* parent)
|
explicit SyncFailed(RATGDOComponent* parent) {
|
||||||
{
|
|
||||||
parent->subscribe_sync_failed([this](optional<bool> state) {
|
parent->subscribe_sync_failed([this](optional<bool> state) {
|
||||||
if (state.value_or(false))
|
if (state.value_or(false)) this->trigger();
|
||||||
this->trigger();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "observable.h"
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "observable.h"
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
void log_once_callbacks_overflow(uint8_t max);
|
void log_once_callbacks_overflow(uint8_t max);
|
||||||
@@ -12,25 +13,24 @@ class OnceCallbacks;
|
|||||||
|
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
class OnceCallbacks<void(Ts...)> {
|
class OnceCallbacks<void(Ts...)> {
|
||||||
public:
|
public:
|
||||||
// Runtime max is 1 for all current usage (door_state waits, command_sent waits).
|
// Runtime max is 1 for all current usage (door_state waits, command_sent
|
||||||
// Set to 2 for safety margin.
|
// waits). Set to 2 for safety margin.
|
||||||
static constexpr uint8_t MAX_CALLBACKS = 2;
|
static constexpr uint8_t MAX_CALLBACKS = 2;
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void operator()(F&& callback)
|
void operator()(F&& callback) {
|
||||||
{
|
|
||||||
if (this->count_ >= MAX_CALLBACKS) {
|
if (this->count_ >= MAX_CALLBACKS) {
|
||||||
log_once_callbacks_overflow(MAX_CALLBACKS);
|
log_once_callbacks_overflow(MAX_CALLBACKS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->callbacks_[this->count_++] = Callback<Ts...>::create(std::forward<F>(callback));
|
this->callbacks_[this->count_++] =
|
||||||
|
Callback<Ts...>::create(std::forward<F>(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-entrant safe: count_ is zeroed before invoking callbacks,
|
// Re-entrant safe: count_ is zeroed before invoking callbacks,
|
||||||
// so callbacks can queue new entries during trigger().
|
// so callbacks can queue new entries during trigger().
|
||||||
void trigger(Ts... args)
|
void trigger(Ts... args) {
|
||||||
{
|
|
||||||
uint8_t count = this->count_;
|
uint8_t count = this->count_;
|
||||||
this->count_ = 0;
|
this->count_ = 0;
|
||||||
for (uint8_t i = 0; i < count; i++) {
|
for (uint8_t i = 0; i < count; i++) {
|
||||||
@@ -41,9 +41,9 @@ public:
|
|||||||
void clear() { this->count_ = 0; }
|
void clear() { this->count_ = 0; }
|
||||||
uint8_t count() const { return this->count_; }
|
uint8_t count() const { return this->count_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Callback<Ts...> callbacks_[MAX_CALLBACKS] { };
|
Callback<Ts...> callbacks_[MAX_CALLBACKS]{};
|
||||||
uint8_t count_ { 0 };
|
uint8_t count_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::ratgdo
|
} // namespace esphome::ratgdo
|
||||||
|
|||||||
@@ -7,9 +7,8 @@
|
|||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
class CoverOpeningTrigger : public Trigger<> {
|
class CoverOpeningTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
CoverOpeningTrigger(cover::Cover* a_cover)
|
CoverOpeningTrigger(cover::Cover* a_cover) {
|
||||||
{
|
|
||||||
a_cover->add_on_state_callback([this, a_cover]() {
|
a_cover->add_on_state_callback([this, a_cover]() {
|
||||||
if (a_cover->current_operation == cover::COVER_OPERATION_OPENING) {
|
if (a_cover->current_operation == cover::COVER_OPERATION_OPENING) {
|
||||||
this->trigger();
|
this->trigger();
|
||||||
@@ -19,9 +18,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class CoverClosingTrigger : public Trigger<> {
|
class CoverClosingTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
CoverClosingTrigger(cover::Cover* a_cover)
|
CoverClosingTrigger(cover::Cover* a_cover) {
|
||||||
{
|
|
||||||
a_cover->add_on_state_callback([this, a_cover]() {
|
a_cover->add_on_state_callback([this, a_cover]() {
|
||||||
if (a_cover->current_operation == cover::COVER_OPERATION_CLOSING) {
|
if (a_cover->current_operation == cover::COVER_OPERATION_CLOSING) {
|
||||||
this->trigger();
|
this->trigger();
|
||||||
@@ -31,12 +29,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class CoverStateTrigger : public Trigger<> {
|
class CoverStateTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
CoverStateTrigger(cover::Cover* a_cover)
|
CoverStateTrigger(cover::Cover* a_cover) {
|
||||||
{
|
a_cover->add_on_state_callback([this, a_cover]() { this->trigger(); });
|
||||||
a_cover->add_on_state_callback([this, a_cover]() {
|
|
||||||
this->trigger();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "ratgdo_cover.h"
|
#include "ratgdo_cover.h"
|
||||||
|
|
||||||
#include "../ratgdo_state.h"
|
#include "../ratgdo_state.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
@@ -8,13 +9,9 @@ using namespace esphome::cover;
|
|||||||
|
|
||||||
static const char* const TAG = "ratgdo.cover";
|
static const char* const TAG = "ratgdo.cover";
|
||||||
|
|
||||||
void RATGDOCover::dump_config()
|
void RATGDOCover::dump_config() { LOG_COVER("", "RATGDO Cover", this); }
|
||||||
{
|
|
||||||
LOG_COVER("", "RATGDO Cover", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RATGDOCover::setup()
|
void RATGDOCover::setup() {
|
||||||
{
|
|
||||||
auto state = this->restore_state_();
|
auto state = this->restore_state_();
|
||||||
if (state.has_value()) {
|
if (state.has_value()) {
|
||||||
this->parent_->set_door_position(state.value().position);
|
this->parent_->set_door_position(state.value().position);
|
||||||
@@ -24,8 +21,7 @@ void RATGDOCover::setup()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOCover::on_door_state(DoorState state, float position)
|
void RATGDOCover::on_door_state(DoorState state, float position) {
|
||||||
{
|
|
||||||
if (!*this->parent_->synced || state == DoorState::UNKNOWN) {
|
if (!*this->parent_->synced || state == DoorState::UNKNOWN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,8 +60,7 @@ void RATGDOCover::on_door_state(DoorState state, float position)
|
|||||||
this->publish_state(save_to_flash);
|
this->publish_state(save_to_flash);
|
||||||
}
|
}
|
||||||
|
|
||||||
CoverTraits RATGDOCover::get_traits()
|
CoverTraits RATGDOCover::get_traits() {
|
||||||
{
|
|
||||||
auto traits = CoverTraits();
|
auto traits = CoverTraits();
|
||||||
traits.set_supports_stop(true);
|
traits.set_supports_stop(true);
|
||||||
traits.set_supports_toggle(true);
|
traits.set_supports_toggle(true);
|
||||||
@@ -73,8 +68,7 @@ CoverTraits RATGDOCover::get_traits()
|
|||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOCover::control(const CoverCall& call)
|
void RATGDOCover::control(const CoverCall& call) {
|
||||||
{
|
|
||||||
if (call.get_stop()) {
|
if (call.get_stop()) {
|
||||||
this->parent_->door_stop();
|
this->parent_->door_stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
class RATGDOCover : public cover::Cover, public RATGDOClient, public Component {
|
class RATGDOCover : public cover::Cover, public RATGDOClient, public Component {
|
||||||
public:
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
||||||
cover::CoverTraits get_traits() override;
|
cover::CoverTraits get_traits() override;
|
||||||
void on_door_state(DoorState state, float position);
|
void on_door_state(DoorState state, float position);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void control(const cover::CoverCall& call) override;
|
void control(const cover::CoverCall& call) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "ratgdo_light_output.h"
|
#include "ratgdo_light_output.h"
|
||||||
|
|
||||||
#include "../ratgdo_state.h"
|
#include "../ratgdo_state.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
@@ -8,52 +9,41 @@ using namespace esphome::light;
|
|||||||
|
|
||||||
static const char* const TAG = "ratgdo.light";
|
static const char* const TAG = "ratgdo.light";
|
||||||
|
|
||||||
void RATGDOLightOutput::dump_config()
|
void RATGDOLightOutput::dump_config() { ESP_LOGCONFIG(TAG, "RATGDO Light"); }
|
||||||
{
|
|
||||||
ESP_LOGCONFIG(TAG, "RATGDO Light");
|
void RATGDOLightOutput::setup() {
|
||||||
|
this->parent_->subscribe_light_state(
|
||||||
|
[this](LightState state) { this->on_light_state(state); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOLightOutput::setup()
|
void RATGDOLightOutput::on_light_state(esphome::ratgdo::LightState state) {
|
||||||
{
|
|
||||||
this->parent_->subscribe_light_state([this](LightState state) {
|
|
||||||
this->on_light_state(state);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void RATGDOLightOutput::on_light_state(esphome::ratgdo::LightState state)
|
|
||||||
{
|
|
||||||
if (this->light_state_) {
|
if (this->light_state_) {
|
||||||
this->has_initial_state_ = true;
|
this->has_initial_state_ = true;
|
||||||
set_state(state);
|
set_state(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOLightOutput::set_state(esphome::ratgdo::LightState state)
|
void RATGDOLightOutput::set_state(esphome::ratgdo::LightState state) {
|
||||||
{
|
|
||||||
bool is_on = state == LightState::ON;
|
bool is_on = state == LightState::ON;
|
||||||
this->light_state_->current_values.set_state(is_on);
|
this->light_state_->current_values.set_state(is_on);
|
||||||
this->light_state_->remote_values.set_state(is_on);
|
this->light_state_->remote_values.set_state(is_on);
|
||||||
this->light_state_->publish_state();
|
this->light_state_->publish_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOLightOutput::setup_state(light::LightState* light_state)
|
void RATGDOLightOutput::setup_state(light::LightState* light_state) {
|
||||||
{
|
|
||||||
esphome::ratgdo::LightState state = this->parent_->get_light_state();
|
esphome::ratgdo::LightState state = this->parent_->get_light_state();
|
||||||
this->light_state_ = light_state;
|
this->light_state_ = light_state;
|
||||||
this->set_state(state);
|
this->set_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
LightTraits RATGDOLightOutput::get_traits()
|
LightTraits RATGDOLightOutput::get_traits() {
|
||||||
{
|
|
||||||
auto traits = LightTraits();
|
auto traits = LightTraits();
|
||||||
traits.set_supported_color_modes({ light::ColorMode::ON_OFF });
|
traits.set_supported_color_modes({light::ColorMode::ON_OFF});
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOLightOutput::write_state(light::LightState* state)
|
void RATGDOLightOutput::write_state(light::LightState* state) {
|
||||||
{
|
if (!this->has_initial_state_) return;
|
||||||
if (!this->has_initial_state_)
|
|
||||||
return;
|
|
||||||
bool binary;
|
bool binary;
|
||||||
state->current_values_as_binary(&binary);
|
state->current_values_as_binary(&binary);
|
||||||
if (binary) {
|
if (binary) {
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
class RATGDOLightOutput : public light::LightOutput, public RATGDOClient, public Component {
|
class RATGDOLightOutput : public light::LightOutput,
|
||||||
public:
|
public RATGDOClient,
|
||||||
|
public Component {
|
||||||
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
light::LightTraits get_traits() override;
|
light::LightTraits get_traits() override;
|
||||||
@@ -19,7 +21,7 @@ public:
|
|||||||
|
|
||||||
void on_light_state(esphome::ratgdo::LightState state);
|
void on_light_state(esphome::ratgdo::LightState state);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
light::LightState* light_state_;
|
light::LightState* light_state_;
|
||||||
bool has_initial_state_ = false;
|
bool has_initial_state_ = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "ratgdo_lock.h"
|
#include "ratgdo_lock.h"
|
||||||
|
|
||||||
#include "../ratgdo_state.h"
|
#include "../ratgdo_state.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
@@ -6,25 +7,23 @@ namespace esphome::ratgdo {
|
|||||||
|
|
||||||
static const char* const TAG = "ratgdo.lock";
|
static const char* const TAG = "ratgdo.lock";
|
||||||
|
|
||||||
void RATGDOLock::dump_config()
|
void RATGDOLock::dump_config() {
|
||||||
{
|
|
||||||
LOG_LOCK("", "RATGDO Lock", this);
|
LOG_LOCK("", "RATGDO Lock", this);
|
||||||
ESP_LOGCONFIG(TAG, " Type: Lock");
|
ESP_LOGCONFIG(TAG, " Type: Lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOLock::setup()
|
void RATGDOLock::setup() {
|
||||||
{
|
this->parent_->subscribe_lock_state(
|
||||||
this->parent_->subscribe_lock_state([this](LockState state) {
|
[this](LockState state) { this->on_lock_state(state); });
|
||||||
this->on_lock_state(state);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOLock::on_lock_state(LockState state)
|
void RATGDOLock::on_lock_state(LockState state) {
|
||||||
{
|
if (state == LockState::LOCKED &&
|
||||||
if (state == LockState::LOCKED && this->state == lock::LockState::LOCK_STATE_LOCKED) {
|
this->state == lock::LockState::LOCK_STATE_LOCKED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (state == LockState::UNLOCKED && this->state == lock::LockState::LOCK_STATE_UNLOCKED) {
|
if (state == LockState::UNLOCKED &&
|
||||||
|
this->state == lock::LockState::LOCK_STATE_UNLOCKED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +36,7 @@ void RATGDOLock::on_lock_state(LockState state)
|
|||||||
this->publish_state(*call.get_state());
|
this->publish_state(*call.get_state());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOLock::control(const lock::LockCall& call)
|
void RATGDOLock::control(const lock::LockCall& call) {
|
||||||
{
|
|
||||||
auto state = *call.get_state();
|
auto state = *call.get_state();
|
||||||
|
|
||||||
if (state == lock::LockState::LOCK_STATE_LOCKED) {
|
if (state == lock::LockState::LOCK_STATE_LOCKED) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
class RATGDOLock : public lock::Lock, public RATGDOClient, public Component {
|
class RATGDOLock : public lock::Lock, public RATGDOClient, public Component {
|
||||||
public:
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "ratgdo_number.h"
|
#include "ratgdo_number.h"
|
||||||
|
|
||||||
#include "../ratgdo_state.h"
|
#include "../ratgdo_state.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
@@ -6,8 +7,7 @@ namespace esphome::ratgdo {
|
|||||||
|
|
||||||
static const char* const TAG = "ratgdo.number";
|
static const char* const TAG = "ratgdo.number";
|
||||||
|
|
||||||
void RATGDONumber::dump_config()
|
void RATGDONumber::dump_config() {
|
||||||
{
|
|
||||||
LOG_NUMBER("", "RATGDO Number", this);
|
LOG_NUMBER("", "RATGDO Number", this);
|
||||||
switch (this->number_type_) {
|
switch (this->number_type_) {
|
||||||
case RATGDO_OPENING_DURATION:
|
case RATGDO_OPENING_DURATION:
|
||||||
@@ -21,8 +21,7 @@ void RATGDONumber::dump_config()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDONumber::setup()
|
void RATGDONumber::setup() {
|
||||||
{
|
|
||||||
float value;
|
float value;
|
||||||
this->pref_ = this->make_entity_preference<float>();
|
this->pref_ = this->make_entity_preference<float>();
|
||||||
if (!this->pref_.load(&value)) {
|
if (!this->pref_.load(&value)) {
|
||||||
@@ -32,22 +31,19 @@ void RATGDONumber::setup()
|
|||||||
|
|
||||||
switch (this->number_type_) {
|
switch (this->number_type_) {
|
||||||
case RATGDO_OPENING_DURATION:
|
case RATGDO_OPENING_DURATION:
|
||||||
this->parent_->subscribe_opening_duration([this](float value) {
|
this->parent_->subscribe_opening_duration(
|
||||||
this->update_state(value);
|
[this](float value) { this->update_state(value); });
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case RATGDO_CLOSING_DURATION:
|
case RATGDO_CLOSING_DURATION:
|
||||||
this->parent_->subscribe_closing_duration([this](float value) {
|
this->parent_->subscribe_closing_duration(
|
||||||
this->update_state(value);
|
[this](float value) { this->update_state(value); });
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDONumber::set_number_type(NumberType number_type_)
|
void RATGDONumber::set_number_type(NumberType number_type_) {
|
||||||
{
|
|
||||||
this->number_type_ = number_type_;
|
this->number_type_ = number_type_;
|
||||||
switch (this->number_type_) {
|
switch (this->number_type_) {
|
||||||
case RATGDO_OPENING_DURATION:
|
case RATGDO_OPENING_DURATION:
|
||||||
@@ -61,8 +57,7 @@ void RATGDONumber::set_number_type(NumberType number_type_)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDONumber::update_state(float value)
|
void RATGDONumber::update_state(float value) {
|
||||||
{
|
|
||||||
if (value == this->state) {
|
if (value == this->state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -70,8 +65,7 @@ void RATGDONumber::update_state(float value)
|
|||||||
this->publish_state(value);
|
this->publish_state(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDONumber::control(float value)
|
void RATGDONumber::control(float value) {
|
||||||
{
|
|
||||||
switch (this->number_type_) {
|
switch (this->number_type_) {
|
||||||
case RATGDO_OPENING_DURATION:
|
case RATGDO_OPENING_DURATION:
|
||||||
this->parent_->set_opening_duration(value);
|
this->parent_->set_opening_duration(value);
|
||||||
|
|||||||
@@ -13,20 +13,24 @@ enum NumberType {
|
|||||||
RATGDO_CLOSING_DURATION,
|
RATGDO_CLOSING_DURATION,
|
||||||
};
|
};
|
||||||
|
|
||||||
class RATGDONumber : public number::Number, public RATGDOClient, public Component {
|
class RATGDONumber : public number::Number,
|
||||||
public:
|
public RATGDOClient,
|
||||||
|
public Component {
|
||||||
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void set_number_type(NumberType number_type);
|
void set_number_type(NumberType number_type);
|
||||||
// other esphome components that persist state in the flash have HARDWARE priority
|
// other esphome components that persist state in the flash have HARDWARE
|
||||||
// ensure we get initialized before them, so that the state doesn't get invalidated
|
// priority ensure we get initialized before them, so that the state doesn't
|
||||||
// by components that might be added in the future
|
// get invalidated by components that might be added in the future
|
||||||
float get_setup_priority() const override { return setup_priority::HARDWARE + 1; }
|
float get_setup_priority() const override {
|
||||||
|
return setup_priority::HARDWARE + 1;
|
||||||
|
}
|
||||||
|
|
||||||
void update_state(float value);
|
void update_state(float value);
|
||||||
void control(float value) override;
|
void control(float value) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NumberType number_type_;
|
NumberType number_type_;
|
||||||
ESPPreferenceObject pref_;
|
ESPPreferenceObject pref_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "observable.h"
|
#include "observable.h"
|
||||||
|
|
||||||
#include "callbacks.h"
|
#include "callbacks.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
@@ -6,19 +7,20 @@ namespace esphome::ratgdo {
|
|||||||
|
|
||||||
static const char* const TAG = "ratgdo.observable";
|
static const char* const TAG = "ratgdo.observable";
|
||||||
|
|
||||||
void log_multiple_subscribers()
|
void log_multiple_subscribers() {
|
||||||
{
|
ESP_LOGE(TAG,
|
||||||
ESP_LOGE(TAG, "single_observable already has a subscriber! This will overwrite the existing subscriber.");
|
"single_observable already has a subscriber! This will overwrite "
|
||||||
|
"the existing subscriber.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_observer_overflow()
|
void log_observer_overflow() {
|
||||||
{
|
ESP_LOGE(TAG,
|
||||||
ESP_LOGE(TAG, "observable has too many subscribers! Ignoring new subscriber.");
|
"observable has too many subscribers! Ignoring new subscriber.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_once_callbacks_overflow(uint8_t max)
|
void log_once_callbacks_overflow(uint8_t max) {
|
||||||
{
|
ESP_LOGE(TAG, "OnceCallbacks overflow (max %u)! Ignoring callback.",
|
||||||
ESP_LOGE(TAG, "OnceCallbacks overflow (max %u)! Ignoring callback.", static_cast<unsigned>(max));
|
static_cast<unsigned>(max));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome::ratgdo
|
} // namespace esphome::ratgdo
|
||||||
|
|||||||
@@ -11,29 +11,32 @@ void log_multiple_subscribers();
|
|||||||
void log_observer_overflow();
|
void log_observer_overflow();
|
||||||
|
|
||||||
// Lightweight type-erased callback (16 bytes on 32-bit).
|
// Lightweight type-erased callback (16 bytes on 32-bit).
|
||||||
// For small trivially-copyable callables (like [this], [this, f], or [this, f, id] lambdas),
|
// For small trivially-copyable callables (like [this], [this, f], or [this, f,
|
||||||
// stores the callable inline — zero heap allocation.
|
// id] lambdas), stores the callable inline — zero heap allocation. Supports up
|
||||||
// Supports up to 3 * sizeof(void*) bytes (12 bytes on 32-bit, 24 on 64-bit).
|
// to 3 * sizeof(void*) bytes (12 bytes on 32-bit, 24 on 64-bit).
|
||||||
inline constexpr size_t CALLBACK_STORAGE_SIZE = 3 * sizeof(void*);
|
inline constexpr size_t CALLBACK_STORAGE_SIZE = 3 * sizeof(void*);
|
||||||
|
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
struct Callback {
|
struct Callback {
|
||||||
using fn_t = void (*)(const void*, Ts...);
|
using fn_t = void (*)(const void*, Ts...);
|
||||||
fn_t fn_ { nullptr };
|
fn_t fn_{nullptr};
|
||||||
alignas(void*) uint8_t storage_[CALLBACK_STORAGE_SIZE] { };
|
alignas(void*) uint8_t storage_[CALLBACK_STORAGE_SIZE]{};
|
||||||
|
|
||||||
void call(Ts... args) const { this->fn_(this->storage_, args...); }
|
void call(Ts... args) const { this->fn_(this->storage_, args...); }
|
||||||
explicit operator bool() const { return this->fn_ != nullptr; }
|
explicit operator bool() const { return this->fn_ != nullptr; }
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
static Callback create(F&& f)
|
static Callback create(F&& f) {
|
||||||
{
|
|
||||||
Callback cb;
|
Callback cb;
|
||||||
using Decay = std::decay_t<F>;
|
using Decay = std::decay_t<F>;
|
||||||
static_assert(!std::is_function_v<std::remove_reference_t<F>>,
|
static_assert(!std::is_function_v<std::remove_reference_t<F>>,
|
||||||
"Pass function pointers, not function references");
|
"Pass function pointers, not function references");
|
||||||
static_assert(std::is_trivially_copyable_v<Decay>, "Observable callbacks must be trivially copyable (e.g. [this] lambdas)");
|
static_assert(std::is_trivially_copyable_v<Decay>,
|
||||||
static_assert(sizeof(Decay) <= CALLBACK_STORAGE_SIZE, "Observable callbacks must fit in storage (capture at most 3 pointers)");
|
"Observable callbacks must be trivially copyable (e.g. "
|
||||||
|
"[this] lambdas)");
|
||||||
|
static_assert(sizeof(Decay) <= CALLBACK_STORAGE_SIZE,
|
||||||
|
"Observable callbacks must fit in storage (capture at most 3 "
|
||||||
|
"pointers)");
|
||||||
cb.fn_ = [](const void* storage, Ts... args) {
|
cb.fn_ = [](const void* storage, Ts... args) {
|
||||||
alignas(Decay) char buf[sizeof(Decay)];
|
alignas(Decay) char buf[sizeof(Decay)];
|
||||||
__builtin_memcpy(buf, storage, sizeof(Decay));
|
__builtin_memcpy(buf, storage, sizeof(Decay));
|
||||||
@@ -47,15 +50,11 @@ struct Callback {
|
|||||||
// Primary template for observable with subscribers.
|
// Primary template for observable with subscribers.
|
||||||
template <typename T, uint8_t MaxObservers>
|
template <typename T, uint8_t MaxObservers>
|
||||||
class observable {
|
class observable {
|
||||||
public:
|
public:
|
||||||
observable(const T& value)
|
observable(const T& value) : value_(value) {}
|
||||||
: value_(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
observable& operator=(U value)
|
observable& operator=(U value) {
|
||||||
{
|
|
||||||
if (value != this->value_) {
|
if (value != this->value_) {
|
||||||
this->value_ = value;
|
this->value_ = value;
|
||||||
this->notify();
|
this->notify();
|
||||||
@@ -67,40 +66,35 @@ public:
|
|||||||
T const& operator*() const { return this->value_; }
|
T const& operator*() const { return this->value_; }
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void subscribe(F&& observer)
|
void subscribe(F&& observer) {
|
||||||
{
|
|
||||||
if (this->count_ >= MaxObservers) {
|
if (this->count_ >= MaxObservers) {
|
||||||
log_observer_overflow();
|
log_observer_overflow();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->observers_[this->count_++] = Callback<T>::create(std::forward<F>(observer));
|
this->observers_[this->count_++] =
|
||||||
|
Callback<T>::create(std::forward<F>(observer));
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify() const
|
void notify() const {
|
||||||
{
|
|
||||||
for (uint8_t i = 0; i < this->count_; i++) {
|
for (uint8_t i = 0; i < this->count_; i++) {
|
||||||
this->observers_[i].call(this->value_);
|
this->observers_[i].call(this->value_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T value_;
|
T value_;
|
||||||
Callback<T> observers_[MaxObservers] { };
|
Callback<T> observers_[MaxObservers]{};
|
||||||
uint8_t count_ { 0 };
|
uint8_t count_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Specialization for zero subscribers — no array, no count, notify is a no-op.
|
// Specialization for zero subscribers — no array, no count, notify is a no-op.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class observable<T, 0> {
|
class observable<T, 0> {
|
||||||
public:
|
public:
|
||||||
observable(const T& value)
|
observable(const T& value) : value_(value) {}
|
||||||
: value_(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
observable& operator=(U value)
|
observable& operator=(U value) {
|
||||||
{
|
|
||||||
if (value != this->value_) {
|
if (value != this->value_) {
|
||||||
this->value_ = value;
|
this->value_ = value;
|
||||||
}
|
}
|
||||||
@@ -111,28 +105,23 @@ public:
|
|||||||
T const& operator*() const { return this->value_; }
|
T const& operator*() const { return this->value_; }
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void subscribe(F&&)
|
void subscribe(F&&) {
|
||||||
{
|
|
||||||
log_observer_overflow();
|
log_observer_overflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify() const { }
|
void notify() const {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T value_;
|
T value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class single_observable {
|
class single_observable {
|
||||||
public:
|
public:
|
||||||
single_observable(const T& value)
|
single_observable(const T& value) : value_(value) {}
|
||||||
: value_(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
single_observable& operator=(U value)
|
single_observable& operator=(U value) {
|
||||||
{
|
|
||||||
if (value != this->value_) {
|
if (value != this->value_) {
|
||||||
this->value_ = value;
|
this->value_ = value;
|
||||||
this->notify();
|
this->notify();
|
||||||
@@ -144,24 +133,22 @@ public:
|
|||||||
T const& operator*() const { return this->value_; }
|
T const& operator*() const { return this->value_; }
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void subscribe(F&& observer)
|
void subscribe(F&& observer) {
|
||||||
{
|
|
||||||
if (this->observer_) {
|
if (this->observer_) {
|
||||||
log_multiple_subscribers();
|
log_multiple_subscribers();
|
||||||
}
|
}
|
||||||
this->observer_ = Callback<T>::create(std::forward<F>(observer));
|
this->observer_ = Callback<T>::create(std::forward<F>(observer));
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify() const
|
void notify() const {
|
||||||
{
|
|
||||||
if (this->observer_) {
|
if (this->observer_) {
|
||||||
this->observer_.call(this->value_);
|
this->observer_.call(this->value_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T value_;
|
T value_;
|
||||||
Callback<T> observer_ { };
|
Callback<T> observer_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::ratgdo
|
} // namespace esphome::ratgdo
|
||||||
|
|||||||
+86
-103
@@ -5,19 +5,16 @@
|
|||||||
************************************/
|
************************************/
|
||||||
|
|
||||||
#include "ratgdo.h"
|
#include "ratgdo.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "ratgdo_state.h"
|
|
||||||
|
|
||||||
#include "secplus2.h"
|
|
||||||
|
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/gpio.h"
|
#include "esphome/core/gpio.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "ratgdo_state.h"
|
||||||
|
#include "secplus2.h"
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char* const TAG = "ratgdo";
|
static const char* const TAG = "ratgdo";
|
||||||
static constexpr int SYNC_DELAY = 5000;
|
static constexpr int SYNC_DELAY = 5000;
|
||||||
// Door state updates arrive over UART every ~200-400ms during movement.
|
// Door state updates arrive over UART every ~200-400ms during movement.
|
||||||
@@ -28,14 +25,12 @@ static constexpr uint32_t DOOR_STATE_CALLBACK_TIMEOUT = 2000;
|
|||||||
|
|
||||||
using namespace scheduler_ids;
|
using namespace scheduler_ids;
|
||||||
|
|
||||||
void log_subscriber_overflow(const LogString* observable_name, uint32_t max)
|
void log_subscriber_overflow(const LogString* observable_name, uint32_t max) {
|
||||||
{
|
|
||||||
ESP_LOGE(TAG, "Too many subscribers for %s (max %d)",
|
ESP_LOGE(TAG, "Too many subscribers for %s (max %d)",
|
||||||
LOG_STR_ARG(observable_name), (int)max);
|
LOG_STR_ARG(observable_name), (int)max);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::setup()
|
void RATGDOComponent::setup() {
|
||||||
{
|
|
||||||
this->output_gdo_pin_->setup();
|
this->output_gdo_pin_->setup();
|
||||||
this->output_gdo_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
this->output_gdo_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||||
|
|
||||||
@@ -55,39 +50,30 @@ void RATGDOComponent::setup()
|
|||||||
this->lock_state.notify();
|
this->lock_state.notify();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializing protocol, this gets called before setup() because
|
// initializing protocol, this gets called before setup() because
|
||||||
// its children components might require that
|
// its children components might require that
|
||||||
void RATGDOComponent::init_protocol()
|
void RATGDOComponent::init_protocol() {
|
||||||
{
|
|
||||||
this->protocol_ = new secplus2::Secplus2();
|
this->protocol_ = new secplus2::Secplus2();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::loop()
|
void RATGDOComponent::loop() { this->protocol_->loop(); }
|
||||||
{
|
|
||||||
this->protocol_->loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RATGDOComponent::dump_config()
|
void RATGDOComponent::dump_config() {
|
||||||
{
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up RATGDO...");
|
ESP_LOGCONFIG(TAG, "Setting up RATGDO...");
|
||||||
LOG_PIN(" Output GDO Pin: ", this->output_gdo_pin_);
|
LOG_PIN(" Output GDO Pin: ", this->output_gdo_pin_);
|
||||||
LOG_PIN(" Input GDO Pin: ", this->input_gdo_pin_);
|
LOG_PIN(" Input GDO Pin: ", this->input_gdo_pin_);
|
||||||
this->protocol_->dump_config();
|
this->protocol_->dump_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::on_shutdown()
|
void RATGDOComponent::on_shutdown() {
|
||||||
{
|
|
||||||
if (this->protocol_ != nullptr) {
|
if (this->protocol_ != nullptr) {
|
||||||
this->protocol_->on_shutdown();
|
this->protocol_->on_shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::received(const DoorState door_state)
|
void RATGDOComponent::received(const DoorState door_state) {
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "Door state=%s", LOG_STR_ARG(DoorState_to_string(door_state)));
|
ESP_LOGD(TAG, "Door state=%s", LOG_STR_ARG(DoorState_to_string(door_state)));
|
||||||
|
|
||||||
auto prev_door_state = *this->door_state;
|
auto prev_door_state = *this->door_state;
|
||||||
@@ -98,10 +84,12 @@ void RATGDOComponent::received(const DoorState door_state)
|
|||||||
|
|
||||||
// opening duration calibration
|
// opening duration calibration
|
||||||
if (*this->opening_duration == 0) {
|
if (*this->opening_duration == 0) {
|
||||||
if (door_state == DoorState::OPENING && prev_door_state == DoorState::CLOSED) {
|
if (door_state == DoorState::OPENING &&
|
||||||
|
prev_door_state == DoorState::CLOSED) {
|
||||||
this->start_opening = millis();
|
this->start_opening = millis();
|
||||||
}
|
}
|
||||||
if (door_state == DoorState::OPEN && prev_door_state == DoorState::OPENING && this->start_opening > 0) {
|
if (door_state == DoorState::OPEN &&
|
||||||
|
prev_door_state == DoorState::OPENING && this->start_opening > 0) {
|
||||||
auto duration = (millis() - this->start_opening) / 1000;
|
auto duration = (millis() - this->start_opening) / 1000;
|
||||||
this->set_opening_duration(round(duration * 10) / 10);
|
this->set_opening_duration(round(duration * 10) / 10);
|
||||||
}
|
}
|
||||||
@@ -111,10 +99,12 @@ void RATGDOComponent::received(const DoorState door_state)
|
|||||||
}
|
}
|
||||||
// closing duration calibration
|
// closing duration calibration
|
||||||
if (*this->closing_duration == 0) {
|
if (*this->closing_duration == 0) {
|
||||||
if (door_state == DoorState::CLOSING && prev_door_state == DoorState::OPEN) {
|
if (door_state == DoorState::CLOSING &&
|
||||||
|
prev_door_state == DoorState::OPEN) {
|
||||||
this->start_closing = millis();
|
this->start_closing = millis();
|
||||||
}
|
}
|
||||||
if (door_state == DoorState::CLOSED && prev_door_state == DoorState::CLOSING && this->start_closing > 0) {
|
if (door_state == DoorState::CLOSED &&
|
||||||
|
prev_door_state == DoorState::CLOSING && this->start_closing > 0) {
|
||||||
auto duration = (millis() - this->start_closing) / 1000;
|
auto duration = (millis() - this->start_closing) / 1000;
|
||||||
this->set_closing_duration(round(duration * 10) / 10);
|
this->set_closing_duration(round(duration * 10) / 10);
|
||||||
}
|
}
|
||||||
@@ -182,21 +172,18 @@ void RATGDOComponent::received(const DoorState door_state)
|
|||||||
this->on_door_state_.trigger(door_state);
|
this->on_door_state_.trigger(door_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::received(const LightState light_state)
|
void RATGDOComponent::received(const LightState light_state) {
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "Light state=%s",
|
ESP_LOGD(TAG, "Light state=%s",
|
||||||
LOG_STR_ARG(LightState_to_string(light_state)));
|
LOG_STR_ARG(LightState_to_string(light_state)));
|
||||||
this->light_state = light_state;
|
this->light_state = light_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::received(const LockState lock_state)
|
void RATGDOComponent::received(const LockState lock_state) {
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "Lock state=%s", LOG_STR_ARG(LockState_to_string(lock_state)));
|
ESP_LOGD(TAG, "Lock state=%s", LOG_STR_ARG(LockState_to_string(lock_state)));
|
||||||
this->lock_state = lock_state;
|
this->lock_state = lock_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::received(const LightAction light_action)
|
void RATGDOComponent::received(const LightAction light_action) {
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "Light cmd=%s state=%s",
|
ESP_LOGD(TAG, "Light cmd=%s state=%s",
|
||||||
LOG_STR_ARG(LightAction_to_string(light_action)),
|
LOG_STR_ARG(LightAction_to_string(light_action)),
|
||||||
LOG_STR_ARG(LightState_to_string(*this->light_state)));
|
LOG_STR_ARG(LightState_to_string(*this->light_state)));
|
||||||
@@ -209,8 +196,7 @@ void RATGDOComponent::received(const LightAction light_action)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::received(const Openings openings)
|
void RATGDOComponent::received(const Openings openings) {
|
||||||
{
|
|
||||||
if (openings.flag == 0 || *this->openings != 0) {
|
if (openings.flag == 0 || *this->openings != 0) {
|
||||||
this->openings = openings.count;
|
this->openings = openings.count;
|
||||||
ESP_LOGD(TAG, "Openings: %d", *this->openings);
|
ESP_LOGD(TAG, "Openings: %d", *this->openings);
|
||||||
@@ -219,8 +205,7 @@ void RATGDOComponent::received(const Openings openings)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::schedule_door_position_sync(float update_period)
|
void RATGDOComponent::schedule_door_position_sync(float update_period) {
|
||||||
{
|
|
||||||
ESP_LOG1(
|
ESP_LOG1(
|
||||||
TAG,
|
TAG,
|
||||||
"Schedule position sync: delta %f, start position: %f, start moving: %d",
|
"Schedule position sync: delta %f, start position: %f, start moving: %d",
|
||||||
@@ -231,7 +216,8 @@ void RATGDOComponent::schedule_door_position_sync(float update_period)
|
|||||||
if (duration == 0) {
|
if (duration == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->position_sync_remaining_ = std::max(static_cast<uint16_t>(1000 * duration / update_period),
|
this->position_sync_remaining_ =
|
||||||
|
std::max(static_cast<uint16_t>(1000 * duration / update_period),
|
||||||
static_cast<uint16_t>(1));
|
static_cast<uint16_t>(1));
|
||||||
set_interval(INTERVAL_POSITION_SYNC, static_cast<uint32_t>(update_period),
|
set_interval(INTERVAL_POSITION_SYNC, static_cast<uint32_t>(update_period),
|
||||||
[this]() {
|
[this]() {
|
||||||
@@ -242,9 +228,10 @@ void RATGDOComponent::schedule_door_position_sync(float update_period)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::door_position_update()
|
void RATGDOComponent::door_position_update() {
|
||||||
{
|
if (this->door_start_moving == 0 ||
|
||||||
if (this->door_start_moving == 0 || this->door_start_position == DOOR_POSITION_UNKNOWN || this->door_move_delta == DOOR_DELTA_UNKNOWN) {
|
this->door_start_position == DOOR_POSITION_UNKNOWN ||
|
||||||
|
this->door_move_delta == DOOR_DELTA_UNKNOWN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto now = millis();
|
auto now = millis();
|
||||||
@@ -253,22 +240,27 @@ void RATGDOComponent::door_position_update()
|
|||||||
if (duration == 0) {
|
if (duration == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto position = this->door_start_position + (now - this->door_start_moving) / (1000 * duration);
|
auto position = this->door_start_position +
|
||||||
|
(now - this->door_start_moving) / (1000 * duration);
|
||||||
position = clamp(position, 0.0f, 1.0f);
|
position = clamp(position, 0.0f, 1.0f);
|
||||||
ESP_LOG2(TAG, "[%d] Position update: %f", now, position);
|
ESP_LOG2(TAG, "[%d] Position update: %f", now, position);
|
||||||
this->door_position = position;
|
this->door_position = position;
|
||||||
|
|
||||||
// Check if we reached our move-to-position target
|
// Check if we reached our move-to-position target
|
||||||
if (this->target_position_ != DOOR_POSITION_UNKNOWN && this->target_direction_ != DoorAction::UNKNOWN) {
|
if (this->target_position_ != DOOR_POSITION_UNKNOWN &&
|
||||||
|
this->target_direction_ != DoorAction::UNKNOWN) {
|
||||||
bool reached = false;
|
bool reached = false;
|
||||||
if (this->target_direction_ == DoorAction::OPEN && position >= this->target_position_) {
|
if (this->target_direction_ == DoorAction::OPEN &&
|
||||||
|
position >= this->target_position_) {
|
||||||
reached = true;
|
reached = true;
|
||||||
} else if (this->target_direction_ == DoorAction::CLOSE && position <= this->target_position_) {
|
} else if (this->target_direction_ == DoorAction::CLOSE &&
|
||||||
|
position <= this->target_position_) {
|
||||||
reached = true;
|
reached = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reached) {
|
if (reached) {
|
||||||
ESP_LOGD(TAG, "Reached target position %.2f, stopping door", this->target_position_);
|
ESP_LOGD(TAG, "Reached target position %.2f, stopping door",
|
||||||
|
this->target_position_);
|
||||||
this->door_stop();
|
this->door_stop();
|
||||||
this->target_position_ = DOOR_POSITION_UNKNOWN;
|
this->target_position_ = DOOR_POSITION_UNKNOWN;
|
||||||
this->target_direction_ = DoorAction::UNKNOWN;
|
this->target_direction_ = DoorAction::UNKNOWN;
|
||||||
@@ -276,37 +268,27 @@ void RATGDOComponent::door_position_update()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::set_opening_duration(float duration)
|
void RATGDOComponent::set_opening_duration(float duration) {
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "Set opening duration: %.1fs", duration);
|
ESP_LOGD(TAG, "Set opening duration: %.1fs", duration);
|
||||||
this->opening_duration = duration;
|
this->opening_duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::set_closing_duration(float duration)
|
void RATGDOComponent::set_closing_duration(float duration) {
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "Set closing duration: %.1fs", duration);
|
ESP_LOGD(TAG, "Set closing duration: %.1fs", duration);
|
||||||
this->closing_duration = duration;
|
this->closing_duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::set_distance_measurement(int16_t distance)
|
void RATGDOComponent::set_distance_measurement(int16_t distance) {
|
||||||
{
|
|
||||||
this->last_distance_measurement = distance;
|
this->last_distance_measurement = distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::query_status() { this->protocol_->query_status(); }
|
void RATGDOComponent::query_status() { this->protocol_->query_status(); }
|
||||||
|
|
||||||
void RATGDOComponent::query_openings()
|
void RATGDOComponent::query_openings() { this->protocol_->query_openings(); }
|
||||||
{
|
|
||||||
this->protocol_->query_openings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RATGDOComponent::sync()
|
void RATGDOComponent::sync() { this->protocol_->sync(); }
|
||||||
{
|
|
||||||
this->protocol_->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RATGDOComponent::set_door_state_expiry()
|
void RATGDOComponent::set_door_state_expiry() {
|
||||||
{
|
|
||||||
this->set_timeout(TIMEOUT_DOOR_STATE_EXPIRY, DOOR_STATE_CALLBACK_TIMEOUT,
|
this->set_timeout(TIMEOUT_DOOR_STATE_EXPIRY, DOOR_STATE_CALLBACK_TIMEOUT,
|
||||||
[this]() {
|
[this]() {
|
||||||
ESP_LOGW(TAG, "Door state callback expired, clearing");
|
ESP_LOGW(TAG, "Door state callback expired, clearing");
|
||||||
@@ -314,29 +296,35 @@ void RATGDOComponent::set_door_state_expiry()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::cancel_door_state_expiry()
|
void RATGDOComponent::cancel_door_state_expiry() {
|
||||||
{
|
|
||||||
this->cancel_timeout(TIMEOUT_DOOR_STATE_EXPIRY);
|
this->cancel_timeout(TIMEOUT_DOOR_STATE_EXPIRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::smart_door_action(DoorAction target_direction)
|
void RATGDOComponent::smart_door_action(DoorAction target_direction) {
|
||||||
{
|
|
||||||
// target_direction must be OPEN or CLOSE
|
// target_direction must be OPEN or CLOSE
|
||||||
if (target_direction != DoorAction::OPEN && target_direction != DoorAction::CLOSE) {
|
if (target_direction != DoorAction::OPEN &&
|
||||||
|
target_direction != DoorAction::CLOSE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DoorState expected_state = (target_direction == DoorAction::OPEN) ? DoorState::OPENING : DoorState::CLOSING;
|
DoorState expected_state = (target_direction == DoorAction::OPEN)
|
||||||
DoorState opposite_state = (target_direction == DoorAction::OPEN) ? DoorState::CLOSING : DoorState::OPENING;
|
? DoorState::OPENING
|
||||||
|
: DoorState::CLOSING;
|
||||||
|
DoorState opposite_state = (target_direction == DoorAction::OPEN)
|
||||||
|
? DoorState::CLOSING
|
||||||
|
: DoorState::OPENING;
|
||||||
|
|
||||||
// 1. Try the discrete command first
|
// 1. Try the discrete command first
|
||||||
this->door_action(target_direction);
|
this->door_action(target_direction);
|
||||||
|
|
||||||
// 2. Set a timeout to check if it worked
|
// 2. Set a timeout to check if it worked
|
||||||
this->set_timeout(2000, [this, target_direction, expected_state, opposite_state]() {
|
this->set_timeout(
|
||||||
|
2000, [this, target_direction, expected_state, opposite_state]() {
|
||||||
if (*this->door_state == expected_state ||
|
if (*this->door_state == expected_state ||
|
||||||
(*this->door_state == DoorState::OPEN && target_direction == DoorAction::OPEN) ||
|
(*this->door_state == DoorState::OPEN &&
|
||||||
(*this->door_state == DoorState::CLOSED && target_direction == DoorAction::CLOSE)) {
|
target_direction == DoorAction::OPEN) ||
|
||||||
|
(*this->door_state == DoorState::CLOSED &&
|
||||||
|
target_direction == DoorAction::CLOSE)) {
|
||||||
// It worked (or is already at destination)
|
// It worked (or is already at destination)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -348,7 +336,8 @@ void RATGDOComponent::smart_door_action(DoorAction target_direction)
|
|||||||
this->door_action(DoorAction::TOGGLE);
|
this->door_action(DoorAction::TOGGLE);
|
||||||
|
|
||||||
// 4. Check what the toggle did
|
// 4. Check what the toggle did
|
||||||
this->on_door_state([this, target_direction, expected_state, opposite_state](DoorState s) {
|
this->on_door_state([this, target_direction, expected_state,
|
||||||
|
opposite_state](DoorState s) {
|
||||||
if (s == opposite_state) {
|
if (s == opposite_state) {
|
||||||
// Wrong direction! Stop it.
|
// Wrong direction! Stop it.
|
||||||
ESP_LOGW(TAG, "Toggle went the wrong way. Stopping...");
|
ESP_LOGW(TAG, "Toggle went the wrong way. Stopping...");
|
||||||
@@ -366,8 +355,7 @@ void RATGDOComponent::smart_door_action(DoorAction target_direction)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::door_open()
|
void RATGDOComponent::door_open() {
|
||||||
{
|
|
||||||
if (*this->door_state == DoorState::OPENING) {
|
if (*this->door_state == DoorState::OPENING) {
|
||||||
return; // gets ignored by opener
|
return; // gets ignored by opener
|
||||||
}
|
}
|
||||||
@@ -379,17 +367,17 @@ void RATGDOComponent::door_open()
|
|||||||
this->set_timeout(
|
this->set_timeout(
|
||||||
TIMEOUT_DOOR_QUERY_STATE, (*this->opening_duration + 5) * 1000,
|
TIMEOUT_DOOR_QUERY_STATE, (*this->opening_duration + 5) * 1000,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (*this->door_state != DoorState::OPEN && *this->door_state != DoorState::STOPPED) {
|
if (*this->door_state != DoorState::OPEN &&
|
||||||
this->received(DoorState::OPEN); // probably missed a status mesage,
|
*this->door_state != DoorState::STOPPED) {
|
||||||
// assume it's open
|
this->received(DoorState::OPEN); // probably missed a status
|
||||||
|
// mesage, assume it's open
|
||||||
this->query_status(); // query in case we're wrong and it's stopped
|
this->query_status(); // query in case we're wrong and it's stopped
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::door_close()
|
void RATGDOComponent::door_close() {
|
||||||
{
|
|
||||||
if (*this->door_state == DoorState::CLOSING) {
|
if (*this->door_state == DoorState::CLOSING) {
|
||||||
return; // gets ignored by opener
|
return; // gets ignored by opener
|
||||||
}
|
}
|
||||||
@@ -414,7 +402,8 @@ void RATGDOComponent::door_close()
|
|||||||
this->set_timeout(
|
this->set_timeout(
|
||||||
TIMEOUT_DOOR_QUERY_STATE, (*this->closing_duration + 5) * 1000,
|
TIMEOUT_DOOR_QUERY_STATE, (*this->closing_duration + 5) * 1000,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (*this->door_state != DoorState::CLOSED && *this->door_state != DoorState::STOPPED) {
|
if (*this->door_state != DoorState::CLOSED &&
|
||||||
|
*this->door_state != DoorState::STOPPED) {
|
||||||
this->received(DoorState::CLOSED); // probably missed a status
|
this->received(DoorState::CLOSED); // probably missed a status
|
||||||
// mesage, assume it's closed
|
// mesage, assume it's closed
|
||||||
this->query_status(); // query in case we're wrong and it's stopped
|
this->query_status(); // query in case we're wrong and it's stopped
|
||||||
@@ -423,9 +412,9 @@ void RATGDOComponent::door_close()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::door_stop()
|
void RATGDOComponent::door_stop() {
|
||||||
{
|
if (*this->door_state != DoorState::OPENING &&
|
||||||
if (*this->door_state != DoorState::OPENING && *this->door_state != DoorState::CLOSING) {
|
*this->door_state != DoorState::CLOSING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->door_action(DoorAction::STOP);
|
this->door_action(DoorAction::STOP);
|
||||||
@@ -433,14 +422,13 @@ void RATGDOComponent::door_stop()
|
|||||||
|
|
||||||
void RATGDOComponent::door_toggle() { this->door_action(DoorAction::TOGGLE); }
|
void RATGDOComponent::door_toggle() { this->door_action(DoorAction::TOGGLE); }
|
||||||
|
|
||||||
void RATGDOComponent::door_action(DoorAction action)
|
void RATGDOComponent::door_action(DoorAction action) {
|
||||||
{
|
|
||||||
this->protocol_->door_action(action);
|
this->protocol_->door_action(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::door_move_to_position(float position)
|
void RATGDOComponent::door_move_to_position(float position) {
|
||||||
{
|
if (*this->door_state == DoorState::OPENING ||
|
||||||
if (*this->door_state == DoorState::OPENING || *this->door_state == DoorState::CLOSING) {
|
*this->door_state == DoorState::CLOSING) {
|
||||||
this->door_action(DoorAction::STOP);
|
this->door_action(DoorAction::STOP);
|
||||||
this->on_door_state([this, position](DoorState s) {
|
this->on_door_state([this, position](DoorState s) {
|
||||||
if (s == DoorState::STOPPED) {
|
if (s == DoorState::STOPPED) {
|
||||||
@@ -456,7 +444,8 @@ void RATGDOComponent::door_move_to_position(float position)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto duration = delta > 0 ? *this->opening_duration : -*this->closing_duration;
|
auto duration =
|
||||||
|
delta > 0 ? *this->opening_duration : -*this->closing_duration;
|
||||||
if (duration == 0) {
|
if (duration == 0) {
|
||||||
ESP_LOGW(TAG, "I don't know duration, ignoring move to position");
|
ESP_LOGW(TAG, "I don't know duration, ignoring move to position");
|
||||||
return;
|
return;
|
||||||
@@ -474,8 +463,7 @@ void RATGDOComponent::door_move_to_position(float position)
|
|||||||
this->smart_door_action(this->target_direction_);
|
this->smart_door_action(this->target_direction_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::cancel_position_sync_callbacks()
|
void RATGDOComponent::cancel_position_sync_callbacks() {
|
||||||
{
|
|
||||||
if (this->door_start_moving != 0) {
|
if (this->door_start_moving != 0) {
|
||||||
ESP_LOGD(TAG, "Cancelling position callbacks");
|
ESP_LOGD(TAG, "Cancelling position callbacks");
|
||||||
this->cancel_timeout(TIMEOUT_MOVE_TO_POSITION);
|
this->cancel_timeout(TIMEOUT_MOVE_TO_POSITION);
|
||||||
@@ -487,32 +475,27 @@ void RATGDOComponent::cancel_position_sync_callbacks()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::light_on()
|
void RATGDOComponent::light_on() {
|
||||||
{
|
|
||||||
this->light_state = LightState::ON;
|
this->light_state = LightState::ON;
|
||||||
this->protocol_->light_action(LightAction::ON);
|
this->protocol_->light_action(LightAction::ON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::light_off()
|
void RATGDOComponent::light_off() {
|
||||||
{
|
|
||||||
this->light_state = LightState::OFF;
|
this->light_state = LightState::OFF;
|
||||||
this->protocol_->light_action(LightAction::OFF);
|
this->protocol_->light_action(LightAction::OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
LightState RATGDOComponent::get_light_state() const
|
LightState RATGDOComponent::get_light_state() const {
|
||||||
{
|
|
||||||
return *this->light_state;
|
return *this->light_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock functions
|
// Lock functions
|
||||||
void RATGDOComponent::lock()
|
void RATGDOComponent::lock() {
|
||||||
{
|
|
||||||
this->lock_state = LockState::LOCKED;
|
this->lock_state = LockState::LOCKED;
|
||||||
this->protocol_->lock_action(LockAction::LOCK);
|
this->protocol_->lock_action(LockAction::LOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::unlock()
|
void RATGDOComponent::unlock() {
|
||||||
{
|
|
||||||
this->lock_state = LockState::UNLOCKED;
|
this->lock_state = LockState::UNLOCKED;
|
||||||
this->protocol_->lock_action(LockAction::UNLOCK);
|
this->protocol_->lock_action(LockAction::UNLOCK);
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-76
@@ -7,22 +7,22 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/preferences.h"
|
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "callbacks.h"
|
#include "callbacks.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
#include "observable.h"
|
#include "observable.h"
|
||||||
#include "ratgdo_state.h"
|
#include "ratgdo_state.h"
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
namespace secplus2 { class Secplus2; }
|
namespace secplus2 {
|
||||||
|
class Secplus2;
|
||||||
|
}
|
||||||
|
|
||||||
class RATGDOComponent;
|
class RATGDOComponent;
|
||||||
typedef Parented<RATGDOComponent> RATGDOClient;
|
typedef Parented<RATGDOComponent> RATGDOClient;
|
||||||
@@ -30,55 +30,55 @@ typedef Parented<RATGDOComponent> RATGDOClient;
|
|||||||
const float DOOR_POSITION_UNKNOWN = -1.0;
|
const float DOOR_POSITION_UNKNOWN = -1.0;
|
||||||
const float DOOR_DELTA_UNKNOWN = -2.0;
|
const float DOOR_DELTA_UNKNOWN = -2.0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RATGDOComponent : public Component {
|
class RATGDOComponent : public Component {
|
||||||
public:
|
public:
|
||||||
RATGDOComponent()
|
RATGDOComponent() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void on_shutdown() override;
|
void on_shutdown() override;
|
||||||
|
|
||||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
float get_setup_priority() const override {
|
||||||
|
return setup_priority::AFTER_WIFI;
|
||||||
|
}
|
||||||
|
|
||||||
void init_protocol();
|
void init_protocol();
|
||||||
|
|
||||||
float start_opening { -1 };
|
float start_opening{-1};
|
||||||
single_observable<float> opening_duration { 0 };
|
single_observable<float> opening_duration{0};
|
||||||
float start_closing { -1 };
|
float start_closing{-1};
|
||||||
single_observable<float> closing_duration { 0 };
|
single_observable<float> closing_duration{0};
|
||||||
|
|
||||||
observable<int16_t, RATGDO_MAX_DISTANCE_SUBSCRIBERS> last_distance_measurement { 0 };
|
observable<int16_t, RATGDO_MAX_DISTANCE_SUBSCRIBERS>
|
||||||
|
last_distance_measurement{0};
|
||||||
|
|
||||||
single_observable<uint16_t> openings { 0 }; // number of times the door has been opened
|
single_observable<uint16_t> openings{
|
||||||
|
0}; // number of times the door has been opened
|
||||||
|
|
||||||
observable<DoorState, RATGDO_MAX_DOOR_STATE_SUBSCRIBERS> door_state { DoorState::UNKNOWN };
|
observable<DoorState, RATGDO_MAX_DOOR_STATE_SUBSCRIBERS> door_state{
|
||||||
observable<float, RATGDO_MAX_DOOR_STATE_SUBSCRIBERS> door_position { DOOR_POSITION_UNKNOWN };
|
DoorState::UNKNOWN};
|
||||||
|
observable<float, RATGDO_MAX_DOOR_STATE_SUBSCRIBERS> door_position{
|
||||||
|
DOOR_POSITION_UNKNOWN};
|
||||||
|
|
||||||
unsigned long door_start_moving { 0 };
|
unsigned long door_start_moving{0};
|
||||||
float door_start_position { DOOR_POSITION_UNKNOWN };
|
float door_start_position{DOOR_POSITION_UNKNOWN};
|
||||||
float door_move_delta { DOOR_DELTA_UNKNOWN };
|
float door_move_delta{DOOR_DELTA_UNKNOWN};
|
||||||
uint16_t position_sync_remaining_ { 0 };
|
uint16_t position_sync_remaining_{0};
|
||||||
float target_position_ { DOOR_POSITION_UNKNOWN };
|
float target_position_{DOOR_POSITION_UNKNOWN};
|
||||||
DoorAction target_direction_ { DoorAction::UNKNOWN };
|
DoorAction target_direction_{DoorAction::UNKNOWN};
|
||||||
|
|
||||||
single_observable<LightState> light_state { LightState::UNKNOWN };
|
single_observable<LightState> light_state{LightState::UNKNOWN};
|
||||||
single_observable<LockState> lock_state { LockState::UNKNOWN };
|
single_observable<LockState> lock_state{LockState::UNKNOWN};
|
||||||
|
|
||||||
OnceCallbacks<void(DoorState)> on_door_state_;
|
OnceCallbacks<void(DoorState)> on_door_state_;
|
||||||
|
|
||||||
single_observable<bool> synced { false };
|
single_observable<bool> synced{false};
|
||||||
single_observable<optional<bool>> sync_failed { nullopt };
|
single_observable<optional<bool>> sync_failed{nullopt};
|
||||||
|
|
||||||
void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; }
|
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_gdo_pin(InternalGPIOPin* pin) { this->input_gdo_pin_ = pin; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void received(const DoorState door_state);
|
void received(const DoorState door_state);
|
||||||
void received(const LightState light_state);
|
void received(const LightState light_state);
|
||||||
void received(const LockState lock_state);
|
void received(const LockState lock_state);
|
||||||
@@ -94,7 +94,9 @@ public:
|
|||||||
void door_action(DoorAction action);
|
void door_action(DoorAction action);
|
||||||
void smart_door_action(DoorAction target_direction);
|
void smart_door_action(DoorAction target_direction);
|
||||||
void door_move_to_position(float position);
|
void door_move_to_position(float position);
|
||||||
void set_door_position(float door_position) { this->door_position = door_position; }
|
void set_door_position(float door_position) {
|
||||||
|
this->door_position = door_position;
|
||||||
|
}
|
||||||
void set_opening_duration(float duration);
|
void set_opening_duration(float duration);
|
||||||
void set_closing_duration(float duration);
|
void set_closing_duration(float duration);
|
||||||
void schedule_door_position_sync(float update_period = 500);
|
void schedule_door_position_sync(float update_period = 500);
|
||||||
@@ -145,10 +147,10 @@ public:
|
|||||||
// meaning no new callback was queued — otherwise we'd cancel
|
// meaning no new callback was queued — otherwise we'd cancel
|
||||||
// expiry B here and leave the inner callback without protection.
|
// expiry B here and leave the inner callback without protection.
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void on_door_state(F&& callback)
|
void on_door_state(F&& callback) {
|
||||||
{
|
|
||||||
using Cb = std::decay_t<F>;
|
using Cb = std::decay_t<F>;
|
||||||
this->on_door_state_([this, cb = Cb(std::forward<F>(callback))](DoorState s) {
|
this->on_door_state_(
|
||||||
|
[this, cb = Cb(std::forward<F>(callback))](DoorState s) {
|
||||||
cb(s);
|
cb(s);
|
||||||
if (!this->on_door_state_.count()) {
|
if (!this->on_door_state_.count()) {
|
||||||
this->cancel_door_state_expiry();
|
this->cancel_door_state_expiry();
|
||||||
@@ -178,7 +180,7 @@ public:
|
|||||||
template <typename F>
|
template <typename F>
|
||||||
void subscribe_distance_measurement(F&& f);
|
void subscribe_distance_measurement(F&& f);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Pointers first (4-byte aligned)
|
// Pointers first (4-byte aligned)
|
||||||
secplus2::Secplus2* protocol_;
|
secplus2::Secplus2* protocol_;
|
||||||
InternalGPIOPin* output_gdo_pin_;
|
InternalGPIOPin* output_gdo_pin_;
|
||||||
@@ -187,24 +189,26 @@ protected:
|
|||||||
// Bool members packed into bitfield
|
// Bool members packed into bitfield
|
||||||
struct {
|
struct {
|
||||||
uint8_t reserved : 8; // Reserved for future use
|
uint8_t reserved : 8; // Reserved for future use
|
||||||
} flags_ { 0 };
|
} flags_{0};
|
||||||
|
|
||||||
// Subscriber counters for defer name allocation
|
// Subscriber counters for defer name allocation
|
||||||
uint8_t door_state_sub_num_ { 0 };
|
uint8_t door_state_sub_num_{0};
|
||||||
uint8_t distance_sub_num_ { 0 };
|
uint8_t distance_sub_num_{0};
|
||||||
}; // RATGDOComponent
|
}; // RATGDOComponent
|
||||||
|
|
||||||
void log_subscriber_overflow(const LogString* observable_name, uint32_t max);
|
void log_subscriber_overflow(const LogString* observable_name, uint32_t max);
|
||||||
|
|
||||||
inline uint32_t get_scheduler_id(uint32_t base, uint32_t count, uint8_t& counter, const LogString* observable_name)
|
inline uint32_t get_scheduler_id(uint32_t base, uint32_t count,
|
||||||
{
|
uint8_t& counter,
|
||||||
|
const LogString* observable_name) {
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
log_subscriber_overflow(observable_name, count);
|
log_subscriber_overflow(observable_name, count);
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
if (counter >= count) {
|
if (counter >= count) {
|
||||||
log_subscriber_overflow(observable_name, count);
|
log_subscriber_overflow(observable_name, count);
|
||||||
return base + count - 1; // reuse last ID to avoid collision with first subscriber
|
return base + count -
|
||||||
|
1; // reuse last ID to avoid collision with first subscriber
|
||||||
}
|
}
|
||||||
return base + counter++;
|
return base + counter++;
|
||||||
}
|
}
|
||||||
@@ -212,18 +216,22 @@ inline uint32_t get_scheduler_id(uint32_t base, uint32_t count, uint8_t& counter
|
|||||||
// Scheduler IDs using uint32_t ranges to avoid heap allocations
|
// Scheduler IDs using uint32_t ranges to avoid heap allocations
|
||||||
// Bases are auto-generated from counts to prevent ID conflicts
|
// Bases are auto-generated from counts to prevent ID conflicts
|
||||||
namespace scheduler_ids {
|
namespace scheduler_ids {
|
||||||
inline constexpr uint32_t INTERVAL_POSITION_SYNC = 0;
|
inline constexpr uint32_t INTERVAL_POSITION_SYNC = 0;
|
||||||
|
|
||||||
// Multi-subscriber ranges — counts derived from codegen defines
|
// Multi-subscriber ranges — counts derived from codegen defines
|
||||||
inline constexpr uint32_t DEFER_DOOR_STATE_COUNT = RATGDO_MAX_DOOR_STATE_SUBSCRIBERS;
|
inline constexpr uint32_t DEFER_DOOR_STATE_COUNT =
|
||||||
inline constexpr uint32_t DEFER_DOOR_STATE_BASE = INTERVAL_POSITION_SYNC + 1;
|
RATGDO_MAX_DOOR_STATE_SUBSCRIBERS;
|
||||||
|
inline constexpr uint32_t DEFER_DOOR_STATE_BASE = INTERVAL_POSITION_SYNC + 1;
|
||||||
|
|
||||||
inline constexpr uint32_t DEFER_DISTANCE_COUNT = RATGDO_MAX_DISTANCE_SUBSCRIBERS;
|
inline constexpr uint32_t DEFER_DISTANCE_COUNT =
|
||||||
inline constexpr uint32_t DEFER_DISTANCE_BASE = DEFER_DOOR_STATE_BASE + DEFER_DOOR_STATE_COUNT;
|
RATGDO_MAX_DISTANCE_SUBSCRIBERS;
|
||||||
inline constexpr uint32_t DEFER_DISTANCE_END = DEFER_DISTANCE_BASE + DEFER_DISTANCE_COUNT;
|
inline constexpr uint32_t DEFER_DISTANCE_BASE =
|
||||||
|
DEFER_DOOR_STATE_BASE + DEFER_DOOR_STATE_COUNT;
|
||||||
|
inline constexpr uint32_t DEFER_DISTANCE_END =
|
||||||
|
DEFER_DISTANCE_BASE + DEFER_DISTANCE_COUNT;
|
||||||
|
|
||||||
// Single-subscriber IDs
|
// Single-subscriber IDs
|
||||||
enum : uint32_t {
|
enum : uint32_t {
|
||||||
DEFER_OPENING_DURATION = DEFER_DISTANCE_END,
|
DEFER_OPENING_DURATION = DEFER_DISTANCE_END,
|
||||||
DEFER_CLOSING_DURATION,
|
DEFER_CLOSING_DURATION,
|
||||||
DEFER_OPENINGS,
|
DEFER_OPENINGS,
|
||||||
@@ -236,7 +244,7 @@ namespace scheduler_ids {
|
|||||||
TIMEOUT_MOVE_TO_POSITION,
|
TIMEOUT_MOVE_TO_POSITION,
|
||||||
TIMEOUT_DOOR_STATE_EXPIRY,
|
TIMEOUT_DOOR_STATE_EXPIRY,
|
||||||
TIMEOUT_SYNC,
|
TIMEOUT_SYNC,
|
||||||
};
|
};
|
||||||
} // namespace scheduler_ids
|
} // namespace scheduler_ids
|
||||||
|
|
||||||
// Template implementations for subscribe methods.
|
// Template implementations for subscribe methods.
|
||||||
@@ -245,33 +253,31 @@ namespace scheduler_ids {
|
|||||||
// is dispatched to the child component.
|
// is dispatched to the child component.
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_opening_duration(F&& f)
|
void RATGDOComponent::subscribe_opening_duration(F&& f) {
|
||||||
{
|
|
||||||
this->opening_duration.subscribe([this, f](float state) {
|
this->opening_duration.subscribe([this, f](float state) {
|
||||||
defer(scheduler_ids::DEFER_OPENING_DURATION, [f, state] { f(state); });
|
defer(scheduler_ids::DEFER_OPENING_DURATION, [f, state] { f(state); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_closing_duration(F&& f)
|
void RATGDOComponent::subscribe_closing_duration(F&& f) {
|
||||||
{
|
|
||||||
this->closing_duration.subscribe([this, f](float state) {
|
this->closing_duration.subscribe([this, f](float state) {
|
||||||
defer(scheduler_ids::DEFER_CLOSING_DURATION, [f, state] { f(state); });
|
defer(scheduler_ids::DEFER_CLOSING_DURATION, [f, state] { f(state); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_openings(F&& f)
|
void RATGDOComponent::subscribe_openings(F&& f) {
|
||||||
{
|
|
||||||
this->openings.subscribe([this, f](uint16_t state) {
|
this->openings.subscribe([this, f](uint16_t state) {
|
||||||
defer(scheduler_ids::DEFER_OPENINGS, [f, state] { f(state); });
|
defer(scheduler_ids::DEFER_OPENINGS, [f, state] { f(state); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_door_state(F&& f)
|
void RATGDOComponent::subscribe_door_state(F&& f) {
|
||||||
{
|
uint32_t id =
|
||||||
uint32_t id = get_scheduler_id(scheduler_ids::DEFER_DOOR_STATE_BASE, scheduler_ids::DEFER_DOOR_STATE_COUNT,
|
get_scheduler_id(scheduler_ids::DEFER_DOOR_STATE_BASE,
|
||||||
|
scheduler_ids::DEFER_DOOR_STATE_COUNT,
|
||||||
this->door_state_sub_num_, LOG_STR("door_state"));
|
this->door_state_sub_num_, LOG_STR("door_state"));
|
||||||
this->door_state.subscribe([this, f, id](DoorState state) {
|
this->door_state.subscribe([this, f, id](DoorState state) {
|
||||||
defer(id, [this, f, state] { f(state, *this->door_position); });
|
defer(id, [this, f, state] { f(state, *this->door_position); });
|
||||||
@@ -282,35 +288,31 @@ void RATGDOComponent::subscribe_door_state(F&& f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_light_state(F&& f)
|
void RATGDOComponent::subscribe_light_state(F&& f) {
|
||||||
{
|
|
||||||
this->light_state.subscribe([this, f](LightState state) {
|
this->light_state.subscribe([this, f](LightState state) {
|
||||||
defer(scheduler_ids::DEFER_LIGHT_STATE, [f, state] { f(state); });
|
defer(scheduler_ids::DEFER_LIGHT_STATE, [f, state] { f(state); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_lock_state(F&& f)
|
void RATGDOComponent::subscribe_lock_state(F&& f) {
|
||||||
{
|
|
||||||
this->lock_state.subscribe([this, f](LockState state) {
|
this->lock_state.subscribe([this, f](LockState state) {
|
||||||
defer(scheduler_ids::DEFER_LOCK_STATE, [f, state] { f(state); });
|
defer(scheduler_ids::DEFER_LOCK_STATE, [f, state] { f(state); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_sync_failed(F&& f)
|
void RATGDOComponent::subscribe_sync_failed(F&& f) {
|
||||||
{
|
|
||||||
this->sync_failed.subscribe(std::forward<F>(f));
|
this->sync_failed.subscribe(std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void RATGDOComponent::subscribe_distance_measurement(F&& f)
|
void RATGDOComponent::subscribe_distance_measurement(F&& f) {
|
||||||
{
|
uint32_t id = get_scheduler_id(
|
||||||
uint32_t id = get_scheduler_id(scheduler_ids::DEFER_DISTANCE_BASE, scheduler_ids::DEFER_DISTANCE_COUNT,
|
scheduler_ids::DEFER_DISTANCE_BASE, scheduler_ids::DEFER_DISTANCE_COUNT,
|
||||||
this->distance_sub_num_, LOG_STR("distance_measurement"));
|
this->distance_sub_num_, LOG_STR("distance_measurement"));
|
||||||
this->last_distance_measurement.subscribe([this, f, id](int16_t state) {
|
this->last_distance_measurement.subscribe(
|
||||||
defer(id, [f, state] { f(state); });
|
[this, f, id](int16_t state) { defer(id, [f, state] { f(state); }); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome::ratgdo
|
} // namespace esphome::ratgdo
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
LightState light_state_toggle(LightState state)
|
LightState light_state_toggle(LightState state) {
|
||||||
{
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case LightState::OFF:
|
case LightState::OFF:
|
||||||
return LightState::ON;
|
return LightState::ON;
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
************************************/
|
************************************/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
enum class DoorState : uint8_t {
|
enum class DoorState : uint8_t {
|
||||||
@@ -21,40 +22,38 @@ enum class DoorState : uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline const char* DoorState_to_string(DoorState e) {
|
inline const char* DoorState_to_string(DoorState e) {
|
||||||
static const char* const names[] = {"UNKNOWN", "OPEN", "CLOSED", "STOPPED", "OPENING", "CLOSING"};
|
static const char* const names[] = {"UNKNOWN", "OPEN", "CLOSED",
|
||||||
|
"STOPPED", "OPENING", "CLOSING"};
|
||||||
auto i = static_cast<uint8_t>(e);
|
auto i = static_cast<uint8_t>(e);
|
||||||
return (i <= static_cast<uint8_t>(DoorState::CLOSING)) ? names[i] : "UNKNOWN";
|
return (i <= static_cast<uint8_t>(DoorState::CLOSING)) ? names[i] : "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DoorState to_DoorState(uint8_t t, DoorState unknown) {
|
inline DoorState to_DoorState(uint8_t t, DoorState unknown) {
|
||||||
return (t <= static_cast<uint8_t>(DoorState::CLOSING)) ? static_cast<DoorState>(t) : unknown;
|
return (t <= static_cast<uint8_t>(DoorState::CLOSING))
|
||||||
|
? static_cast<DoorState>(t)
|
||||||
|
: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum for all states a the light can be in.
|
/// Enum for all states a the light can be in.
|
||||||
enum class LightState : uint8_t {
|
enum class LightState : uint8_t { OFF = 0, ON = 1, UNKNOWN = 2 };
|
||||||
OFF = 0,
|
|
||||||
ON = 1,
|
|
||||||
UNKNOWN = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const char* LightState_to_string(LightState e) {
|
inline const char* LightState_to_string(LightState e) {
|
||||||
static const char* const names[] = {"OFF", "ON", "UNKNOWN"};
|
static const char* const names[] = {"OFF", "ON", "UNKNOWN"};
|
||||||
auto i = static_cast<uint8_t>(e);
|
auto i = static_cast<uint8_t>(e);
|
||||||
return (i <= static_cast<uint8_t>(LightState::UNKNOWN)) ? names[i] : "UNKNOWN";
|
return (i <= static_cast<uint8_t>(LightState::UNKNOWN)) ? names[i]
|
||||||
|
: "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LightState to_LightState(uint8_t t, LightState unknown) {
|
inline LightState to_LightState(uint8_t t, LightState unknown) {
|
||||||
return (t <= static_cast<uint8_t>(LightState::UNKNOWN)) ? static_cast<LightState>(t) : unknown;
|
return (t <= static_cast<uint8_t>(LightState::UNKNOWN))
|
||||||
|
? static_cast<LightState>(t)
|
||||||
|
: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
LightState light_state_toggle(LightState state);
|
LightState light_state_toggle(LightState state);
|
||||||
|
|
||||||
/// Enum for all states a the lock can be in.
|
/// Enum for all states a the lock can be in.
|
||||||
enum class LockState : uint8_t {
|
enum class LockState : uint8_t { UNLOCKED = 0, LOCKED = 1, UNKNOWN = 2 };
|
||||||
UNLOCKED = 0,
|
|
||||||
LOCKED = 1,
|
|
||||||
UNKNOWN = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const char* LockState_to_string(LockState e) {
|
inline const char* LockState_to_string(LockState e) {
|
||||||
static const char* const names[] = {"UNLOCKED", "LOCKED", "UNKNOWN"};
|
static const char* const names[] = {"UNLOCKED", "LOCKED", "UNKNOWN"};
|
||||||
@@ -63,41 +62,42 @@ inline const char* LockState_to_string(LockState e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline LockState to_LockState(uint8_t t, LockState unknown) {
|
inline LockState to_LockState(uint8_t t, LockState unknown) {
|
||||||
return (t <= static_cast<uint8_t>(LockState::UNKNOWN)) ? static_cast<LockState>(t) : unknown;
|
return (t <= static_cast<uint8_t>(LockState::UNKNOWN))
|
||||||
|
? static_cast<LockState>(t)
|
||||||
|
: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
enum class LightAction : uint8_t {
|
enum class LightAction : uint8_t { OFF = 0, ON = 1, TOGGLE = 2, UNKNOWN = 3 };
|
||||||
OFF = 0,
|
|
||||||
ON = 1,
|
|
||||||
TOGGLE = 2,
|
|
||||||
UNKNOWN = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const char* LightAction_to_string(LightAction e) {
|
inline const char* LightAction_to_string(LightAction e) {
|
||||||
static const char* const names[] = {"OFF", "ON", "TOGGLE", "UNKNOWN"};
|
static const char* const names[] = {"OFF", "ON", "TOGGLE", "UNKNOWN"};
|
||||||
auto i = static_cast<uint8_t>(e);
|
auto i = static_cast<uint8_t>(e);
|
||||||
return (i <= static_cast<uint8_t>(LightAction::UNKNOWN)) ? names[i] : "UNKNOWN";
|
return (i <= static_cast<uint8_t>(LightAction::UNKNOWN)) ? names[i]
|
||||||
|
: "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LightAction to_LightAction(uint8_t t, LightAction unknown) {
|
inline LightAction to_LightAction(uint8_t t, LightAction unknown) {
|
||||||
return (t <= static_cast<uint8_t>(LightAction::UNKNOWN)) ? static_cast<LightAction>(t) : unknown;
|
return (t <= static_cast<uint8_t>(LightAction::UNKNOWN))
|
||||||
|
? static_cast<LightAction>(t)
|
||||||
|
: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class LockAction : uint8_t {
|
enum class LockAction : uint8_t { UNLOCK = 0, LOCK = 1, UNKNOWN = 3 };
|
||||||
UNLOCK = 0,
|
|
||||||
LOCK = 1,
|
|
||||||
UNKNOWN = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const char* LockAction_to_string(LockAction e) {
|
inline const char* LockAction_to_string(LockAction e) {
|
||||||
static const char* const names[] = {"UNLOCK", "LOCK", "UNKNOWN", "UNKNOWN"};
|
static const char* const names[] = {"UNLOCK", "LOCK", "UNKNOWN", "UNKNOWN"};
|
||||||
auto i = static_cast<uint8_t>(e);
|
auto i = static_cast<uint8_t>(e);
|
||||||
return (i <= static_cast<uint8_t>(LockAction::UNKNOWN)) ? names[i] : "UNKNOWN";
|
return (i <= static_cast<uint8_t>(LockAction::UNKNOWN)) ? names[i]
|
||||||
|
: "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LockAction to_LockAction(uint8_t t, LockAction unknown) {
|
inline LockAction to_LockAction(uint8_t t, LockAction unknown) {
|
||||||
return (t == static_cast<uint8_t>(LockAction::UNLOCK) || t == static_cast<uint8_t>(LockAction::LOCK) || t == static_cast<uint8_t>(LockAction::UNKNOWN)) ? static_cast<LockAction>(t) : unknown;
|
return (t == static_cast<uint8_t>(LockAction::UNLOCK) ||
|
||||||
|
t == static_cast<uint8_t>(LockAction::LOCK) ||
|
||||||
|
t == static_cast<uint8_t>(LockAction::UNKNOWN))
|
||||||
|
? static_cast<LockAction>(t)
|
||||||
|
: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class DoorAction : uint8_t {
|
enum class DoorAction : uint8_t {
|
||||||
@@ -109,13 +109,17 @@ enum class DoorAction : uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline const char* DoorAction_to_string(DoorAction e) {
|
inline const char* DoorAction_to_string(DoorAction e) {
|
||||||
static const char* const names[] = {"CLOSE", "OPEN", "TOGGLE", "STOP", "UNKNOWN"};
|
static const char* const names[] = {"CLOSE", "OPEN", "TOGGLE", "STOP",
|
||||||
|
"UNKNOWN"};
|
||||||
auto i = static_cast<uint8_t>(e);
|
auto i = static_cast<uint8_t>(e);
|
||||||
return (i <= static_cast<uint8_t>(DoorAction::UNKNOWN)) ? names[i] : "UNKNOWN";
|
return (i <= static_cast<uint8_t>(DoorAction::UNKNOWN)) ? names[i]
|
||||||
|
: "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DoorAction to_DoorAction(uint8_t t, DoorAction unknown) {
|
inline DoorAction to_DoorAction(uint8_t t, DoorAction unknown) {
|
||||||
return (t <= static_cast<uint8_t>(DoorAction::UNKNOWN)) ? static_cast<DoorAction>(t) : unknown;
|
return (t <= static_cast<uint8_t>(DoorAction::UNKNOWN))
|
||||||
|
? static_cast<DoorAction>(t)
|
||||||
|
: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Openings {
|
struct Openings {
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
#include "ratgdo_uart_esp32.h"
|
#include "ratgdo_uart_esp32.h"
|
||||||
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include <driver/uart.h>
|
|
||||||
|
|
||||||
#include <driver/rmt_tx.h>
|
|
||||||
#include <esp_private/rmt.h> // for rmt_get_channel_id (used once during init)
|
|
||||||
|
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
|
#include <driver/rmt_tx.h>
|
||||||
|
#include <driver/uart.h>
|
||||||
|
#include <esp_private/rmt.h> // for rmt_get_channel_id (used once during init)
|
||||||
#include <esp_rom_gpio.h>
|
#include <esp_rom_gpio.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#include <soc/gpio_sig_map.h>
|
#include <soc/gpio_sig_map.h>
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
static const char* const TAG = "ratgdo_uart";
|
static const char* const TAG = "ratgdo_uart";
|
||||||
@@ -32,10 +31,9 @@ static constexpr size_t RMT_TRANS_QUEUE_DEPTH = 4;
|
|||||||
static constexpr int UART_PORT = UART_NUM_1;
|
static constexpr int UART_PORT = UART_NUM_1;
|
||||||
static constexpr int UART_TX_SIGNAL_IDX = U1TXD_OUT_IDX;
|
static constexpr int UART_TX_SIGNAL_IDX = U1TXD_OUT_IDX;
|
||||||
|
|
||||||
RatgdoUART::RatgdoUART() { }
|
RatgdoUART::RatgdoUART() {}
|
||||||
|
|
||||||
RatgdoUART::~RatgdoUART()
|
RatgdoUART::~RatgdoUART() {
|
||||||
{
|
|
||||||
if (this->is_initialized_) {
|
if (this->is_initialized_) {
|
||||||
uart_driver_delete((uart_port_t)this->uart_num_);
|
uart_driver_delete((uart_port_t)this->uart_num_);
|
||||||
if (this->rmt_copy_encoder_) {
|
if (this->rmt_copy_encoder_) {
|
||||||
@@ -51,8 +49,7 @@ RatgdoUART::~RatgdoUART()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RatgdoUART::begin(int baud, RatgdoUARTConfig config, int rx_pin,
|
void RatgdoUART::begin(int baud, RatgdoUARTConfig config, int rx_pin,
|
||||||
int tx_pin, bool invert)
|
int tx_pin, bool invert) {
|
||||||
{
|
|
||||||
this->tx_pin_ = tx_pin;
|
this->tx_pin_ = tx_pin;
|
||||||
this->rx_pin_ = rx_pin;
|
this->rx_pin_ = rx_pin;
|
||||||
this->baud_ = baud;
|
this->baud_ = baud;
|
||||||
@@ -60,19 +57,21 @@ void RatgdoUART::begin(int baud, RatgdoUARTConfig config, int rx_pin,
|
|||||||
|
|
||||||
this->uart_num_ = UART_PORT;
|
this->uart_num_ = UART_PORT;
|
||||||
|
|
||||||
uart_config_t uart_config = { };
|
uart_config_t uart_config = {};
|
||||||
uart_config.baud_rate = baud;
|
uart_config.baud_rate = baud;
|
||||||
uart_config.data_bits = UART_DATA_8_BITS;
|
uart_config.data_bits = UART_DATA_8_BITS;
|
||||||
uart_config.parity = (config == RATGDO_UART_8E1) ? UART_PARITY_EVEN : UART_PARITY_DISABLE;
|
uart_config.parity =
|
||||||
|
(config == RATGDO_UART_8E1) ? UART_PARITY_EVEN : UART_PARITY_DISABLE;
|
||||||
uart_config.stop_bits = UART_STOP_BITS_1;
|
uart_config.stop_bits = UART_STOP_BITS_1;
|
||||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||||
uart_config.source_clk = UART_SCLK_APB;
|
uart_config.source_clk = UART_SCLK_APB;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(uart_driver_install((uart_port_t)this->uart_num_,
|
||||||
|
UART_RX_BUFFER_SIZE, 0, 0, NULL, 0));
|
||||||
ESP_ERROR_CHECK(
|
ESP_ERROR_CHECK(
|
||||||
uart_driver_install((uart_port_t)this->uart_num_, UART_RX_BUFFER_SIZE, 0, 0, NULL, 0));
|
uart_param_config((uart_port_t)this->uart_num_, &uart_config));
|
||||||
ESP_ERROR_CHECK(uart_param_config((uart_port_t)this->uart_num_, &uart_config));
|
|
||||||
|
|
||||||
rmt_tx_channel_config_t tx_chan_config = { };
|
rmt_tx_channel_config_t tx_chan_config = {};
|
||||||
tx_chan_config.gpio_num = (gpio_num_t)tx_pin;
|
tx_chan_config.gpio_num = (gpio_num_t)tx_pin;
|
||||||
tx_chan_config.clk_src = RMT_CLK_SRC_DEFAULT;
|
tx_chan_config.clk_src = RMT_CLK_SRC_DEFAULT;
|
||||||
tx_chan_config.resolution_hz = RMT_RESOLUTION_HZ;
|
tx_chan_config.resolution_hz = RMT_RESOLUTION_HZ;
|
||||||
@@ -81,7 +80,7 @@ void RatgdoUART::begin(int baud, RatgdoUARTConfig config, int rx_pin,
|
|||||||
tx_chan_config.flags.invert_out = 0;
|
tx_chan_config.flags.invert_out = 0;
|
||||||
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &this->rmt_chan_handle_));
|
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &this->rmt_chan_handle_));
|
||||||
|
|
||||||
rmt_copy_encoder_config_t copy_encoder_config = { };
|
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||||
ESP_ERROR_CHECK(
|
ESP_ERROR_CHECK(
|
||||||
rmt_new_copy_encoder(©_encoder_config, &this->rmt_copy_encoder_));
|
rmt_new_copy_encoder(©_encoder_config, &this->rmt_copy_encoder_));
|
||||||
|
|
||||||
@@ -90,7 +89,8 @@ void RatgdoUART::begin(int baud, RatgdoUARTConfig config, int rx_pin,
|
|||||||
// Cache the channel ID for GPIO matrix switching during preamble.
|
// Cache the channel ID for GPIO matrix switching during preamble.
|
||||||
// The RMT driver allocates channels dynamically, so we query it once
|
// The RMT driver allocates channels dynamically, so we query it once
|
||||||
// here rather than relying on the private esp_private/rmt.h API at runtime.
|
// here rather than relying on the private esp_private/rmt.h API at runtime.
|
||||||
ESP_ERROR_CHECK(rmt_get_channel_id(this->rmt_chan_handle_, &this->rmt_channel_id_));
|
ESP_ERROR_CHECK(
|
||||||
|
rmt_get_channel_id(this->rmt_chan_handle_, &this->rmt_channel_id_));
|
||||||
|
|
||||||
ESP_ERROR_CHECK(uart_set_pin((uart_port_t)this->uart_num_, tx_pin, rx_pin,
|
ESP_ERROR_CHECK(uart_set_pin((uart_port_t)this->uart_num_, tx_pin, rx_pin,
|
||||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||||
@@ -105,14 +105,12 @@ void RatgdoUART::begin(int baud, RatgdoUARTConfig config, int rx_pin,
|
|||||||
rx_pin);
|
rx_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RatgdoUART::transmit_secplus2_preamble()
|
void RatgdoUART::transmit_secplus2_preamble() {
|
||||||
{
|
if (!this->is_initialized_) return;
|
||||||
if (!this->is_initialized_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Switch GPIO matrix from UART TX to RMT output
|
// Switch GPIO matrix from UART TX to RMT output
|
||||||
esp_rom_gpio_connect_out_signal(this->tx_pin_, RMT_SIG_OUT0_IDX + this->rmt_channel_id_,
|
esp_rom_gpio_connect_out_signal(
|
||||||
false, false);
|
this->tx_pin_, RMT_SIG_OUT0_IDX + this->rmt_channel_id_, false, false);
|
||||||
|
|
||||||
esp_rom_delay_us(SIGNAL_SETTLE_US);
|
esp_rom_delay_us(SIGNAL_SETTLE_US);
|
||||||
|
|
||||||
@@ -127,19 +125,19 @@ void RatgdoUART::transmit_secplus2_preamble()
|
|||||||
symbols[0].duration1 = PREAMBLE_MARK_US;
|
symbols[0].duration1 = PREAMBLE_MARK_US;
|
||||||
symbols[0].level1 = 0;
|
symbols[0].level1 = 0;
|
||||||
|
|
||||||
rmt_transmit_config_t transmit_config = { };
|
rmt_transmit_config_t transmit_config = {};
|
||||||
transmit_config.loop_count = 0;
|
transmit_config.loop_count = 0;
|
||||||
rmt_transmit(this->rmt_chan_handle_, this->rmt_copy_encoder_, symbols,
|
rmt_transmit(this->rmt_chan_handle_, this->rmt_copy_encoder_, symbols,
|
||||||
sizeof(symbols), &transmit_config);
|
sizeof(symbols), &transmit_config);
|
||||||
rmt_tx_wait_all_done(this->rmt_chan_handle_, -1);
|
rmt_tx_wait_all_done(this->rmt_chan_handle_, -1);
|
||||||
|
|
||||||
// Switch GPIO matrix back to UART TX
|
// Switch GPIO matrix back to UART TX
|
||||||
esp_rom_gpio_connect_out_signal(this->tx_pin_, UART_TX_SIGNAL_IDX, false, false);
|
esp_rom_gpio_connect_out_signal(this->tx_pin_, UART_TX_SIGNAL_IDX, false,
|
||||||
|
false);
|
||||||
esp_rom_delay_us(SIGNAL_SETTLE_US);
|
esp_rom_delay_us(SIGNAL_SETTLE_US);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RatgdoUART::write(const uint8_t* data, size_t len)
|
void RatgdoUART::write(const uint8_t* data, size_t len) {
|
||||||
{
|
|
||||||
if (this->is_initialized_) {
|
if (this->is_initialized_) {
|
||||||
uart_write_bytes((uart_port_t)this->uart_num_, (const char*)data, len);
|
uart_write_bytes((uart_port_t)this->uart_num_, (const char*)data, len);
|
||||||
uart_wait_tx_done((uart_port_t)this->uart_num_, portMAX_DELAY);
|
uart_wait_tx_done((uart_port_t)this->uart_num_, portMAX_DELAY);
|
||||||
@@ -148,19 +146,15 @@ void RatgdoUART::write(const uint8_t* data, size_t len)
|
|||||||
|
|
||||||
void RatgdoUART::write(uint8_t data) { write(&data, 1); }
|
void RatgdoUART::write(uint8_t data) { write(&data, 1); }
|
||||||
|
|
||||||
int RatgdoUART::available()
|
int RatgdoUART::available() {
|
||||||
{
|
if (!this->is_initialized_) return 0;
|
||||||
if (!this->is_initialized_)
|
|
||||||
return 0;
|
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
uart_get_buffered_data_len((uart_port_t)this->uart_num_, &length);
|
uart_get_buffered_data_len((uart_port_t)this->uart_num_, &length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RatgdoUART::read()
|
int RatgdoUART::read() {
|
||||||
{
|
if (!this->is_initialized_) return -1;
|
||||||
if (!this->is_initialized_)
|
|
||||||
return -1;
|
|
||||||
uint8_t data = 0;
|
uint8_t data = 0;
|
||||||
int len = uart_read_bytes((uart_port_t)this->uart_num_, &data, 1, 0);
|
int len = uart_read_bytes((uart_port_t)this->uart_num_, &data, 1, 0);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
@@ -169,12 +163,12 @@ int RatgdoUART::read()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RatgdoUART::on_shutdown()
|
void RatgdoUART::on_shutdown() {
|
||||||
{
|
|
||||||
if (this->is_initialized_) {
|
if (this->is_initialized_) {
|
||||||
// Unmap the matrix output signal so that UART peripheral resets do not
|
// Unmap the matrix output signal so that UART peripheral resets do not
|
||||||
// pull the hardware line dominant.
|
// pull the hardware line dominant.
|
||||||
esp_rom_gpio_connect_out_signal(this->tx_pin_, SIG_GPIO_OUT_IDX, false, false);
|
esp_rom_gpio_connect_out_signal(this->tx_pin_, SIG_GPIO_OUT_IDX, false,
|
||||||
|
false);
|
||||||
gpio_set_direction((gpio_num_t)this->tx_pin_, GPIO_MODE_INPUT);
|
gpio_set_direction((gpio_num_t)this->tx_pin_, GPIO_MODE_INPUT);
|
||||||
gpio_set_direction((gpio_num_t)this->rx_pin_, GPIO_MODE_INPUT);
|
gpio_set_direction((gpio_num_t)this->rx_pin_, GPIO_MODE_INPUT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
|
|
||||||
#include <driver/rmt_tx.h>
|
#include <driver/rmt_tx.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
enum RatgdoUARTConfig {
|
enum RatgdoUARTConfig {
|
||||||
@@ -14,7 +14,7 @@ enum RatgdoUARTConfig {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class RatgdoUART {
|
class RatgdoUART {
|
||||||
public:
|
public:
|
||||||
RatgdoUART();
|
RatgdoUART();
|
||||||
~RatgdoUART();
|
~RatgdoUART();
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ public:
|
|||||||
void write(uint8_t data);
|
void write(uint8_t data);
|
||||||
int available();
|
int available();
|
||||||
int read();
|
int read();
|
||||||
void enableIntTx(bool enable) { }
|
void enableIntTx(bool enable) {}
|
||||||
void enableAutoBaud(bool enable) { }
|
void enableAutoBaud(bool enable) {}
|
||||||
int baudRate() { return this->baud_; }
|
int baudRate() { return this->baud_; }
|
||||||
|
|
||||||
// Sends the SecPlus 2.0 preamble using RMT
|
// Sends the SecPlus 2.0 preamble using RMT
|
||||||
@@ -33,21 +33,21 @@ public:
|
|||||||
|
|
||||||
void on_shutdown();
|
void on_shutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Pointers (4 bytes on 32-bit)
|
// Pointers (4 bytes on 32-bit)
|
||||||
rmt_channel_handle_t rmt_chan_handle_ { nullptr };
|
rmt_channel_handle_t rmt_chan_handle_{nullptr};
|
||||||
rmt_encoder_handle_t rmt_copy_encoder_ { nullptr };
|
rmt_encoder_handle_t rmt_copy_encoder_{nullptr};
|
||||||
|
|
||||||
// 4-byte members
|
// 4-byte members
|
||||||
int tx_pin_ { -1 };
|
int tx_pin_{-1};
|
||||||
int rx_pin_ { -1 };
|
int rx_pin_{-1};
|
||||||
int baud_ { 9600 };
|
int baud_{9600};
|
||||||
int uart_num_ { -1 };
|
int uart_num_{-1};
|
||||||
int rmt_channel_id_ { 0 };
|
int rmt_channel_id_{0};
|
||||||
|
|
||||||
// 1-byte members packed at the end
|
// 1-byte members packed at the end
|
||||||
bool inverted_ { true };
|
bool inverted_{true};
|
||||||
bool is_initialized_ { false };
|
bool is_initialized_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::ratgdo
|
} // namespace esphome::ratgdo
|
||||||
|
|||||||
+145
-127
@@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
#include "secplus2.h"
|
#include "secplus2.h"
|
||||||
#include "ratgdo.h"
|
|
||||||
|
|
||||||
#include "esphome/core/gpio.h"
|
#include "esphome/core/gpio.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/scheduler.h"
|
#include "esphome/core/scheduler.h"
|
||||||
|
#include "ratgdo.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "secplus.h"
|
#include "secplus.h"
|
||||||
@@ -14,49 +14,57 @@ extern "C" {
|
|||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
namespace secplus2 {
|
namespace secplus2 {
|
||||||
|
|
||||||
using namespace scheduler_ids;
|
using namespace scheduler_ids;
|
||||||
|
|
||||||
// MAX_CODES_WITHOUT_FLASH_WRITE is a bit of a guess
|
// MAX_CODES_WITHOUT_FLASH_WRITE is a bit of a guess
|
||||||
// since we write the flash at most every every 1min
|
// since we write the flash at most every every 1min
|
||||||
//
|
//
|
||||||
// We want the rolling counter to be high enough that the
|
// We want the rolling counter to be high enough that the
|
||||||
// GDO will accept the command after an unexpected reboot
|
// GDO will accept the command after an unexpected reboot
|
||||||
// that did not save the counter to flash in time which
|
// that did not save the counter to flash in time which
|
||||||
// results in the rolling counter being behind what the GDO
|
// results in the rolling counter being behind what the GDO
|
||||||
// expects.
|
// expects.
|
||||||
static const uint8_t MAX_CODES_WITHOUT_FLASH_WRITE = 60;
|
static const uint8_t MAX_CODES_WITHOUT_FLASH_WRITE = 60;
|
||||||
|
|
||||||
static const char* const TAG = "ratgdo_secplus2";
|
static const char* const TAG = "ratgdo_secplus2";
|
||||||
|
|
||||||
void Secplus2::setup(RATGDOComponent* ratgdo, Scheduler* scheduler, InternalGPIOPin* rx_pin, InternalGPIOPin* tx_pin)
|
void Secplus2::setup(RATGDOComponent* ratgdo, Scheduler* scheduler,
|
||||||
{
|
InternalGPIOPin* rx_pin, InternalGPIOPin* tx_pin) {
|
||||||
this->ratgdo_ = ratgdo;
|
this->ratgdo_ = ratgdo;
|
||||||
this->scheduler_ = scheduler;
|
this->scheduler_ = scheduler;
|
||||||
this->tx_pin_ = tx_pin;
|
this->tx_pin_ = tx_pin;
|
||||||
this->rx_pin_ = rx_pin;
|
this->rx_pin_ = rx_pin;
|
||||||
|
|
||||||
if (mqtt::global_mqtt_client != nullptr) {
|
if (mqtt::global_mqtt_client != nullptr) {
|
||||||
this->mqtt_rolling_code_topic_ = mqtt::global_mqtt_client->get_topic_prefix() + "/gdo/rolling_code";
|
this->mqtt_rolling_code_topic_ =
|
||||||
this->mqtt_client_id_topic_ = mqtt::global_mqtt_client->get_topic_prefix() + "/gdo/client_id";
|
mqtt::global_mqtt_client->get_topic_prefix() + "/gdo/rolling_code";
|
||||||
|
this->mqtt_client_id_topic_ =
|
||||||
|
mqtt::global_mqtt_client->get_topic_prefix() + "/gdo/client_id";
|
||||||
}
|
}
|
||||||
|
|
||||||
this->client_id_pref_ = global_preferences->make_preference<uint32_t>(3497851610U); // fnv1_hash("ratgdo_client_id")
|
this->client_id_pref_ = global_preferences->make_preference<uint32_t>(
|
||||||
|
3497851610U); // fnv1_hash("ratgdo_client_id")
|
||||||
uint32_t stored_client_id;
|
uint32_t stored_client_id;
|
||||||
if (this->client_id_pref_.load(&stored_client_id)) {
|
if (this->client_id_pref_.load(&stored_client_id)) {
|
||||||
this->client_id_ = stored_client_id;
|
this->client_id_ = stored_client_id;
|
||||||
ESP_LOGI(TAG, "Restored Client ID from flash: 0x%04X", (unsigned)this->client_id_);
|
ESP_LOGI(TAG, "Restored Client ID from flash: 0x%04X",
|
||||||
|
(unsigned)this->client_id_);
|
||||||
if (mqtt::global_mqtt_client != nullptr) {
|
if (mqtt::global_mqtt_client != nullptr) {
|
||||||
mqtt::global_mqtt_client->publish(this->mqtt_client_id_topic_, std::to_string(this->client_id_), 0, true);
|
mqtt::global_mqtt_client->publish(this->mqtt_client_id_topic_,
|
||||||
|
std::to_string(this->client_id_), 0,
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
} else if (mqtt::global_mqtt_client != nullptr) {
|
} else if (mqtt::global_mqtt_client != nullptr) {
|
||||||
ESP_LOGI(TAG, "No Client ID in flash, waiting for MQTT: %s", this->mqtt_client_id_topic_.c_str());
|
ESP_LOGI(TAG, "No Client ID in flash, waiting for MQTT: %s",
|
||||||
|
this->mqtt_client_id_topic_.c_str());
|
||||||
mqtt::global_mqtt_client->subscribe(
|
mqtt::global_mqtt_client->subscribe(
|
||||||
this->mqtt_client_id_topic_,
|
this->mqtt_client_id_topic_,
|
||||||
[this](const std::string& topic, const std::string& payload) {
|
[this](const std::string& topic, const std::string& payload) {
|
||||||
if (this->client_id_ == 0x539) {
|
if (this->client_id_ == 0x539) {
|
||||||
uint32_t cid = strtoul(payload.c_str(), nullptr, 10);
|
uint32_t cid = strtoul(payload.c_str(), nullptr, 10);
|
||||||
if (cid != 0) {
|
if (cid != 0) {
|
||||||
ESP_LOGI(TAG, "Received Client ID from MQTT: 0x%04X", (unsigned)cid);
|
ESP_LOGI(TAG, "Received Client ID from MQTT: 0x%04X",
|
||||||
|
(unsigned)cid);
|
||||||
this->set_client_id(cid);
|
this->set_client_id(cid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,16 +79,19 @@ namespace secplus2 {
|
|||||||
ESP_LOGI(TAG, "Generated new unique Client ID: 0x%04X", (unsigned)new_id);
|
ESP_LOGI(TAG, "Generated new unique Client ID: 0x%04X", (unsigned)new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->rolling_code_pref_ = global_preferences->make_preference<uint32_t>(1868352652U); // fnv1_hash("ratgdo_rolling_code")
|
this->rolling_code_pref_ = global_preferences->make_preference<uint32_t>(
|
||||||
|
1868352652U); // fnv1_hash("ratgdo_rolling_code")
|
||||||
uint32_t rolling_code;
|
uint32_t rolling_code;
|
||||||
if (this->rolling_code_pref_.load(&rolling_code)) {
|
if (this->rolling_code_pref_.load(&rolling_code)) {
|
||||||
this->rolling_code_counter_ = rolling_code;
|
this->rolling_code_counter_ = rolling_code;
|
||||||
ESP_LOGI(TAG, "Restored rolling code from flash: %u", rolling_code);
|
ESP_LOGI(TAG, "Restored rolling code from flash: %u", rolling_code);
|
||||||
if (mqtt::global_mqtt_client != nullptr) {
|
if (mqtt::global_mqtt_client != nullptr) {
|
||||||
mqtt::global_mqtt_client->publish(this->mqtt_rolling_code_topic_, std::to_string(rolling_code), 0, true);
|
mqtt::global_mqtt_client->publish(this->mqtt_rolling_code_topic_,
|
||||||
|
std::to_string(rolling_code), 0, true);
|
||||||
}
|
}
|
||||||
} else if (mqtt::global_mqtt_client != nullptr) {
|
} else if (mqtt::global_mqtt_client != nullptr) {
|
||||||
ESP_LOGI(TAG, "No rolling code in flash, waiting for MQTT: %s", this->mqtt_rolling_code_topic_.c_str());
|
ESP_LOGI(TAG, "No rolling code in flash, waiting for MQTT: %s",
|
||||||
|
this->mqtt_rolling_code_topic_.c_str());
|
||||||
mqtt::global_mqtt_client->subscribe(
|
mqtt::global_mqtt_client->subscribe(
|
||||||
this->mqtt_rolling_code_topic_,
|
this->mqtt_rolling_code_topic_,
|
||||||
[this](const std::string& topic, const std::string& payload) {
|
[this](const std::string& topic, const std::string& payload) {
|
||||||
@@ -99,13 +110,13 @@ namespace secplus2 {
|
|||||||
this->rolling_code_pref_.save(&rolling_code);
|
this->rolling_code_pref_.save(&rolling_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->uart_.begin(9600, RATGDO_UART_8N1, rx_pin->get_pin(), tx_pin->get_pin(), true);
|
this->uart_.begin(9600, RATGDO_UART_8N1, rx_pin->get_pin(), tx_pin->get_pin(),
|
||||||
|
true);
|
||||||
this->uart_.enableIntTx(false);
|
this->uart_.enableIntTx(false);
|
||||||
this->uart_.enableAutoBaud(true);
|
this->uart_.enableAutoBaud(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::loop()
|
void Secplus2::loop() {
|
||||||
{
|
|
||||||
if (this->flags_.transmit_pending) {
|
if (this->flags_.transmit_pending) {
|
||||||
if (!this->transmit_packet()) {
|
if (!this->transmit_packet()) {
|
||||||
return;
|
return;
|
||||||
@@ -116,22 +127,18 @@ namespace secplus2 {
|
|||||||
if (cmd) {
|
if (cmd) {
|
||||||
this->handle_command(*cmd);
|
this->handle_command(*cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::dump_config()
|
void Secplus2::dump_config() {
|
||||||
{
|
ESP_LOGCONFIG(TAG, " Rolling Code Counter: %d",
|
||||||
ESP_LOGCONFIG(TAG, " Rolling Code Counter: %d", *this->rolling_code_counter_);
|
*this->rolling_code_counter_);
|
||||||
ESP_LOGCONFIG(TAG, " Client ID: %d", this->client_id_);
|
ESP_LOGCONFIG(TAG, " Client ID: %d", this->client_id_);
|
||||||
ESP_LOGCONFIG(TAG, " Protocol: SEC+ v2");
|
ESP_LOGCONFIG(TAG, " Protocol: SEC+ v2");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::on_shutdown()
|
void Secplus2::on_shutdown() { this->uart_.on_shutdown(); }
|
||||||
{
|
|
||||||
this->uart_.on_shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Secplus2::sync_helper(uint32_t start, uint32_t delay, uint8_t tries)
|
void Secplus2::sync_helper(uint32_t start, uint32_t delay, uint8_t tries) {
|
||||||
{
|
|
||||||
if (tries == 0 || *this->ratgdo_->door_state == DoorState::UNKNOWN) {
|
if (tries == 0 || *this->ratgdo_->door_state == DoorState::UNKNOWN) {
|
||||||
ESP_LOGD(TAG, "Sync: querying status (attempt %d)...", tries);
|
ESP_LOGD(TAG, "Sync: querying status (attempt %d)...", tries);
|
||||||
this->query_status();
|
this->query_status();
|
||||||
@@ -151,69 +158,63 @@ namespace secplus2 {
|
|||||||
this->ratgdo_->synced = false;
|
this->ratgdo_->synced = false;
|
||||||
this->ratgdo_->sync_failed = true;
|
this->ratgdo_->sync_failed = true;
|
||||||
} else {
|
} else {
|
||||||
// Use a slightly longer delay between queries during sync to avoid bus saturation
|
// Use a slightly longer delay between queries during sync to avoid bus
|
||||||
|
// saturation
|
||||||
uint32_t next_delay = (tries < 5) ? 1000 : 2000;
|
uint32_t next_delay = (tries < 5) ? 1000 : 2000;
|
||||||
this->scheduler_->set_timeout(this->ratgdo_, TIMEOUT_SYNC, next_delay, [this, start, next_delay, tries]() {
|
this->scheduler_->set_timeout(this->ratgdo_, TIMEOUT_SYNC, next_delay,
|
||||||
this->sync_helper(start, next_delay, tries + 1);
|
[this, start, next_delay, tries]() {
|
||||||
|
this->sync_helper(start, next_delay,
|
||||||
|
tries + 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::sync()
|
void Secplus2::sync() {
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "Starting sync...");
|
ESP_LOGD(TAG, "Starting sync...");
|
||||||
this->ratgdo_->synced = false;
|
this->ratgdo_->synced = false;
|
||||||
this->scheduler_->cancel_timeout(this->ratgdo_, TIMEOUT_SYNC);
|
this->scheduler_->cancel_timeout(this->ratgdo_, TIMEOUT_SYNC);
|
||||||
this->sync_helper(millis(), 500, 0);
|
this->sync_helper(millis(), 500, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::light_action(LightAction action)
|
void Secplus2::light_action(LightAction action) {
|
||||||
{
|
|
||||||
if (action == LightAction::UNKNOWN) {
|
if (action == LightAction::UNKNOWN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->send_command(Command(CommandType::LIGHT, static_cast<uint8_t>(action)));
|
this->send_command(Command(CommandType::LIGHT, static_cast<uint8_t>(action)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::lock_action(LockAction action)
|
void Secplus2::lock_action(LockAction action) {
|
||||||
{
|
|
||||||
if (action == LockAction::UNKNOWN) {
|
if (action == LockAction::UNKNOWN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->send_command(Command(CommandType::LOCK, static_cast<uint8_t>(action)));
|
this->send_command(Command(CommandType::LOCK, static_cast<uint8_t>(action)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::door_action(DoorAction action)
|
void Secplus2::door_action(DoorAction action) {
|
||||||
{
|
|
||||||
if (action == DoorAction::UNKNOWN) {
|
if (action == DoorAction::UNKNOWN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->door_command(action);
|
this->door_command(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Secplus2::door_command(DoorAction action) {
|
||||||
|
this->send_command(
|
||||||
void Secplus2::door_command(DoorAction action)
|
Command(CommandType::DOOR_ACTION, static_cast<uint8_t>(action), 1, 1),
|
||||||
{
|
IncrementRollingCode::NO, [this, action]() {
|
||||||
this->send_command(Command(CommandType::DOOR_ACTION, static_cast<uint8_t>(action), 1, 1), IncrementRollingCode::NO, [this, action]() {
|
|
||||||
this->ratgdo_->set_timeout(150, [this, action] {
|
this->ratgdo_->set_timeout(150, [this, action] {
|
||||||
this->send_command(Command(CommandType::DOOR_ACTION, static_cast<uint8_t>(action), 0, 1));
|
this->send_command(Command(CommandType::DOOR_ACTION,
|
||||||
|
static_cast<uint8_t>(action), 0, 1));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::query_status()
|
void Secplus2::query_status() { this->send_command(CommandType::GET_STATUS); }
|
||||||
{
|
|
||||||
this->send_command(CommandType::GET_STATUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Secplus2::query_openings()
|
void Secplus2::query_openings() {
|
||||||
{
|
|
||||||
this->send_command(CommandType::GET_OPENINGS);
|
this->send_command(CommandType::GET_OPENINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<Command> Secplus2::read_command()
|
optional<Command> Secplus2::read_command() {
|
||||||
{
|
|
||||||
while (this->uart_.available()) {
|
while (this->uart_.available()) {
|
||||||
uint8_t ser_byte = this->uart_.read();
|
uint8_t ser_byte = this->uart_.read();
|
||||||
this->rx_last_read_ = millis();
|
this->rx_last_read_ = millis();
|
||||||
@@ -241,7 +242,8 @@ namespace secplus2 {
|
|||||||
if (this->rx_byte_count_ == PACKET_LENGTH) {
|
if (this->rx_byte_count_ == PACKET_LENGTH) {
|
||||||
this->flags_.rx_reading_msg = false;
|
this->flags_.rx_reading_msg = false;
|
||||||
this->rx_byte_count_ = 0;
|
this->rx_byte_count_ = 0;
|
||||||
this->rx_msg_start_ = 0; // clear window to prevent immediate re-trigger
|
this->rx_msg_start_ =
|
||||||
|
0; // clear window to prevent immediate re-trigger
|
||||||
this->print_packet(LOG_STR("Received packet"), this->rx_packet_);
|
this->print_packet(LOG_STR("Received packet"), this->rx_packet_);
|
||||||
return this->decode_packet(this->rx_packet_);
|
return this->decode_packet(this->rx_packet_);
|
||||||
}
|
}
|
||||||
@@ -249,26 +251,27 @@ namespace secplus2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->flags_.rx_reading_msg && (millis() - this->rx_last_read_ > 100)) {
|
if (this->flags_.rx_reading_msg && (millis() - this->rx_last_read_ > 100)) {
|
||||||
// if we have a partial packet and it's been over 100ms since last byte was read,
|
// if we have a partial packet and it's been over 100ms since last byte was
|
||||||
// the rest is not coming, discard it.
|
// read, the rest is not coming, discard it.
|
||||||
ESP_LOGW(TAG, "Discard incomplete packet, length: %d", this->rx_byte_count_);
|
ESP_LOGW(TAG, "Discard incomplete packet, length: %d",
|
||||||
|
this->rx_byte_count_);
|
||||||
this->flags_.rx_reading_msg = false;
|
this->flags_.rx_reading_msg = false;
|
||||||
this->rx_byte_count_ = 0;
|
this->rx_byte_count_ = 0;
|
||||||
this->rx_msg_start_ = 0;
|
this->rx_msg_start_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { };
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::print_packet(const esphome::LogString* prefix, const WirePacket& packet) const
|
void Secplus2::print_packet(const esphome::LogString* prefix,
|
||||||
{
|
const WirePacket& packet) const {
|
||||||
constexpr size_t hex_size = format_hex_pretty_size(PACKET_LENGTH);
|
constexpr size_t hex_size = format_hex_pretty_size(PACKET_LENGTH);
|
||||||
char hex_buf[hex_size];
|
char hex_buf[hex_size];
|
||||||
ESP_LOGD(TAG, "%s: [%s]", LOG_STR_ARG(prefix), format_hex_pretty_to(hex_buf, packet, PACKET_LENGTH));
|
ESP_LOGD(TAG, "%s: [%s]", LOG_STR_ARG(prefix),
|
||||||
}
|
format_hex_pretty_to(hex_buf, packet, PACKET_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
optional<Command> Secplus2::decode_packet(const WirePacket& packet) const
|
optional<Command> Secplus2::decode_packet(const WirePacket& packet) const {
|
||||||
{
|
|
||||||
uint32_t rolling = 0;
|
uint32_t rolling = 0;
|
||||||
uint64_t fixed = 0;
|
uint64_t fixed = 0;
|
||||||
uint32_t data = 0;
|
uint32_t data = 0;
|
||||||
@@ -276,17 +279,22 @@ namespace secplus2 {
|
|||||||
int err = decode_wireline(packet, &rolling, &fixed, &data);
|
int err = decode_wireline(packet, &rolling, &fixed, &data);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
ESP_LOGW(TAG, "Decode failed (parity error or invalid frame)");
|
ESP_LOGW(TAG, "Decode failed (parity error or invalid frame)");
|
||||||
return { };
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t cmd = ((fixed >> 24) & 0xf00) | (data & 0xff);
|
uint16_t cmd = ((fixed >> 24) & 0xf00) | (data & 0xff);
|
||||||
data &= ~0xf000; // clear parity nibble
|
data &= ~0xf000; // clear parity nibble
|
||||||
|
|
||||||
if ((fixed & 0xFFFFFFFF) == this->client_id_) { // my commands
|
if ((fixed & 0xFFFFFFFF) == this->client_id_) { // my commands
|
||||||
ESP_LOGD(TAG, " mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, rolling, fixed, data);
|
ESP_LOGD(TAG,
|
||||||
return { };
|
" mine: rolling=%07" PRIx32 " fixed=%010" PRIx64
|
||||||
|
" data=%08" PRIx32,
|
||||||
|
rolling, fixed, data);
|
||||||
|
return {};
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, " rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, rolling, fixed, data);
|
ESP_LOGD(TAG,
|
||||||
|
" rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32,
|
||||||
|
rolling, fixed, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandType cmd_type = to_CommandType(cmd, CommandType::UNKNOWN);
|
CommandType cmd_type = to_CommandType(cmd, CommandType::UNKNOWN);
|
||||||
@@ -294,34 +302,38 @@ namespace secplus2 {
|
|||||||
uint8_t byte1 = (data >> 16) & 0xff;
|
uint8_t byte1 = (data >> 16) & 0xff;
|
||||||
uint8_t byte2 = (data >> 24) & 0xff;
|
uint8_t byte2 = (data >> 24) & 0xff;
|
||||||
|
|
||||||
ESP_LOGD(TAG, " cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, LOG_STR_ARG(CommandType_to_string(cmd_type)), byte2, byte1, nibble);
|
ESP_LOGD(TAG, " cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd,
|
||||||
|
LOG_STR_ARG(CommandType_to_string(cmd_type)), byte2, byte1, nibble);
|
||||||
|
|
||||||
return Command { cmd_type, nibble, byte1, byte2 };
|
return Command{cmd_type, nibble, byte1, byte2};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::handle_command(const Command& cmd)
|
void Secplus2::handle_command(const Command& cmd) {
|
||||||
{
|
ESP_LOGD(TAG, "Handle command: %s (nibble=%01x byte1=%02x byte2=%02x)",
|
||||||
ESP_LOGD(TAG, "Handle command: %s (nibble=%01x byte1=%02x byte2=%02x)", LOG_STR_ARG(CommandType_to_string(cmd.type)), cmd.nibble, cmd.byte1, cmd.byte2);
|
LOG_STR_ARG(CommandType_to_string(cmd.type)), cmd.nibble, cmd.byte1,
|
||||||
|
cmd.byte2);
|
||||||
|
|
||||||
if (cmd.type == CommandType::STATUS) {
|
if (cmd.type == CommandType::STATUS) {
|
||||||
|
|
||||||
this->ratgdo_->received(to_DoorState(cmd.nibble, DoorState::UNKNOWN));
|
this->ratgdo_->received(to_DoorState(cmd.nibble, DoorState::UNKNOWN));
|
||||||
this->ratgdo_->received(to_LightState((cmd.byte2 >> 1) & 1, LightState::UNKNOWN));
|
this->ratgdo_->received(
|
||||||
|
to_LightState((cmd.byte2 >> 1) & 1, LightState::UNKNOWN));
|
||||||
this->ratgdo_->received(to_LockState((cmd.byte2 & 1), LockState::UNKNOWN));
|
this->ratgdo_->received(to_LockState((cmd.byte2 & 1), LockState::UNKNOWN));
|
||||||
} else if (cmd.type == CommandType::LIGHT) {
|
} else if (cmd.type == CommandType::LIGHT) {
|
||||||
this->ratgdo_->received(to_LightAction(cmd.nibble, LightAction::UNKNOWN));
|
this->ratgdo_->received(to_LightAction(cmd.nibble, LightAction::UNKNOWN));
|
||||||
} else if (cmd.type == CommandType::OPENINGS) {
|
} else if (cmd.type == CommandType::OPENINGS) {
|
||||||
this->ratgdo_->received(Openings { static_cast<uint16_t>((cmd.byte1 << 8) | cmd.byte2), cmd.nibble });
|
this->ratgdo_->received(Openings{
|
||||||
}
|
static_cast<uint16_t>((cmd.byte1 << 8) | cmd.byte2), cmd.nibble});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Secplus2::send_command(Command command, IncrementRollingCode increment)
|
void Secplus2::send_command(Command command, IncrementRollingCode increment) {
|
||||||
{
|
{
|
||||||
{
|
uint8_t data[] = {command.byte2, command.byte1, command.nibble};
|
||||||
uint8_t data[] = { command.byte2, command.byte1, command.nibble };
|
|
||||||
constexpr size_t hex_size = format_hex_pretty_size(3);
|
constexpr size_t hex_size = format_hex_pretty_size(3);
|
||||||
char hex[hex_size];
|
char hex[hex_size];
|
||||||
ESP_LOGD(TAG, "Send command: %s, data: %s", LOG_STR_ARG(CommandType_to_string(command.type)), format_hex_pretty_to(hex, data, 3));
|
ESP_LOGD(TAG, "Send command: %s, data: %s",
|
||||||
|
LOG_STR_ARG(CommandType_to_string(command.type)),
|
||||||
|
format_hex_pretty_to(hex, data, 3));
|
||||||
}
|
}
|
||||||
if (!this->flags_.transmit_pending) { // have an untransmitted packet
|
if (!this->flags_.transmit_pending) { // have an untransmitted packet
|
||||||
this->encode_packet(command, this->tx_packet_);
|
this->encode_packet(command, this->tx_packet_);
|
||||||
@@ -329,29 +341,34 @@ namespace secplus2 {
|
|||||||
this->increment_rolling_code_counter();
|
this->increment_rolling_code_counter();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// unlikely this would happed (unless not connected to GDO), we're ensuring any pending packet
|
// unlikely this would happed (unless not connected to GDO), we're ensuring
|
||||||
// is transmitted each loop before doing anyting else
|
// any pending packet is transmitted each loop before doing anyting else
|
||||||
if (this->transmit_pending_start_ > 0) {
|
if (this->transmit_pending_start_ > 0) {
|
||||||
ESP_LOGW(TAG, "Have untransmitted packet, ignoring command: %s", LOG_STR_ARG(CommandType_to_string(command.type)));
|
ESP_LOGW(TAG, "Have untransmitted packet, ignoring command: %s",
|
||||||
|
LOG_STR_ARG(CommandType_to_string(command.type)));
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "Not connected to GDO, ignoring command: %s", LOG_STR_ARG(CommandType_to_string(command.type)));
|
ESP_LOGW(TAG, "Not connected to GDO, ignoring command: %s",
|
||||||
|
LOG_STR_ARG(CommandType_to_string(command.type)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->transmit_packet();
|
this->transmit_packet();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::encode_packet(Command command, WirePacket& packet)
|
void Secplus2::encode_packet(Command command, WirePacket& packet) {
|
||||||
{
|
|
||||||
auto cmd = static_cast<uint64_t>(command.type);
|
auto cmd = static_cast<uint64_t>(command.type);
|
||||||
uint64_t fixed = ((cmd & ~0xff) << 24) | this->client_id_;
|
uint64_t fixed = ((cmd & ~0xff) << 24) | this->client_id_;
|
||||||
uint32_t data = (static_cast<uint64_t>(command.byte2) << 24) | (static_cast<uint64_t>(command.byte1) << 16) | (static_cast<uint64_t>(command.nibble) << 8) | (cmd & 0xff);
|
uint32_t data = (static_cast<uint64_t>(command.byte2) << 24) |
|
||||||
|
(static_cast<uint64_t>(command.byte1) << 16) |
|
||||||
|
(static_cast<uint64_t>(command.nibble) << 8) | (cmd & 0xff);
|
||||||
|
|
||||||
ESP_LOGD(TAG, " transmit: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, *this->rolling_code_counter_, fixed, data);
|
ESP_LOGD(TAG,
|
||||||
|
" transmit: rolling=%07" PRIx32 " fixed=%010" PRIx64
|
||||||
|
" data=%08" PRIx32,
|
||||||
|
*this->rolling_code_counter_, fixed, data);
|
||||||
encode_wireline(*this->rolling_code_counter_, fixed, data, packet);
|
encode_wireline(*this->rolling_code_counter_, fixed, data, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Secplus2::transmit_packet()
|
bool Secplus2::transmit_packet() {
|
||||||
{
|
|
||||||
auto now = micros();
|
auto now = micros();
|
||||||
|
|
||||||
while (micros() - now < 1300) {
|
while (micros() - now < 1300) {
|
||||||
@@ -361,7 +378,8 @@ namespace secplus2 {
|
|||||||
this->transmit_pending_start_ = millis();
|
this->transmit_pending_start_ = millis();
|
||||||
ESP_LOGD(TAG, "Collision detected, waiting to send packet");
|
ESP_LOGD(TAG, "Collision detected, waiting to send packet");
|
||||||
} else if (millis() - this->transmit_pending_start_ >= 5000) {
|
} else if (millis() - this->transmit_pending_start_ >= 5000) {
|
||||||
this->transmit_pending_start_ = 0; // to indicate GDO not connected state
|
this->transmit_pending_start_ =
|
||||||
|
0; // to indicate GDO not connected state
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -377,37 +395,37 @@ namespace secplus2 {
|
|||||||
this->transmit_pending_start_ = 0;
|
this->transmit_pending_start_ = 0;
|
||||||
this->on_command_sent_.trigger();
|
this->on_command_sent_.trigger();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Secplus2::increment_rolling_code_counter(int delta)
|
void Secplus2::increment_rolling_code_counter(int delta) {
|
||||||
{
|
|
||||||
uint32_t counter = (*this->rolling_code_counter_ + delta) & 0xfffffff;
|
uint32_t counter = (*this->rolling_code_counter_ + delta) & 0xfffffff;
|
||||||
this->rolling_code_counter_ = counter;
|
this->rolling_code_counter_ = counter;
|
||||||
this->rolling_code_pref_.save(&counter);
|
this->rolling_code_pref_.save(&counter);
|
||||||
if (mqtt::global_mqtt_client != nullptr) {
|
if (mqtt::global_mqtt_client != nullptr) {
|
||||||
mqtt::global_mqtt_client->publish(this->mqtt_rolling_code_topic_, std::to_string(counter), 0, true);
|
mqtt::global_mqtt_client->publish(this->mqtt_rolling_code_topic_,
|
||||||
}
|
std::to_string(counter), 0, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Secplus2::set_rolling_code_counter(uint32_t counter)
|
void Secplus2::set_rolling_code_counter(uint32_t counter) {
|
||||||
{
|
|
||||||
ESP_LOGV(TAG, "Set rolling code counter to %d", counter);
|
ESP_LOGV(TAG, "Set rolling code counter to %d", counter);
|
||||||
this->rolling_code_counter_ = counter;
|
this->rolling_code_counter_ = counter;
|
||||||
this->rolling_code_pref_.save(&counter);
|
this->rolling_code_pref_.save(&counter);
|
||||||
if (mqtt::global_mqtt_client != nullptr) {
|
if (mqtt::global_mqtt_client != nullptr) {
|
||||||
mqtt::global_mqtt_client->publish(this->mqtt_rolling_code_topic_, std::to_string(counter), 0, true);
|
mqtt::global_mqtt_client->publish(this->mqtt_rolling_code_topic_,
|
||||||
}
|
std::to_string(counter), 0, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Secplus2::set_client_id(uint64_t client_id)
|
void Secplus2::set_client_id(uint64_t client_id) {
|
||||||
{
|
|
||||||
uint32_t cid = client_id & 0xFFFFFFFF;
|
uint32_t cid = client_id & 0xFFFFFFFF;
|
||||||
this->client_id_ = cid;
|
this->client_id_ = cid;
|
||||||
this->client_id_pref_.save(&cid);
|
this->client_id_pref_.save(&cid);
|
||||||
if (mqtt::global_mqtt_client != nullptr) {
|
if (mqtt::global_mqtt_client != nullptr) {
|
||||||
mqtt::global_mqtt_client->publish(this->mqtt_client_id_topic_, std::to_string(cid), 0, true);
|
mqtt::global_mqtt_client->publish(this->mqtt_client_id_topic_,
|
||||||
}
|
std::to_string(cid), 0, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace secplus2
|
} // namespace secplus2
|
||||||
} // namespace esphome::ratgdo
|
} // namespace esphome::ratgdo
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/optional.h"
|
|
||||||
#include "esphome/core/preferences.h"
|
|
||||||
#include "esphome/components/mqtt/mqtt_client.h"
|
|
||||||
#include "ratgdo_uart_esp32.h"
|
|
||||||
|
|
||||||
#include "callbacks.h"
|
#include "callbacks.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "esphome/components/mqtt/mqtt_client.h"
|
||||||
|
#include "esphome/core/optional.h"
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
#include "observable.h"
|
#include "observable.h"
|
||||||
#include "ratgdo_state.h"
|
#include "ratgdo_state.h"
|
||||||
|
#include "ratgdo_uart_esp32.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
@@ -22,10 +21,10 @@ class RATGDOComponent;
|
|||||||
|
|
||||||
namespace secplus2 {
|
namespace secplus2 {
|
||||||
|
|
||||||
static const uint8_t PACKET_LENGTH = 19;
|
static const uint8_t PACKET_LENGTH = 19;
|
||||||
typedef uint8_t WirePacket[PACKET_LENGTH];
|
typedef uint8_t WirePacket[PACKET_LENGTH];
|
||||||
|
|
||||||
enum class CommandType : uint16_t {
|
enum class CommandType : uint16_t {
|
||||||
UNKNOWN = 0x000,
|
UNKNOWN = 0x000,
|
||||||
GET_STATUS = 0x080,
|
GET_STATUS = 0x080,
|
||||||
STATUS = 0x081,
|
STATUS = 0x081,
|
||||||
@@ -34,66 +33,82 @@ namespace secplus2 {
|
|||||||
LIGHT = 0x281,
|
LIGHT = 0x281,
|
||||||
GET_OPENINGS = 0x48b,
|
GET_OPENINGS = 0x48b,
|
||||||
OPENINGS = 0x48c // openings = (byte1<<8)+byte2
|
OPENINGS = 0x48c // openings = (byte1<<8)+byte2
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const char* CommandType_to_string(CommandType e) {
|
inline const char* CommandType_to_string(CommandType e) {
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case CommandType::UNKNOWN: return "UNKNOWN";
|
case CommandType::UNKNOWN:
|
||||||
case CommandType::GET_STATUS: return "GET_STATUS";
|
return "UNKNOWN";
|
||||||
case CommandType::STATUS: return "STATUS";
|
case CommandType::GET_STATUS:
|
||||||
case CommandType::LOCK: return "LOCK";
|
return "GET_STATUS";
|
||||||
case CommandType::DOOR_ACTION: return "DOOR_ACTION";
|
case CommandType::STATUS:
|
||||||
case CommandType::LIGHT: return "LIGHT";
|
return "STATUS";
|
||||||
case CommandType::GET_OPENINGS: return "GET_OPENINGS";
|
case CommandType::LOCK:
|
||||||
case CommandType::OPENINGS: return "OPENINGS";
|
return "LOCK";
|
||||||
default: return "UNKNOWN";
|
case CommandType::DOOR_ACTION:
|
||||||
}
|
return "DOOR_ACTION";
|
||||||
|
case CommandType::LIGHT:
|
||||||
|
return "LIGHT";
|
||||||
|
case CommandType::GET_OPENINGS:
|
||||||
|
return "GET_OPENINGS";
|
||||||
|
case CommandType::OPENINGS:
|
||||||
|
return "OPENINGS";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline CommandType to_CommandType(uint16_t t, CommandType unknown) {
|
inline CommandType to_CommandType(uint16_t t, CommandType unknown) {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case static_cast<uint16_t>(CommandType::UNKNOWN): return CommandType::UNKNOWN;
|
case static_cast<uint16_t>(CommandType::UNKNOWN):
|
||||||
case static_cast<uint16_t>(CommandType::GET_STATUS): return CommandType::GET_STATUS;
|
return CommandType::UNKNOWN;
|
||||||
case static_cast<uint16_t>(CommandType::STATUS): return CommandType::STATUS;
|
case static_cast<uint16_t>(CommandType::GET_STATUS):
|
||||||
case static_cast<uint16_t>(CommandType::LOCK): return CommandType::LOCK;
|
return CommandType::GET_STATUS;
|
||||||
case static_cast<uint16_t>(CommandType::DOOR_ACTION): return CommandType::DOOR_ACTION;
|
case static_cast<uint16_t>(CommandType::STATUS):
|
||||||
case static_cast<uint16_t>(CommandType::LIGHT): return CommandType::LIGHT;
|
return CommandType::STATUS;
|
||||||
case static_cast<uint16_t>(CommandType::GET_OPENINGS): return CommandType::GET_OPENINGS;
|
case static_cast<uint16_t>(CommandType::LOCK):
|
||||||
case static_cast<uint16_t>(CommandType::OPENINGS): return CommandType::OPENINGS;
|
return CommandType::LOCK;
|
||||||
default: return unknown;
|
case static_cast<uint16_t>(CommandType::DOOR_ACTION):
|
||||||
}
|
return CommandType::DOOR_ACTION;
|
||||||
|
case static_cast<uint16_t>(CommandType::LIGHT):
|
||||||
|
return CommandType::LIGHT;
|
||||||
|
case static_cast<uint16_t>(CommandType::GET_OPENINGS):
|
||||||
|
return CommandType::GET_OPENINGS;
|
||||||
|
case static_cast<uint16_t>(CommandType::OPENINGS):
|
||||||
|
return CommandType::OPENINGS;
|
||||||
|
default:
|
||||||
|
return unknown;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator==(const uint16_t cmd_i, const CommandType& cmd_e) { return cmd_i == static_cast<uint16_t>(cmd_e); }
|
inline bool operator==(const uint16_t cmd_i, const CommandType& cmd_e) {
|
||||||
inline bool operator==(const CommandType& cmd_e, const uint16_t cmd_i) { return cmd_i == static_cast<uint16_t>(cmd_e); }
|
return cmd_i == static_cast<uint16_t>(cmd_e);
|
||||||
|
}
|
||||||
|
inline bool operator==(const CommandType& cmd_e, const uint16_t cmd_i) {
|
||||||
|
return cmd_i == static_cast<uint16_t>(cmd_e);
|
||||||
|
}
|
||||||
|
|
||||||
enum class IncrementRollingCode {
|
enum class IncrementRollingCode {
|
||||||
NO,
|
NO,
|
||||||
YES,
|
YES,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Command {
|
struct Command {
|
||||||
CommandType type;
|
CommandType type;
|
||||||
uint8_t nibble;
|
uint8_t nibble;
|
||||||
uint8_t byte1;
|
uint8_t byte1;
|
||||||
uint8_t byte2;
|
uint8_t byte2;
|
||||||
|
|
||||||
Command()
|
Command() : type(CommandType::UNKNOWN) {}
|
||||||
: type(CommandType::UNKNOWN)
|
Command(CommandType type_, uint8_t nibble_ = 0, uint8_t byte1_ = 0,
|
||||||
{
|
uint8_t byte2_ = 0)
|
||||||
}
|
: type(type_), nibble(nibble_), byte1(byte1_), byte2(byte2_) {}
|
||||||
Command(CommandType type_, uint8_t nibble_ = 0, uint8_t byte1_ = 0, uint8_t byte2_ = 0)
|
};
|
||||||
: type(type_)
|
|
||||||
, nibble(nibble_)
|
|
||||||
, byte1(byte1_)
|
|
||||||
, byte2(byte2_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Secplus2 {
|
class Secplus2 {
|
||||||
public:
|
public:
|
||||||
void setup(RATGDOComponent* ratgdo, Scheduler* scheduler, InternalGPIOPin* rx_pin, InternalGPIOPin* tx_pin);
|
void setup(RATGDOComponent* ratgdo, Scheduler* scheduler,
|
||||||
|
InternalGPIOPin* rx_pin, InternalGPIOPin* tx_pin);
|
||||||
void loop();
|
void loop();
|
||||||
void dump_config();
|
void dump_config();
|
||||||
void on_shutdown();
|
void on_shutdown();
|
||||||
@@ -115,10 +130,10 @@ namespace secplus2 {
|
|||||||
optional<Command> read_command();
|
optional<Command> read_command();
|
||||||
void handle_command(const Command& cmd);
|
void handle_command(const Command& cmd);
|
||||||
|
|
||||||
void send_command(Command cmd, IncrementRollingCode increment = IncrementRollingCode::YES);
|
void send_command(Command cmd,
|
||||||
|
IncrementRollingCode increment = IncrementRollingCode::YES);
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void send_command(Command cmd, IncrementRollingCode increment, F&& on_sent)
|
void send_command(Command cmd, IncrementRollingCode increment, F&& on_sent) {
|
||||||
{
|
|
||||||
// Only register the callback if the command will be accepted.
|
// Only register the callback if the command will be accepted.
|
||||||
// If transmit_pending is set the command will be dropped, and
|
// If transmit_pending is set the command will be dropped, and
|
||||||
// a stale callback would fire when the previous pending packet
|
// a stale callback would fire when the previous pending packet
|
||||||
@@ -138,15 +153,14 @@ namespace secplus2 {
|
|||||||
|
|
||||||
void door_command(DoorAction action);
|
void door_command(DoorAction action);
|
||||||
|
|
||||||
|
void print_packet(const esphome::LogString* prefix,
|
||||||
|
const WirePacket& packet) const;
|
||||||
void print_packet(const esphome::LogString* prefix, const WirePacket& packet) const;
|
|
||||||
optional<Command> decode_packet(const WirePacket& packet) const;
|
optional<Command> decode_packet(const WirePacket& packet) const;
|
||||||
|
|
||||||
void sync_helper(uint32_t start, uint32_t delay, uint8_t tries);
|
void sync_helper(uint32_t start, uint32_t delay, uint8_t tries);
|
||||||
|
|
||||||
// 8-byte member first (may require 8-byte alignment on some 32-bit systems)
|
// 8-byte member first (may require 8-byte alignment on some 32-bit systems)
|
||||||
uint64_t client_id_ { 0x539 };
|
uint64_t client_id_{0x539};
|
||||||
|
|
||||||
// Pointers (4-byte aligned)
|
// Pointers (4-byte aligned)
|
||||||
InternalGPIOPin* tx_pin_;
|
InternalGPIOPin* tx_pin_;
|
||||||
@@ -155,12 +169,12 @@ namespace secplus2 {
|
|||||||
Scheduler* scheduler_;
|
Scheduler* scheduler_;
|
||||||
|
|
||||||
// 4-byte members
|
// 4-byte members
|
||||||
uint32_t transmit_pending_start_ { 0 };
|
uint32_t transmit_pending_start_{0};
|
||||||
uint32_t rx_msg_start_ { 0 };
|
uint32_t rx_msg_start_{0};
|
||||||
uint32_t rx_last_read_ { 0 };
|
uint32_t rx_last_read_{0};
|
||||||
|
|
||||||
// Larger structures
|
// Larger structures
|
||||||
single_observable<uint32_t> rolling_code_counter_ { 0 };
|
single_observable<uint32_t> rolling_code_counter_{0};
|
||||||
ESPPreferenceObject rolling_code_pref_;
|
ESPPreferenceObject rolling_code_pref_;
|
||||||
ESPPreferenceObject client_id_pref_;
|
ESPPreferenceObject client_id_pref_;
|
||||||
std::string mqtt_rolling_code_topic_;
|
std::string mqtt_rolling_code_topic_;
|
||||||
@@ -173,11 +187,11 @@ namespace secplus2 {
|
|||||||
WirePacket rx_packet_;
|
WirePacket rx_packet_;
|
||||||
|
|
||||||
// Small members at the end
|
// Small members at the end
|
||||||
uint16_t rx_byte_count_ { 0 };
|
uint16_t rx_byte_count_{0};
|
||||||
struct {
|
struct {
|
||||||
uint8_t transmit_pending : 1;
|
uint8_t transmit_pending : 1;
|
||||||
uint8_t rx_reading_msg : 1;
|
uint8_t rx_reading_msg : 1;
|
||||||
} flags_ { 0 };
|
} flags_{0};
|
||||||
};
|
};
|
||||||
} // namespace secplus2
|
} // namespace secplus2
|
||||||
} // namespace esphome::ratgdo
|
} // namespace esphome::ratgdo
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
#include "ratgdo_sensor.h"
|
#include "ratgdo_sensor.h"
|
||||||
|
|
||||||
#include "../ratgdo_state.h"
|
#include "../ratgdo_state.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
static const char* const TAG = "ratgdo.sensor";
|
static const char* const TAG = "ratgdo.sensor";
|
||||||
static const int MIN_DISTANCE = 100; // ignore bugs crawling on the distance sensor & dust protection film
|
static const int MIN_DISTANCE =
|
||||||
|
100; // ignore bugs crawling on the distance sensor & dust protection film
|
||||||
static const int MAX_DISTANCE = 4500; // default maximum distance
|
static const int MAX_DISTANCE = 4500; // default maximum distance
|
||||||
|
|
||||||
void RATGDOSensor::setup()
|
void RATGDOSensor::setup() {
|
||||||
{
|
|
||||||
switch (this->ratgdo_sensor_type_) {
|
switch (this->ratgdo_sensor_type_) {
|
||||||
case RATGDOSensorType::RATGDO_OPENINGS:
|
case RATGDOSensorType::RATGDO_OPENINGS:
|
||||||
this->parent_->subscribe_openings([this](uint16_t value) {
|
this->parent_->subscribe_openings(
|
||||||
this->publish_state(value);
|
[this](uint16_t value) { this->publish_state(value); });
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case RATGDOSensorType::RATGDO_DISTANCE:
|
case RATGDOSensorType::RATGDO_DISTANCE:
|
||||||
this->distance_sensor_.setI2cDevice(&I2C);
|
this->distance_sensor_.setI2cDevice(&I2C);
|
||||||
@@ -23,19 +23,18 @@ void RATGDOSensor::setup()
|
|||||||
this->distance_sensor_.begin();
|
this->distance_sensor_.begin();
|
||||||
this->distance_sensor_.VL53L4CX_Off();
|
this->distance_sensor_.VL53L4CX_Off();
|
||||||
this->distance_sensor_.InitSensor(0x59);
|
this->distance_sensor_.InitSensor(0x59);
|
||||||
this->distance_sensor_.VL53L4CX_SetDistanceMode(VL53L4CX_DISTANCEMODE_LONG);
|
this->distance_sensor_.VL53L4CX_SetDistanceMode(
|
||||||
|
VL53L4CX_DISTANCEMODE_LONG);
|
||||||
this->distance_sensor_.VL53L4CX_StartMeasurement();
|
this->distance_sensor_.VL53L4CX_StartMeasurement();
|
||||||
this->parent_->subscribe_distance_measurement([this](int16_t value) {
|
this->parent_->subscribe_distance_measurement(
|
||||||
this->publish_state(value);
|
[this](int16_t value) { this->publish_state(value); });
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOSensor::dump_config()
|
void RATGDOSensor::dump_config() {
|
||||||
{
|
|
||||||
LOG_SENSOR("", "RATGDO Sensor", this);
|
LOG_SENSOR("", "RATGDO Sensor", this);
|
||||||
switch (this->ratgdo_sensor_type_) {
|
switch (this->ratgdo_sensor_type_) {
|
||||||
case RATGDOSensorType::RATGDO_OPENINGS:
|
case RATGDOSensorType::RATGDO_OPENINGS:
|
||||||
@@ -49,8 +48,7 @@ void RATGDOSensor::dump_config()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOSensor::loop()
|
void RATGDOSensor::loop() {
|
||||||
{
|
|
||||||
if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_DISTANCE) {
|
if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_DISTANCE) {
|
||||||
VL53L4CX_MultiRangingData_t distanceData;
|
VL53L4CX_MultiRangingData_t distanceData;
|
||||||
VL53L4CX_MultiRangingData_t* pDistanceData = &distanceData;
|
VL53L4CX_MultiRangingData_t* pDistanceData = &distanceData;
|
||||||
@@ -59,8 +57,11 @@ void RATGDOSensor::loop()
|
|||||||
int16_t maxDistance = -1;
|
int16_t maxDistance = -1;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (this->distance_sensor_.VL53L4CX_GetMeasurementDataReady(&dataReady) == 0 && dataReady) {
|
if (this->distance_sensor_.VL53L4CX_GetMeasurementDataReady(&dataReady) ==
|
||||||
status = this->distance_sensor_.VL53L4CX_GetMultiRangingData(pDistanceData);
|
0 &&
|
||||||
|
dataReady) {
|
||||||
|
status =
|
||||||
|
this->distance_sensor_.VL53L4CX_GetMultiRangingData(pDistanceData);
|
||||||
objCount = pDistanceData->NumberOfObjectsFound;
|
objCount = pDistanceData->NumberOfObjectsFound;
|
||||||
|
|
||||||
for (int i = 0; i < distanceData.NumberOfObjectsFound; i++) {
|
for (int i = 0; i < distanceData.NumberOfObjectsFound; i++) {
|
||||||
@@ -71,21 +72,22 @@ void RATGDOSensor::loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxDistance < 0)
|
if (maxDistance < 0) maxDistance = MAX_DISTANCE;
|
||||||
maxDistance = MAX_DISTANCE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if the sensor is pointed at glass, there are many error -1 readings which will fill the
|
* if the sensor is pointed at glass, there are many error -1 readings
|
||||||
* vector with out of range data. The sensor should be sensitive enough to detect the floor
|
* which will fill the vector with out of range data. The sensor should be
|
||||||
* in most situations, but daylight and/or really high ceilings can cause long distance
|
* sensitive enough to detect the floor in most situations, but daylight
|
||||||
* measurements to be out of range.
|
* and/or really high ceilings can cause long distance measurements to be
|
||||||
|
* out of range.
|
||||||
*/
|
*/
|
||||||
this->parent_->set_distance_measurement(maxDistance);
|
this->parent_->set_distance_measurement(maxDistance);
|
||||||
|
|
||||||
// ESP_LOGD(TAG,"# obj found %d; distance %d",objCount, maxDistance);
|
// ESP_LOGD(TAG,"# obj found %d; distance %d",objCount, maxDistance);
|
||||||
|
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
status = this->distance_sensor_.VL53L4CX_ClearInterruptAndStartMeasurement();
|
status =
|
||||||
|
this->distance_sensor_.VL53L4CX_ClearInterruptAndStartMeasurement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,29 +2,29 @@
|
|||||||
|
|
||||||
#include "../ratgdo.h"
|
#include "../ratgdo.h"
|
||||||
#include "../ratgdo_state.h"
|
#include "../ratgdo_state.h"
|
||||||
|
#include "Wire.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
#include "Wire.h"
|
|
||||||
#include "vl53l4cx_class.h"
|
#include "vl53l4cx_class.h"
|
||||||
#define I2C Wire
|
#define I2C Wire
|
||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
enum RATGDOSensorType : uint8_t {
|
enum RATGDOSensorType : uint8_t { RATGDO_OPENINGS, RATGDO_DISTANCE = 6 };
|
||||||
RATGDO_OPENINGS,
|
|
||||||
RATGDO_DISTANCE = 6
|
|
||||||
};
|
|
||||||
|
|
||||||
class RATGDOSensor : public sensor::Sensor, public RATGDOClient, public Component {
|
class RATGDOSensor : public sensor::Sensor,
|
||||||
public:
|
public RATGDOClient,
|
||||||
|
public Component {
|
||||||
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
void set_ratgdo_sensor_type(RATGDOSensorType ratgdo_sensor_type_) { this->ratgdo_sensor_type_ = ratgdo_sensor_type_; }
|
void set_ratgdo_sensor_type(RATGDOSensorType ratgdo_sensor_type_) {
|
||||||
|
this->ratgdo_sensor_type_ = ratgdo_sensor_type_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
RATGDOSensorType ratgdo_sensor_type_;
|
RATGDOSensorType ratgdo_sensor_type_;
|
||||||
|
|
||||||
VL53L4CX distance_sensor_;
|
VL53L4CX distance_sensor_;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "ratgdo_switch.h"
|
#include "ratgdo_switch.h"
|
||||||
|
|
||||||
#include "../ratgdo_state.h"
|
#include "../ratgdo_state.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
@@ -6,8 +7,7 @@ namespace esphome::ratgdo {
|
|||||||
|
|
||||||
static const char* const TAG = "ratgdo.switch";
|
static const char* const TAG = "ratgdo.switch";
|
||||||
|
|
||||||
void RATGDOSwitch::dump_config()
|
void RATGDOSwitch::dump_config() {
|
||||||
{
|
|
||||||
LOG_SWITCH("", "RATGDO Switch", this);
|
LOG_SWITCH("", "RATGDO Switch", this);
|
||||||
switch (this->switch_type_) {
|
switch (this->switch_type_) {
|
||||||
case SwitchType::RATGDO_LED:
|
case SwitchType::RATGDO_LED:
|
||||||
@@ -18,8 +18,7 @@ void RATGDOSwitch::dump_config()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOSwitch::setup()
|
void RATGDOSwitch::setup() {
|
||||||
{
|
|
||||||
switch (this->switch_type_) {
|
switch (this->switch_type_) {
|
||||||
case SwitchType::RATGDO_LED:
|
case SwitchType::RATGDO_LED:
|
||||||
this->pin_->setup();
|
this->pin_->setup();
|
||||||
@@ -29,8 +28,7 @@ void RATGDOSwitch::setup()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOSwitch::write_state(bool state)
|
void RATGDOSwitch::write_state(bool state) {
|
||||||
{
|
|
||||||
switch (this->switch_type_) {
|
switch (this->switch_type_) {
|
||||||
case SwitchType::RATGDO_LED:
|
case SwitchType::RATGDO_LED:
|
||||||
this->pin_->digital_write(state);
|
this->pin_->digital_write(state);
|
||||||
|
|||||||
@@ -8,20 +8,22 @@
|
|||||||
|
|
||||||
namespace esphome::ratgdo {
|
namespace esphome::ratgdo {
|
||||||
|
|
||||||
enum SwitchType {
|
enum SwitchType { RATGDO_LED = 1 };
|
||||||
RATGDO_LED = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
class RATGDOSwitch : public switch_::Switch, public RATGDOClient, public Component {
|
class RATGDOSwitch : public switch_::Switch,
|
||||||
public:
|
public RATGDOClient,
|
||||||
|
public Component {
|
||||||
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void set_switch_type(SwitchType switch_type_) { this->switch_type_ = switch_type_; }
|
void set_switch_type(SwitchType switch_type_) {
|
||||||
|
this->switch_type_ = switch_type_;
|
||||||
|
}
|
||||||
|
|
||||||
void write_state(bool state) override;
|
void write_state(bool state) override;
|
||||||
void set_pin(GPIOPin* pin) { pin_ = pin; }
|
void set_pin(GPIOPin* pin) { pin_ = pin; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SwitchType switch_type_;
|
SwitchType switch_type_;
|
||||||
GPIOPin* pin_;
|
GPIOPin* pin_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace somfy {
|
namespace somfy {
|
||||||
static const char *const TAG = "somfy";
|
static const char* const TAG = "somfy";
|
||||||
|
|
||||||
static const uint8_t CMD_STOP = 0x01;
|
static const uint8_t CMD_STOP = 0x01;
|
||||||
static const uint8_t CMD_UP = 0x02;
|
static const uint8_t CMD_UP = 0x02;
|
||||||
@@ -34,8 +34,8 @@ struct SomfyRTSProtocolData {
|
|||||||
class SomfyRTSProtocol
|
class SomfyRTSProtocol
|
||||||
: public remote_base::RemoteProtocol<SomfyRTSProtocolData> {
|
: public remote_base::RemoteProtocol<SomfyRTSProtocolData> {
|
||||||
public:
|
public:
|
||||||
virtual void encode(remote_base::RemoteTransmitData *dst,
|
virtual void encode(remote_base::RemoteTransmitData* dst,
|
||||||
const ProtocolData &data) override {
|
const ProtocolData& data) override {
|
||||||
/*
|
/*
|
||||||
* 1. Build a frame.
|
* 1. Build a frame.
|
||||||
*/
|
*/
|
||||||
@@ -101,7 +101,7 @@ class SomfyRTSProtocol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void dump(const ProtocolData &data) override { data.dump(); }
|
virtual void dump(const ProtocolData& data) override { data.dump(); }
|
||||||
|
|
||||||
virtual optional<ProtocolData> decode(
|
virtual optional<ProtocolData> decode(
|
||||||
remote_base::RemoteReceiveData src) override {
|
remote_base::RemoteReceiveData src) override {
|
||||||
@@ -155,7 +155,7 @@ void SomfyRTSCover::setup() {
|
|||||||
// MQTT.
|
// MQTT.
|
||||||
mqtt::global_mqtt_client->subscribe(
|
mqtt::global_mqtt_client->subscribe(
|
||||||
mqtt_topic_prefix_ + std::to_string(remote_id_),
|
mqtt_topic_prefix_ + std::to_string(remote_id_),
|
||||||
[this](const std::string &topic, const std::string &payload) {
|
[this](const std::string& topic, const std::string& payload) {
|
||||||
if (rolling_code_ == 0) {
|
if (rolling_code_ == 0) {
|
||||||
ESP_LOGI(TAG, "Received rolling code for remote #%d from MQTT: %s",
|
ESP_LOGI(TAG, "Received rolling code for remote #%d from MQTT: %s",
|
||||||
remote_id_, payload.c_str());
|
remote_id_, payload.c_str());
|
||||||
@@ -171,7 +171,7 @@ void SomfyRTSCover::setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SomfyRTSCover::control(const cover::CoverCall &call) {
|
void SomfyRTSCover::control(const cover::CoverCall& call) {
|
||||||
if (call.get_position().has_value()) {
|
if (call.get_position().has_value()) {
|
||||||
float position = *call.get_position();
|
float position = *call.get_position();
|
||||||
send(position > 0.5 ? CMD_UP : CMD_DOWN);
|
send(position > 0.5 ? CMD_UP : CMD_DOWN);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace somfy {
|
|||||||
class SomfyRTSCover : public cover::Cover, public Component {
|
class SomfyRTSCover : public cover::Cover, public Component {
|
||||||
public:
|
public:
|
||||||
void set_transmitter(
|
void set_transmitter(
|
||||||
remote_transmitter::RemoteTransmitterComponent *transmitter) {
|
remote_transmitter::RemoteTransmitterComponent* transmitter) {
|
||||||
this->transmitter_ = transmitter;
|
this->transmitter_ = transmitter;
|
||||||
}
|
}
|
||||||
void set_remote_id(uint32_t remote_id) { this->remote_id_ = remote_id; }
|
void set_remote_id(uint32_t remote_id) { this->remote_id_ = remote_id; }
|
||||||
@@ -24,7 +24,7 @@ class SomfyRTSCover : public cover::Cover, public Component {
|
|||||||
cover::CoverTraits get_traits() override;
|
cover::CoverTraits get_traits() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void control(const cover::CoverCall &call) override;
|
void control(const cover::CoverCall& call) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* Wake up the blinds motor controller and send the command.
|
/* Wake up the blinds motor controller and send the command.
|
||||||
@@ -39,7 +39,7 @@ class SomfyRTSCover : public cover::Cover, public Component {
|
|||||||
uint32_t rolling_code_;
|
uint32_t rolling_code_;
|
||||||
std::string mqtt_topic_prefix_;
|
std::string mqtt_topic_prefix_;
|
||||||
ESPPreferenceObject rolling_code_pref_;
|
ESPPreferenceObject rolling_code_pref_;
|
||||||
remote_transmitter::RemoteTransmitterComponent *transmitter_{nullptr};
|
remote_transmitter::RemoteTransmitterComponent* transmitter_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace somfy
|
} // namespace somfy
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
#include "esphome/components/remote_base/remote_base.h"
|
#include "esphome/components/remote_base/remote_base.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
static const char *const TAG = __FILE__;
|
static const char* const TAG = __FILE__;
|
||||||
static const uint8_t MIN_TEMPERATURE = 35;
|
static const uint8_t MIN_TEMPERATURE = 35;
|
||||||
static const uint8_t MAX_TEMPERATURE = 70;
|
static const uint8_t MAX_TEMPERATURE = 70;
|
||||||
static const uint8_t TEMPERATURE_LEVELS = 8;
|
static const uint8_t TEMPERATURE_LEVELS = 8;
|
||||||
|
|
||||||
static void add_header_(esphome::remote_base::RemoteTransmitData *data) {
|
static void add_header_(esphome::remote_base::RemoteTransmitData* data) {
|
||||||
data->item(30, 1000);
|
data->item(30, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_byte_(esphome::remote_base::RemoteTransmitData *data,
|
static void add_byte_(esphome::remote_base::RemoteTransmitData* data,
|
||||||
uint8_t value) {
|
uint8_t value) {
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
data->item(30, value & 0b10000000 ? 830 : 650);
|
data->item(30, value & 0b10000000 ? 830 : 650);
|
||||||
@@ -20,7 +20,7 @@ static void add_byte_(esphome::remote_base::RemoteTransmitData *data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_post_(esphome::remote_base::RemoteTransmitData *data) {
|
static void add_post_(esphome::remote_base::RemoteTransmitData* data) {
|
||||||
data->item(30, 460);
|
data->item(30, 460);
|
||||||
data->item(30, 650);
|
data->item(30, 650);
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ static void add_post_(esphome::remote_base::RemoteTransmitData *data) {
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace zehnder {
|
namespace zehnder {
|
||||||
|
|
||||||
void ZehnderComponent::control(const climate::ClimateCall &call) {
|
void ZehnderComponent::control(const climate::ClimateCall& call) {
|
||||||
float target_temp = this->target_temperature;
|
float target_temp = this->target_temperature;
|
||||||
climate::ClimateMode mode = this->mode;
|
climate::ClimateMode mode = this->mode;
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ void ZehnderComponent::dump_config() {
|
|||||||
this->has_temperature_sensor_ ? "yes" : "no");
|
this->has_temperature_sensor_ ? "yes" : "no");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ZehnderComponent::transmit_temperature_(float *temp) {
|
bool ZehnderComponent::transmit_temperature_(float* temp) {
|
||||||
uint8_t level;
|
uint8_t level;
|
||||||
if (*temp == 0) {
|
if (*temp == 0) {
|
||||||
level = 0;
|
level = 0;
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ class ZehnderComponent : public climate::Climate, public PollingComponent {
|
|||||||
// PollingComponent.
|
// PollingComponent.
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
void set_transmitter(remote_transmitter::RemoteTransmitterComponent *t) {
|
void set_transmitter(remote_transmitter::RemoteTransmitterComponent* t) {
|
||||||
this->transmitter_ = t;
|
this->transmitter_ = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_temperature_sensor(sensor::Sensor *s) {
|
void add_temperature_sensor(sensor::Sensor* s) {
|
||||||
has_temperature_sensor_ = true;
|
has_temperature_sensor_ = true;
|
||||||
s->add_on_state_callback([this](float temperature_value) {
|
s->add_on_state_callback([this](float temperature_value) {
|
||||||
this->current_temperature = temperature_value;
|
this->current_temperature = temperature_value;
|
||||||
@@ -36,13 +36,13 @@ class ZehnderComponent : public climate::Climate, public PollingComponent {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// climate::Climate.
|
// climate::Climate.
|
||||||
void control(const climate::ClimateCall &call) override;
|
void control(const climate::ClimateCall& call) override;
|
||||||
climate::ClimateTraits traits() override;
|
climate::ClimateTraits traits() override;
|
||||||
|
|
||||||
bool transmit_temperature_(float *temp);
|
bool transmit_temperature_(float* temp);
|
||||||
bool transmit_level_(uint8_t level);
|
bool transmit_level_(uint8_t level);
|
||||||
|
|
||||||
remote_transmitter::RemoteTransmitterComponent *transmitter_ = nullptr;
|
remote_transmitter::RemoteTransmitterComponent* transmitter_ = nullptr;
|
||||||
bool has_temperature_sensor_ = false;
|
bool has_temperature_sensor_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ esp32_camera:
|
|||||||
idle_framerate: 0.1fps
|
idle_framerate: 0.1fps
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: ledc
|
- platform: ledc
|
||||||
pin: GPIO4
|
pin: GPIO4
|
||||||
channel: 2 # PWM channel 1 is used for esp32_camera.
|
channel: 2 # PWM channel 1 is used for esp32_camera.
|
||||||
id: led
|
id: led
|
||||||
|
|
||||||
light:
|
light:
|
||||||
- platform: monochromatic
|
- platform: monochromatic
|
||||||
output: led
|
output: led
|
||||||
name: Flash
|
name: Flash
|
||||||
|
|||||||
+4
-4
@@ -19,7 +19,7 @@ external_components:
|
|||||||
- source:
|
- source:
|
||||||
type: local
|
type: local
|
||||||
path: components
|
path: components
|
||||||
components: [ ratgdo ]
|
components: [ratgdo]
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
wifi: !include templates/wifi.yaml
|
wifi: !include templates/wifi.yaml
|
||||||
@@ -49,7 +49,7 @@ time:
|
|||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Sync failed"
|
name: "Sync failed"
|
||||||
lambda: 'return *id(garage_gate_controller).sync_failed;'
|
lambda: "return *id(garage_gate_controller).sync_failed;"
|
||||||
device_class: problem
|
device_class: problem
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ cover:
|
|||||||
on_state_change:
|
on_state_change:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(garage_gate).current_operation == COVER_OPERATION_CLOSING;'
|
lambda: "return id(garage_gate).current_operation == COVER_OPERATION_CLOSING;"
|
||||||
then:
|
then:
|
||||||
- script.execute: play_siren
|
- script.execute: play_siren
|
||||||
else:
|
else:
|
||||||
@@ -167,7 +167,7 @@ script:
|
|||||||
then:
|
then:
|
||||||
- while:
|
- while:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(garage_gate).current_operation == COVER_OPERATION_CLOSING;'
|
lambda: "return id(garage_gate).current_operation == COVER_OPERATION_CLOSING;"
|
||||||
then:
|
then:
|
||||||
- rtttl.play: "siren:d=8,o=6,b=100:e,d#,e,d#,e,d#,e,d#"
|
- rtttl.play: "siren:d=8,o=6,b=100:e,d#,e,d#,e,d#,e,d#"
|
||||||
- wait_until:
|
- wait_until:
|
||||||
|
|||||||
+2
-3
@@ -40,7 +40,7 @@ ethernet:
|
|||||||
domain: ""
|
domain: ""
|
||||||
|
|
||||||
text_sensor:
|
text_sensor:
|
||||||
- platform: ethernet_info
|
- platform: ethernet_info
|
||||||
ip_address:
|
ip_address:
|
||||||
name: "ESP chip IP (Ethernet)"
|
name: "ESP chip IP (Ethernet)"
|
||||||
icon: mdi:ip-network
|
icon: mdi:ip-network
|
||||||
@@ -48,9 +48,8 @@ text_sensor:
|
|||||||
name: "ESP chip MAC (Ethernet)"
|
name: "ESP chip MAC (Ethernet)"
|
||||||
icon: mdi:expansion-card
|
icon: mdi:expansion-card
|
||||||
|
|
||||||
|
|
||||||
sn74hc595: # write
|
sn74hc595: # write
|
||||||
- id: 'sn74hc595_hub'
|
- id: "sn74hc595_hub"
|
||||||
clock_pin: GPIO14 # SRCLK (serial clock)
|
clock_pin: GPIO14 # SRCLK (serial clock)
|
||||||
data_pin: GPIO13 # SER (serial input)
|
data_pin: GPIO13 # SER (serial input)
|
||||||
latch_pin: GPIO15 # RCLK (latch for outputs)
|
latch_pin: GPIO15 # RCLK (latch for outputs)
|
||||||
|
|||||||
+12
-12
@@ -42,12 +42,12 @@ i2c:
|
|||||||
scl: GPIO18
|
scl: GPIO18
|
||||||
|
|
||||||
font:
|
font:
|
||||||
- file: "fonts/DejaVuSans.ttf"
|
- file: "fonts/DejaVuSans.ttf"
|
||||||
id: font_sans
|
id: font_sans
|
||||||
size: 20
|
size: 20
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: ssd1306_i2c
|
- platform: ssd1306_i2c
|
||||||
model: "SSD1306 128x32"
|
model: "SSD1306 128x32"
|
||||||
id: oled_display
|
id: oled_display
|
||||||
lambda: |-
|
lambda: |-
|
||||||
@@ -64,28 +64,28 @@ display:
|
|||||||
}
|
}
|
||||||
|
|
||||||
one_wire:
|
one_wire:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: GPIO27
|
pin: GPIO27
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: dallas_temp
|
- platform: dallas_temp
|
||||||
address: 0x133c2cf648f40728
|
address: 0x133c2cf648f40728
|
||||||
id: boiler_temperature
|
id: boiler_temperature
|
||||||
name: "Boiler temperature"
|
name: "Boiler temperature"
|
||||||
filters:
|
filters:
|
||||||
- offset: 0.5
|
- offset: 0.5
|
||||||
|
|
||||||
- platform: dallas_temp
|
- platform: dallas_temp
|
||||||
address: 0x4845b7211864ff28
|
address: 0x4845b7211864ff28
|
||||||
id: hot_water_temperature
|
id: hot_water_temperature
|
||||||
name: "Hot water temperature"
|
name: "Hot water temperature"
|
||||||
|
|
||||||
- platform: dallas_temp
|
- platform: dallas_temp
|
||||||
address: 0x6a0316643959ff28
|
address: 0x6a0316643959ff28
|
||||||
id: floor_temperature
|
id: floor_temperature
|
||||||
name: "Floor temperature"
|
name: "Floor temperature"
|
||||||
|
|
||||||
- platform: adc
|
- platform: adc
|
||||||
pin: GPIO32
|
pin: GPIO32
|
||||||
update_interval: 1s
|
update_interval: 1s
|
||||||
id: furnace_burning_led_brightness
|
id: furnace_burning_led_brightness
|
||||||
@@ -95,7 +95,7 @@ sensor:
|
|||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
accuracy_decimals: 2
|
accuracy_decimals: 2
|
||||||
|
|
||||||
- platform: adc
|
- platform: adc
|
||||||
pin: GPIO33
|
pin: GPIO33
|
||||||
update_interval: 1s
|
update_interval: 1s
|
||||||
id: floor_active_led_brightness
|
id: floor_active_led_brightness
|
||||||
@@ -106,7 +106,7 @@ sensor:
|
|||||||
accuracy_decimals: 2
|
accuracy_decimals: 2
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: analog_threshold
|
- platform: analog_threshold
|
||||||
id: furnace_burning
|
id: furnace_burning
|
||||||
name: "Furnace burning"
|
name: "Furnace burning"
|
||||||
sensor_id: furnace_burning_led_brightness
|
sensor_id: furnace_burning_led_brightness
|
||||||
@@ -116,7 +116,7 @@ binary_sensor:
|
|||||||
filters:
|
filters:
|
||||||
- invert:
|
- invert:
|
||||||
|
|
||||||
- platform: analog_threshold
|
- platform: analog_threshold
|
||||||
id: heating_enabled
|
id: heating_enabled
|
||||||
name: "Floor active"
|
name: "Floor active"
|
||||||
sensor_id: floor_active_led_brightness
|
sensor_id: floor_active_led_brightness
|
||||||
@@ -126,14 +126,14 @@ binary_sensor:
|
|||||||
- invert:
|
- invert:
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: GPIO25
|
pin: GPIO25
|
||||||
id: relay_temperature_manual
|
id: relay_temperature_manual
|
||||||
inverted: true
|
inverted: true
|
||||||
# Non-manual is no longer wired to anything.
|
# Non-manual is no longer wired to anything.
|
||||||
restore_mode: ALWAYS_ON
|
restore_mode: ALWAYS_ON
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: GPIO26
|
pin: GPIO26
|
||||||
name: "Floor intent"
|
name: "Floor intent"
|
||||||
restore_mode: RESTORE_DEFAULT_OFF
|
restore_mode: RESTORE_DEFAULT_OFF
|
||||||
|
|||||||
+4
-4
@@ -72,19 +72,19 @@ binary_sensor:
|
|||||||
- platform: template
|
- platform: template
|
||||||
name: "Garden Peach relay"
|
name: "Garden Peach relay"
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
lambda: 'return id(relay_1).state;'
|
lambda: "return id(relay_1).state;"
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Garden Berries relay"
|
name: "Garden Berries relay"
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
lambda: 'return id(relay_2).state;'
|
lambda: "return id(relay_2).state;"
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Shower relay"
|
name: "Shower relay"
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
lambda: 'return id(relay_3).state;'
|
lambda: "return id(relay_3).state;"
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Relay 4"
|
name: "Relay 4"
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
lambda: 'return id(relay_4).state;'
|
lambda: "return id(relay_4).state;"
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ packages:
|
|||||||
time: !include templates/time.yaml
|
time: !include templates/time.yaml
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: sntp
|
- platform: sntp
|
||||||
id: sntp_time
|
id: sntp_time
|
||||||
servers: !secret sntp_servers
|
servers: !secret sntp_servers
|
||||||
timezone: Europe/Zurich
|
timezone: Europe/Zurich
|
||||||
on_time:
|
on_time:
|
||||||
|
|
||||||
#
|
#
|
||||||
# Pool pump: must run during saltwater system cycle, and an additional hour.
|
# Pool pump: must run during saltwater system cycle, and an additional hour.
|
||||||
# Saltwater 16:30 - 19:30, so we stop at 20:35 (Pool instructions).
|
# Saltwater 16:30 - 19:30, so we stop at 20:35 (Pool instructions).
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ output:
|
|||||||
id: mic_power
|
id: mic_power
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: pulse_meter
|
- platform: pulse_meter
|
||||||
internal_filter: 50ms
|
internal_filter: 50ms
|
||||||
|
|
||||||
pin:
|
pin:
|
||||||
|
|||||||
+1
-2
@@ -8,9 +8,8 @@ esphome:
|
|||||||
packages:
|
packages:
|
||||||
device_base: !include templates/mystrom.yaml
|
device_base: !include templates/mystrom.yaml
|
||||||
|
|
||||||
|
|
||||||
interval:
|
interval:
|
||||||
- interval: 1min
|
- interval: 1min
|
||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ globals:
|
|||||||
- id: ble_scans
|
- id: ble_scans
|
||||||
type: int
|
type: int
|
||||||
restore_value: no
|
restore_value: no
|
||||||
initial_value: '0'
|
initial_value: "0"
|
||||||
|
|
||||||
esp32_ble_tracker:
|
esp32_ble_tracker:
|
||||||
scan_parameters:
|
scan_parameters:
|
||||||
@@ -25,7 +25,7 @@ esp32_ble_tracker:
|
|||||||
- then:
|
- then:
|
||||||
- sensor.template.publish:
|
- sensor.template.publish:
|
||||||
id: ble_scans_finished
|
id: ble_scans_finished
|
||||||
state: !lambda 'return ++id(ble_scans);'
|
state: !lambda "return ++id(ble_scans);"
|
||||||
- script.execute: restart_after_delay
|
- script.execute: restart_after_delay
|
||||||
|
|
||||||
bluetooth_proxy:
|
bluetooth_proxy:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ packages:
|
|||||||
logger:
|
logger:
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: GPIO0
|
pin: GPIO0
|
||||||
name: None
|
name: None
|
||||||
inverted: true
|
inverted: true
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ ethernet:
|
|||||||
domain: ""
|
domain: ""
|
||||||
|
|
||||||
text_sensor:
|
text_sensor:
|
||||||
- platform: ethernet_info
|
- platform: ethernet_info
|
||||||
ip_address:
|
ip_address:
|
||||||
name: "ESP chip IP (Ethernet)"
|
name: "ESP chip IP (Ethernet)"
|
||||||
icon: mdi:ip-network
|
icon: mdi:ip-network
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Make sure nothing is connected to the A0 pin.
|
# Make sure nothing is connected to the A0 pin.
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: adc
|
- platform: adc
|
||||||
pin: VCC
|
pin: VCC
|
||||||
name: "ESP chip VCC Voltage"
|
name: "ESP chip VCC Voltage"
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ status_led:
|
|||||||
pin: GPIO16
|
pin: GPIO16
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
id: mystrom_side
|
id: mystrom_side
|
||||||
name: "Side button (plus) pressed"
|
name: "Side button (plus) pressed"
|
||||||
pin:
|
pin:
|
||||||
@@ -50,7 +50,7 @@ binary_sensor:
|
|||||||
input: true
|
input: true
|
||||||
pullup: true
|
pullup: true
|
||||||
inverted: true
|
inverted: true
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
id: mystrom_bottom
|
id: mystrom_bottom
|
||||||
name: "Bottom button (power) pressed"
|
name: "Bottom button (power) pressed"
|
||||||
pin:
|
pin:
|
||||||
@@ -64,7 +64,7 @@ binary_sensor:
|
|||||||
- switch.toggle: mystrom_relay
|
- switch.toggle: mystrom_relay
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: power_cycle_relay
|
- id: power_cycle_relay
|
||||||
mode: single
|
mode: single
|
||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
@@ -83,7 +83,7 @@ button:
|
|||||||
- script.execute: power_cycle_relay
|
- script.execute: power_cycle_relay
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: pulse_counter
|
- platform: pulse_counter
|
||||||
pin: 4
|
pin: 4
|
||||||
update_interval: 10s
|
update_interval: 10s
|
||||||
id: mystrom_power_consumption
|
id: mystrom_power_consumption
|
||||||
@@ -104,7 +104,7 @@ sensor:
|
|||||||
filters:
|
filters:
|
||||||
- multiply: 5.48E-07 # pulses -> kWh
|
- multiply: 5.48E-07 # pulses -> kWh
|
||||||
|
|
||||||
- platform: tmp102
|
- platform: tmp102
|
||||||
id: mystrom_temperature
|
id: mystrom_temperature
|
||||||
name: "Internal temperature"
|
name: "Internal temperature"
|
||||||
update_interval: 60s
|
update_interval: 60s
|
||||||
@@ -115,14 +115,14 @@ sensor:
|
|||||||
then:
|
then:
|
||||||
- switch.turn_off: mystrom_relay
|
- switch.turn_off: mystrom_relay
|
||||||
|
|
||||||
- platform: duty_time
|
- platform: duty_time
|
||||||
id: continuous_on_time
|
id: continuous_on_time
|
||||||
name: Continuous on time
|
name: Continuous on time
|
||||||
lambda: "return id(mystrom_relay).state;"
|
lambda: "return id(mystrom_relay).state;"
|
||||||
accuracy_decimals: 0
|
accuracy_decimals: 0
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: None # Main entity for the device.
|
name: None # Main entity for the device.
|
||||||
pin: GPIO27
|
pin: GPIO27
|
||||||
id: mystrom_relay
|
id: mystrom_relay
|
||||||
@@ -131,7 +131,7 @@ switch:
|
|||||||
on_turn_off:
|
on_turn_off:
|
||||||
- sensor.duty_time.reset: continuous_on_time
|
- sensor.duty_time.reset: continuous_on_time
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
# Exposing this as a switch rather than light to avoid
|
# Exposing this as a switch rather than light to avoid
|
||||||
# automations and dashboard picking it up as illuminance
|
# automations and dashboard picking it up as illuminance
|
||||||
# source.
|
# source.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
button:
|
button:
|
||||||
- platform: restart
|
- platform: restart
|
||||||
name: "ESP chip restart"
|
name: "ESP chip restart"
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
id: restart_button
|
id: restart_button
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "ESP chip clock invalid"
|
name: "ESP chip clock invalid"
|
||||||
lambda: 'return !id(sntp_time).now().is_valid();'
|
lambda: "return !id(sntp_time).now().is_valid();"
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
device_class: problem
|
device_class: problem
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ external_components:
|
|||||||
- source: github://oxan/esphome-stream-server
|
- source: github://oxan/esphome-stream-server
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: fw_update_mode
|
- id: fw_update_mode
|
||||||
then:
|
then:
|
||||||
- switch.turn_on: zBSL
|
- switch.turn_on: zBSL
|
||||||
- delay: 1s
|
- delay: 1s
|
||||||
@@ -51,21 +51,21 @@ script:
|
|||||||
- logger.log: "Please try update with cc2538-bsl tool now"
|
- logger.log: "Please try update with cc2538-bsl tool now"
|
||||||
- logger.log: "cc-bsl usage: cc2538-bsl.py -p socket://ip-of-gw:6638 -evw firmware.hex"
|
- logger.log: "cc-bsl usage: cc2538-bsl.py -p socket://ip-of-gw:6638 -evw firmware.hex"
|
||||||
|
|
||||||
- id: cc2652_reset
|
- id: cc2652_reset
|
||||||
then:
|
then:
|
||||||
- switch.turn_on: zRST_gpio
|
- switch.turn_on: zRST_gpio
|
||||||
- delay: 15ms
|
- delay: 15ms
|
||||||
- switch.turn_off: zRST_gpio
|
- switch.turn_off: zRST_gpio
|
||||||
|
|
||||||
button:
|
button:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "ZigBee Module reset"
|
name: "ZigBee Module reset"
|
||||||
disabled_by_default: true
|
disabled_by_default: true
|
||||||
id: zRST
|
id: zRST
|
||||||
on_press:
|
on_press:
|
||||||
- script.execute: cc2652_reset
|
- script.execute: cc2652_reset
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "ZigBee Module enter programming mode"
|
name: "ZigBee Module enter programming mode"
|
||||||
disabled_by_default: true
|
disabled_by_default: true
|
||||||
on_press:
|
on_press:
|
||||||
@@ -78,6 +78,6 @@ stream_server:
|
|||||||
buffer_size: 4096
|
buffer_size: 4096
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: stream_server
|
- platform: stream_server
|
||||||
connected:
|
connected:
|
||||||
name: "ZigBee Module serial stream connected"
|
name: "ZigBee Module serial stream connected"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
sensor:
|
sensor:
|
||||||
- platform: uptime
|
- platform: uptime
|
||||||
name: "ESP chip uptime"
|
name: "ESP chip uptime"
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
sensor:
|
sensor:
|
||||||
- platform: wifi_signal
|
- platform: wifi_signal
|
||||||
name: "ESP chip WiFi signal"
|
name: "ESP chip WiFi signal"
|
||||||
|
|
||||||
text_sensor:
|
text_sensor:
|
||||||
- platform: wifi_info
|
- platform: wifi_info
|
||||||
mac_address:
|
mac_address:
|
||||||
name: "ESP chip MAC"
|
name: "ESP chip MAC"
|
||||||
icon: mdi:wifi-cog
|
icon: mdi:wifi-cog
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ packages:
|
|||||||
sensor: !include templates/esp8266-vcc.yaml
|
sensor: !include templates/esp8266-vcc.yaml
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: pulse_meter
|
- platform: pulse_meter
|
||||||
# Our meter (USLC/20) is rated Qmax=5.0 (m3/h).
|
# Our meter (USLC/20) is rated Qmax=5.0 (m3/h).
|
||||||
# The official one is rated 5.0.
|
# The official one is rated 5.0.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ packages:
|
|||||||
device_base: !include templates/tubeszb-coordinator.yaml
|
device_base: !include templates/tubeszb-coordinator.yaml
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: 33
|
pin: 33
|
||||||
id: zRST_gpio
|
id: zRST_gpio
|
||||||
inverted: yes
|
inverted: yes
|
||||||
restore_mode: ALWAYS_OFF
|
restore_mode: ALWAYS_OFF
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: 32
|
pin: 32
|
||||||
id: zBSL
|
id: zBSL
|
||||||
inverted: yes
|
inverted: yes
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ packages:
|
|||||||
device_base: !include templates/tubeszb-coordinator.yaml
|
device_base: !include templates/tubeszb-coordinator.yaml
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: 5
|
pin: 5
|
||||||
id: zRST_gpio
|
id: zRST_gpio
|
||||||
inverted: yes
|
inverted: yes
|
||||||
restore_mode: ALWAYS_OFF
|
restore_mode: ALWAYS_OFF
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: 16
|
pin: 16
|
||||||
id: zBSL
|
id: zBSL
|
||||||
inverted: yes
|
inverted: yes
|
||||||
|
|||||||
Reference in New Issue
Block a user