Skip to content
Draft
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
8815184
Fix obsruction isr for ESP32
mulcmu Aug 18, 2023
27b7bbf
close_with_alert
mulcmu Aug 29, 2023
4849712
Set TTC close and clear
mulcmu Aug 29, 2023
775cde7
Use proper CANCEL_TTC
mulcmu Aug 29, 2023
7cb100e
Update base.yaml
mulcmu Aug 29, 2023
1240b00
TTC changes
mulcmu Aug 30, 2023
ead72e9
buttons for testing
mulcmu Aug 30, 2023
7b078c9
add ttc hold open switch
mulcmu Aug 31, 2023
d60ed0a
add ttc select
mulcmu Aug 31, 2023
caf73c8
implement ttc logic
mulcmu Sep 1, 2023
ae2e6ba
cleanup
mulcmu Sep 1, 2023
8c8a996
consolidate ttc functions
mulcmu Sep 9, 2023
bb5ddeb
close_with_alert
mulcmu Sep 9, 2023
a8cb645
fix ttc off from wall pad
mulcmu Sep 10, 2023
ecc12a3
restore hold state after close with alert
mulcmu Sep 11, 2023
8929b32
query status retry
mulcmu Sep 12, 2023
5518518
Update links
bdraco Sep 12, 2023
d110d61
Update index.html
bdraco Sep 12, 2023
5d72946
more ttc
mulcmu Sep 12, 2023
e285003
fix: update urls
bdraco Sep 12, 2023
9dda7b8
clang-format
mulcmu Sep 14, 2023
1a3ff77
Create .clang-format
mulcmu Sep 15, 2023
7281273
reduce blocking for select updates
mulcmu Sep 15, 2023
8a7eec3
rework ttc status
mulcmu Sep 15, 2023
d2ae60e
Refactor sync function to be more DRY and easier to extend (#50)
mariusmuja Sep 18, 2023
dee9f3d
Lolin s2 mini support (#54)
rako77 Oct 2, 2023
e4ecce0
Fixed corrupted image (#55)
rako77 Oct 2, 2023
7d0422a
Rename S2 board to match convention (#56)
bdraco Oct 2, 2023
9bc41c2
fix link to v2board_esp32_lolin_s2_mini.yaml (#57)
bdraco Oct 2, 2023
fe593a9
Allow interrupts to be disabled during transmit (#60)
mariusmuja Oct 6, 2023
575471b
Rework of door position sync (#61)
mariusmuja Oct 8, 2023
f7a6fe8
Update ratgdo.cpp
mulcmu Oct 9, 2023
180534f
Merge remote-tracking branch 'upstream/main' into ttc_beep
mulcmu Oct 9, 2023
6d8f3e0
Door opening/close relliability updates (#64)
mariusmuja Oct 19, 2023
f323e02
chore: add .pre-commit-config.yaml for end of line fixer (#66)
bdraco Oct 19, 2023
7da1282
Automatically tune the optimal baud value for the next packet (#69)
mariusmuja Oct 19, 2023
89e2910
feat: add YAML files and binaries for v2.5 boards (#72)
bdraco Oct 23, 2023
a7b0230
Add v2.5 to the website (#74)
bdraco Oct 24, 2023
a6a225d
Fix v2.5 board images (#75)
bdraco Oct 24, 2023
e248c70
Tidy (#77)
bdraco Oct 25, 2023
2091be8
Use random client_id in lieu of static remote_id (#76)
bdraco Oct 26, 2023
472ec6c
Adding cover on_opening and on_closing triggers
mariusmuja Oct 26, 2023
bdae84b
ref for live testing
bdraco Oct 26, 2023
bbfded5
Revert "ref for live testing"
bdraco Oct 26, 2023
db22a36
tidy
bdraco Oct 26, 2023
b662957
tidy
bdraco Oct 26, 2023
75f95f1
black
bdraco Oct 26, 2023
e97204f
Merge branch 'main' into ttc_beep
mulcmu Oct 29, 2023
ded41d7
Merge remote-tracking branch 'upstream/cover_opening_closing_triggers…
mulcmu Oct 29, 2023
b0504bd
1 sec to 3 sec
mulcmu Oct 29, 2023
0f84d14
Merge branch 'main' of https://github.com/mulcmu/esphome-ratgdo
mulcmu Oct 29, 2023
c677ded
fix merge issues
mulcmu Oct 29, 2023
7306135
on_sync_failed
mulcmu Oct 30, 2023
61dd234
remove ttc for close_with_alert
mulcmu Oct 31, 2023
c09136d
Fix door requiring two close commands to close (#88)
bdraco Nov 5, 2023
e2f4b6e
Adding cover on_opening and on_closing triggers (#81)
mariusmuja Nov 5, 2023
8317f18
Fix issue #89 by imposing some limits on the random client_id (#92)
mariusmuja Nov 5, 2023
66ba19e
Update potentially wrong client_ids (#93)
mariusmuja Nov 6, 2023
718237a
Increase max client id to 0xFFFFFFFF (#94)
bdraco Nov 6, 2023
7be33e1
Add limits on the range of client_id values (#95)
mariusmuja Nov 6, 2023
5bd2464
Add security + 2.0 clarification
PaulWieland Nov 7, 2023
cdc7780
Normalize client_id when manually entered (#97)
mariusmuja Nov 7, 2023
c4a9b6e
fix package import urls for the v25 boards (#98)
bdraco Nov 7, 2023
bb29b9b
Add v2.5 board esp32 (#101)
bdraco Nov 7, 2023
36c4e55
Move v2.5 boards above v2.0 boards for anyone who does not know which…
bdraco Nov 7, 2023
0b7b851
Add v2.5 board with ESP32 D1 Mini to readme
bdraco Nov 7, 2023
a1dbdbe
Add timeout for motion detection (#104)
mariusmuja Nov 9, 2023
ff2acab
Add links to drivers
PaulWieland Nov 10, 2023
e36225c
Merge branch 'main' of https://github.com/ratgdo/esphome-ratgdo
PaulWieland Nov 10, 2023
5a0294f
Add a 500ms delay filter to the dry connect binary sensors (#112)
bdraco Nov 16, 2023
4f2e599
Merge branch 'ratgdo:main' into ttc_beep
mulcmu Nov 18, 2023
44ecf15
Add 2.5i board (#116)
bdraco Nov 19, 2023
72977f2
Merge branch 'ratgdo:main' into ttc_beep
mulcmu Nov 19, 2023
c4e2776
fix v2.5i board manifest filename (#117)
bdraco Nov 19, 2023
ffbef79
Link wiring diagrams (#118)
bdraco Nov 19, 2023
4714508
Merge branch 'ratgdo:main' into ttc_beep
mulcmu Nov 30, 2023
2ea7ca6
Convert lock remotes switch to a lock component (#132)
mariusmuja Dec 6, 2023
892c4e2
Detect "not connected to GDO" condition (#143)
mariusmuja Dec 16, 2023
53752d5
Drop an incomplete packet if the missing bytes are lost (#144)
mariusmuja Dec 16, 2023
bb4c995
Merge branch 'ratgdo:main' into main
mulcmu Dec 16, 2023
f942292
Merge branch 'main' into ttc_beep
mulcmu Dec 16, 2023
65c52d0
feat: add 2.52i board to the page (#147)
bdraco Dec 17, 2023
e7efd2c
Bump actions/deploy-pages to 1.2.9 (#148)
bdraco Dec 17, 2023
587ff4c
Bump actions/upload-pages-artifact to v2.0.0
bdraco Dec 17, 2023
82c62ee
Merge branch 'ratgdo:main' into ttc_beep
mulcmu Dec 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,27 @@ sensor:
unit_of_measurement: "openings"
icon: mdi:open-in-app

select:
- platform: ratgdo
id: ${id_prefix}_ttc_select
type: ttc
entity_category: config
ratgdo_id: ${id_prefix}
name: "Time to close"

switch:
- platform: ratgdo
id: ${id_prefix}_lock_remotes
type: lock
entity_category: config
ratgdo_id: ${id_prefix}
name: "Lock remotes"
- platform: ratgdo
id: ${id_prefix}_TTC_hold
type: hold
entity_category: config
ratgdo_id: ${id_prefix}
name: "TTC Hold Open"
- platform: gpio
id: "${id_prefix}_status_door"
internal: true
Expand Down Expand Up @@ -195,13 +209,12 @@ button:
id($id_prefix).query_status();

- platform: template
id: ${id_prefix}_query_openings
name: "Query openings"
entity_category: diagnostic
id: ${id_prefix}_close_with_alert
name: "Close with Alert"
on_press:
then:
lambda: !lambda |-
id($id_prefix).query_openings();
id($id_prefix).close_with_alert();

- platform: template
id: ${id_prefix}_sync
Expand All @@ -210,4 +223,5 @@ button:
on_press:
then:
lambda: !lambda |-
id($id_prefix).sync();
id($id_prefix).sync();

136 changes: 119 additions & 17 deletions components/ratgdo/ratgdo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,17 @@ namespace ratgdo {
this->door_position = 1.0;
} else if (door_state == DoorState::CLOSED) {
this->door_position = 0.0;
if(this->restore_TTC_) {
//GET_OPENINGS is sent when door closes, delay this tx
set_timeout(100, [=] {
if (*this->ttc_time_seconds == 0) {
this->turn_ttc_off();
} else {
this->set_ttc_sec(*ttc_time_seconds);
}
} );
this->restore_TTC_ = false;
}
} else {
if (*this->closing_duration == 0 || *this->opening_duration == 0 || *this->door_position == DOOR_POSITION_UNKNOWN) {
this->door_position = 0.5; // best guess
Expand All @@ -157,7 +168,7 @@ namespace ratgdo {
this->position_sync_while_opening(1.0 - *this->door_position);
this->moving_to_position = true;
}
if (door_state == DoorState::CLOSING && !this->moving_to_position) {
if (door_state == DoorState::CLOSING && !this->moving_to_position) {
this->position_sync_while_closing(*this->door_position);
this->moving_to_position = true;
}
Expand Down Expand Up @@ -222,10 +233,47 @@ namespace ratgdo {
this->send_command(Command::GET_STATUS);
}
ESP_LOGD(TAG, "Motion: %s", MotionState_to_string(*this->motion_state));
} else if (cmd == Command::SET_TTC) {
} else if (cmd == Command::TTC_SET_DURATION) {
auto seconds = (byte1 << 8) | byte2;
ESP_LOGD(TAG, "Time to close (TTC): %ds", seconds);
}
ESP_LOGD(TAG, "Time to close (TTC) update request: %ds", seconds);
} else if (cmd == Command::TTC_DURATION) {
auto seconds = (byte1 << 8) | byte2;
ESP_LOGD(TAG, "Time to close (TTC) set to: %ds", seconds);
if (seconds == 60) {
this->ttc_time_seconds=seconds;
} else if (seconds == 300) {
this->ttc_time_seconds=seconds;
} else if (seconds == 600) {
this->ttc_time_seconds=seconds;
} else if (seconds == 0) {
this->ttc_time_seconds=seconds;
} else {
this->ttc_time_seconds=0;
ESP_LOGW(TAG, "Unsupported TTC time: %ds", seconds);
}
} else if (cmd == Command::TTC_COUNTDOWN) {
auto seconds = (byte1 << 8) | byte2;
ESP_LOGD(TAG, "(TTC) door will close in: %ds", seconds);
} else if (cmd == Command::TTC_CANCEL) {
if (byte1 == 0x04) {
ESP_LOGD(TAG, "TTC: Auto Hold Toggled");
} else if (byte1 == 0x05) {
ESP_LOGD(TAG, "TTC: Disabled");
} else {
ESP_LOGD(TAG, "TTC_CANCEL: Unknown Data");
}
} else if (cmd == Command::EXT_STATUS) {
if ( (byte1 & 0b00000111) == 0b00000001) {
ESP_LOGD(TAG, "TTC is disabled.");
this->hold_state = HoldState::HOLD_DISABLED;
} else if ( (byte1 & 0b00000111) == 0b00000010) {
ESP_LOGD(TAG, "TTC is enabled but in Hold Open.");
this->hold_state = HoldState::HOLD_ENABLED;
} else if ( (byte1 & 0b00000111) == 0b00000100) {
ESP_LOGD(TAG, "TTC is enabled.");
this->hold_state = HoldState::HOLD_DISABLED;
}
}

return cmd;
}
Expand Down Expand Up @@ -383,13 +431,46 @@ namespace ratgdo {
void RATGDOComponent::query_status()
{
send_command(Command::GET_STATUS);
set_timeout(100, [=] {this->send_command(Command::GET_EXT_STATUS, data::GET_EXT_STATUS);} );
set_timeout(200, [=] {this->send_command(Command::TTC_GET_DURATION, data::TTC_GET_DURATION);} );
set_timeout(300, [=] {this->send_command(Command::GET_OPENINGS);} );
}

void RATGDOComponent::query_openings()
{
send_command(Command::GET_OPENINGS);
this->query_status();
}

//TODO does gdo send status that door is closing???
void RATGDOComponent::close_with_alert()
{
//Check if door is closed and ignore, only works if fully open
if(*this->door_state != DoorState::OPEN) {
ESP_LOGW(TAG, "Door must be fully open to Close with alert!");
return;
}
//SET_TTC closes door in 1 second with light and beeper
set_ttc_sec(1);
this->restore_TTC_ = true;
}

void RATGDOComponent::turn_ttc_off()
{
send_command(Command::TTC_CANCEL, data::TTC_CANCEL_OFF);
this->ttc_time_seconds=0;
}

void RATGDOComponent::ttc_toggle_hold()
{
send_command(Command::TTC_CANCEL, data::TTC_CANCEL_TOGGLE_HOLD);
}

void RATGDOComponent::set_ttc_sec(uint16_t duration)
{
send_command(Command::TTC_SET_DURATION, (duration & 0xff) << 16 | (duration & 0xff00) | 0x01);
}


/************************* DOOR COMMUNICATION *************************/
/*
* Transmit a message to the door opener over uart1
Expand Down Expand Up @@ -441,17 +522,9 @@ namespace ratgdo {

set_retry(
500, 10, [=](uint8_t r) {
if (*this->door_state != DoorState::UNKNOWN) { // have status
if (*this->openings != 0) { // have openings
return RetryResult::DONE;
} else {
if (r == 0) { // failed to sync probably rolling counter is wrong, notify
ESP_LOGD(TAG, "Triggering sync failed actions.");
this->sync_failed = true;
};
this->send_command(Command::GET_OPENINGS);
return RetryResult::RETRY;
}
if (*this->door_state != DoorState::UNKNOWN) { // GET_STATUS succeeded
this->query_status(); //Get openings and TTC settings to initalize
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work ok if one of the request fails. For example if GET_OPENINGS doesn't receive a response it doesn't retry the request like the original code did. (The opener can choose not to respond to commands if too many are sent too quickly, or the response is not received for any number of reasons).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking at it like if GET_STATUS succeeded then the rolling code was okay. It is possible the door sends a STATUS response by chance during the sync retry period as well. I think adding the retry mechanism to the query_status() to make sure gdo sends new data for each request would make it robust for initial sync and any future calls but without the increment to the rolling counter. Does this address your comment?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking at it like if GET_STATUS succeeded then the rolling code was okay.

True, but the rolling code is not the only reason of failure. I've see it happen that the GET_STATUS succeeds and GET_OPENINGS does not get a response (for various reasons as I initially mentioned).

It is possible the door sends a STATUS response by chance during the sync retry period as well. I think adding the retry mechanism to the query_status() to make sure gdo sends new data for each request would make it robust for initial sync and any future calls but without the increment to the rolling counter. Does this address your comment?

Not sure what you mean "without the increment to the rolling counter". Each new command needs incrementing the counter by one. Additionally we increment the counter initially in sync in case we crashed and didn't manage to persist to flash the last counter value.

The retry mechanism is to ensure we sync the initial state correctly, rolling counter mismatch being the most likely reason for failure, but not the only one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean "without the increment to the rolling counter". Each new command needs incrementing the counter by one. Additionally we increment the counter initially in sync in case we crashed and didn't manage to persist to flash the last counter value.

I was referring to the initial increment by 10 in sync(). query_status() was reworked in ESPHome-RATGDO@8929b32 to retry with confirmation that each of the 4 status messages returned successfully.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I can get working ratgdo hardware to the point I no longer care about my HomeKit bridge, I have a good MSO (200Mhz 4 analog and 32 digital channels) with SPI decode and would be willing to try and poke around/capture data from said bridge.

Unfortunately my ratgdo seems to be doa, and I haven’t had any luck getting a response to deal with that.

return RetryResult::DONE;
} else {
if (r == 0) { // failed to sync probably rolling counter is wrong, notify
ESP_LOGD(TAG, "Triggering sync failed actions.");
Expand Down Expand Up @@ -648,6 +721,27 @@ namespace ratgdo {
this->send_command(Command::LOCK, data::LOCK_TOGGLE);
}

// Hold functions
void RATGDOComponent::hold_enable()
{
if(*(this->hold_state) == HoldState::HOLD_DISABLED) {
this->toggle_hold();
}
}

void RATGDOComponent::hold_disable()
{
if(*(this->hold_state) == HoldState::HOLD_ENABLED) {
this->toggle_hold();
}
}

void RATGDOComponent::toggle_hold()
{
this->hold_state = hold_state_toggle(*this->hold_state);
this->send_command(Command::TTC_CANCEL, data::TTC_CANCEL_TOGGLE_HOLD);
}

LightState RATGDOComponent::get_light_state() const
{
return *this->light_state;
Expand Down Expand Up @@ -688,6 +782,14 @@ namespace ratgdo {
{
this->lock_state.subscribe([=](LockState state) { defer("lock_state", [=] { f(state); }); });
}
void RATGDOComponent::subscribe_hold_state(std::function<void(HoldState)>&& f)
{
this->hold_state.subscribe([=](HoldState state) { defer("hold_state", [=] { f(state); }); });
}
void RATGDOComponent::subscribe_ttc_seconds(std::function<void(uint16_t)>&& f)
{
this->ttc_time_seconds.subscribe([=](uint16_t state) { defer("ttc_time", [=] { f(state); }); });
}
void RATGDOComponent::subscribe_obstruction_state(std::function<void(ObstructionState)>&& f)
{
this->obstruction_state.subscribe([=](ObstructionState state) { defer("obstruction_state", [=] { f(state); }); });
Expand Down
46 changes: 36 additions & 10 deletions components/ratgdo/ratgdo.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,24 @@ namespace ratgdo {
const uint32_t DOOR_OPEN = 1;
const uint32_t DOOR_TOGGLE = 2;
const uint32_t DOOR_STOP = 3;

const uint32_t TTC_GET_DURATION= 1 ;
const uint32_t TTC_CANCEL_OFF = 0x000501; //Unknown meaning for these bytes, mimic button pad
const uint32_t TTC_CANCEL_TOGGLE_HOLD = 0x000401; //Unknown meaning for these bytes, mimic button pad

const uint32_t GET_EXT_STATUS = 1;
}

ENUM(Command, uint16_t,
(UNKNOWN, 0x000),
(GET_STATUS, 0x080),
(STATUS, 0x081),

(GET_STATUS, 0x080),
(STATUS, 0x081),
(GET_EXT_STATUS, 0x0a0), //Extended status has TTC state in bit0 to bit2 of byte1. Bit3 has something but not sure what it is
(EXT_STATUS, 0x0a1),

(OBST_1, 0x084), // sent when an obstruction happens?
(OBST_2, 0x085), // sent when an obstruction happens?
(PAIR_3, 0x0a0),
(PAIR_3_RESP, 0x0a1),

(LEARN_2, 0x181),
(LOCK, 0x18c),
Expand All @@ -73,11 +81,12 @@ namespace ratgdo {
(PING, 0x392),
(PING_RESP, 0x393),

(PAIR_2, 0x400),
(PAIR_2_RESP, 0x401),
(SET_TTC, 0x402), // ttc_in_seconds = (byte1<<8)+byte2
(CANCEL_TTC, 0x408), // ?
(TTC, 0x40a), // Time to close
(TTC_GET_DURATION, 0x400),
(TTC_DURATION, 0x401), //data appears to contain the current TTC setting in gdo
(TTC_SET_DURATION, 0x402), // Set time to close in seconds = (byte1<<8)+byte2
(TTC_CANCEL, 0x408), //OFF or TOGGLE_HOLD are options in data
(TTC_COUNTDOWN, 0x40a), // Time to close countdown in seconds

(GET_OPENINGS, 0x48b),
(OPENINGS, 0x48c), // openings = (byte1<<8)+byte2
)
Expand Down Expand Up @@ -115,6 +124,8 @@ namespace ratgdo {

observable<LightState> light_state { LightState::UNKNOWN };
observable<LockState> lock_state { LockState::UNKNOWN };
observable<HoldState> hold_state { HoldState::UNKNOWN };
observable<uint16_t> ttc_time_seconds { 0xFFFF };
observable<ObstructionState> obstruction_state { ObstructionState::UNKNOWN };
observable<MotorState> motor_state { MotorState::UNKNOWN };
observable<ButtonState> button_state { ButtonState::UNKNOWN };
Expand Down Expand Up @@ -164,10 +175,22 @@ namespace ratgdo {
void lock();
void unlock();

// hold
void toggle_hold();
void hold_enable();
void hold_disable();

//TTC
void turn_ttc_off();
void ttc_toggle_hold();
void set_ttc_sec(uint16_t duration);

// button functionality
void query_status();
[[deprecated("query_status() now requests the opening count.")]]
void query_openings();
void sync();
void close_with_alert();

// children subscriptions
void subscribe_rolling_code_counter(std::function<void(uint32_t)>&& f);
Expand All @@ -177,6 +200,8 @@ namespace ratgdo {
void subscribe_door_state(std::function<void(DoorState, float)>&& f);
void subscribe_light_state(std::function<void(LightState)>&& f);
void subscribe_lock_state(std::function<void(LockState)>&& f);
void subscribe_hold_state(std::function<void(HoldState)>&& f);
void subscribe_ttc_seconds(std::function<void(uint16_t)>&& f);
void subscribe_obstruction_state(std::function<void(ObstructionState)>&& f);
void subscribe_motor_state(std::function<void(MotorState)>&& f);
void subscribe_button_state(std::function<void(ButtonState)>&& f);
Expand All @@ -192,7 +217,8 @@ namespace ratgdo {
SoftwareSerial sw_serial_;

bool obstruction_from_status_ { false };

bool restore_TTC_ { false };

InternalGPIOPin* output_gdo_pin_;
InternalGPIOPin* input_gdo_pin_;
InternalGPIOPin* input_obst_pin_;
Expand Down
14 changes: 14 additions & 0 deletions components/ratgdo/ratgdo_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,19 @@ namespace ratgdo {
}
}

HoldState hold_state_toggle(HoldState state)
{
switch (state) {
case HoldState::HOLD_DISABLED:
return HoldState::HOLD_ENABLED;
case HoldState::HOLD_ENABLED:
return HoldState::HOLD_DISABLED;
// 2 and 3 appears sometimes
case HoldState::UNKNOWN:
default:
return HoldState::UNKNOWN;
}
}

} // namespace ratgdo
} // namespace esphome
Loading