Skip to content

HomeKit plugin extension with a choice between two integrations#527

Open
modsmthng wants to merge 23 commits intojniebuhr:masterfrom
modsmthng:master
Open

HomeKit plugin extension with a choice between two integrations#527
modsmthng wants to merge 23 commits intojniebuhr:masterfrom
modsmthng:master

Conversation

@modsmthng
Copy link

@modsmthng modsmthng commented Dec 16, 2025

Thermostat Mode: Emulates a thermostat for direct temperature control (as before).

Bridge Mode: Creates multiple devices:
Sensor that reflects the heating status (red / heating = closed, green / ready = open) Switch for Standby - Brew
Switch for Brew - Steam
In Bridge mode, devices can be activated and deactivated individually.

An automatic migration logic for transferring existing Homekit plugin settings has been implemented.

When switching integrations, Apple Home automatically recognizes that the devices have been changed, removes the old ones, and adds the new ones. This allows you to switch between integrations without any problems and without devices appearing twice. Devices that are no longer initialized are automatically removed from Apple Home.

The UI has been adjusted accordingly and a fold-out troubleshooting section has been added:

SCR-20260106-qkao SCR-20260106-qkco SCR-20260106-qkkb SCR-20260106-qjxz

Summary by CodeRabbit

  • New Features

    • HomeKit now supports modes (Disabled / Thermostat / Bridge) with a UI mode selector, pairing instructions, and per-device toggles.
    • Bridge mode adds Power & Steam switches plus a Heating stability sensor.
  • Bug Fixes

    • mDNS/HomeKit registration follows the selected mode.
    • Accessories and UI stay synchronized with boiler/heating stability and controller mode changes.
  • Chores

    • One-time migration from boolean HomeKit flag to numeric homekitMode and updated settings API.

✏️ Tip: You can customize this high-level summary in your review settings.

Thermostat Mode: Emulates a thermostat for direct temperature control (as before).

Bridge Mode: Creates multiple devices:
Sensor that reflects the heating status (red / heating = closed, green / ready = open)
Switch for Standby - Brew
Switch for Brew - Steam

An automatic update logic to transfer existing Homekit plugin settings did not work (see commented-out code in Settings.cpp), but the plugin can simply be reactivated and by switching the integration worked without resetting the display.

A reset function can be worked on next, but is not really necessary.
@cla-bot
Copy link

cla-bot bot commented Dec 16, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

📝 Walkthrough

Walkthrough

Switch HomeKit from a boolean to a mode enum (DISABLED, THERMOSTAT, BRIDGE); migrate legacy "hk" → "hkm"; always register mDNS and register the appropriate HomeKit plugin by mode; add HomeKit Bridge plugin, rename thermostat plugin, update web settings UI, and emit heating‑stability events only on change.

Changes

Cohort / File(s) Change Summary
Settings & Mode API
src/display/core/Settings.h, src/display/core/Settings.cpp
Replace boolean homekit with homekitMode enum/int ("hkm"); migrate legacy "hk""hkm"; add getHomekitMode()/setHomekitMode(int) and per‑accessory toggles (hkPowerEnabled, hkSteamEnabled, hkSensorEnabled) with persistence.
Controller plugin registration
src/display/core/Controller.cpp
Always register mDNS; register HomekitThermostatPlugin when mode=THERMOSTAT, HomekitBridgePlugin when mode=BRIDGE; otherwise omit HomeKit plugin.
HomeKit Thermostat plugin (rename)
src/display/plugins/HomekitThermostatPlugin.h, src/display/plugins/HomekitThermostatPlugin.cpp
Rename HomekitPluginHomekitThermostatPlugin across header/source; update symbols; behavior retained.
HomeKit Bridge plugin (new)
src/display/plugins/HomekitBridgePlugin.h, src/display/plugins/HomekitBridgePlugin.cpp
New HomekitBridgePlugin implementing HomeSpan bridge with accessories (GaggiMatePowerSwitch, GaggiMateSteamSwitch, GaggiMateHeatingSensor), Wi‑Fi creds, atomic action tracking, event handlers and loop to apply HomeKit actions to controller and reflect controller events.
Web UI: Settings & PluginCard
web/src/pages/Settings/index.jsx, web/src/pages/Settings/PluginCard.jsx
Replace boolean HomeKit handling with integer homekitMode; add updateHomekitMode(mode) prop; add mode selector, pairing/troubleshooting UI, and accessory toggles; include homekitMode and hk* flags in payload.
Web UI: Settings form wiring
web/src/pages/Settings/...
Parse homekitMode as integer; default hk* toggles to true; handle accessory toggle inversion and include values in submission.
Web: Settings POST handling
src/display/core/WebUIPlugin.cpp
Replace boolean homekit POST handling with integer homekitMode; persist accessory toggles via new Settings setters.
UI: heating stability event
src/display/ui/default/DefaultUI.h, src/display/ui/default/DefaultUI.cpp
Add prevIsTemperatureStable and emit "boiler:heating:stable" plugin event only when stability value changes.

Sequence Diagram(s)

sequenceDiagram
    participant Controller
    participant PluginManager
    participant HomeSpan as HomeSpan/HomeKit
    participant HomekitBridge as HomekitBridgePlugin
    participant HomekitTherm as HomekitThermostatPlugin

    Controller->>PluginManager: register plugins (always mDNS + mode-based)
    alt HOMEKIT_MODE_BRIDGE
      PluginManager->>HomekitBridge: setup(controller, pluginManager)
      HomekitBridge->>HomeSpan: init bridge & accessories
      HomeSpan-->>HomekitBridge: accessory callbacks (switch toggles)
      HomekitBridge->>Controller: schedule/apply actions via atomic flags in loop
      Controller-->>PluginManager: emit events/state updates
      PluginManager-->>HomekitBridge: handlers update accessory states
    else HOMEKIT_MODE_THERMOSTAT
      PluginManager->>HomekitTherm: setup(controller, pluginManager)
      HomekitTherm->>HomeSpan: init thermostat accessory & callbacks
      HomekitTherm->>Controller: apply thermostat actions in loop
      Controller-->>PluginManager: emit events/state updates
      PluginManager-->>HomekitTherm: handlers update accessory
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • feat: Add headless version #335 — Related changes to Controller UI initialization and headless conditionalization; touches same Controller paths.
  • Dasiy Ui #347 — Overlaps web settings UI and HomeKit-related fields; likely touches same frontend logic and payload keys.

Suggested reviewers

  • jniebuhr
  • nayrod485

Poem

🐰 I hopped through modes and code so bright,

Disabled, Thermostat, or Bridge takes flight.
Switches chatter, sensors hum in tune,
Events now whisper when the boilers croon.
A rabbit cheers — HomeKit finds the moon.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: introducing a selectable HomeKit plugin with two integration modes (Thermostat and Bridge).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (11)
src/display/core/Controller.cpp (1)

55-65: Consider extracting common mDNS registration to reduce duplication.

The HomeKit mode selection logic is correct. However, mDNSPlugin is registered identically in all three branches. You could simplify by registering it unconditionally after the HomeKit block.

     // HomeKit Mode Selection
     if (settings.getHomekitMode() == HOMEKIT_MODE_THERMOSTAT) {
         pluginManager->registerPlugin(new HomekitThermostatPlugin(settings.getWifiSsid(), settings.getWifiPassword()));
-        pluginManager->registerPlugin(new mDNSPlugin());
     } else if (settings.getHomekitMode() == HOMEKIT_MODE_BRIDGE) {
-        pluginManager->registerPlugin(new HomekitBridgePlugin(settings.getWifiSsid(), settings.getWifiPassword())); 
-        pluginManager->registerPlugin(new mDNSPlugin());
-    } else { 
-        pluginManager->registerPlugin(new mDNSPlugin());
+        pluginManager->registerPlugin(new HomekitBridgePlugin(settings.getWifiSsid(), settings.getWifiPassword()));
     }
+    pluginManager->registerPlugin(new mDNSPlugin());
web/src/pages/Settings/index.jsx (1)

165-170: Fix inconsistent indentation in updateHomekitMode function.

The function body is not properly indented relative to the function declaration.

-  const updateHomekitMode = (mode) => {
-  setFormData({
-    ...formData,
-    homekitMode: parseInt(mode, 10),
-  });
-};
+  const updateHomekitMode = (mode) => {
+    setFormData({
+      ...formData,
+      homekitMode: parseInt(mode, 10),
+    });
+  };
src/display/plugins/HomekitThermostatPlugin.h (1)

1-3: Redundant include guard with #pragma once.

Using both #pragma once (Line 1) and traditional include guards (Lines 2-3, 50) is redundant. Most modern compilers support #pragma once, so the traditional guards can be removed for cleaner code. However, keeping both provides compatibility with older compilers.

src/display/core/Settings.h (1)

85-85: Consider using enum type for stronger type safety.

getHomekitMode() returns int instead of HomeKitMode. While this works, using the enum type would provide compile-time type checking and make the API more self-documenting.

-    int getHomekitMode() const { return homekitMode; }
+    HomeKitMode getHomekitMode() const { return static_cast<HomeKitMode>(homekitMode); }

Note: This would require updating call sites to compare against enum values, which they should already be doing.

src/display/core/Settings.cpp (2)

5-5: Unused include when migration code is commented out.

The esp32-hal-log.h include is only used by the commented-out migration logic. If this migration code is permanently disabled, consider removing this include to avoid unnecessary dependencies.


525-525: Commented-out save for smartGrindToggle.

This appears to be intentional deprecation (replaced by smartGrindMode on line 526), but consider removing the commented line if the migration is complete.

src/display/plugins/HomekitBridgePlugin.h (3)

1-3: Redundant include guards.

#pragma once is sufficient on its own. The traditional #ifndef guards are redundant and can be removed.

 #pragma once
-#ifndef HOMEKITBRIDGEPLUGIN_H
-#define HOMEKITBRIDGEPLUGIN_H

And remove #endif at line 97.


27-48: Near-identical switch classes could share a base or template.

GaggiMatePowerSwitch and GaggiMateSteamSwitch have identical structure - they only differ in the HomekitAction constant passed to the callback. Consider a single parameterized class:

class GaggiMateSwitch : public Service::Switch {
  public:
    GaggiMateSwitch(bridge_callback_t callback, HomekitAction actionType);
    boolean update() override;
    void setState(bool active);
  private:
    bridge_callback_t callback;
    HomekitAction actionType;
    SpanCharacteristic *power;
};

This would reduce code duplication while maintaining the same functionality.


12-13: Extract duplicate Homekit configuration macros to shared header.

HOMESPAN_PORT and DEVICE_NAME are defined identically in both HomekitBridgePlugin.h and HomekitThermostatPlugin.h. Since both headers are included in Controller.cpp, these duplicates should be moved to a shared header (e.g., HomekitCommon.h) to maintain single source of truth and simplify future updates.

src/display/plugins/HomekitBridgePlugin.cpp (2)

7-43: Duplicate switch implementations.

As noted in the header review, GaggiMatePowerSwitch and GaggiMateSteamSwitch have nearly identical implementations. The only difference is the HomekitAction enum value. A parameterized class would reduce this duplication.


200-210: Redundant sync with controller:mode:change handler.

Both handleBoilerStatus (lines 200-210) and the controller:mode:change event handler (lines 89-103) update powerSwitch and steamSwitch states. This could result in duplicate state updates. If boiler:status:change fires in response to mode changes, consider whether one handler is sufficient.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21f08c1 and a1b56b2.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • src/display/core/Controller.cpp (2 hunks)
  • src/display/core/Settings.cpp (7 hunks)
  • src/display/core/Settings.h (4 hunks)
  • src/display/plugins/HomekitBridgePlugin.cpp (1 hunks)
  • src/display/plugins/HomekitBridgePlugin.h (1 hunks)
  • src/display/plugins/HomekitThermostatPlugin.cpp (3 hunks)
  • src/display/plugins/HomekitThermostatPlugin.h (3 hunks)
  • src/display/plugins/WebUIPlugin.cpp (2 hunks)
  • src/display/ui/default/DefaultUI.cpp (2 hunks)
  • src/display/ui/default/DefaultUI.h (1 hunks)
  • web/src/pages/Settings/PluginCard.jsx (2 hunks)
  • web/src/pages/Settings/index.jsx (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (10)
web/src/pages/Settings/PluginCard.jsx (2)
web/src/pages/Settings/index.jsx (2)
  • formData (23-23)
  • updateHomekitMode (165-170)
web/src/pages/OTA/index.jsx (1)
  • formData (25-25)
src/display/core/Settings.h (2)
web/src/pages/Settings/PluginCard.jsx (1)
  • homekitMode (17-17)
src/display/core/Settings.cpp (2)
  • setHomekitMode (264-271)
  • setHomekitMode (264-264)
src/display/plugins/HomekitThermostatPlugin.h (2)
src/display/plugins/HomekitThermostatPlugin.cpp (1)
  • HomekitThermostatPlugin (45-49)
src/display/core/Plugin.h (1)
  • Plugin (6-12)
src/display/plugins/WebUIPlugin.cpp (1)
web/src/pages/Home/ProcessControls.jsx (1)
  • settings (177-187)
src/display/core/Controller.cpp (1)
src/display/core/Controller.h (1)
  • pluginManager (134-134)
src/display/core/Settings.cpp (1)
web/src/pages/Settings/PluginCard.jsx (1)
  • homekitMode (17-17)
src/display/plugins/HomekitBridgePlugin.cpp (2)
src/display/plugins/HomekitBridgePlugin.h (3)
  • update (42-58)
  • HomekitBridgePlugin (61-95)
  • loop (65-73)
src/display/core/Controller.h (1)
  • pluginManager (134-134)
src/display/ui/default/DefaultUI.cpp (1)
src/display/core/Controller.h (1)
  • pluginManager (134-134)
web/src/pages/Settings/index.jsx (1)
web/src/pages/OTA/index.jsx (1)
  • formData (25-25)
src/display/plugins/HomekitBridgePlugin.h (3)
src/display/plugins/HomekitBridgePlugin.cpp (32)
  • GaggiMatePowerSwitch (8-10)
  • update (12-19)
  • update (12-12)
  • update (32-39)
  • update (32-32)
  • setState (21-23)
  • setState (21-21)
  • setState (41-43)
  • setState (41-41)
  • GaggiMateSteamSwitch (28-30)
  • GaggiMateHeatingSensor (48-50)
  • setHeatingState (52-60)
  • setHeatingState (52-52)
  • HomekitBridgePlugin (64-67)
  • setup (77-170)
  • setup (77-77)
  • loop (172-196)
  • loop (172-172)
  • getAction (69-69)
  • getAction (69-69)
  • getSwitch1State (70-70)
  • getSwitch1State (70-70)
  • getSwitch2State (71-71)
  • getSwitch2State (71-71)
  • hasAction (73-73)
  • hasAction (73-73)
  • clearAction (75-75)
  • clearAction (75-75)
  • handleHeatingStatus (213-220)
  • handleHeatingStatus (213-213)
  • handleBoilerStatus (200-210)
  • handleBoilerStatus (200-200)
src/display/core/Plugin.h (1)
  • Plugin (6-12)
src/display/core/Controller.h (1)
  • pluginManager (134-134)
🔇 Additional comments (20)
src/display/ui/default/DefaultUI.h (1)

72-73: LGTM - follows existing pattern for stability tracking.

The new prevIsTemperatureStable member correctly mirrors the existing isTemperatureStable pattern (Line 69) and enables detection of stability status changes for plugin event broadcasting.

Note: Both variables use int with boolean semantics and false initialization. This is an existing pattern in this file, though bool would be more type-safe. Not blocking since it matches the established convention.

src/display/ui/default/DefaultUI.cpp (1)

43-71: LGTM - Clean implementation of stability change detection.

The logic correctly:

  1. Captures the previous stability state before recalculation
  2. Triggers the event only when stability status actually changes
  3. Passes the current stability state to subscribers

This enables the HomekitBridgePlugin to react to heating status changes.

web/src/pages/Settings/index.jsx (1)

220-221: LGTM - correct handling of homekitMode in form submission.

The default value of 0 correctly maps to HOMEKIT_MODE_DISABLED when the value is undefined.

src/display/plugins/HomekitThermostatPlugin.h (1)

30-32: LGTM - class renamed consistently.

The rename from HomekitPlugin to HomekitThermostatPlugin correctly reflects the mode-based architecture and aligns with the new HomekitBridgePlugin.

src/display/core/Settings.h (2)

13-17: LGTM - well-defined enum for HomeKit modes.

The explicit values (0, 1, 2) are appropriate for serialization/persistence. The enum names are clear and self-documenting.


148-149: LGTM - convenient helper method.

The isHomekitEnabled() helper provides a clean abstraction for code that only needs to check if HomeKit is active, regardless of mode.

src/display/plugins/WebUIPlugin.cpp (2)

456-457: LGTM - correctly uses new Settings API.

The handler now properly calls setHomekitMode(int) to update the HomeKit mode setting.


574-574: Breaking API change: homekit boolean replaced with homekitMode integer.

The response now returns homekitMode (integer: 0, 1, or 2) instead of the previous homekit boolean. This is a breaking change for any external clients or automations consuming this API.

Please confirm this breaking change is acceptable and document it in release notes. External integrations reading the settings API will need to update their parsing logic.

web/src/pages/Settings/PluginCard.jsx (2)

16-37: LGTM - correct state synchronization for HomeKit mode.

The implementation correctly:

  1. Derives initial state from homekitMode
  2. Syncs panel open state when mode changes externally
  3. Defaults to Thermostat mode (1) when toggling on from disabled

The toggle-on default to mode 1 is reasonable. If you want to preserve the user's last-selected mode, you'd need additional state management.


161-231: LGTM - well-structured HomeKit mode selection UI.

The expanded panel provides:

  • Clear mode selection buttons with visual feedback
  • Mode-specific descriptions
  • Troubleshooting guidance
  • Pairing instructions with visual

The UI is accessible with semantic HTML and appropriate labels.

src/display/core/Settings.cpp (3)

27-46: Migration logic is correctly documented but permanently disabled.

The commented block clearly explains the read-only mode limitation. Line 27 already unconditionally reads from "hkm", so users with the old "hk" key will get the default HOMEKIT_MODE_DISABLED rather than a migrated value.

If automatic migration is important, you could implement it once at the start of doSave() (which opens preferences in write mode), or accept that users upgrading must reconfigure HomeKit.


263-271: LGTM - Good use of clamping and conditional save.

The setter correctly clamps input to valid range and avoids unnecessary writes when the value hasn't changed.


513-513: LGTM - Persistence key updated consistently.

The save logic correctly uses "hkm" for the integer mode value, matching the load logic.

src/display/plugins/HomekitThermostatPlugin.cpp (2)

1-1: LGTM - Include updated for renamed header.


45-102: LGTM - Clean rename with unchanged behavior.

The class rename from HomekitPlugin to HomekitThermostatPlugin aligns with the new mode-based architecture while preserving the existing thermostat functionality.

src/display/plugins/HomekitBridgePlugin.h (1)

61-95: LGTM - Well-structured plugin class.

The class interface is clean, with proper separation between action query methods and internal state. The getName() method helps with debugging/logging.

src/display/plugins/HomekitBridgePlugin.cpp (4)

52-60: LGTM - Clear mapping of heating state to HomeKit contact sensor.

The logic correctly maps heating (unstable) to "open/detected" and ready (stable) to "closed/not detected", with helpful comments explaining the color semantics.


133-157: LGTM - HomeSpan bridge setup with multiple accessories.

The accessory chain is correctly structured with a bridge accessory followed by named device accessories. The callback is properly shared across switches.


213-219: LGTM - Heating status event handler.

Correctly maps stability flag to heating sensor state.


185-193: The deactivate() call is necessary and correctly placed.

The deactivate() method cleans up any active process and triggers proper end events (brew:end, grind:end, process:end). The setMode(MODE_BREW) method only changes the mode state and triggers a mode change event—it does not handle process cleanup. Without deactivate(), an active process would remain in memory. This pattern is consistent with the existing deactivateStandby() helper method, which also calls deactivate() before setMode(MODE_BREW). The difference between SWITCH_1_TOGGLE and SWITCH_2_TOGGLE is the order of operations (setMode→deactivate vs deactivate→setMode), not whether deactivate() is needed.

Reinserted commented-out line that has nothing to do with this pull request
@cla-bot
Copy link

cla-bot bot commented Dec 17, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

comment has been added back
@cla-bot
Copy link

cla-bot bot commented Dec 17, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/display/core/Settings.cpp (2)

5-5: Remove unused include.

The esp32-hal-log.h header is only used in the commented-out migration logic. Since the migration code is inactive, this include is unnecessary.

Apply this diff:

-#include <esp32-hal-log.h>

263-271: LGTM: Setter correctly implements mode validation and conditional save.

The implementation properly clamps the mode to valid range and only triggers a save when the value actually changes, avoiding unnecessary writes.

Optional: The comment on line 266 could be more descriptive:

-    // check save
+    // Only save if the value changed
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0bed35 and 8567e3a.

📒 Files selected for processing (1)
  • src/display/core/Settings.cpp (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/display/core/Settings.cpp (1)
web/src/pages/Settings/PluginCard.jsx (1)
  • homekitMode (17-17)
🔇 Additional comments (2)
src/display/core/Settings.cpp (2)

27-27: LGTM: Loading logic correctly migrated.

The change from boolean to integer mode is implemented correctly with an appropriate default value.


513-513: LGTM: Save logic correctly migrated.

The change from boolean to integer persistence is implemented correctly and consistent with the loading logic.

Fixed nitpick "The function body is not properly indented relative to the function declaration."
@cla-bot
Copy link

cla-bot bot commented Dec 17, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

@cla-bot
Copy link

cla-bot bot commented Dec 17, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/display/plugins/HomekitBridgePlugin.cpp (2)

85-87: Document or make configurable the hardcoded HomeKit pairing code.

The pairing code "46637726" is hardcoded. If this is a shared default for all GaggiMate devices, consider documenting the security implications or making it configurable per device for better security.


125-129: Inconsistent event naming convention.

Event names use different styles: "BOILER_STATUS" (uppercase) versus "boiler:heating:stable" (lowercase with colons). Consider standardizing to a single convention for maintainability.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9652500 and dee3302.

📒 Files selected for processing (2)
  • src/display/plugins/HomekitBridgePlugin.cpp (1 hunks)
  • src/display/plugins/HomekitBridgePlugin.h (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/display/plugins/HomekitBridgePlugin.cpp (2)
src/display/plugins/HomekitBridgePlugin.h (2)
  • update (43-60)
  • loop (68-73)
src/display/core/Controller.cpp (4)
  • setup (34-99)
  • setup (34-34)
  • loop (266-349)
  • loop (266-266)
src/display/plugins/HomekitBridgePlugin.h (4)
src/display/plugins/HomekitBridgePlugin.cpp (24)
  • GaggiMatePowerSwitch (9-11)
  • update (13-20)
  • update (13-13)
  • update (32-39)
  • update (32-32)
  • setState (22-24)
  • setState (22-22)
  • setState (41-43)
  • setState (41-41)
  • GaggiMateSteamSwitch (28-30)
  • GaggiMateHeatingSensor (47-50)
  • setStability (52-60)
  • setStability (52-52)
  • HomekitBridgePlugin (65-67)
  • setup (69-131)
  • setup (69-69)
  • loop (133-157)
  • loop (133-133)
  • clearAction (159-162)
  • clearAction (159-159)
  • handleHeatingStatus (176-182)
  • handleHeatingStatus (176-176)
  • handleBoilerStatus (166-174)
  • handleBoilerStatus (166-166)
src/display/core/Plugin.h (1)
  • Plugin (6-12)
src/display/core/Controller.cpp (4)
  • setup (34-99)
  • setup (34-34)
  • loop (266-349)
  • loop (266-266)
src/display/core/Controller.h (1)
  • pluginManager (134-134)
🔇 Additional comments (9)
src/display/plugins/HomekitBridgePlugin.cpp (5)

72-83: LGTM! Thread safety improved with atomic operations.

The callback now uses atomic .store() operations for all shared state variables, properly addressing the race condition flagged in the previous review.


91-120: LGTM! Proper HomeSpan accessory setup.

The bridge and accessory initialization follows HomeSpan's declarative API pattern correctly, with custom accessories properly stored for later reference.


166-182: LGTM! Event handlers are well-structured.

Both event handlers properly check for null pointers and correctly map event data to accessory states.


147-154: The code asymmetry is intentional and follows established patterns in the codebase.

The deactivate() call before setMode(MODE_BREW) is not an oversight. Throughout the codebase, all transitions to MODE_BREW consistently call deactivate() first (see Controller.cpp:645, 743-744, 776-777 and ui_events.cpp:35-36, 53-54, 68-69), while MODE_STEAM transitions use setMode(MODE_STEAM) directly. This is an intentional design: deactivate() stops the current process and triggers cleanup events ("controller:brew:end", "controller:process:end"), which is necessary before starting a new brew but not required for steam mode.


8-11: No destructors needed. HomeSpan automatically registers instantiated Characteristics and adds them to the HomeSpan HAP Database, and creating Characteristics with new is the documented pattern for HomeSpan. The framework manages the lifecycle of these objects. This code follows HomeSpan best practices.

Likely an incorrect or invalid review comment.

src/display/plugins/HomekitBridgePlugin.h (4)

1-14: LGTM! Proper header setup with necessary includes.

The header guards and includes are correctly structured, including <atomic> for the atomic member variables.


16-23: LGTM! Clean enum and typedef declarations.

Using enum class provides type safety, and the std::function typedef makes the callback signature clear and maintainable.


85-88: LGTM! Proper atomic variable declarations for thread safety.

The shared state variables are correctly declared as std::atomic types with appropriate initializers, fully resolving the race condition concerns from the previous review.


28-60: Add virtual destructors to HomeSpan-derived Service classes.

These derived classes (GaggiMatePowerSwitch, GaggiMateSteamSwitch, GaggiMateHeatingSensor) inherit from HomeSpan base classes that have virtual methods. Although HomeSpan appears to manage object lifecycle through its framework, declaring virtual destructors follows C++ best practices for polymorphic base classes and prevents potential undefined behavior if these objects are ever deleted through base class pointers.

Fixed comment: Thread-Safe variables (Atomic), fixing race condition
@cla-bot
Copy link

cla-bot bot commented Dec 17, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/display/plugins/HomekitBridgePlugin.h (1)

27-49: Consider consolidating duplicate switch classes.

GaggiMatePowerSwitch and GaggiMateSteamSwitch share identical structure and differ only in the HomekitAction passed to the callback. Consider refactoring into a single parameterized class to reduce duplication and improve maintainability.

Example approach:

class GaggiMateSwitch : public Service::Switch {
  public:
    GaggiMateSwitch(bridge_callback_t callback, HomekitAction action);
    boolean update() override;
    void setState(bool active);

  private:
    bridge_callback_t callback;
    HomekitAction action;
    SpanCharacteristic *power;
};
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dee3302 and c1644ea.

📒 Files selected for processing (1)
  • src/display/plugins/HomekitBridgePlugin.h (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/display/plugins/HomekitBridgePlugin.h (1)
src/display/plugins/HomekitBridgePlugin.cpp (24)
  • GaggiMatePowerSwitch (9-11)
  • update (13-20)
  • update (13-13)
  • update (32-39)
  • update (32-32)
  • setState (22-24)
  • setState (22-22)
  • setState (41-43)
  • setState (41-41)
  • GaggiMateSteamSwitch (28-30)
  • GaggiMateHeatingSensor (47-50)
  • setStability (52-60)
  • setStability (52-52)
  • HomekitBridgePlugin (65-67)
  • setup (69-131)
  • setup (69-69)
  • loop (133-157)
  • loop (133-133)
  • clearAction (159-162)
  • clearAction (159-159)
  • handleHeatingStatus (176-182)
  • handleHeatingStatus (176-176)
  • handleBoilerStatus (166-174)
  • handleBoilerStatus (166-166)
🔇 Additional comments (5)
src/display/plugins/HomekitBridgePlugin.h (5)

1-14: LGTM! Clean header structure.

The includes are appropriate for the HomeKit bridge functionality, and the header guards follow standard conventions.


16-23: LGTM! Clear action enum and callback type.

The enum class provides type safety, and the callback signature is well-defined for the bridge pattern.


52-60: LGTM! Clear sensor interface with good documentation.

The setStability method is well-documented with its boolean mapping, making the interface intuitive.


84-88: LGTM! Proper thread-safety with atomics.

The use of std::atomic for lastAction, state flags, and actionRequired correctly addresses race conditions between HomeSpan callbacks (which run on HomeSpan's thread) and the main loop() execution.


64-95: LGTM! Well-structured plugin class.

The plugin follows the Plugin interface correctly, uses appropriate event handlers, and maintains proper separation of concerns. The null-initialized pointers align with HomeSpan's lifecycle management patterns.

@modsmthng modsmthng marked this pull request as draft December 17, 2025 23:05
Synchronization problem with the heating status caused by the use of atomic has been fixed.
Atomic is now only used for the switches, but not for the heating status, because Atomics was introduced to prevent race conditions when HomeKit (a separate process/task) attempts to control the GaggiMate controller.
However, the data flow is reversed for the heating status:
- Source: The controller triggers an event (boiler:heating:stable).
- Target: The HomeKit plugin receives the event and writes the value to the HomeSpan sensor (heatingSensor->setStability).
Since HomeSpan is programmed to be thread-safe internally and the setVal(value, true) method ensures that the value is copied safely to the HomeKit memory, no additional atomic is necessary here. There is no “decision loop” that could be interrupted.

In addition, homespan classes have been cleaned up again so that Apple Home accepts the devices without any problems.
@cla-bot
Copy link

cla-bot bot commented Dec 18, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

@modsmthng modsmthng marked this pull request as ready for review December 18, 2025 11:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1644ea and 45085f9.

📒 Files selected for processing (1)
  • src/display/plugins/HomekitBridgePlugin.cpp (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/display/plugins/HomekitBridgePlugin.cpp (1)
src/display/plugins/HomekitBridgePlugin.h (2)
  • update (43-60)
  • loop (68-73)
🔇 Additional comments (7)
src/display/plugins/HomekitBridgePlugin.cpp (7)

8-25: LGTM: Power switch implementation is clean.

The callback pattern and state management logic are correct. The null check in setState() is a good defensive practice.


27-44: LGTM: Steam switch follows the same clean pattern.

Consistent implementation with the power switch. No concerns.


46-56: LGTM: Heating sensor mapping is logical.

The stable-to-closed mapping (1 = ready, 0 = heating) is intuitive and correctly implemented. The value-check before updating avoids unnecessary HomeKit notifications.


61-67: LGTM: Constructor properly initializes atomic state.

The use of std::move for the WiFi credentials and explicit atomic initialization are both correct.


142-145: LGTM: Action clearing is correctly scoped.

Appropriately resets only the action flag and type, while preserving the switch state atomics which represent the current state rather than pending actions.


147-151: LGTM: Boiler status synchronization is correct.

The logic properly maps the boiler state to HomeKit accessories:

  • Power switch reflects the inverse of standby status
  • Steam switch reflects steam mode status

Null checks add appropriate safety.


153-158: LGTM: Heating status handler is straightforward.

The direct transfer of stability state to the sensor is clean. The comment on line 155 helpfully documents the thread-safety assumption.

@modsmthng modsmthng marked this pull request as draft December 18, 2025 13:44
@cla-bot
Copy link

cla-bot bot commented Dec 18, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Author

@modsmthng modsmthng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An automatic migration logic for transferring existing Homekit plugin settings has been implemented.
The migration works on the controller side, but sometimes the controller needs to be restarted or sailed through the two integrations once for Apple Home to update the devices.

…e integration of the devices has also been simplified again to avoid connection problems with Homekit.
@cla-bot
Copy link

cla-bot bot commented Dec 19, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

…op-down menu, and an info text about the integration added, as with the other plugins.
@cla-bot
Copy link

cla-bot bot commented Dec 19, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

@modsmthng modsmthng marked this pull request as ready for review December 19, 2025 22:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
web/src/pages/Settings/PluginCard.jsx (1)

207-207: Consider improving clarity of heating sensor description.

The description "red / heating = closed, green / ready = open" maps visual states (red/green) to contact sensor states (closed/open). While accurate, this mapping may confuse users unfamiliar with HomeKit's ContactSensor semantics. Consider adding a brief explanation or simplifying the language.

💡 Suggested clarification
-                    <li>Sensor that reflects the heating status (red / heating = closed, green / ready = open)</li>
+                    <li>Sensor that reflects the heating status (shows red while heating, green when ready)</li>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45085f9 and 19c931d.

📒 Files selected for processing (4)
  • src/display/core/Settings.cpp (4 hunks)
  • src/display/plugins/HomekitBridgePlugin.cpp (1 hunks)
  • src/display/plugins/HomekitBridgePlugin.h (1 hunks)
  • web/src/pages/Settings/PluginCard.jsx (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.
📚 Learning: 2025-12-18T12:44:54.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.

Applied to files:

  • src/display/plugins/HomekitBridgePlugin.cpp
  • web/src/pages/Settings/PluginCard.jsx
  • src/display/plugins/HomekitBridgePlugin.h
📚 Learning: 2025-12-18T12:44:54.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: The HomeSpan library handles connection retries internally, so explicit error handling for initialization failures in HomeKit plugins is not required in the GaggiMate codebase.

Applied to files:

  • src/display/plugins/HomekitBridgePlugin.cpp
  • src/display/plugins/HomekitBridgePlugin.h
📚 Learning: 2025-12-18T12:44:54.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: Do not add or rely on WiFi credential validation inside HomekitBridgePlugin.cpp. This project validates HomeKit plugin credentials in the WebUI before passing them to the plugin; reviews should assume upstream validation has occurred and avoid duplicating validation in the plugin unless there is a concrete, tested reason to revalidate at the plugin boundary.

Applied to files:

  • src/display/plugins/HomekitBridgePlugin.cpp
📚 Learning: 2025-12-18T12:44:54.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase, WiFi credentials for HomeKit plugins are managed and validated by the WebUI before being passed to the HomeKit plugin, so additional validation in the plugin is unnecessary.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
  • src/display/plugins/HomekitBridgePlugin.h
  • src/display/core/Settings.cpp
🧬 Code graph analysis (4)
src/display/plugins/HomekitBridgePlugin.cpp (1)
src/display/plugins/HomekitBridgePlugin.h (2)
  • update (43-60)
  • loop (68-73)
web/src/pages/Settings/PluginCard.jsx (1)
web/src/pages/Settings/index.jsx (2)
  • formData (23-23)
  • updateHomekitMode (165-170)
src/display/plugins/HomekitBridgePlugin.h (1)
src/display/plugins/HomekitBridgePlugin.cpp (20)
  • GaggiMatePowerSwitch (11-14)
  • update (16-23)
  • update (16-16)
  • update (35-42)
  • update (35-35)
  • setState (25-27)
  • setState (25-25)
  • setState (44-46)
  • setState (44-44)
  • GaggiMateSteamSwitch (30-33)
  • GaggiMateHeatingSensor (49-51)
  • setStability (53-59)
  • setStability (53-53)
  • HomekitBridgePlugin (64-77)
  • setup (79-141)
  • setup (79-79)
  • loop (143-186)
  • loop (143-143)
  • clearAction (188-191)
  • clearAction (188-188)
src/display/core/Settings.cpp (1)
src/display/core/Settings.h (1)
  • Settings (55-271)
🔇 Additional comments (6)
src/display/plugins/HomekitBridgePlugin.cpp (2)

79-141: LGTM!

The setup method correctly initializes HomeSpan, creates accessories, and registers event handlers. Thread safety is properly handled using atomic stores in callbacks, and memory management follows HomeSpan's ownership model. WiFi credential validation is handled upstream by the WebUI as per the established pattern.

Based on learnings, HomeSpan acts as a secondary, non-critical service, and initialization failures do not affect boiler safety.


143-186: LGTM!

The loop correctly synchronizes state between HomeKit and the controller using atomic exchange operations. The SWITCH_2_TOGGLE asymmetry (entering steam mode without explicit deactivate) is intentional, as the controller's state machine handles the transition internally.

src/display/core/Settings.cpp (2)

8-31: LGTM! Migration logic properly implemented.

The two-phase approach (read-write for migration, then read-only for initialization) correctly handles the upgrade path from boolean hk to integer hkm. The migration preserves existing user settings by mapping true → HOMEKIT_MODE_THERMOSTAT and false → HOMEKIT_MODE_DISABLED, and properly removes the obsolete key.

This addresses the previous review concern about enabling migration for existing users.


270-278: LGTM!

The setter correctly clamps the mode to the valid range and only triggers a save when the value actually changes, consistent with other setters in this file.

src/display/plugins/HomekitBridgePlugin.h (2)

1-23: LGTM!

The header structure is well-organized with proper include guards, necessary dependencies, and type-safe enums. The callback typedef clearly documents the interface between HomeKit and the controller.


85-99: LGTM! Thread-safe design with atomic variables.

The atomic state variables correctly address the race condition concerns identified in previous reviews. All shared state accessed by both event callbacks and the main loop is properly protected using std::atomic.

Added: "My Homekit stopped working" as "Reset the HomeKit plugin via the console" from official GaggiMate troubleshooting web page
@cla-bot
Copy link

cla-bot bot commented Dec 22, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
web/src/pages/Settings/PluginCard.jsx (2)

29-37: Consider preserving the last selected mode when re-enabling.

Currently, when toggling HomeKit off and then back on, the handler always defaults to mode 1 (Thermostat) regardless of the previous selection. If a user had selected Bridge mode (2), toggled off, then toggled back on, they would need to reselect Bridge mode.

Consider storing the last active mode (1 or 2) before setting it to 0, so re-enabling restores the previous selection rather than always defaulting to Thermostat.

🔎 Potential improvement
+  const lastModeRef = useRef(homekitMode > 0 ? homekitMode : 1);
+
+  useEffect(() => {
+    if (homekitMode > 0) {
+      lastModeRef.current = homekitMode;
+    }
+  }, [homekitMode]);
+
   const handleHomekitToggle = () => {
     const nextState = !isHomekitOpen;
     setIsHomekitOpen(nextState);
     if (!nextState) {
       updateHomekitMode(0);
-    } else if (homekitMode === 0) {
-      updateHomekitMode(1);
+    } else if (homekitMode === 0) {
+      updateHomekitMode(lastModeRef.current);
     }
   };

Note: This would require importing useRef from preact/hooks.


180-195: Consider adding accessibility attributes to mode selector buttons.

The mode selector buttons lack aria-pressed or aria-checked attributes to communicate the active state to screen readers. While the visual styling conveys the active mode to sighted users, assistive technology users would benefit from explicit ARIA attributes.

🔎 Suggested improvement
                 <button
                   type='button'
                   className={`btn btn-sm ${homekitMode === 1 ? 'btn-primary' : 'btn-outline'}`}
                   onClick={() => updateHomekitMode(1)}
+                  aria-pressed={homekitMode === 1}
+                  aria-label='Thermostat mode integration'
                 >
                   Thermostat
                 </button>
                 <button
                   type='button'
                   className={`btn btn-sm ${homekitMode === 2 ? 'btn-primary' : 'btn-outline'}`}
                   onClick={() => updateHomekitMode(2)}
+                  aria-pressed={homekitMode === 2}
+                  aria-label='Bridge mode integration with multiple accessories'
                 >
                   Bridge
                 </button>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19c931d and 2563b2d.

📒 Files selected for processing (1)
  • web/src/pages/Settings/PluginCard.jsx
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:37.672Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase, WiFi credentials for HomeKit plugins are managed and validated by the WebUI before being passed to the HomeKit plugin, so additional validation in the plugin is unnecessary.
📚 Learning: 2025-08-14T20:05:55.096Z
Learnt from: igadmile
Repo: jniebuhr/gaggimate PR: 360
File: web/src/pages/Home/ProcessControls.jsx:106-114
Timestamp: 2025-08-14T20:05:55.096Z
Learning: In React components that use WebSocket-based state updates, there can be race conditions when using useEffect to watch for state transitions. For example, when a flush is started, local state (`isFlushing`) is updated immediately, but WebSocket-driven state (`active`) is updated asynchronously after the message is received, creating timing issues if useEffect tries to act on both states simultaneously.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
📚 Learning: 2025-12-19T22:16:37.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:37.672Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
📚 Learning: 2025-12-18T12:44:54.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
🧬 Code graph analysis (1)
web/src/pages/Settings/PluginCard.jsx (1)
web/src/pages/Settings/index.jsx (2)
  • formData (23-23)
  • updateHomekitMode (165-170)
🔇 Additional comments (4)
web/src/pages/Settings/PluginCard.jsx (4)

4-4: LGTM!

The new imports and updateHomekitMode prop are correctly added to support the HomeKit mode selection functionality.

Also applies to: 9-9


17-27: LGTM!

The state initialization and sync logic are correct. The one-way sync in the useEffect (only opening when homekitMode > 0, not closing when it becomes 0) was previously reviewed and confirmed as intentional for migration logic, with user-initiated toggles handled separately by handleHomekitToggle.


168-176: Excellent migration guidance!

The migration warning provides clear, proactive instructions to prevent common issues during the update. Highlighting not to remove the device and directing users to troubleshooting is great UX.


231-282: Excellent troubleshooting documentation!

The collapsible troubleshooting section is comprehensive and well-structured with:

  • Clear escalation path from simple restarts to full resets
  • Proper formatting of console commands with <code> tags
  • Appropriate severity styling for critical warnings
  • Step-by-step instructions that are easy to follow

This will significantly reduce user frustration and support requests.

@cla-bot
Copy link

cla-bot bot commented Dec 22, 2025

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
web/src/pages/Settings/PluginCard.jsx (2)

180-196: Enhance accessibility for mode selector buttons.

The mode selector buttons function correctly but could benefit from accessibility attributes to announce the active state to screen readers. Similar to the day-of-week buttons in the Auto Wakeup section (lines 76-111), consider adding role and aria-pressed attributes.

🔎 Suggested accessibility enhancement
              <div className='grid grid-cols-2 gap-2'>
                <button
                  type='button'
                  className={`btn btn-sm ${homekitMode === 1 ? 'btn-primary' : 'btn-outline'}`}
                  onClick={() => updateHomekitMode(1)}
+                 role='radio'
+                 aria-pressed={homekitMode === 1}
+                 aria-label='Thermostat mode'
                >
                  Thermostat
                </button>
                <button
                  type='button'
                  className={`btn btn-sm ${homekitMode === 2 ? 'btn-primary' : 'btn-outline'}`}
                  onClick={() => updateHomekitMode(2)}
+                 role='radio'
+                 aria-pressed={homekitMode === 2}
+                 aria-label='Bridge mode'
                >
                  Bridge
                </button>
              </div>

217-222: Consider Tailwind classes for style consistency.

The inline maxHeight style works but breaks the Tailwind-first pattern used throughout the codebase. For consistency, consider using Tailwind's arbitrary value syntax.

🔎 Suggested refactor
              <img 
                src={homekitImage} 
-               alt='Homekit Setup Code' 
+               alt='HomeKit pairing QR code for GaggiMate setup' 
                className='h-auto w-auto max-w-full object-contain'
-               style={{ maxHeight: '150px' }} 
+               className='h-auto w-auto max-w-full object-contain max-h-[150px]'
              />

Note: Also improves the alt text for better screen reader context.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2563b2d and f5bc382.

📒 Files selected for processing (1)
  • web/src/pages/Settings/PluginCard.jsx
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:37.672Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.
📚 Learning: 2025-08-14T20:05:55.096Z
Learnt from: igadmile
Repo: jniebuhr/gaggimate PR: 360
File: web/src/pages/Home/ProcessControls.jsx:106-114
Timestamp: 2025-08-14T20:05:55.096Z
Learning: In React components that use WebSocket-based state updates, there can be race conditions when using useEffect to watch for state transitions. For example, when a flush is started, local state (`isFlushing`) is updated immediately, but WebSocket-driven state (`active`) is updated asynchronously after the message is received, creating timing issues if useEffect tries to act on both states simultaneously.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
📚 Learning: 2025-12-19T22:16:37.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:37.672Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
📚 Learning: 2025-12-18T12:44:54.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
🧬 Code graph analysis (1)
web/src/pages/Settings/PluginCard.jsx (1)
web/src/pages/Settings/index.jsx (2)
  • formData (23-23)
  • updateHomekitMode (165-170)
🔇 Additional comments (2)
web/src/pages/Settings/PluginCard.jsx (2)

17-37: LGTM! State management logic is sound.

The local state and toggle handler work correctly together:

  • isHomekitOpen tracks UI expansion state
  • handleHomekitToggle ensures consistent bidirectional sync between UI and mode
  • The useEffect one-way sync (opening when homekitMode > 0) aligns with the migration logic you described in previous comments

The implementation correctly prevents mode conflicts and handles the disable/enable flow.


149-285: Excellent UX implementation for HomeKit integration modes.

The expanded HomeKit UI is comprehensive and user-friendly:

Migration warning alerts users upfront about potential connection issues
Clear mode descriptions explain Thermostat vs Bridge functionality
Progressive troubleshooting guides users from simple (restart) to advanced (console reset) to last-resort (re-flash) solutions
Appropriate warnings for destructive actions (re-flash styled with text-error)

The implementation handles the migration path thoughtfully and provides users with the information they need to troubleshoot independently.

…d UI

- Added settings to toggle Power, Steam, and Sensor in Bridge Mode independently.
- Assigned static Accessory IDs to prevent HomeKit cache conflicts when toggling devices.
- Updated WebUI with new toggles
- WebUI descriptions shortened and adapted.
- Fixed WebUI form handling for new boolean values.
- Comments on the HomekitBridgePlugin have been slightly adjusted and corrected with the help of AI (and checked by me).
@cla-bot
Copy link

cla-bot bot commented Jan 6, 2026

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/display/core/Settings.cpp (1)

284-297: Consider adding change-check to per-device setters.

Unlike setHomekitMode, the setHk*Enabled setters always call save() even if the value hasn't changed. This could cause unnecessary flash writes when toggling settings repeatedly.

🔎 Optional optimization
 void Settings::setHkPowerEnabled(bool enabled) {
+    if (hkPowerEnabled == enabled) return;
     hkPowerEnabled = enabled;
     save();
 }

 void Settings::setHkSteamEnabled(bool enabled) {
+    if (hkSteamEnabled == enabled) return;
     hkSteamEnabled = enabled;
     save();
 }

 void Settings::setHkSensorEnabled(bool enabled) {
+    if (hkSensorEnabled == enabled) return;
     hkSensorEnabled = enabled;
     save();
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5bc382 and 0654d30.

📒 Files selected for processing (6)
  • src/display/core/Settings.cpp
  • src/display/core/Settings.h
  • src/display/plugins/HomekitBridgePlugin.cpp
  • src/display/plugins/WebUIPlugin.cpp
  • web/src/pages/Settings/PluginCard.jsx
  • web/src/pages/Settings/index.jsx
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:46.304Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: In the GaggiMate codebase, WiFi credentials for HomeKit plugins are managed and validated by the WebUI before being passed to the HomeKit plugin, so additional validation in the plugin is unnecessary.
📚 Learning: 2025-12-18T12:45:27.712Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: In the GaggiMate codebase, WiFi credentials for HomeKit plugins are managed and validated by the WebUI before being passed to the HomeKit plugin, so additional validation in the plugin is unnecessary.

Applied to files:

  • src/display/plugins/WebUIPlugin.cpp
  • src/display/core/Settings.cpp
📚 Learning: 2025-12-19T22:16:46.304Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:46.304Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.

Applied to files:

  • src/display/core/Settings.cpp
  • web/src/pages/Settings/PluginCard.jsx
  • src/display/core/Settings.h
📚 Learning: 2025-12-18T12:45:27.712Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.

Applied to files:

  • src/display/plugins/HomekitBridgePlugin.cpp
  • web/src/pages/Settings/PluginCard.jsx
📚 Learning: 2025-12-19T22:16:37.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:37.672Z
Learning: In src/display/plugins/HomekitBridgePlugin.cpp, the GaggiMateHeatingSensor maps the stable (ready) state to ContactSensorState value 1 (Open) so Apple Home highlights the sensor in the overview. This is a design decision to improve visibility for users when the machine is ready. Reviewers should ensure this mapping is intentional, well-documented in code comments, and covered by tests that verify the correct HomeKit status is exposed for the ready state. If similar sensors exist, consider documenting a consistent policy for what HomeKit states correspond to readiness, and avoid duplicated logic that could drift over time.

Applied to files:

  • src/display/plugins/HomekitBridgePlugin.cpp
📚 Learning: 2025-12-18T12:44:54.672Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:44:54.672Z
Learning: Do not add or rely on WiFi credential validation inside HomekitBridgePlugin.cpp. This project validates HomeKit plugin credentials in the WebUI before passing them to the plugin; reviews should assume upstream validation has occurred and avoid duplicating validation in the plugin unless there is a concrete, tested reason to revalidate at the plugin boundary.

Applied to files:

  • src/display/plugins/HomekitBridgePlugin.cpp
📚 Learning: 2025-12-18T12:45:27.712Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: The HomeSpan library handles connection retries internally, so explicit error handling for initialization failures in HomeKit plugins is not required in the GaggiMate codebase.

Applied to files:

  • src/display/plugins/HomekitBridgePlugin.cpp
📚 Learning: 2025-08-14T20:05:55.096Z
Learnt from: igadmile
Repo: jniebuhr/gaggimate PR: 360
File: web/src/pages/Home/ProcessControls.jsx:106-114
Timestamp: 2025-08-14T20:05:55.096Z
Learning: In React components that use WebSocket-based state updates, there can be race conditions when using useEffect to watch for state transitions. For example, when a flush is started, local state (`isFlushing`) is updated immediately, but WebSocket-driven state (`active`) is updated asynchronously after the message is received, creating timing issues if useEffect tries to act on both states simultaneously.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
🧬 Code graph analysis (5)
src/display/plugins/WebUIPlugin.cpp (1)
web/src/pages/Home/ProcessControls.jsx (1)
  • settings (177-187)
src/display/core/Settings.cpp (2)
src/display/core/Settings.h (1)
  • Settings (55-283)
web/src/pages/Settings/index.jsx (1)
  • Settings (20-986)
src/display/plugins/HomekitBridgePlugin.cpp (1)
src/display/plugins/HomekitBridgePlugin.h (2)
  • update (43-60)
  • loop (68-73)
web/src/pages/Settings/PluginCard.jsx (1)
web/src/pages/Settings/index.jsx (3)
  • formData (23-23)
  • updateHomekitMode (172-177)
  • onChange (105-160)
src/display/core/Settings.h (2)
web/src/pages/Settings/PluginCard.jsx (1)
  • homekitMode (17-17)
src/display/core/Settings.cpp (8)
  • setHomekitMode (275-282)
  • setHomekitMode (275-275)
  • setHkPowerEnabled (285-288)
  • setHkPowerEnabled (285-285)
  • setHkSteamEnabled (290-293)
  • setHkSteamEnabled (290-290)
  • setHkSensorEnabled (295-298)
  • setHkSensorEnabled (295-295)
🔇 Additional comments (19)
web/src/pages/Settings/PluginCard.jsx (3)

16-37: LGTM on HomeKit state management.

The local state management with isHomekitOpen and the sync effect is well-structured. The one-way sync (homekitMode > 0 → open) is intentional for migration, as the toggle handler ensures proper bidirectional updates during user interaction.


168-186: Mode selector implementation looks good.

The two-button selector correctly maps mode values (1=Thermostat, 2=Bridge) and uses updateHomekitMode to propagate changes upstream.


201-261: Accessory toggles correctly default to enabled.

The checked={formData.hkPowerEnabled !== false} pattern ensures new users get all accessories enabled by default, which aligns with the backend defaults (hkPowerEnabled = true in Settings.h).

src/display/plugins/WebUIPlugin.cpp (2)

456-462: HomeKit settings handling is correct.

The unconditional setHk*Enabled(request->hasArg(...)) pattern is appropriate for HTML checkboxes—when unchecked, the parameter is absent, so hasArg returns false, correctly disabling the feature.


579-582: API response includes all HomeKit fields.

The GET response correctly exposes homekitMode and the three per-device toggles, enabling the frontend to initialize state properly.

src/display/core/Settings.h (3)

13-17: Well-structured HomeKit mode enum.

The enum values are clear and well-documented. Using explicit integer values (0, 1, 2) ensures stable serialization and aligns with the mode selector in the UI.


85-89: Clean public API for HomeKit settings.

The getter/setter pairs follow the existing codebase patterns, and the isHomekitEnabled() convenience method simplifies conditional logic elsewhere.

Also applies to: 152-157


226-230: Sensible default values for HomeKit state.

Defaulting homekitMode to HOMEKIT_MODE_DISABLED and the per-device toggles to true is appropriate—when users enable HomeKit, all accessories will be available by default.

web/src/pages/Settings/index.jsx (3)

47-50: Consistent initialization of HomeKit toggles.

Defaulting to true when undefined matches the backend defaults in Settings.h, ensuring a smooth experience for new users enabling HomeKit.


118-123: Correct onChange handling for HomeKit fields.

The homekitMode is properly parsed as an integer, and the toggle fields use the standard boolean inversion pattern consistent with other checkboxes in this form.


172-177: HomeKit mode update and submission logic is correct.

The updateHomekitMode function correctly updates formData, and the submission fallback to 0 ensures disabled state is persisted when homekitMode is undefined.

Also applies to: 227-227

src/display/core/Settings.cpp (3)

8-31: Solid migration implementation.

The two-phase approach (read-write for migration, then read-only for standard init) is well-structured. The migration correctly maps the legacy boolean to the new integer mode and cleans up the obsolete key.


274-282: Safe mode setter with clamping.

The std::clamp ensures invalid values can't be persisted, and the change-check optimization avoids unnecessary flash writes.


540-545: Persistence includes all HomeKit state.

The doSave method correctly persists homekitMode and all three per-device toggles with consistent key naming.

src/display/plugins/HomekitBridgePlugin.cpp (5)

68-81: Proper atomic initialization.

All shared state variables are correctly initialized as atomics in the constructor, ensuring thread-safe access between HomeSpan callbacks and the main loop.


86-89: Conditional accessory creation is well-implemented.

Reading the enable flags from settings and conditionally creating accessories avoids HomeKit clutter. Using explicit static IDs (1-4) ensures stable accessory identification across restarts and configuration changes.

Also applies to: 118-146


55-61: Sensor state mapping is intentional per learnings.

The mapping of isStable ? 1 : 0 (stable=Open) is intentional to leverage Apple Home's UX where "open" sensors appear prominently in the overview. The comment could be updated to clarify this design choice.

Based on learnings, this is the correct behavior.


165-210: Thread-safe loop implementation.

The exchange(false) pattern for status/heating flags prevents missed updates and ensures atomic read-reset. Null checks before accessing powerSwitch, steamSwitch, and heatingSensor correctly handle partially-enabled configurations.


92-100: Event name and payload key are consistent. The event "boiler:heating:stable" is correctly emitted from DefaultUI.cpp with the payload key "isStable", matching the subscription and key access in HomekitBridgePlugin.cpp at lines 156–159.

@cla-bot
Copy link

cla-bot bot commented Jan 6, 2026

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

@cla-bot
Copy link

cla-bot bot commented Jan 11, 2026

We require contributors to sign our Contributor License Agreement, and we don't have yours on file. In order for us to review and merge your code, please contact @jniebuhr (mdwasp) on Discord to get yourself added.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @web/src/pages/Settings/PluginCard.jsx:
- Around line 23-27: The useEffect currently only opens the UI when homekitMode
> 0 but never closes it when homekitMode becomes 0; update the effect that
references homekitMode, setIsHomekitOpen to set false when homekitMode === 0
(e.g., setIsHomekitOpen(homekitMode > 0)), ensuring the effect depends on
homekitMode so local state is kept in sync with external form/state changes.
🧹 Nitpick comments (1)
web/src/pages/Settings/PluginCard.jsx (1)

171-184: Consider adding aria-pressed for accessibility parity.

The mode selector buttons indicate active state visually but lack aria-pressed attributes that the day-of-week buttons use (line 85). This would improve screen reader support.

Suggested accessibility improvement
                <button
                  type='button'
                  className={`btn btn-sm ${homekitMode === 1 ? 'btn-primary' : 'btn-outline'}`}
                  onClick={() => updateHomekitMode(1)}
+                 aria-pressed={homekitMode === 1}
                >
                  Thermostat
                </button>
                <button
                  type='button'
                  className={`btn btn-sm ${homekitMode === 2 ? 'btn-primary' : 'btn-outline'}`}
                  onClick={() => updateHomekitMode(2)}
+                 aria-pressed={homekitMode === 2}
                >
                  Bridge
                </button>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0654d30 and e8b36da.

📒 Files selected for processing (1)
  • web/src/pages/Settings/PluginCard.jsx
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:46.304Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: In the GaggiMate codebase, WiFi credentials for HomeKit plugins are managed and validated by the WebUI before being passed to the HomeKit plugin, so additional validation in the plugin is unnecessary.
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.
📚 Learning: 2025-08-14T20:05:55.096Z
Learnt from: igadmile
Repo: jniebuhr/gaggimate PR: 360
File: web/src/pages/Home/ProcessControls.jsx:106-114
Timestamp: 2025-08-14T20:05:55.096Z
Learning: In React components that use WebSocket-based state updates, there can be race conditions when using useEffect to watch for state transitions. For example, when a flush is started, local state (`isFlushing`) is updated immediately, but WebSocket-driven state (`active`) is updated asynchronously after the message is received, creating timing issues if useEffect tries to act on both states simultaneously.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
📚 Learning: 2025-12-19T22:16:46.304Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:53-59
Timestamp: 2025-12-19T22:16:46.304Z
Learning: In the GaggiMate HomeKit bridge plugin (src/display/plugins/HomekitBridgePlugin.cpp), the GaggiMateHeatingSensor intentionally maps stable (ready) state to ContactSensorState value 1 (Open) so that Apple Home displays the sensor prominently in the overview when the machine is ready. This is the correct behavior because Apple Home automatically highlights "open" devices, making the ready status more visible to users.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
📚 Learning: 2025-12-18T12:45:27.712Z
Learnt from: modsmthng
Repo: jniebuhr/gaggimate PR: 527
File: src/display/plugins/HomekitBridgePlugin.cpp:69-120
Timestamp: 2025-12-18T12:45:27.712Z
Learning: In the GaggiMate codebase (src/display/plugins/HomekitBridgePlugin.cpp), HomeSpan acts as a secondary, non-critical service that operates independently of core machine control. HomeSpan initialization failures do not affect boiler safety or operation.

Applied to files:

  • web/src/pages/Settings/PluginCard.jsx
🧬 Code graph analysis (1)
web/src/pages/Settings/PluginCard.jsx (1)
web/src/pages/Settings/index.jsx (3)
  • formData (23-23)
  • updateHomekitMode (172-177)
  • onChange (105-160)
🔇 Additional comments (2)
web/src/pages/Settings/PluginCard.jsx (2)

201-261: LGTM!

The accessory toggles are well-structured. The !== false pattern correctly defaults all accessories to enabled when entering Bridge mode, which is good UX for first-time setup. The labels and descriptions are clear.


268-335: Well-implemented troubleshooting section.

The collapsible troubleshooting section using <details>/<summary> is a clean approach that keeps advanced help available without cluttering the main UI. The image has appropriate alt text for accessibility.

@cla-bot cla-bot bot added the cla-signed label Jan 19, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/display/core/Controller.cpp (1)

45-62: Avoid double-creating DefaultUI (leak + stale state).
DefaultUI is allocated at Line 47 and again at Lines 60-62. The first instance is leaked and any state/listeners it held are discarded. Keep a single initialization (prefer the later one if UI depends on ProfileManager setup).

🛠️ Suggested fix (keep only the later init)
 `#ifndef` GAGGIMATE_HEADLESS
-    ui = new DefaultUI(this, driver, pluginManager);
     if (driver->supportsSDCard() && driver->installSDCard()) {
         sdcard = true;
         ESP_LOGI(LOG_TAG, "SD Card detected and mounted");
         ESP_LOGI(LOG_TAG, "Used: %lluMB, Capacity: %lluMB", SD_MMC.usedBytes() / 1024 / 1024, SD_MMC.cardSize() / 1024 / 1024);
     }
 `#endif`
🤖 Fix all issues with AI agents
In `@src/display/core/Controller.cpp`:
- Around line 64-72: The Bridge mode path currently omits registering the
mDNSPlugin, so when settings.getHomekitMode() == HOMEKIT_MODE_BRIDGE the device
won't be advertised via mDNS; update the control flow in Controller.cpp to
ensure pluginManager->registerPlugin(new mDNSPlugin()) is invoked for bridge
mode as well (either by registering mDNSPlugin unconditionally before/after the
HomekitBridgePlugin registration or by adding a registerPlugin call alongside
HomekitBridgePlugin), referencing the existing HomekitBridgePlugin and
mDNSPlugin instantiations to keep behavior consistent with the
thermostat/disabled branches.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 4, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant