homebridge-roborock-matter 3.4.18 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +49 -26
- package/config.schema.json +31 -0
- package/dist/action_switch_accessory.js +181 -0
- package/dist/action_switch_accessory.js.map +1 -0
- package/dist/matter_vacuum_accessory.js +103 -62
- package/dist/matter_vacuum_accessory.js.map +1 -1
- package/dist/platform.js +206 -12
- package/dist/platform.js.map +1 -1
- package/dist/settings.js +26 -2
- package/dist/settings.js.map +1 -1
- package/dist/timers.js +35 -0
- package/dist/timers.js.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -1
- package/homebridge-ui/public/index.html +40 -0
- package/homebridge-ui/public/index.js +95 -0
- package/homebridge-ui/public/styles.css +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.5.0
|
|
4
|
+
|
|
5
|
+
**Apple Home cannot send a Matter vacuum to its dock from an automation, so the plugin now offers a switch that can.** The measurement is pponce's, in [#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3): the commands all work from the tile and from Siri, but "send the vacuum to its dock" is not on Apple's list of automation actions for a Matter vacuum, and he moved that part of his setup to a HAP-based plugin rather than go without it. 3.4.19 stopped the README from promising what could not be delivered. This release delivers it.
|
|
6
|
+
|
|
7
|
+
Turning on **Add Home app switches for Dock, Pause and Find** publishes one plain HomeKit switch per robot per action you select — `Vicky Return to Dock`, `Vicky Pause`, `Vicky Find`. A switch is an automation action everywhere, so the schedule that could not reach the tile can reach the switch. Each one is momentary: it turns itself off again 1.5 s after it is pressed, because there is no docking state worth mirroring and a second state machine racing the same laggy Roborock snapshot is exactly what issues #4 and #12 were about.
|
|
8
|
+
|
|
9
|
+
**The press takes the existing command path rather than a second one.** It routes into the same `returnToDock` / `pauseCleaning` / `identifyVacuum` the Matter cluster handlers use, so it inherits the acknowledgement wait and timing log (#12), the decision to forward a command the cached snapshot claims is unnecessary (#4), the retry when Roborock times out while the robot is still cleaning, and the optimistic cluster write that moves the tile so a robot driving home does not read Ready. The log now names the surface that asked: `Sending Vicky back to dock from the Home switch.` next to `Sending Vicky back to dock from Matter.` — the first question when a schedule misfires is which one sent it.
|
|
10
|
+
|
|
11
|
+
Three things this had to get right that are not in the feature description:
|
|
12
|
+
|
|
13
|
+
- **The Matter-only sweep would have deleted them.** `discoverDevices()` has always unregistered every cached HAP accessory without looking at what it was, which was correct while this plugin registered none. A switch shipped against that rule would work until the first restart and then vanish out of every automation using it, while the log went on calling it a legacy accessory. The sweep now partitions on a context marker written into the accessory — not on its name, which is editable in the Home app.
|
|
14
|
+
- **They are registered under the real package name.** `PLUGIN_NAME` has never matched package.json, and Homebridge stores whatever it is given as the accessory's owning plugin. On restore it falls back to searching by dynamic platform name, which repairs the mismatch with an alarming log line — and throws when two plugins claim the same platform name, at which point the accessory is called orphaned and removed. Matter keeps its own cache and cannot be moved without forcing every user to re-pair, so the correct identifier is introduced for HAP only.
|
|
15
|
+
- **An empty device list does not remove anything.** The same trap `unregisterStaleMatterAccessories` documents: a failed startup arrives at discovery as "the account has no robots". Removing a switch because the config no longer asks for it is safe; removing one because the Roborock cloud had a bad minute is not.
|
|
16
|
+
|
|
17
|
+
Off by default, per robot per action, and the Find switch is only published for robots that report `find_me` at all. No re-pairing is needed to add or remove them — they are HomeKit accessories and arrive over the Homebridge bridge, which does mean a user who has only ever paired the Matter robot has to pair the bridge itself before they appear. The robot stays a Matter vacuum and is untouched.
|
|
18
|
+
|
|
19
|
+
`__tests__/action-switches-survive-the-legacy-sweep.test.js` enumerates the partition over context shapes rather than the two cases I happened to think of, `__tests__/action-switches-are-an-opt-in.test.js` covers the config and removal rules, and `__tests__/action-switch-press-uses-the-matter-command-path.test.js` pins that a press reaches Roborock through the shared path and is named apart from Matter in the log. Verified red: 4 of 8 fail with the old sweep restored, and the empty-device-list rule fails 1 of 17 with the guard removed.
|
|
20
|
+
|
|
21
|
+
**And the gap turns out to be narrower than this release was written to believe.** pponce went back into Shortcuts after the above was written and measured the rest of the list ([#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3)): **starting a clean is offered as an automation action — whole home or a chosen set of rooms — and so is stopping one that is already running.** Only return-to-dock is missing. So an Apple Home schedule could already do the two things schedules are mostly built for, and the switches are for the one thing it could not: ending a clean early and sending the robot home. The README said "whether it offers the other commands as actions … has not been verified here", which was true the day it was written and false the day after — the same defect as a promise nobody checked, pointed the other way, and more expensive here, because it sends a user off to install a second plugin for a job this one never blocked. The feature table also claimed Apple offers no automation action for Pause and Find either; nobody has measured those, and it no longer says so. `readme-claims-match-what-was-measured.test.js` now enumerates both directions — the dock claim must deny itself in its own words, the two measured-present actions must be stated, and pause/resume/trigger must stay qualified. **Verified red against this release's own README: 3 of 10 failed, exactly the three positive claims.**
|
|
22
|
+
|
|
23
|
+
## 3.4.19
|
|
24
|
+
|
|
25
|
+
**Two things the README told users were not what had been measured.** No behaviour changes in this release; both are wording, and wording is what people choose a plugin on.
|
|
26
|
+
|
|
27
|
+
The feature table promised control "from the Home app, Siri, or automations". Nobody had ever checked the last word, and a user has now measured it: Apple Home does **not** offer sending a Matter vacuum to its dock as an automation action, which is why he had to move that part of his setup to a HAP-based plugin. The table no longer claims it, and a new [Automations in Apple Home](README.md#automations-in-apple-home) section says exactly what is known — the commands all work from the tile and from Siri, one automation action has been measured absent, and the rest is unverified rather than promised.
|
|
28
|
+
|
|
29
|
+
The fault-reporting section also blamed the "Report faults in Apple Home" setting for a tile that got stuck on "Updating…". A controlled test on the same robot, with fault reporting and dock-fault escalation both switched on and a genuinely empty clean-water tank, has now shown the tile staying in Ready throughout: the wedge was a stale pairing from an earlier install, which the Troubleshooting section already explained. The correction is written into the section rather than quietly deleted, because someone may have left the feature off on the strength of it. The same test also confirms the main finding more strongly than before — the fault was published beside a full Matter **Error** state this time, not just beside Charging, and Apple Home still drew nothing.
|
|
30
|
+
|
|
3
31
|
## 3.4.18
|
|
4
32
|
|
|
5
33
|
**A robot that drops off the Roborock cloud filled the log with stack traces about the plugin correctly deciding not to send.** When the transport a request would need is not there — the robot is marked offline, MQTT is down, or the local socket is not connected — the request queue declines to put it on the wire. That is a deliberate, calm decision, and it writes its own debug line where it happens. But the rejection then arrived at the error handler unclassified, so it was logged as a plugin error, with a full stack trace, once per poll, for as long as the condition lasted.
|
package/README.md
CHANGED
|
@@ -37,21 +37,22 @@ This is the most feature-packed, most thoroughly engineered Roborock plugin for
|
|
|
37
37
|
- 📍 **See where it's cleaning — live.** Apple Home shows _"Cleaning — Kitchen"_ with the room the robot is actually inside, updating as it moves from room to room. Works even for cleans started from the robot's button or the Roborock app. No other Homebridge plugin does this.
|
|
38
38
|
- 🧭 **One robot, one tile — and as many robots as you own.** Sign in once and your whole fleet comes along: every vacuum on your account appears as its own clean, native accessory in Apple Home. No clutter of fake fans and helper switches, and rooms appear with the names you gave them in the Roborock app.
|
|
39
39
|
- ⚡ **Fast and reliable.** Commands go directly to the robot over your own network whenever possible, with the Roborock cloud as automatic backup — and built-in diagnostics in the settings if you ever want to look under the hood.
|
|
40
|
-
- 🛡️ **Verified by Homebridge.** Reviewed and endorsed by the Homebridge team.
|
|
40
|
+
- 🛡️ **Verified by Homebridge.** Reviewed and endorsed by the Homebridge team. 613 automated tests, zero known vulnerabilities, no analytics, and a startup designed to never crash your Homebridge — even when your Wi-Fi or the Roborock cloud has a bad day.
|
|
41
41
|
|
|
42
42
|
## Features
|
|
43
43
|
|
|
44
|
-
| |
|
|
45
|
-
| ----------------------------------- |
|
|
46
|
-
| 🤖 **Full control from Apple Home** | Start, stop, pause and send the robot home to its dock — from the Home app
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
44
|
+
| | |
|
|
45
|
+
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
46
|
+
| 🤖 **Full control from Apple Home** | Start, stop, pause and send the robot home to its dock — from the Home app or Siri |
|
|
47
|
+
| 🕹️ **Switches for automations** | Optional per-robot Return to Dock, Pause and Find switches — Apple Home does not offer a dock action for a Matter vacuum ([details](#automations-in-apple-home)) |
|
|
48
|
+
| 🚪 **Clean specific rooms** | Pick rooms right in Apple Home, with the names you gave them in the Roborock app — multi-floor homes included |
|
|
49
|
+
| 📍 **Live room tracking** | See which room the robot is cleaning right now, updated as it moves ([details](#live-room-tracking)) |
|
|
50
|
+
| 📊 **Honest cleaning progress** | Each room goes pending → cleaning → done — and a room only counts as done when the robot was actually there |
|
|
51
|
+
| 🌀 **Cleaning & suction modes** | Vacuum / Mop / Vacuum + Mop on models that support it — plus optional Quiet / Balanced / Turbo / Max suction levels (Max+ on Q7) |
|
|
52
|
+
| 🔋 **Battery & charging** | Battery level and charging state on the accessory ([one Apple-side caveat](#battery-percentage-in-apple-home)) |
|
|
53
|
+
| 🧠 **New models just work** | Brand-new Roborock models get sensible defaults automatically, and the plugin adapts to what each robot actually supports |
|
|
54
|
+
| 🩺 **Built-in diagnostics** | Connection status, a one-click connection test, and a ready-to-share report if you ever need help |
|
|
55
|
+
| 🔐 **Easy, safe login** | Sign in with your Roborock account right in the settings — two-factor supported, session stored encrypted |
|
|
55
56
|
|
|
56
57
|
## Quick start
|
|
57
58
|
|
|
@@ -102,27 +103,49 @@ The clean mode follows the robot as well: start a vacuum+mop or mop-only clean f
|
|
|
102
103
|
|
|
103
104
|
Everything is configurable from the Homebridge UI. The essentials:
|
|
104
105
|
|
|
105
|
-
| Option | Default
|
|
106
|
-
| ------------------------------- |
|
|
107
|
-
| `email` / password | —
|
|
108
|
-
| `skipDevices` | —
|
|
109
|
-
| `enableMatterServiceArea` | `true`
|
|
110
|
-
| `enableLiveRoomTracking` | `true`
|
|
111
|
-
| `enableMatterCleanMode` | `true`
|
|
112
|
-
| `enableFanPowerCleanModes` | `false`
|
|
113
|
-
| `enableMatterPowerSource` | `true`
|
|
114
|
-
| `enableMatterFaultReporting` | `false`
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
106
|
+
| Option | Default | What it does |
|
|
107
|
+
| ------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
108
|
+
| `email` / password | — | Your Roborock app account (2FA handled in the UI; the session token is stored encrypted) |
|
|
109
|
+
| `skipDevices` | — | Comma-separated device IDs the plugin should ignore |
|
|
110
|
+
| `enableMatterServiceArea` | `true` | Room/map selection in Apple Home |
|
|
111
|
+
| `enableLiveRoomTracking` | `true` | Live current-room from the robot's map position while cleaning |
|
|
112
|
+
| `enableMatterCleanMode` | `true` | Vacuum / Mop / Vacuum + Mop mode selection |
|
|
113
|
+
| `enableFanPowerCleanModes` | `false` | Adds Quiet / Balanced / Turbo / Max (and Max+ on Q7) suction modes to the Matter mode list. **Re-pair the robot once after toggling** — Matter locks the mode list at pairing |
|
|
114
|
+
| `enableMatterPowerSource` | `true` | Battery cluster |
|
|
115
|
+
| `enableMatterFaultReporting` | `false` | Report a robot that has genuinely halted as Error instead of Ready ([details](#why-the-robot-needs-attention)) |
|
|
116
|
+
| `enableHomeKitActionSwitches` | `false` | Adds a plain Home app switch per robot for Return to Dock / Pause / Find, so automations can reach commands Apple does not offer for a Matter vacuum ([details](#automations-in-apple-home)) |
|
|
117
|
+
| `homeKitActionSwitches` | `["dock"]` | Which of those switches to publish: `dock`, `pause`, `locate` |
|
|
118
|
+
| `cloudOnlyMode` | `false` | Skip local TCP entirely and use the cloud for everything |
|
|
119
|
+
| `transientWarningThrottleHours` | `6` | How often recurring transient-timeout warnings may repeat (0 = only in debug) |
|
|
117
120
|
|
|
118
121
|
## Why the robot needs attention
|
|
119
122
|
|
|
120
123
|
By default a robot that has stopped for any reason shows as **Ready** in Apple Home — whether it finished the job or is wedged under the sofa. Turning on **Report faults in Apple Home** changes that: a robot that is stuck, has a blocked brush or wheel, a missing dust bin, a flat battery or a dock it cannot reach reports the Matter **Error** state instead of Ready. It is off by default because a robot in Error may be refused a Start command by Apple Home.
|
|
121
124
|
|
|
122
|
-
**Dock and tank conditions are deliberately not reported, and this is worth explaining.** The plugin can read them all accurately — empty clean-water tank, full waste-water tank, missing dust bag, blocked air duct — and two releases tried to surface them through Matter's fault attribute (`OperationalError`).
|
|
125
|
+
**Dock and tank conditions are deliberately not reported, and this is worth explaining.** The plugin can read them all accurately — empty clean-water tank, full waste-water tank, missing dust bag, blocked air duct — and two releases tried to surface them through Matter's fault attribute (`OperationalError`). Four controlled tests on an S8 Pro Ultra with a genuinely empty clean-water tank showed it does not work: Apple Home drew no warning when the fault was published beside a Charging state, and drew no warning in the final test either, where the robot was raised all the way to the Matter **Error** state carrying "Clean water tank empty" — the tile simply kept reading Ready. **Apple Home does not appear to render Matter vacuum faults from a bridged accessory at all** — which is also why an earlier version removed the same write back in 1.4.61. Reporting them was therefore pure cost, and the attribute is no longer published in any configuration.
|
|
126
|
+
|
|
127
|
+
This section also used to blame the setting for a tile stuck on "Updating…", which was not caused by it: in the final test the same robot, with both switches on, stayed in Ready throughout. That wedge came from a stale pairing left behind by an earlier install — see [Troubleshooting](#troubleshooting). The correction is stated here rather than quietly deleted, because someone may have left the feature switched off on the strength of it.
|
|
123
128
|
|
|
124
129
|
A detached water tank or mop pad is never treated as a fault either: that is the normal, correct configuration for a vacuum-only run.
|
|
125
130
|
|
|
131
|
+
## Automations in Apple Home
|
|
132
|
+
|
|
133
|
+
Every command lives on the tile: start, stop, pause and send-to-dock all work from the Home app and from Siri, because the plugin implements Matter's own `RvcOperationalState` commands — including **GoHome**, which is exactly what the dock button sends.
|
|
134
|
+
|
|
135
|
+
What Apple offers _inside_ Home automations is a separate question, and it is Apple's to answer, not the plugin's. It has now been measured twice by the same user in [#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3), and the answer turns out to be partial rather than flat:
|
|
136
|
+
|
|
137
|
+
- **Starting a clean is offered as an automation action** — either the whole home or a chosen set of rooms — and so is **stopping a clean that is already running**. An Apple Home schedule can therefore do the two things most schedules are built for, without any help from this section.
|
|
138
|
+
- **Sending the vacuum to its dock is not offered as an automation action.** A robot that finishes a clean normally returns to its dock by itself, so the gap only shows up when you want to end a clean early: the automation can cut it short, but it cannot call the robot home.
|
|
139
|
+
- **Pause, resume, and whether a vacuum can act as an automation _trigger_ rather than an action, are still unverified.** Nobody has measured them, so this page claims nothing about them in either direction.
|
|
140
|
+
|
|
141
|
+
**Optional Home app switches close the docking gap.** Turn on **Add Home app switches for Dock, Pause and Find** in the plugin settings and each robot gets one plain HomeKit switch per action you pick — `Vicky Return to Dock`, `Vicky Pause`, `Vicky Find`. A switch is something every automation, scene and Shortcut can turn on, which is the whole point: an automation that cannot send the robot to its dock directly can flip a switch that does it instead. Each one is momentary and turns itself off again about a second and a half after it is pressed, so it never claims a command is still running.
|
|
142
|
+
|
|
143
|
+
A press takes exactly the same route as a press on the tile — the same acknowledgement wait, the same timing line in the log, the same retry if Roborock times out while the robot is still cleaning — and it moves the tile with it, so a robot sent home by a schedule does not sit there reading Ready. The log line names which surface asked, so `Sending Vicky back to dock from the Home switch.` and `Sending Vicky back to dock from Matter.` are told apart when a schedule misfires.
|
|
144
|
+
|
|
145
|
+
Three things are worth knowing before you turn it on. They are off by default because switching them on adds accessories to your Home app, one per robot per action. They are ordinary HomeKit accessories, so they arrive over the Homebridge bridge rather than over Matter — if you have only ever paired the Matter robot and never the Homebridge bridge itself, they will not show up until you pair the bridge too. And the Find switch is only published for robots that actually support the command, because a switch that silently does nothing is worse than no switch at all.
|
|
146
|
+
|
|
147
|
+
Deselecting an action, or turning the feature off, removes those switches on the next restart. The robot itself is untouched throughout: it stays a Matter vacuum, and no re-pairing is needed to add or remove the switches.
|
|
148
|
+
|
|
126
149
|
## Battery percentage in Apple Home
|
|
127
150
|
|
|
128
151
|
Apple Home renders the battery percentage from pairing time and refreshes it only on a fresh read (commissioning, hub restart) — while charging state on the very same cluster updates live. This is not a plugin bug, and the root cause is now **confirmed in the source of matter.js** (the Matter stack Homebridge uses): the percentage attribute carries the spec's "changes omitted" quality, and matter.js currently never emits subscription reports for such attributes — while Apple Home never re-reads them on its own. The fix is tracked upstream in [matter-js/matter.js#4163](https://github.com/matter-js/matter.js/issues/4163) (an opt-in to report them anyway, which the spec permits); once it lands, Homebridge can enable it for bridged accessories and every plugin gets working battery percentages at once. Full investigation: [homebridge#3958](https://github.com/homebridge/homebridge/issues/3958).
|
|
@@ -143,7 +166,7 @@ The complete path — robot → plugin → Homebridge → matter.js store — wa
|
|
|
143
166
|
|
|
144
167
|
## Contributing
|
|
145
168
|
|
|
146
|
-
Model reports, diagnostics exports, and pull requests are very welcome. The codebase ships with
|
|
169
|
+
Model reports, diagnostics exports, and pull requests are very welcome. The codebase ships with 613 tests (protocol fixtures verified against the [python-roborock](https://github.com/Python-roborock/python-roborock) reference), strict TypeScript checking, and CI across Node 22/24 × Homebridge 1.11/2.x — `npm test` before you push and you're set.
|
|
147
170
|
|
|
148
171
|
## Support the project
|
|
149
172
|
|
package/config.schema.json
CHANGED
|
@@ -124,6 +124,37 @@
|
|
|
124
124
|
"maximum": 100,
|
|
125
125
|
"default": 100
|
|
126
126
|
},
|
|
127
|
+
"enableHomeKitActionSwitches": {
|
|
128
|
+
"title": "Add Home App Switches for Dock, Pause and Find",
|
|
129
|
+
"description": "Publishes one extra HomeKit switch per robot per action, so Apple Home automations and Shortcuts can send a command that Apple does not offer for a Matter vacuum. Measured in issue #3: Apple Home has no \"send the vacuum to its dock\" automation action for a Matter vacuum, and a plain switch is an automation action everywhere. Each switch is momentary: it turns itself off again about a second after it is pressed. Off by default, because turning it on adds accessories to your Home app.",
|
|
130
|
+
"type": "boolean",
|
|
131
|
+
"default": false
|
|
132
|
+
},
|
|
133
|
+
"homeKitActionSwitches": {
|
|
134
|
+
"title": "Which Switches to Add",
|
|
135
|
+
"description": "Which actions get a switch. Return to Dock is the one Apple Home cannot do any other way; Pause and Find are there because they cost nothing extra once the first switch exists. Only used when the setting above is on.",
|
|
136
|
+
"type": "array",
|
|
137
|
+
"uniqueItems": true,
|
|
138
|
+
"default": ["dock"],
|
|
139
|
+
"items": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"enum": ["dock", "pause", "locate"],
|
|
142
|
+
"oneOf": [
|
|
143
|
+
{
|
|
144
|
+
"title": "Return to Dock",
|
|
145
|
+
"enum": ["dock"]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"title": "Pause",
|
|
149
|
+
"enum": ["pause"]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"title": "Find (robot announces where it is)",
|
|
153
|
+
"enum": ["locate"]
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
},
|
|
127
158
|
"cloudOnlyMode": {
|
|
128
159
|
"title": "Use Roborock Cloud Only",
|
|
129
160
|
"description": "Disables local LAN discovery and local TCP commands for this plugin, routing commands and status polling through Roborock cloud when available. Useful when local LAN connections appear connected but consistently time out. Restart the Roborock child bridge after changing this setting.",
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACTION_SWITCH_KIND = exports.ACTION_SWITCH_DEFINITIONS = void 0;
|
|
4
|
+
exports.getActionSwitchDefinition = getActionSwitchDefinition;
|
|
5
|
+
exports.isActionSwitchAccessory = isActionSwitchAccessory;
|
|
6
|
+
exports.actionSwitchUuidSeed = actionSwitchUuidSeed;
|
|
7
|
+
const timers_1 = require("./timers");
|
|
8
|
+
/**
|
|
9
|
+
* How long a pressed switch stays on before it falls back to off.
|
|
10
|
+
*
|
|
11
|
+
* These are momentary switches: the press is the command, and there is no
|
|
12
|
+
* "docking" state worth mirroring back. The alternative — holding the switch
|
|
13
|
+
* on until the robot reaches the dock — would be a second state machine racing
|
|
14
|
+
* the same laggy Roborock snapshot that issues #4 and #12 were about, for no
|
|
15
|
+
* gain to the automation that pressed it.
|
|
16
|
+
*
|
|
17
|
+
* 1.5 s is long enough that the Home app draws the press and an automation
|
|
18
|
+
* records it, and short enough that the switch is ready for the next one.
|
|
19
|
+
*/
|
|
20
|
+
const SWITCH_AUTO_RESET_MS = 1500;
|
|
21
|
+
/**
|
|
22
|
+
* Every action a switch can expose.
|
|
23
|
+
*
|
|
24
|
+
* The table is the extension point. A fourth action is a row here plus an arm
|
|
25
|
+
* in runHomeKitAction — no new class, no new registration path, no second
|
|
26
|
+
* partition rule in the platform's accessory sweep.
|
|
27
|
+
*/
|
|
28
|
+
exports.ACTION_SWITCH_DEFINITIONS = [
|
|
29
|
+
{
|
|
30
|
+
key: "dock",
|
|
31
|
+
nameSuffix: "Return to Dock",
|
|
32
|
+
summary: "sends the robot back to its dock",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: "pause",
|
|
36
|
+
nameSuffix: "Pause",
|
|
37
|
+
summary: "pauses the current clean",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: "locate",
|
|
41
|
+
nameSuffix: "Find",
|
|
42
|
+
summary: "makes the robot announce where it is",
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
function getActionSwitchDefinition(key) {
|
|
46
|
+
return exports.ACTION_SWITCH_DEFINITIONS.find((definition) => definition.key === key);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The marker that keeps these accessories out of the Matter-only cleanup.
|
|
50
|
+
*
|
|
51
|
+
* discoverDevices() unregisters every cached HAP accessory it does not
|
|
52
|
+
* recognise, because the Matter-only rebuild had to remove the old fan and
|
|
53
|
+
* helper switches and a user who upgraded mid-way could otherwise keep a
|
|
54
|
+
* duplicate robot in Apple Home forever. That sweep predates these switches
|
|
55
|
+
* and would delete them on the first restart after they were added, so the
|
|
56
|
+
* partition is written into the accessory itself rather than inferred from
|
|
57
|
+
* its name — a name is user-editable and a duid is not.
|
|
58
|
+
*/
|
|
59
|
+
exports.ACTION_SWITCH_KIND = "actionSwitch";
|
|
60
|
+
function isActionSwitchAccessory(accessory) {
|
|
61
|
+
const context = accessory === null || accessory === void 0 ? void 0 : accessory.context;
|
|
62
|
+
return Boolean(context &&
|
|
63
|
+
typeof context === "object" &&
|
|
64
|
+
context.kind === exports.ACTION_SWITCH_KIND &&
|
|
65
|
+
typeof context.duid === "string" &&
|
|
66
|
+
typeof context.action === "string");
|
|
67
|
+
}
|
|
68
|
+
/** The UUID seed for one robot's switch. Namespaced away from Matter's. */
|
|
69
|
+
function actionSwitchUuidSeed(duid, action) {
|
|
70
|
+
return `hap:roborock:action:${duid}:${action}`;
|
|
71
|
+
}
|
|
72
|
+
class RoborockActionSwitchAccessory {
|
|
73
|
+
constructor(platform, accessory, definition, duid) {
|
|
74
|
+
this.platform = platform;
|
|
75
|
+
this.accessory = accessory;
|
|
76
|
+
this.definition = definition;
|
|
77
|
+
this.duid = duid;
|
|
78
|
+
this.resetTimer = null;
|
|
79
|
+
this.configureAccessory();
|
|
80
|
+
}
|
|
81
|
+
get action() {
|
|
82
|
+
return this.definition.key;
|
|
83
|
+
}
|
|
84
|
+
get summary() {
|
|
85
|
+
return this.definition.summary;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Re-apply the identity and re-bind the handlers.
|
|
89
|
+
*
|
|
90
|
+
* Called for cached accessories too: a cached PlatformAccessory arrives with
|
|
91
|
+
* its services intact but no handlers, because those live in the closure of
|
|
92
|
+
* the process that registered it and did not survive the restart.
|
|
93
|
+
*/
|
|
94
|
+
configureAccessory() {
|
|
95
|
+
const { Service, Characteristic } = this.platform;
|
|
96
|
+
const name = this.accessory.displayName;
|
|
97
|
+
const information = this.accessory.getService(Service.AccessoryInformation) ||
|
|
98
|
+
this.accessory.addService(Service.AccessoryInformation);
|
|
99
|
+
information
|
|
100
|
+
.setCharacteristic(Characteristic.Manufacturer, "Roborock")
|
|
101
|
+
.setCharacteristic(Characteristic.Model, `${this.platform.getVacuumModel(this.duid)} ${this.definition.nameSuffix}`)
|
|
102
|
+
// The robot's own serial number belongs to the Matter accessory. Suffixing
|
|
103
|
+
// it keeps Apple Home from treating the two as the same device.
|
|
104
|
+
.setCharacteristic(Characteristic.SerialNumber, `${this.platform.getVacuumSerialNumber(this.duid)}-${this.definition.key}`);
|
|
105
|
+
const service = this.accessory.getService(Service.Switch) ||
|
|
106
|
+
this.accessory.addService(Service.Switch, name);
|
|
107
|
+
service.setCharacteristic(Characteristic.Name, name);
|
|
108
|
+
const on = service.getCharacteristic(Characteristic.On);
|
|
109
|
+
// Cached accessories are configured again on every launch, and a second
|
|
110
|
+
// set of handlers on the same characteristic would run the command twice.
|
|
111
|
+
on.removeAllListeners("get");
|
|
112
|
+
on.removeAllListeners("set");
|
|
113
|
+
on.onGet(() => false).onSet((value) => this.handlePress(value));
|
|
114
|
+
// Whatever the cache remembered, the switch starts off: it is momentary,
|
|
115
|
+
// and an accessory restored in the on position would tell an automation
|
|
116
|
+
// that a command it never sent is still running.
|
|
117
|
+
service.updateCharacteristic(Characteristic.On, false);
|
|
118
|
+
}
|
|
119
|
+
/** Follow a rename in the Roborock app through to Apple Home. */
|
|
120
|
+
updateIdentity(vacuumName) {
|
|
121
|
+
var _a;
|
|
122
|
+
const name = `${vacuumName} ${this.definition.nameSuffix}`;
|
|
123
|
+
if (this.accessory.displayName === name) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
this.accessory.displayName = name;
|
|
127
|
+
(_a = this.accessory
|
|
128
|
+
.getService(this.platform.Service.Switch)) === null || _a === void 0 ? void 0 : _a.updateCharacteristic(this.platform.Characteristic.Name, name);
|
|
129
|
+
}
|
|
130
|
+
dispose() {
|
|
131
|
+
if (this.resetTimer) {
|
|
132
|
+
(0, timers_1.clearTimer)(this.resetTimer);
|
|
133
|
+
this.resetTimer = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* One press.
|
|
138
|
+
*
|
|
139
|
+
* Nothing here is allowed to throw: an error out of a HAP set handler is
|
|
140
|
+
* shown to the user as a failed accessory rather than as the thing that
|
|
141
|
+
* actually went wrong, and the command path already logs its own failures
|
|
142
|
+
* with the robot's name and the transport it used.
|
|
143
|
+
*/
|
|
144
|
+
async handlePress(value) {
|
|
145
|
+
if (!value) {
|
|
146
|
+
// The switch turning itself off again. Not a command.
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.scheduleReset();
|
|
150
|
+
try {
|
|
151
|
+
const vacuum = this.platform.getMatterVacuum(this.duid);
|
|
152
|
+
if (!vacuum) {
|
|
153
|
+
this.platform.log.warn(`${this.accessory.displayName} was pressed, but the robot behind it is not set up yet. Try again once startup has finished.`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (!vacuum.supportsHomeKitAction(this.definition.key)) {
|
|
157
|
+
this.platform.log.warn(`${this.accessory.displayName} was pressed, but ${vacuum.getDisplayName()} does not support that command.`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
await vacuum.runHomeKitAction(this.definition.key);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
this.platform.log.error(`Unable to run ${this.accessory.displayName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
scheduleReset() {
|
|
167
|
+
if (this.resetTimer) {
|
|
168
|
+
(0, timers_1.clearTimer)(this.resetTimer);
|
|
169
|
+
}
|
|
170
|
+
const timer = (0, timers_1.scheduleTimer)(() => {
|
|
171
|
+
var _a;
|
|
172
|
+
this.resetTimer = null;
|
|
173
|
+
(_a = this.accessory
|
|
174
|
+
.getService(this.platform.Service.Switch)) === null || _a === void 0 ? void 0 : _a.updateCharacteristic(this.platform.Characteristic.On, false);
|
|
175
|
+
}, SWITCH_AUTO_RESET_MS);
|
|
176
|
+
(0, timers_1.unrefTimer)(timer);
|
|
177
|
+
this.resetTimer = timer;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.default = RoborockActionSwitchAccessory;
|
|
181
|
+
//# sourceMappingURL=action_switch_accessory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action_switch_accessory.js","sourceRoot":"","sources":["../src/action_switch_accessory.ts"],"names":[],"mappings":"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"}
|