homebridge-roborock-vacuum 1.4.6 → 1.4.7
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/CLAUDE.md +50 -0
- package/package.json +1 -1
- package/roborockLib/roborockAPI.js +18 -5
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- Install: `npm ci`
|
|
8
|
+
- Build: `npm run build` (runs `rimraf ./dist && tsc`; only `src/` is compiled to `dist/`)
|
|
9
|
+
- Lint: `npm run lint` (Prettier check) / `npm run lint:fix`
|
|
10
|
+
- Test: `npm test` (Jest)
|
|
11
|
+
- Run a single test file: `npm test -- path/to/file.test.ts`
|
|
12
|
+
- Run by name: `npm test -- -t "should do something"`
|
|
13
|
+
- File + name: `npm test -- path/to/file.test.ts -t "should do something"`
|
|
14
|
+
- Coverage: `npm test -- --coverage`
|
|
15
|
+
- Debug flaky/async: add `--runInBand`
|
|
16
|
+
|
|
17
|
+
CI parity sequence: `npm ci` → `npm run lint` → `npm run build` → `npm test -- --coverage`.
|
|
18
|
+
|
|
19
|
+
Note: there are currently no committed Jest specs under `src/`. `roborockLib/test.js` is integration-style, not a Jest spec. New tests should be `*.test.ts` colocated in `src/`.
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
This is a Homebridge dynamic platform plugin that bridges Roborock vacuums to HomeKit. It has three source roots with very different concerns:
|
|
24
|
+
|
|
25
|
+
- **`src/`** (TypeScript, strict mode, `module: commonjs`, `target: ES2018`) — the Homebridge plugin surface. Compiled to `dist/`; `dist/index.js` is the published entry point.
|
|
26
|
+
- `src/index.ts` registers the platform with Homebridge via `PLATFORM_NAME` from `settings.ts`.
|
|
27
|
+
- `src/platform.ts` is the `DynamicPlatformPlugin`. It restores cached accessories, then on `APIEvent.DID_FINISH_LAUNCHING` discovers devices via the Roborock API and registers/unregisters `PlatformAccessory`s. It also installs a process-wide filter for Node's DEP0040 deprecation warning emitted by an upstream dep — keep that filter in place when editing.
|
|
28
|
+
- `src/vacuum_accessory.ts` wires HomeKit Services/Characteristics to vacuum state and scene-switch behavior. Characteristic handlers must stay lightweight and resilient to null state.
|
|
29
|
+
- `src/crypto.ts` handles Roborock token encryption/decryption and key files. Returns safe defaults on decryption failure rather than throwing — preserve that behavior.
|
|
30
|
+
- `src/types.ts`, `src/settings.ts`, `src/logger.ts` — config shape, plugin/platform name constants, and the platform logger wrapper.
|
|
31
|
+
- `src/ui/index.ts` — TypeScript shared by the config UI; distinct from the `homebridge-ui/` runtime assets below.
|
|
32
|
+
|
|
33
|
+
- **`roborockLib/`** (legacy JavaScript, **not** part of `tsc` compilation) — Roborock cloud/local protocol implementation, loaded from `src/platform.ts` via `require("../roborockLib/roborockAPI")`. Subdirectories cover MQTT transport, local connector, message queueing, auth, map parsing (`RRMapParser.js`), per-model feature flags (`deviceFeatures.js`), and packaging helpers. Treat changes here as risky — they impact both cloud and local device communication paths.
|
|
34
|
+
|
|
35
|
+
- **`homebridge-ui/`** — Homebridge config UI: `server.js` (Express server using `@homebridge/plugin-ui-utils`) plus `public/` static assets. This is independent of `src/` compilation.
|
|
36
|
+
|
|
37
|
+
The `src/` ↔ `roborockLib/` boundary is the main complexity: TypeScript is strict but `roborockAPI` is consumed via `require` and typed as `any` on the platform. When threading new fields through, either widen the surface in `src/types.ts` or keep narrow `unknown` + narrowing at the boundary.
|
|
38
|
+
|
|
39
|
+
## OpenSpec workflow
|
|
40
|
+
|
|
41
|
+
This repo uses OpenSpec (`openspec/`) for change proposals — see `openspec/AGENTS.md` and `openspec/project.md`. For non-trivial behavior changes, new capabilities, breaking changes, or architecture shifts, create a change proposal under `openspec/changes/<change-id>/` before coding rather than going straight to implementation.
|
|
42
|
+
|
|
43
|
+
## Conventions worth knowing
|
|
44
|
+
|
|
45
|
+
- Prettier: `tabWidth: 2`, `semi: true`, `trailingComma: "es5"`. Ignored: `coverage`, `dist`, `node_modules`, `__snapshots__`.
|
|
46
|
+
- TS strict is on, but `noImplicitAny: false` for legacy interop. Don't introduce broad new `any` in TS code; prefer `unknown` + narrowing.
|
|
47
|
+
- Existing snake_case filenames (e.g. `vacuum_accessory.ts`) are intentional — preserve them.
|
|
48
|
+
- Constants like `PLUGIN_NAME` / `PLATFORM_NAME` are `UPPER_SNAKE_CASE` and load-bearing for Homebridge accessory caching — UUID generation must stay deterministic.
|
|
49
|
+
- `config.schema.json` is the user-visible config contract; keep it aligned with `src/types.ts` and README when adding options.
|
|
50
|
+
- `AGENTS.md` carries overlapping command/architecture guidance for other agentic tools; keep it in sync when changing the equivalent sections here.
|
package/package.json
CHANGED
|
@@ -455,6 +455,9 @@ class Roborock {
|
|
|
455
455
|
this.devices = this.devices.filter(
|
|
456
456
|
(device) => !ignoredSet.has(device.sn)
|
|
457
457
|
);
|
|
458
|
+
this.receivedDevices = (homedataResult.receivedDevices || []).filter(
|
|
459
|
+
(device) => !ignoredSet.has(device.sn)
|
|
460
|
+
);
|
|
458
461
|
|
|
459
462
|
const allManagedDevices = (homedataResult.devices || []).concat(
|
|
460
463
|
homedataResult.receivedDevices || []
|
|
@@ -1008,11 +1011,21 @@ class Roborock {
|
|
|
1008
1011
|
}
|
|
1009
1012
|
|
|
1010
1013
|
getProductAttribute(duid, attribute) {
|
|
1011
|
-
|
|
1012
|
-
const
|
|
1013
|
-
(device) => device.duid == duid
|
|
1014
|
-
|
|
1015
|
-
|
|
1014
|
+
// Owned devices live in this.devices; shared devices in this.receivedDevices.
|
|
1015
|
+
const device =
|
|
1016
|
+
this.devices.find((device) => device.duid == duid) ||
|
|
1017
|
+
(this.receivedDevices || []).find((device) => device.duid == duid);
|
|
1018
|
+
|
|
1019
|
+
if (!device) {
|
|
1020
|
+
this.log.warn(
|
|
1021
|
+
`getProductAttribute: no device found for duid ${duid}, returning null for "${attribute}".`
|
|
1022
|
+
);
|
|
1023
|
+
return null;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
const product = (this.products || []).find(
|
|
1027
|
+
(product) => product.id == device.productId
|
|
1028
|
+
);
|
|
1016
1029
|
|
|
1017
1030
|
return product ? product[attribute] : null;
|
|
1018
1031
|
}
|