Skip to content

Commit b24fa37

Browse files
committed
WC: Fix HAP input handling logic
* Separate last HAP and external (buttons) movement states * Expire HAP movement state after 1 minute. This provides intuitive behavior within short time span so short-term interactive use is unaffected and allow automated changes to work properly. #254
1 parent 085f329 commit b24fa37

File tree

2 files changed

+39
-15
lines changed

2 files changed

+39
-15
lines changed

src/shelly_hap_window_covering.cpp

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,10 @@ Component::Type WindowCovering::type() const {
180180
}
181181

182182
StatusOr<std::string> WindowCovering::GetInfo() const {
183-
return mgos::SPrintf("c:%d mp:%.2f mt_ms:%d cp:%.2f tp:%.2f lemd:%d",
183+
return mgos::SPrintf("c:%d mp:%.2f mt_ms:%d cp:%.2f tp:%.2f lemd:%d lhmd:%d",
184184
cfg_->calibrated, cfg_->move_power, cfg_->move_time_ms,
185-
cur_pos_, tgt_pos_, (int) last_ext_move_dir_);
185+
cur_pos_, tgt_pos_, (int) last_ext_move_dir_,
186+
(int) last_hap_move_dir_);
186187
}
187188

188189
StatusOr<std::string> WindowCovering::GetInfoJSON() const {
@@ -331,19 +332,35 @@ void WindowCovering::SetTgtPos(float new_tgt_pos, const char *src) {
331332
tgt_pos_char_->RaiseEvent();
332333
}
333334

335+
// We want tile taps to cycle the open-stop-close-stop sequence.
336+
// Problem is, tile taps behave as "prefer close": Home will send
337+
// 0 ("fully close") if tile is tapped while in the intermediate position.
338+
// We try to detect this case and ignore the target setting, instead
339+
// we use the opposite of last action. This results in more natural and
340+
// intuitive behavior (originally requested in
341+
// https://github.com/mongoose-os-apps/shelly-homekit/issues/181).
342+
// However, this causes issues with automations (reported in
343+
// https://github.com/mongoose-os-apps/shelly-homekit/issues/254).
344+
// To address this, we "expire" last state after 1 minute. This provides
345+
// intuitive behavior within short time span so short-term interactive use
346+
// is unaffected and allow automated changes to work properly.
334347
void WindowCovering::HAPSetTgtPos(float value) {
335-
LOG(LL_INFO, ("WC %d: HAPSetTgtPos %.2f cur %.2f tgt %.2f lemd %d", id(),
336-
value, cur_pos_, tgt_pos_, (int) last_ext_move_dir_));
348+
// If last action was a while ago, ignore it.
349+
if (mgos_uptime_micros() - last_hap_set_tgt_pos_ > 60 * 1000000) {
350+
last_hap_move_dir_ = Direction::kNone;
351+
}
352+
LOG(LL_INFO, ("WC %d: HAPSetTgtPos %.2f cur %.2f tgt %.2f lhmd %d", id(),
353+
value, cur_pos_, tgt_pos_, (int) last_hap_move_dir_));
337354
// If the specified position is intermediate, just do what we are told.
338355
if ((value != kFullyClosed && value != kFullyOpen) ||
339-
last_ext_move_dir_ == Direction::kNone) {
356+
last_hap_move_dir_ == Direction::kNone) {
340357
SetTgtPos(value, "HAP");
341358
if (value == kFullyClosed) {
342-
last_ext_move_dir_ = Direction::kClose;
359+
last_hap_move_dir_ = Direction::kClose;
343360
} else if (value == kFullyOpen) {
344-
last_ext_move_dir_ = Direction::kOpen;
361+
last_hap_move_dir_ = Direction::kOpen;
345362
} else {
346-
last_ext_move_dir_ = Direction::kNone;
363+
last_hap_move_dir_ = Direction::kNone;
347364
}
348365
} else if ((value == kFullyClosed &&
349366
(cur_pos_ == kFullyClosed || tgt_pos_ == kFullyClosed)) ||
@@ -352,8 +369,9 @@ void WindowCovering::HAPSetTgtPos(float value) {
352369
// Nothing to do.
353370
} else {
354371
// This is most likely a tap on the tile.
355-
HandleInputSingle("HAPalt");
372+
HandleInputSingle("HAPalt", &last_hap_move_dir_);
356373
}
374+
last_hap_set_tgt_pos_ = mgos_uptime_micros();
357375
// Run state machine immediately to improve reaction time,
358376
RunOnce();
359377
}
@@ -622,6 +640,7 @@ void WindowCovering::HandleInputEvent01(Direction dir, Input::Event ev,
622640
if (state) {
623641
if (moving_dir_ == Direction::kNone || moving_dir_ != dir) {
624642
float pos = (dir == Direction::kOpen ? kFullyOpen : kFullyClosed);
643+
last_ext_move_dir_ = dir;
625644
SetTgtPos(pos, "ext");
626645
} else {
627646
stop = true;
@@ -634,6 +653,7 @@ void WindowCovering::HandleInputEvent01(Direction dir, Input::Event ev,
634653
RunOnce();
635654
SetTgtPos(cur_pos_, "ext");
636655
}
656+
last_hap_move_dir_ = Direction::kNone;
637657
// Run the state machine immediately for quicker response.
638658
RunOnce();
639659
}
@@ -645,7 +665,8 @@ void WindowCovering::HandleInputEvent2(Input::Event ev, bool state) {
645665
}
646666
if (ev != Input::Event::kChange) return;
647667
if (!state) return;
648-
HandleInputSingle("ext");
668+
HandleInputSingle("ext", &last_ext_move_dir_);
669+
last_hap_move_dir_ = Direction::kNone;
649670
// Run the state machine immediately for quicker response.
650671
RunOnce();
651672
}
@@ -665,15 +686,16 @@ void WindowCovering::HandleInputEventNotCalibrated() {
665686
out_close_->SetState(want_close, "ext");
666687
}
667688

668-
void WindowCovering::HandleInputSingle(const char *src) {
689+
void WindowCovering::HandleInputSingle(const char *src,
690+
Direction *last_move_dir) {
669691
switch (moving_dir_) {
670692
case Direction::kNone: {
671-
if (cur_pos_ == kFullyClosed || last_ext_move_dir_ != Direction::kOpen) {
693+
if (cur_pos_ == kFullyClosed || *last_move_dir != Direction::kOpen) {
672694
SetTgtPos(kFullyOpen, src);
673-
last_ext_move_dir_ = Direction::kOpen;
695+
*last_move_dir = Direction::kOpen;
674696
} else {
675697
SetTgtPos(kFullyClosed, src);
676-
last_ext_move_dir_ = Direction::kClose;
698+
*last_move_dir = Direction::kClose;
677699
}
678700
break;
679701
}

src/shelly_hap_window_covering.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class WindowCovering : public Component, public Service {
109109
void HandleInputEvent01(Direction dir, Input::Event ev, bool state);
110110
void HandleInputEvent2(Input::Event ev, bool state);
111111
void HandleInputEventNotCalibrated();
112-
void HandleInputSingle(const char *src);
112+
void HandleInputSingle(const char *src, Direction *last_move_dir);
113113

114114
Input *in_open_, *in_close_;
115115
Output *out_open_, *out_close_;
@@ -137,8 +137,10 @@ class WindowCovering : public Component, public Service {
137137
float move_start_pos_ = 0;
138138
float move_ms_per_pct_ = 0;
139139
bool obstruction_detected_ = false;
140+
int64_t last_hap_set_tgt_pos_ = 0;
140141
Direction moving_dir_ = Direction::kNone;
141142
Direction last_ext_move_dir_ = Direction::kNone;
143+
Direction last_hap_move_dir_ = Direction::kNone;
142144
};
143145

144146
} // namespace hap

0 commit comments

Comments
 (0)