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,