Skip to content

Commit a31eb96

Browse files
committed
fix(ameba): port the "77" exit + teardown fixes, minus the BLE change
Brings node 14's recommission path in line with the esp32 half. The trigger fix itself was already shared: the debounce lives in hisense_rs485.cpp, so the "asserts for exactly one frame" correction applies to both halves automatically -- and that was the reason "77" had never fired anywhere. Ported: - Single teardown for every exit (expired / user left / paired). The expiry path previously only flipped flags and sent exit_77, leaving the CHIP commissioning window OPEN and, when Wi-Fi was down, BLE advertising indefinitely -- a lapsed window stayed pairable with nothing on the panel to show it. - Toggle: pressing the pattern again while the window is open now EXITS, matching the stock dongle. Enter and exit are identical on the wire (a one-frame pulse), so holding a window is the only thing that distinguishes them. - Any non-swing remote button exits, with a 6 s settling grace. - The driver's new cancel callback is wired to close the window on a sustained release. SWING IS EXCLUDED from the remote-activity check, and this is the important one: the gesture that ENTERS "77" is "Horizon Airflow x6" -- the swing button. Including vswing/hswing made entering the mode instantly exit it. Measured on the esp32 half at window-open 95424 ms, killed 95674 ms, on every single attempt. The entry gesture can never be the exit signal. ## NOT ported: BLE-always-on during the window The esp32 half now keeps CHIPoBLE up for the whole window, because IP-only commissionable discovery does not work here (matter-server's discover_commissionable_nodes returns nothing and commissioning over IP fails with "Discovery timed out"), so a BLE-suppressed window is invisible. That reasoning does NOT transfer. The RTL8710C is SINGLE-RADIO: this file already records that the BLE window starved Wi-Fi via coex and dropped weak-signal units, and the recommission-77 notes list it as a compounding factor in the living-room drops. Forcing BLE on here trades a discoverable window for the risk of dropping the node that is currently healthy. So ameba keeps the existing conditional: suppress BLE only when Wi-Fi is actually up, keep it when Wi-Fi is down (the lockout audit fix). The consequence is that a "77" window on node 14 may still be hard for a controller to find over IP alone -- unresolved, and it needs a decision about whether coex risk or discoverability matters more on this hardware. Host tests: 330 codec + 119 matter-map pass. Ameba builds clean (v10300). NOTE: node 14 is still RUNNING v1.3.0 without these fixes. version.txt is 1.3.0 and .released-version is 10300, so shipping them needs a bump to 1.3.1 -- the serial derives from the version and an equal serial will not switch boot slots. Assisted-by: AI
1 parent e431e70 commit a31eb96

1 file changed

Lines changed: 96 additions & 5 deletions

File tree

firmware/src/sdk-edits/matter_drivers.cpp

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,37 @@ static void hisense_send_power(bool on)
175175
* Downlink: A/C status -> Matter. hisense_on_status runs in the bus task, so
176176
* it only snapshots + posts an event; the handler does the CHIP Sets.
177177
* ------------------------------------------------------------------------ */
178+
// Forward decls: the "77" machinery lives further down, but hisense_on_status (above it) needs
179+
// to know whether a window is open so remote activity can close it.
180+
static bool recommission_window_is_open(void);
181+
static bool recommission_grace_expired(void);
182+
static void recommission_user_cancel(intptr_t);
183+
178184
static void hisense_on_status(const HisenseState *state)
179185
{
180186
if (state == NULL || !state->valid) {
181187
return;
182188
}
183189
// Bus-task write vs the CHIP-task snapshot reads (uplink echo-guard / downlink):
184190
// guard the multi-word store so a reader can't observe a torn HisenseState (#57).
191+
/* Stock behaviour: ANY remote button exits "77", not just pressing the pattern again. We
192+
* cannot see IR, but every press lands on the bus as a change to a user-settable field.
193+
*
194+
* SWING IS EXCLUDED ON PURPOSE -- the gesture that ENTERS "77" is "Horizon Airflow x6", i.e.
195+
* the swing button, so including vswing/hswing makes entering the mode instantly exit it
196+
* (measured on the esp32 half: window open at 95424 ms, killed at 95674 ms, every attempt).
197+
* Temperatures / compressor / current are excluded too: they drift every frame. */
198+
if (recommission_window_is_open() && s_status.valid && recommission_grace_expired()) {
199+
const bool user_touched =
200+
state->power_on != s_status.power_on || state->mode != s_status.mode ||
201+
state->setpoint_c != s_status.setpoint_c || state->fan_raw != s_status.fan_raw ||
202+
state->eco_on != s_status.eco_on || state->turbo_on != s_status.turbo_on ||
203+
state->mute_on != s_status.mute_on || state->sleep_raw != s_status.sleep_raw;
204+
if (user_touched) {
205+
ChipLogProgress(DeviceLayer, "A/C driven from the remote during \"77\" -> EXIT");
206+
chip::DeviceLayer::PlatformMgr().ScheduleWork(recommission_user_cancel, 0);
207+
}
208+
}
185209
hisense_diag_on_status(state); // #23: snapshot for the debug console (no-op in release)
186210
taskENTER_CRITICAL();
187211
s_status = *state;
@@ -269,10 +293,16 @@ static void matter_power_meter_update(const HisenseState &st)
269293
* ------------------------------------------------------------------------ */
270294
static const uint32_t kRecommissionWindowSec = 180;
271295
static bool s_recommission_pending = false;
296+
/* Remote activity is ignored until this deadline after the window opens: the "77" entry gesture
297+
* ("Horizon Airflow x6") is itself a burst of remote presses. Without it the mode cancels itself
298+
* the instant it opens -- measured on the esp32 half, every single attempt. */
299+
static const uint32_t kRecommissionGraceMs = 6000;
300+
static uint32_t s_recommission_grace_ms = 0;
272301
static chip::FabricIndex s_old_fabrics[16];
273302
static uint8_t s_old_fabric_count = 0;
274303

275304
static void recommission_timeout(chip::System::Layer *, void *);
305+
static void recommission_finish(bool paired, const char *why);
276306

277307
// FabricTable delegate: a NEW fabric committing while our window is open means the
278308
// re-pair succeeded -> drop the old fabric(s) and stand down.
@@ -298,20 +328,70 @@ class RecommissionFabricDelegate : public chip::FabricTable::Delegate
298328
};
299329
static RecommissionFabricDelegate s_recommission_delegate;
300330

301-
// Window expired with no new pairing -> keep the old fabric, tell the A/C to exit "77".
302-
static void recommission_timeout(chip::System::Layer *, void *)
331+
/* Single teardown for EVERY exit from "77" so the device is never left half-in it. Three ways
332+
* out -- expired, the user left "77" on the A/C, or the re-pair succeeded -- and the first two
333+
* must restore the previous state exactly: old fabric intact, CHIP window shut, BLE advert back
334+
* to the commissioned-node resting state, A/C told to drop "77".
335+
*
336+
* The expiry path previously only flipped flags and sent exit_77: it left the CHIP commissioning
337+
* window OPEN and (when Wi-Fi was down) BLE advertising indefinitely, so a lapsed window stayed
338+
* pairable long after the panel had stopped showing anything. */
339+
static void recommission_finish(bool paired, const char *why)
303340
{
304341
if (!s_recommission_pending) return;
305342
s_recommission_pending = false;
306343
s_old_fabric_count = 0;
307-
ChipLogProgress(DeviceLayer, "recommission: window expired, no new pairing -> revert + signal A/C out of 77");
308-
hisense_send_exit_77();
344+
chip::DeviceLayer::SystemLayer().CancelTimer(recommission_timeout, nullptr);
345+
346+
auto &cwm = chip::Server::GetInstance().GetCommissioningWindowManager();
347+
if (cwm.IsCommissioningWindowOpen()) cwm.CloseCommissioningWindow();
348+
349+
CHIP_ERROR berr = chip::DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(false);
350+
if (berr != CHIP_NO_ERROR) {
351+
ChipLogError(DeviceLayer, "recommission: SetBLEAdvertisingEnabled(false) failed: %" CHIP_ERROR_FORMAT,
352+
berr.Format());
353+
}
354+
if (!paired) hisense_send_exit_77(); // on success the delegate already cleared it
355+
ChipLogProgress(DeviceLayer, "recommission: %s (%s) -> window closed, BLE advert off",
356+
paired ? "paired" : "reverted", why);
357+
}
358+
359+
// Window expired with no new pairing -> keep the old fabric, tell the A/C to exit "77".
360+
static void recommission_timeout(chip::System::Layer *, void *)
361+
{
362+
recommission_finish(false, "window expired");
363+
}
364+
365+
/* The user took the A/C out of "77" themselves (pressed the pattern again, or any other remote
366+
* button). Runs in Matter context via ScheduleWork. */
367+
static bool recommission_window_is_open(void)
368+
{
369+
return s_recommission_pending;
370+
}
371+
372+
static bool recommission_grace_expired(void)
373+
{
374+
return (uint32_t) chip::System::SystemClock().GetMonotonicTimestamp().count()
375+
> s_recommission_grace_ms;
376+
}
377+
378+
static void recommission_user_cancel(intptr_t)
379+
{
380+
recommission_finish(false, "user left 77");
309381
}
310382

311383
// Matter-context entry (via ScheduleWork): snapshot fabrics + open the window + arm the timer.
312384
static void recommission_open_window(intptr_t)
313385
{
314-
if (s_recommission_pending) return; // a window is already open
386+
/* TOGGLE, matching the stock dongle: pressing the pattern again while a window is open EXITS
387+
* "77". The A/C pulses the request for one frame per press, so enter and exit look identical
388+
* on the wire -- the only difference is whether we already hold a window. Without this the
389+
* documented way out did nothing and the device stayed joinable for the full window. */
390+
if (s_recommission_pending) {
391+
ChipLogProgress(DeviceLayer, "\"77\" pressed again while the window is open -> EXIT");
392+
recommission_finish(false, "user pressed 77 again");
393+
return;
394+
}
315395
s_old_fabric_count = 0;
316396
for (auto it = chip::Server::GetInstance().GetFabricTable().begin();
317397
it != chip::Server::GetInstance().GetFabricTable().end(); ++it) {
@@ -349,11 +429,21 @@ static void recommission_open_window(intptr_t)
349429
s_recommission_pending = true;
350430
chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(kRecommissionWindowSec),
351431
recommission_timeout, nullptr);
432+
s_recommission_grace_ms = (uint32_t) chip::System::SystemClock().GetMonotonicTimestamp().count()
433+
+ kRecommissionGraceMs;
352434
hisense_set_provisioning(true); // report prov=1 -> A/C lights "77" while the window is open
353435
ChipLogProgress(DeviceLayer, "recommission: window open %lus, snapshot %u old fabric(s)",
354436
(unsigned long) kRecommissionWindowSec, s_old_fabric_count);
355437
}
356438

439+
/* The A/C dropped a SUSTAINED "77" request (user left the mode). Momentary pulses are filtered
440+
* in the driver, so this only fires for a genuine release. */
441+
static void matter_driver_on_recommission_cancel(void)
442+
{
443+
ChipLogProgress(DeviceLayer, "A/C left \"77\" -> closing the commissioning window");
444+
chip::DeviceLayer::PlatformMgr().ScheduleWork(recommission_user_cancel, 0);
445+
}
446+
357447
// Driver "77" callback (bus-task context) -> defer the real work to Matter context.
358448
static void matter_driver_on_recommission(uint8_t reason)
359449
{
@@ -466,6 +556,7 @@ CHIP_ERROR matter_driver_room_aircon_init(void)
466556

467557
chip::Server::GetInstance().GetFabricTable().AddFabricDelegate(&s_recommission_delegate); // F1: swap fabric on new pairing
468558
hisense_set_recommission_cb(matter_driver_on_recommission); // F1: remote "77" -> open window
559+
hisense_set_recommission_cancel_cb(matter_driver_on_recommission_cancel); // user left "77"
469560
hisense_set_link_cb(matter_driver_on_link); // #56: bus silence -> mark unavailable
470561
hisense_set_features_cb(matter_driver_on_features); // 0x66/40 feature flags -> device log
471562
hisense_diag_console_start(); // #23: :2323 console, DEBUG flavour only

0 commit comments

Comments
 (0)