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); 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)"}] diff --git a/src/shelly_hap_window_covering.cpp b/src/shelly_hap_window_covering.cpp index 8e94fd05..78e9dfc2 100644 --- a/src/shelly_hap_window_covering.cpp +++ b/src/shelly_hap_window_covering.cpp @@ -30,17 +30,21 @@ 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((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; @@ -59,6 +63,7 @@ WindowCovering::WindowCovering(int id, Input *in0, Input *in1, Output *out0, pm_open_ = pm1; pm_close_ = pm0; } + set_primary(true); } WindowCovering::~WindowCovering() { @@ -199,11 +204,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, @@ -212,9 +218,11 @@ 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) { @@ -224,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); @@ -244,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(); } @@ -742,8 +758,13 @@ 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); - std::unique_ptr wc(new hap::WindowCovering( - id, in1, in2, out1, out2, pm1, pm2, (struct mgos_config_wc *) wc_cfg)); + // 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)); if (wc == nullptr || !wc->Init().ok()) { return; } @@ -753,7 +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(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 2d2e4d79..966c525c 100644 --- a/src/shelly_hap_window_covering.hpp +++ b/src/shelly_hap_window_covering.hpp @@ -20,6 +20,7 @@ #include #include +#include "mgos_config.h" #include "mgos_hap.hpp" #include "mgos_sys_config.h" #include "mgos_timers.hpp" @@ -43,8 +44,14 @@ 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. @@ -120,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; @@ -127,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; @@ -143,6 +150,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,