Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDO blaQ compatibility enhancements #62

Merged
merged 14 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 20 additions & 11 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ jobs:
with:
command: cp
source: assets/
destination: s3://konnected-io/builds/esphome/
destination: s3://konnected-io/builds/esphome/${{ github.ref_name }}
aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws_region: us-east-2
Expand All @@ -164,7 +164,6 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}

- run: mkdir -p install-konnected/manifests
if: github.event.release

- name: Build Manifest for Alarm Panel / WiFi
if: github.event.release
Expand All @@ -174,8 +173,8 @@ jobs:
MANIFEST_NAME: Konnected Alarm Panel / Alarm Panel Pro for ESPHome (WiFi)
MANIFEST_FNAME: esphome-alarm-panel-wifi.json
MANIFEST_VERSION: "${{ steps.alarm-panel-esp8266-version.outputs.data }} / ${{ steps.alarm-panel-pro-wifi-version.outputs.data }}"
ESP8266_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ steps.build-alarm-panel-esp8266.outputs.fname }}
ESP32_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ steps.build-alarm-panel-pro-esp32-wifi.outputs.fname }}
ESP8266_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-alarm-panel-esp8266.outputs.fname }}
ESP32_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-alarm-panel-pro-esp32-wifi.outputs.fname }}

- name: Build Manifest for GDO White
if: github.event.release
Expand All @@ -185,19 +184,29 @@ jobs:
MANIFEST_NAME: Konnected GDO White (GDOv1-S / GDOv2-S) for ESPHome
MANIFEST_FNAME: esphome-garage-door-wifi.json
MANIFEST_VERSION: "${{ steps.garage-door-gdov1-s-version.outputs.data }} / ${{ steps.garage-door-gdov2-s-version.outputs.data }}"
ESP8266_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ steps.build-garage-door-gdov1-s.outputs.fname }}
ESP32_S3_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ steps.build-garage-door-gdov2-s.outputs.fname }}
ESP8266_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-garage-door-gdov1-s.outputs.fname }}
ESP32_S3_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-garage-door-gdov2-s.outputs.fname }}

- name: Build Manifest for GDO blaQ
if: github.event.release
run: |
ruby ./scripts/update-espwebtools-manifests.rb
env:
MANIFEST_NAME: Konnected GDO blaQ (GDOv2-Q) for ESPHome
MANIFEST_NAME: Konnected GDO blaQ (GDOv2-Q)
MANIFEST_FNAME: esphome-gdov2-q.json
MANIFEST_VERSION: "${{ steps.garage-door-gdov2-q-version.outputs.data }}"
ESP32_S3_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ steps.build-garage-door-gdov2-q.outputs.fname }}

ESP32_S3_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-garage-door-gdov2-q.outputs.fname }}

- name: Build Dev Manifest for GDO blaQ
if: github.ref_name == 'dev'
run: |
ruby ./scripts/update-espwebtools-manifests.rb
env:
MANIFEST_NAME: Konnected GDO blaQ (GDOv2-Q)[DEV]
MANIFEST_FNAME: esphome-gdov2-q-dev.json
MANIFEST_VERSION: "${{ steps.garage-door-gdov2-q-version.outputs.data }}"
ESP32_S3_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-garage-door-gdov2-q.outputs.fname }}

- name: Build Manifest for Alarm Panel Pro v1.x / LAN8720
if: github.event.release
run: |
Expand All @@ -206,7 +215,7 @@ jobs:
MANIFEST_NAME: Konnected Alarm Panel Pro v1.x for ESPHome (Ethernet)
MANIFEST_FNAME: esphome-alarm-panel-lan8720.json
MANIFEST_VERSION: "${{ steps.alarm-panel-pro-ethernet-version.outputs.data }}"
ESP32_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ steps.build-alarm-panel-pro-esp32-lan8720.outputs.fname }}
ESP32_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-alarm-panel-pro-esp32-lan8720.outputs.fname }}

- name: Build Manifest for Alarm Panel Pro v1.8 / RTL8201
if: github.event.release
Expand All @@ -216,7 +225,7 @@ jobs:
MANIFEST_NAME: Konnected Alarm Panel Pro v1.8 for ESPHome (Ethernet)
MANIFEST_FNAME: esphome-alarm-panel-rtl8201.json
MANIFEST_VERSION: "${{ steps.alarm-panel-pro-v18-ethernet-version.outputs.data }}"
ESP32_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ steps.build-alarm-panel-pro-esp32-rtl8201.outputs.fname }}
ESP32_IMAGE_URI: https://konnected-io.s3.us-east-2.amazonaws.com/builds/esphome/${{ github.ref_name }}/${{ steps.build-alarm-panel-pro-esp32-rtl8201.outputs.fname }}

- name: Deploy
if: github.event.release
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
# You can modify this file to suit your needs.
/.esphome/
/secrets.yaml
__pycache__/
__pycache__/
.vscode
6 changes: 1 addition & 5 deletions components/secplus_gdo/cover/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
CONF_PRE_CLOSE_WARNING_DURATION = "pre_close_warning_duration"
CONF_PRE_CLOSE_WARNING_START = "pre_close_warning_start"
CONF_PRE_CLOSE_WARNING_END = "pre_close_warning_end"
CONF_TOGGLE_ONLY = "toggle_only"

CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
{
Expand All @@ -52,7 +51,6 @@
cv.Optional(CONF_PRE_CLOSE_WARNING_END): automation.validate_automation(
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosingEndTrigger)}
),
cv.Optional(CONF_TOGGLE_ONLY, default=False): cv.boolean,
}
).extend(SECPLUS_GDO_CONFIG_SCHEMA)

Expand All @@ -62,10 +60,8 @@ async def to_code(config):
await cg.register_component(var, config)
await cover.register_cover(var, config)
parent = await cg.get_variable(config[CONF_SECPLUS_GDO_ID])
text = "std::bind(&" + str(GDODoor) + "::set_state," + str(config[CONF_ID]) + ",std::placeholders::_1,std::placeholders::_2)"
cg.add(parent.register_door(cg.RawExpression(text)))
cg.add(parent.register_door(var))
cg.add(var.set_pre_close_warning_duration(config[CONF_PRE_CLOSE_WARNING_DURATION]))
cg.add(var.set_toggle_only(config[CONF_TOGGLE_ONLY]))
for conf in config.get(CONF_PRE_CLOSE_WARNING_START, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
Expand Down
57 changes: 53 additions & 4 deletions components/secplus_gdo/cover/gdo_door.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ void GDODoor::set_state(gdo_door_state_t state, float position) {
this->position = position;
break;
case GDO_DOOR_STATE_STOPPED:
this->prev_operation = this->current_operation;
// falls through
case GDO_DOOR_STATE_MAX:
default:
this->current_operation = COVER_OPERATION_IDLE;
Expand All @@ -49,6 +51,7 @@ void GDODoor::set_state(gdo_door_state_t state, float position) {
}

this->publish_state(false);
this->state_ = state;
}

void GDODoor::do_action_after_warning(const cover::CoverCall& call) {
Expand Down Expand Up @@ -79,7 +82,8 @@ void GDODoor::do_action(const cover::CoverCall& call) {
ESP_LOGD(TAG, "Sending STOP action");
gdo_door_stop();
}
if (call.get_toggle()) {

if (call.get_toggle()) {
if (this->position == COVER_CLOSED) {
this->set_state(GDO_DOOR_STATE_OPENING, this->position);
} else if (this->position == COVER_OPEN) {
Expand All @@ -88,26 +92,47 @@ void GDODoor::do_action(const cover::CoverCall& call) {
ESP_LOGD(TAG, "Sending TOGGLE action");
gdo_door_toggle();
}

if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (pos == COVER_OPEN) {
this->set_state(GDO_DOOR_STATE_OPENING, this->position);
if (this->toggle_only_) {
ESP_LOGD(TAG, "Sending TOGGLE action");
gdo_door_toggle();
if (this->state_ == GDO_DOOR_STATE_STOPPED && this->prev_operation == COVER_OPERATION_OPENING) {
// If the door was stopped while opening, then we need to toggle to stop, then toggle again to open,
this->set_timeout("stop_door", 1000, [=]() {
gdo_door_stop();
});
this->set_timeout("open_door", 2000, [=]() {
gdo_door_toggle();
});
}
} else {
ESP_LOGD(TAG, "Sending OPEN action");
gdo_door_open();
}
} else if (pos == COVER_CLOSED) {
this->set_state(GDO_DOOR_STATE_CLOSING, this->position);

this->set_state(GDO_DOOR_STATE_OPENING, this->position);
} else if (pos == COVER_CLOSED) {
if (this->toggle_only_) {
ESP_LOGD(TAG, "Sending TOGGLE action");
gdo_door_toggle();
if (this->state_ == GDO_DOOR_STATE_STOPPED && this->prev_operation == COVER_OPERATION_CLOSING) {
// If the door was stopped while closing, then we need to toggle to stop, then toggle again to close,
this->set_timeout("stop_door", 1000, [=]() {
gdo_door_stop();
});
this->set_timeout("close_door", 2000, [=]() {
gdo_door_toggle();
});
}
} else {
ESP_LOGD(TAG, "Sending CLOSE action");
gdo_door_close();
}

this->set_state(GDO_DOOR_STATE_CLOSING, this->position);
} else {
ESP_LOGD(TAG, "Moving garage door to position %f", pos);
gdo_door_move_to_target(10000 - (pos * 10000));
Expand All @@ -126,22 +151,44 @@ void GDODoor::control(const cover::CoverCall& call) {
this->pre_close_end_trigger->trigger();
}
}
this->target_position_ = this->position;
this->do_action(call);
}

if (call.get_toggle()) {
ESP_LOGD(TAG, "Toggle command received");
if (this->position != COVER_CLOSED) {
this->target_position_ = COVER_CLOSED;
this->do_action_after_warning(call);
} else {
this->target_position_ = COVER_OPEN;
this->do_action(call);
}
}

if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (this->position == pos) {
ESP_LOGD(TAG, "Door is already %s", pos == COVER_OPEN ? "open" : "closed");
this->publish_state(false);
return;
}

if ((this->current_operation == COVER_OPERATION_OPENING && pos > this->position) ||
(this->current_operation == COVER_OPERATION_CLOSING && pos < this->position)) {
ESP_LOGD(TAG, "Door is already moving in target direction; target position: %.0f%%", *this->target_position_);
this->publish_state(false);
return;
}

if (this->pre_close_active_) {
// don't start the pre-close again if the door is already going to close.
if (pos < this->position) {
ESP_LOGD(TAG, "Door is already closing");
this->publish_state(false);
return;
}

ESP_LOGD(TAG, "Canceling pending action");
this->cancel_timeout("pre_close");
this->pre_close_active_ = false;
Expand Down Expand Up @@ -172,6 +219,8 @@ void GDODoor::control(const cover::CoverCall& call) {
this->do_action(call);
}
}

this->target_position_ = pos;
}
}

Expand Down
7 changes: 5 additions & 2 deletions components/secplus_gdo/cover/gdo_door.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ using namespace esphome::cover;
}

void do_action(const cover::CoverCall& call);
void do_action_after_warning(const cover::CoverCall& call);
void do_action_after_warning(const cover::CoverCall& call);
void set_pre_close_warning_duration(uint32_t ms) { this->pre_close_duration_ = ms; }
void set_toggle_only(bool val) { this->toggle_only_ = val; }
void set_state(gdo_door_state_t state, float position);
Expand All @@ -55,9 +55,12 @@ using namespace esphome::cover;

CoverClosingStartTrigger *pre_close_start_trigger{nullptr};
CoverClosingEndTrigger *pre_close_end_trigger{nullptr};
uint32_t pre_close_duration_{0};
uint32_t pre_close_duration_{0};
bool pre_close_active_{false};
bool toggle_only_{false};
optional<float> target_position_{0};
CoverOperation prev_operation{COVER_OPERATION_IDLE};
gdo_door_state_t state_{GDO_DOOR_STATE_MAX};
};
} // namespace secplus_gdo
} // namespace esphome
5 changes: 5 additions & 0 deletions components/secplus_gdo/secplus_gdo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ namespace secplus_gdo {
}

void GDOComponent::setup() {
// Set the toggle only state and control here because we cannot guarantee the cover instance was created before the switch
this->door_->set_toggle_only(this->toggle_only_switch_->state);
this->toggle_only_switch_->set_control_function(std::bind(&esphome::secplus_gdo::GDODoor::set_toggle_only,
this->door_, std::placeholders::_1));

gdo_config_t gdo_conf = {
.uart_num = UART_NUM_1,
.obst_from_status = true,
Expand Down
25 changes: 17 additions & 8 deletions components/secplus_gdo/secplus_gdo.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "number/gdo_number.h"
#include "esphome/core/defines.h"
#include "select/gdo_select.h"
#include "switch/gdo_switch.h"
#include "cover/gdo_door.h"
#include "gdo.h"

namespace esphome {
Expand All @@ -35,7 +37,9 @@ namespace secplus_gdo {
float get_setup_priority() const override { return setup_priority::LATE; }

void register_protocol_select(GDOSelect *select) { this->protocol_select_ = select; }
void set_protocol_state(gdo_protocol_type_t protocol) { if (this->protocol_select_) { this->protocol_select_->update_state(protocol); } }
void set_protocol_state(gdo_protocol_type_t protocol) { if (this->protocol_select_) {
this->protocol_select_->update_state(protocol); }
}

void register_motion(std::function<void(bool)> f) { f_motion = f; }
void set_motion_state(gdo_motion_state_t state) { if (f_motion) { f_motion(state == GDO_MOTION_STATE_DETECTED); } }
Expand All @@ -54,17 +58,19 @@ namespace secplus_gdo {
void register_openings(std::function<void(uint16_t)> f) { f_openings = f; }
void set_openings(uint16_t openings) { if (f_openings) { f_openings(openings); } }

void register_door(std::function<void(gdo_door_state_t, float)> f) { f_door = f; }
void set_door_state(gdo_door_state_t state, float position) { if (f_door) { f_door(state, position); } }
void register_door(GDODoor *door) { this->door_ = door; }
void set_door_state(gdo_door_state_t state, float position) { if (this->door_) { this->door_->set_state(state, position); } }

void register_light(std::function<void(gdo_light_state_t)> f) { f_light = f; }
void set_light_state(gdo_light_state_t state) { if (f_light) { f_light(state); } }

void register_lock(std::function<void(gdo_lock_state_t)> f) { f_lock = f; }
void set_lock_state(gdo_lock_state_t state) { if (f_lock) { f_lock(state); } }

void register_learn(std::function<void(bool)> f) { f_learn = f; }
void set_learn_state(gdo_learn_state_t state) { if (f_learn) { f_learn(state == GDO_LEARN_STATE_ACTIVE); } }
void register_learn(GDOSwitch *sw) { this->learn_switch_ = sw; }
void set_learn_state(gdo_learn_state_t state) { if (this->learn_switch_) {
this->learn_switch_->write_state(state == GDO_LEARN_STATE_ACTIVE); }
}

void register_open_duration(GDONumber* num) { open_duration_ = num; }
void set_open_duration(uint16_t ms ) { if (open_duration_) { open_duration_->update_state(ms); } }
Expand All @@ -78,22 +84,25 @@ namespace secplus_gdo {
void register_rolling_code(GDONumber* num) { rolling_code_ = num; }
void set_rolling_code(uint32_t num) { if (rolling_code_) { rolling_code_->update_state(num); } }

void register_toggle_only(GDOSwitch *sw) { this->toggle_only_switch_ = sw; }

protected:
gdo_status_t status_;
std::function<void(gdo_door_state_t, float)> f_door{nullptr};
gdo_status_t status_{};
std::function<void(gdo_lock_state_t)> f_lock{nullptr};
std::function<void(gdo_light_state_t)> f_light{nullptr};
std::function<void(uint16_t)> f_openings{nullptr};
std::function<void(bool)> f_motion{nullptr};
std::function<void(bool)> f_obstruction{nullptr};
std::function<void(bool)> f_button{nullptr};
std::function<void(bool)> f_motor{nullptr};
std::function<void(bool)> f_learn{nullptr};
GDODoor* door_{nullptr};
GDONumber* open_duration_{nullptr};
GDONumber* close_duration_{nullptr};
GDONumber* client_id_{nullptr};
GDONumber* rolling_code_{nullptr};
GDOSelect* protocol_select_{nullptr};
GDOSwitch* learn_switch_{nullptr};
GDOSwitch* toggle_only_switch_{nullptr};

}; // GDOComponent
} // namespace secplus_gdo
Expand Down
2 changes: 1 addition & 1 deletion components/secplus_gdo/select/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

GDOSelect = secplus_gdo_ns.class_("GDOSelect", select.Select, cg.Component)

CONF_PROTOCOL_SELECT_OPTIONS = ["auto", "security+1.0", "security+2.0"]
CONF_PROTOCOL_SELECT_OPTIONS = ["auto", "security+1.0", "security+2.0", "security+1.0 with smart panel"]

CONFIG_SCHEMA = (
select.select_schema(GDOSelect)
Expand Down
7 changes: 5 additions & 2 deletions components/secplus_gdo/switch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CONF_TYPE = "type"
TYPES = {
"learn": "register_learn",
"toggle_only": "register_toggle_only",
}


Expand All @@ -32,5 +33,7 @@ async def to_code(config):
await cg.register_component(var, config)
parent = await cg.get_variable(config[CONF_SECPLUS_GDO_ID])
fcall = str(parent) + "->" + str(TYPES[config[CONF_TYPE]])
text = fcall + "(std::bind(&" + str(GDOSwitch) + "::write_state," + str(config[CONF_ID]) + ",std::placeholders::_1))"
cg.add((cg.RawExpression(text)))
text = fcall + "(" + str(var) + ")"
cg.add((cg.RawExpression(text)))
text = "secplus_gdo::SwitchType::" + str(config[CONF_TYPE]).upper()
cg.add(var.set_type(cg.RawExpression(text)))
Loading
Loading