From 8ed3babacbcd52c51800e5d28616889620ceb384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20L=C3=B3pez?= Date: Tue, 18 Mar 2025 12:29:19 +0100 Subject: [PATCH 1/6] Window support try --- src/shelly_hap_window_covering.cpp | 12 +++++++++++- src/shelly_hap_window_covering.hpp | 9 ++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/shelly_hap_window_covering.cpp b/src/shelly_hap_window_covering.cpp index 8e94fd05..f7dba479 100644 --- a/src/shelly_hap_window_covering.cpp +++ b/src/shelly_hap_window_covering.cpp @@ -30,8 +30,9 @@ namespace hap { WindowCovering::WindowCovering(int id, Input *in0, Input *in1, Output *out0, Output *out1, PowerMeter *pm0, PowerMeter *pm1, - struct mgos_config_wc *cfg) + struct mgos_config_wc *cfg, ServiceType type) : Component(id), + service_type_(type), Service((SHELLY_HAP_IID_BASE_WINDOW_COVERING + (SHELLY_HAP_IID_STEP_WINDOW_COVERING * (id - 1))), &kHAPServiceType_WindowCovering, @@ -59,6 +60,15 @@ WindowCovering::WindowCovering(int id, Input *in0, Input *in1, Output *out0, pm_open_ = pm1; pm_close_ = pm0; } + + uint8_t service_type = (service_type_ == ServiceType::WINDOW) ? + kHAPServiceType_Window : + kHAPServiceType_WindowCovering; + + svc_.service.iid = iid++; + svc_.service.serviceType = service_type; + svc_.service.debugDescription = service_type == kHAPServiceType_Window ? + "Window" : "WindowCovering"; } WindowCovering::~WindowCovering() { diff --git a/src/shelly_hap_window_covering.hpp b/src/shelly_hap_window_covering.hpp index 2d2e4d79..b1a5aa57 100644 --- a/src/shelly_hap_window_covering.hpp +++ b/src/shelly_hap_window_covering.hpp @@ -43,8 +43,13 @@ class WindowCovering : public Component, public mgos::hap::Service { kDetached = 3, }; + enum class ServiceType { + WINDOW_COVERING = 0, // Default HAP Window Covering + WINDOW = 1, // HAP Window service type + }; + WindowCovering(int id, Input *in0, Input *in1, Output *out0, Output *out1, - PowerMeter *pm0, PowerMeter *pm1, struct mgos_config_wc *cfg); + PowerMeter *pm0, PowerMeter *pm1, struct mgos_config_wc *cfg, ServiceType type = ServiceType::WINDOW_COVERING); virtual ~WindowCovering(); // Component interface impl. @@ -143,6 +148,8 @@ class WindowCovering : public Component, public mgos::hap::Service { int64_t last_hap_set_tgt_pos_ = 0; Direction moving_dir_ = Direction::kNone; Direction last_move_dir_ = Direction::kNone; + + ServiceType service_type_; }; void CreateHAPWC(int id, Input *in1, Input *in2, Output *out1, Output *out2, PowerMeter *pm1, PowerMeter *pm2, From 9248df3b13d6737b0d0655c51e327cf50cadd060 Mon Sep 17 00:00:00 2001 From: esAdrian Date: Mon, 24 Mar 2025 11:53:44 +0100 Subject: [PATCH 2/6] Add display type in web interface --- fs_src/index.html | 7 +++++++ fs_src/script.js | 2 ++ 2 files changed, 9 insertions(+) diff --git a/fs_src/index.html b/fs_src/index.html index 9566da54..998fffc4 100644 --- a/fs_src/index.html +++ b/fs_src/index.html @@ -692,6 +692,13 @@

Window

+
+ + +
pos diff --git a/fs_src/script.js b/fs_src/script.js index d8402269..75a1587a 100644 --- a/fs_src/script.js +++ b/fs_src/script.js @@ -470,6 +470,7 @@ function wcSetConfig(c, cfg, spinner) { } cfg = { name: name, + display_type: parseInt(el(c, "display_type").value), in_mode: parseInt(el(c, "in_mode").value), swap_inputs: el(c, "swap_inputs").checked, swap_outputs: el(c, "swap_outputs").checked, @@ -833,6 +834,7 @@ function updateComponent(cd) { case Component_Type.kWindowCovering: { updateInnerText(el(c, "head"), cd.name); setValueIfNotModified(el(c, "name"), cd.name); + selectIfNotModified(el(c, "display_type"), cd.display_type); updateInnerText(el(c, "state"), cd.state_str); selectIfNotModified(el(c, "in_mode"), cd.in_mode); checkIfNotModified(el(c, "swap_inputs"), cd.swap_inputs); From d5b7d1ebca000c145960d7ae865bb3599f63a325 Mon Sep 17 00:00:00 2001 From: esAdrian Date: Mon, 24 Mar 2025 12:07:21 +0100 Subject: [PATCH 3/6] Missing mos.yml --- mos.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mos.yml b/mos.yml index fae6bc19..797cf655 100644 --- a/mos.yml +++ b/mos.yml @@ -73,6 +73,7 @@ config_schema: - ["wc", "o", {title: "Roller shutter settings", abstract: true}] - ["wc.name", "s", "Shutter 1", {title: "Accessory name"}] + - ["wc.display_type", "i", 0, {title: "Display type: 0 - Roller Shutter, 1 - Window"}] - ["wc.in_mode", "i", 0, {title: "Input mode: 0 - separate, momentary; 0 - separate, toggle; 2 - single, momentary; 3 - detached"}] - ["wc.swap_inputs", "b", false, {title: "Swap inputs (2 - open, 1 - close)"}] - ["wc.swap_outputs", "b", false, {title: "Swap outputs (2 - open, 1 - close)"}] From 9ca1b05190f3a9facc2a972c2810112bace55916 Mon Sep 17 00:00:00 2001 From: esAdrian Date: Mon, 24 Mar 2025 20:31:34 +0100 Subject: [PATCH 4/6] Fix build errors --- src/shelly_hap_window_covering.cpp | 34 ++++++++++++++++-------------- src/shelly_hap_window_covering.hpp | 10 +++++---- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/shelly_hap_window_covering.cpp b/src/shelly_hap_window_covering.cpp index f7dba479..09b33d6a 100644 --- a/src/shelly_hap_window_covering.cpp +++ b/src/shelly_hap_window_covering.cpp @@ -32,16 +32,20 @@ WindowCovering::WindowCovering(int id, Input *in0, Input *in1, Output *out0, Output *out1, PowerMeter *pm0, PowerMeter *pm1, struct mgos_config_wc *cfg, ServiceType type) : Component(id), - service_type_(type), Service((SHELLY_HAP_IID_BASE_WINDOW_COVERING + (SHELLY_HAP_IID_STEP_WINDOW_COVERING * (id - 1))), - &kHAPServiceType_WindowCovering, - kHAPServiceDebugDescription_WindowCovering), + (type == ServiceType::WINDOW ? + &kHAPServiceType_Window : + &kHAPServiceType_WindowCovering), + (type == ServiceType::WINDOW ? + kHAPServiceDebugDescription_Window : + kHAPServiceDebugDescription_WindowCovering)), cfg_(cfg), - state_timer_(std::bind(&WindowCovering::RunOnce, this)), cur_pos_(cfg_->current_pos), tgt_pos_(cfg_->current_pos), - move_ms_per_pct_(cfg_->move_time_ms / 100.0) { + state_timer_(std::bind(&WindowCovering::RunOnce, this)), + move_ms_per_pct_(cfg_->move_time_ms / 100.0), + service_type_(type) { if (!cfg_->swap_inputs) { in_open_ = in0; in_close_ = in1; @@ -60,15 +64,7 @@ WindowCovering::WindowCovering(int id, Input *in0, Input *in1, Output *out0, pm_open_ = pm1; pm_close_ = pm0; } - - uint8_t service_type = (service_type_ == ServiceType::WINDOW) ? - kHAPServiceType_Window : - kHAPServiceType_WindowCovering; - - svc_.service.iid = iid++; - svc_.service.serviceType = service_type; - svc_.service.debugDescription = service_type == kHAPServiceType_Window ? - "Window" : "WindowCovering"; + set_primary(true); } WindowCovering::~WindowCovering() { @@ -752,8 +748,12 @@ void CreateHAPWC(int id, Input *in1, Input *in2, Output *out1, Output *out2, std::vector> *accs, HAPAccessoryServerRef *svr) { auto im = static_cast(wc_cfg->in_mode); + // Determine service type based on display_type configuration + auto service_type = (wc_cfg->display_type == 1 ? + hap::WindowCovering::ServiceType::WINDOW : + hap::WindowCovering::ServiceType::WINDOW_COVERING); std::unique_ptr wc(new hap::WindowCovering( - id, in1, in2, out1, out2, pm1, pm2, (struct mgos_config_wc *) wc_cfg)); + id, in1, in2, out1, out2, pm1, pm2, (struct mgos_config_wc *) wc_cfg, service_type)); if (wc == nullptr || !wc->Init().ok()) { return; } @@ -763,7 +763,9 @@ void CreateHAPWC(int id, Input *in1, Input *in2, Output *out1, Output *out2, case hap::WindowCovering::InMode::kSeparateToggle: { // Single accessory with a single primary service. mgos::hap::Accessory *pri_acc = (*accs)[0].get(); - pri_acc->SetCategory(kHAPAccessoryCategory_WindowCoverings); + pri_acc->SetCategory(service_type == hap::WindowCovering::ServiceType::WINDOW ? + kHAPAccessoryCategory_Windows : + kHAPAccessoryCategory_WindowCoverings); pri_acc->AddService(wc.get()); break; } diff --git a/src/shelly_hap_window_covering.hpp b/src/shelly_hap_window_covering.hpp index b1a5aa57..bd892c6f 100644 --- a/src/shelly_hap_window_covering.hpp +++ b/src/shelly_hap_window_covering.hpp @@ -22,6 +22,7 @@ #include "mgos_hap.hpp" #include "mgos_sys_config.h" +#include "mgos_config.h" #include "mgos_timers.hpp" #include "shelly_common.hpp" @@ -49,7 +50,8 @@ class WindowCovering : public Component, public mgos::hap::Service { }; WindowCovering(int id, Input *in0, Input *in1, Output *out0, Output *out1, - PowerMeter *pm0, PowerMeter *pm1, struct mgos_config_wc *cfg, ServiceType type = ServiceType::WINDOW_COVERING); + PowerMeter *pm0, PowerMeter *pm1, struct mgos_config_wc *cfg, + ServiceType type = ServiceType::WINDOW_COVERING); virtual ~WindowCovering(); // Component interface impl. @@ -125,6 +127,9 @@ class WindowCovering : public Component, public mgos::hap::Service { Input::HandlerID in_open_handler_ = Input::kInvalidHandlerID; Input::HandlerID in_close_handler_ = Input::kInvalidHandlerID; + + float cur_pos_ = kNotSet; + float tgt_pos_ = kNotSet; mgos::Timer state_timer_; mgos::hap::Characteristic *cur_pos_char_ = nullptr; @@ -132,9 +137,6 @@ class WindowCovering : public Component, public mgos::hap::Service { mgos::hap::Characteristic *pos_state_char_ = nullptr; mgos::hap::Characteristic *obst_char_ = nullptr; - float cur_pos_ = kNotSet; - float tgt_pos_ = kNotSet; - State state_ = State::kIdle; State tgt_state_ = State::kNone; From a2d2f4e6d2ad433c0dbb40db56b7e04b06f22e87 Mon Sep 17 00:00:00 2001 From: esAdrian Date: Tue, 25 Mar 2025 10:08:36 +0100 Subject: [PATCH 5/6] Display_type save & state --- src/shelly_hap_window_covering.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/shelly_hap_window_covering.cpp b/src/shelly_hap_window_covering.cpp index 09b33d6a..708404d2 100644 --- a/src/shelly_hap_window_covering.cpp +++ b/src/shelly_hap_window_covering.cpp @@ -205,11 +205,12 @@ StatusOr WindowCovering::GetInfoJSON() const { "{id: %d, type: %d, name: %Q, " "in_mode: %d, swap_inputs: %B, swap_outputs: %B, " "cal_done: %B, move_time_ms: %d, move_power: %d, " - "state: %d, state_str: %Q, cur_pos: %d, tgt_pos: %d}", + "state: %d, state_str: %Q, cur_pos: %d, tgt_pos: %d, " + "display_type: %d}", id(), type(), cfg_->name, cfg_->in_mode, cfg_->swap_inputs, cfg_->swap_outputs, cfg_->calibrated, cfg_->move_time_ms, (int) cfg_->move_power, (int) state_, StateStr(state_), (int) cur_pos_, - (int) tgt_pos_); + (int) tgt_pos_, (int) service_type_); } Status WindowCovering::SetConfig(const std::string &config_json, @@ -218,9 +219,10 @@ Status WindowCovering::SetConfig(const std::string &config_json, cfg.name = nullptr; int in_mode = -1; int8_t swap_inputs = -1, swap_outputs = -1; + int display_type = -1; json_scanf(config_json.c_str(), config_json.size(), - "{name: %Q, in_mode: %d, swap_inputs: %B, swap_outputs: %B}", - &cfg.name, &in_mode, &swap_inputs, &swap_outputs); + "{name: %Q, in_mode: %d, swap_inputs: %B, swap_outputs: %B, display_type: %d}", + &cfg.name, &in_mode, &swap_inputs, &swap_outputs, &display_type); mgos::ScopedCPtr name_owner((void *) cfg.name); // Validate. if (cfg.name != nullptr && strlen(cfg.name) > 64) { @@ -230,6 +232,9 @@ Status WindowCovering::SetConfig(const std::string &config_json, if (in_mode > 3) { return mgos::Errorf(STATUS_INVALID_ARGUMENT, "invalid %s", "in_mode"); } + if (display_type > 1) { + return mgos::Errorf(STATUS_INVALID_ARGUMENT, "invalid %s", "display_type"); + } // Apply. if (cfg.name != nullptr && strcmp(cfg_->name, cfg.name) != 0) { mgos_conf_set_str(&cfg_->name, cfg.name); @@ -250,6 +255,11 @@ Status WindowCovering::SetConfig(const std::string &config_json, cfg_->calibrated = false; *restart_required = true; } + if (display_type != -1) { + service_type_ = static_cast(display_type); + cfg_->display_type = display_type; + *restart_required = true; + } return Status::OK(); } From e505b08a376669dbfa1f5eaa706ce99ec61812b2 Mon Sep 17 00:00:00 2001 From: Markus Kirberg Date: Thu, 10 Apr 2025 18:53:16 +0000 Subject: [PATCH 6/6] fix format --- src/shelly_hap_window_covering.cpp | 32 ++++++++++++++++-------------- src/shelly_hap_window_covering.hpp | 4 ++-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/shelly_hap_window_covering.cpp b/src/shelly_hap_window_covering.cpp index 708404d2..78e9dfc2 100644 --- a/src/shelly_hap_window_covering.cpp +++ b/src/shelly_hap_window_covering.cpp @@ -34,12 +34,11 @@ WindowCovering::WindowCovering(int id, Input *in0, Input *in1, Output *out0, : Component(id), Service((SHELLY_HAP_IID_BASE_WINDOW_COVERING + (SHELLY_HAP_IID_STEP_WINDOW_COVERING * (id - 1))), - (type == ServiceType::WINDOW ? - &kHAPServiceType_Window : - &kHAPServiceType_WindowCovering), - (type == ServiceType::WINDOW ? - kHAPServiceDebugDescription_Window : - kHAPServiceDebugDescription_WindowCovering)), + (type == ServiceType::WINDOW ? &kHAPServiceType_Window + : &kHAPServiceType_WindowCovering), + (type == ServiceType::WINDOW + ? kHAPServiceDebugDescription_Window + : kHAPServiceDebugDescription_WindowCovering)), cfg_(cfg), cur_pos_(cfg_->current_pos), tgt_pos_(cfg_->current_pos), @@ -221,7 +220,8 @@ Status WindowCovering::SetConfig(const std::string &config_json, int8_t swap_inputs = -1, swap_outputs = -1; int display_type = -1; json_scanf(config_json.c_str(), config_json.size(), - "{name: %Q, in_mode: %d, swap_inputs: %B, swap_outputs: %B, display_type: %d}", + "{name: %Q, in_mode: %d, swap_inputs: %B, swap_outputs: %B, " + "display_type: %d}", &cfg.name, &in_mode, &swap_inputs, &swap_outputs, &display_type); mgos::ScopedCPtr name_owner((void *) cfg.name); // Validate. @@ -759,11 +759,12 @@ void CreateHAPWC(int id, Input *in1, Input *in2, Output *out1, Output *out2, HAPAccessoryServerRef *svr) { auto im = static_cast(wc_cfg->in_mode); // Determine service type based on display_type configuration - auto service_type = (wc_cfg->display_type == 1 ? - hap::WindowCovering::ServiceType::WINDOW : - hap::WindowCovering::ServiceType::WINDOW_COVERING); - std::unique_ptr wc(new hap::WindowCovering( - id, in1, in2, out1, out2, pm1, pm2, (struct mgos_config_wc *) wc_cfg, service_type)); + auto service_type = (wc_cfg->display_type == 1 + ? hap::WindowCovering::ServiceType::WINDOW + : hap::WindowCovering::ServiceType::WINDOW_COVERING); + std::unique_ptr wc( + new hap::WindowCovering(id, in1, in2, out1, out2, pm1, pm2, + (struct mgos_config_wc *) wc_cfg, service_type)); if (wc == nullptr || !wc->Init().ok()) { return; } @@ -773,9 +774,10 @@ void CreateHAPWC(int id, Input *in1, Input *in2, Output *out1, Output *out2, case hap::WindowCovering::InMode::kSeparateToggle: { // Single accessory with a single primary service. mgos::hap::Accessory *pri_acc = (*accs)[0].get(); - pri_acc->SetCategory(service_type == hap::WindowCovering::ServiceType::WINDOW ? - kHAPAccessoryCategory_Windows : - kHAPAccessoryCategory_WindowCoverings); + pri_acc->SetCategory(service_type == + hap::WindowCovering::ServiceType::WINDOW + ? kHAPAccessoryCategory_Windows + : kHAPAccessoryCategory_WindowCoverings); pri_acc->AddService(wc.get()); break; } diff --git a/src/shelly_hap_window_covering.hpp b/src/shelly_hap_window_covering.hpp index bd892c6f..966c525c 100644 --- a/src/shelly_hap_window_covering.hpp +++ b/src/shelly_hap_window_covering.hpp @@ -20,9 +20,9 @@ #include #include +#include "mgos_config.h" #include "mgos_hap.hpp" #include "mgos_sys_config.h" -#include "mgos_config.h" #include "mgos_timers.hpp" #include "shelly_common.hpp" @@ -46,7 +46,7 @@ class WindowCovering : public Component, public mgos::hap::Service { enum class ServiceType { WINDOW_COVERING = 0, // Default HAP Window Covering - WINDOW = 1, // HAP Window service type + WINDOW = 1, // HAP Window service type }; WindowCovering(int id, Input *in0, Input *in1, Output *out0, Output *out1,