@homebridge-plugins/homebridge-eufy-security 4.6.0-beta.4 → 4.6.0-beta.41

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.
Files changed (46) hide show
  1. package/.claude/CLAUDE.md +175 -0
  2. package/.claude/PRD.md +241 -0
  3. package/.claude/docs/hksv-recording-fix.md +160 -0
  4. package/.claude/skills/architect/SKILL.md +76 -0
  5. package/.claude/skills/developer/SKILL.md +59 -0
  6. package/.claude/skills/new-device-support/SKILL.md +102 -51
  7. package/.claude/skills/new-device-support/check-device.mjs +363 -0
  8. package/.claude/skills/new-device-support/map-properties.mjs +144 -10
  9. package/.claude/skills/new-device-support/verify-device.mjs +272 -0
  10. package/.claude/skills/planner/SKILL.md +100 -0
  11. package/.claude/skills/qa/SKILL.md +79 -0
  12. package/.claude/skills/support/SKILL.md +175 -0
  13. package/dist/accessories/CameraAccessory.js +20 -20
  14. package/dist/accessories/CameraAccessory.js.map +1 -1
  15. package/dist/controller/LocalLivestreamManager.js +197 -10
  16. package/dist/controller/LocalLivestreamManager.js.map +1 -1
  17. package/dist/controller/recordingDelegate.js +25 -9
  18. package/dist/controller/recordingDelegate.js.map +1 -1
  19. package/dist/controller/snapshotDelegate.js +5 -1
  20. package/dist/controller/snapshotDelegate.js.map +1 -1
  21. package/dist/controller/streamingDelegate.js +13 -7
  22. package/dist/controller/streamingDelegate.js.map +1 -1
  23. package/dist/platform.js +0 -23
  24. package/dist/platform.js.map +1 -1
  25. package/dist/settings.js +7 -0
  26. package/dist/settings.js.map +1 -1
  27. package/dist/utils/Talkback.js +127 -55
  28. package/dist/utils/Talkback.js.map +1 -1
  29. package/dist/utils/configTypes.js +1 -0
  30. package/dist/utils/configTypes.js.map +1 -1
  31. package/dist/utils/ffmpeg.js +137 -23
  32. package/dist/utils/ffmpeg.js.map +1 -1
  33. package/dist/utils/utils.js +27 -2
  34. package/dist/utils/utils.js.map +1 -1
  35. package/dist/version.js +1 -1
  36. package/dist/version.js.map +1 -1
  37. package/homebridge-ui/public/assets/devices/batterydoorbell2k_large.png +0 -0
  38. package/homebridge-ui/public/assets/devices/eufyCamS4_large.png +0 -0
  39. package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
  40. package/homebridge-ui/public/assets/devices/nvr_s4_max_T8N00_large.png +0 -0
  41. package/homebridge-ui/public/assets/devices/poe_bullet_ptz_cam_s4_T8E00_large.png +0 -0
  42. package/homebridge-ui/public/utils/device-images.js +2 -0
  43. package/homebridge-ui/public/views/device-detail.js +10 -0
  44. package/homebridge-ui/server.js +23 -31
  45. package/package.json +12 -12
  46. package/scripts/decrypt-diagnostics.mjs +6 -7
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: developer
3
+ description: Execute code changes following an approved plan or direct user instructions. Use this skill when the user says "implement", "code it", "go ahead", "execute the plan", or when transitioning from the planner skill after approval. Also use for direct coding tasks where the user provides clear, specific instructions. This skill writes clean, correct code respecting all project conventions.
4
+ ---
5
+
6
+ # Developer
7
+
8
+ You are a code execution agent for homebridge-eufy-security. You write code that is clean, correct, and follows every project convention. You either execute an approved plan from the planner skill, or implement direct user instructions.
9
+
10
+ Follow all conventions in CLAUDE.md (ESM imports, lint, architecture patterns, code style, git workflow).
11
+
12
+ ## Before writing any code
13
+
14
+ 1. **Read first**: Read every file you intend to modify. Understand existing patterns before touching them.
15
+ 2. **Check the plan**: If a plan was approved, follow it exactly. If you spot an issue with the plan during implementation, stop and flag it -- don't silently deviate.
16
+ 3. **Branch check**: Verify you're on the correct branch per CLAUDE.md git workflow.
17
+
18
+ ## Implementation workflow
19
+
20
+ ### Step 1 -- Write the code
21
+
22
+ Make changes file by file, following the plan order. For each file:
23
+ - Use the Edit tool for modifications (not Bash with sed/awk)
24
+ - Use Write only for new files
25
+ - Keep changes minimal and focused
26
+ - Don't add features beyond what was requested
27
+ - Don't refactor surrounding code while fixing a bug
28
+
29
+ ### Step 2 -- Self-review
30
+
31
+ After all changes are made, re-read each modified file to verify:
32
+ - No syntax errors
33
+ - Imports use `.js` extensions
34
+ - No accidental duplicate code
35
+ - Changes match the plan
36
+
37
+ ### Step 3 -- Verify
38
+
39
+ ```bash
40
+ npm run lint
41
+ npm run build
42
+ ```
43
+
44
+ If either fails, fix the issue immediately. Do not commit code that doesn't lint or build.
45
+
46
+ ### Step 4 -- Commit
47
+
48
+ Follow the git workflow from CLAUDE.md. Stage each logical change individually.
49
+
50
+ ## Chaining
51
+
52
+ After committing, automatically invoke the **qa** skill to verify the changes before pushing. Do not wait for the user to ask -- QA is part of the development flow.
53
+
54
+ ## When things go wrong
55
+
56
+ - **Lint failure**: Fix the warning. Don't disable the rule.
57
+ - **Build failure**: Read the error. If it's a type error, trace it back. If it's an import error, check `.js` extensions.
58
+ - **Plan doesn't work**: Stop. Explain what you found. Don't improvise a workaround without user approval.
59
+ - **Unclear requirement**: Ask. Don't guess.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: new-device-support
3
- description: Full workflow to add support for a new Eufy Security device type across eufy-security-client and homebridge-eufy-security. Covers exploration, implementation, build verification, and git/PR creation.
3
+ description: Full workflow to add support for a new Eufy Security device type (cameras, locks, sensors, and other devices) across eufy-security-client and homebridge-eufy-security. Covers exploration, implementation, build verification, and git/PR creation.
4
4
  ---
5
5
 
6
6
  # Add New Eufy Security Device Support
@@ -11,13 +11,28 @@ Use `$ARGUMENTS` for the issue URL or device details.
11
11
 
12
12
  ## Phase 1 — Gather Information
13
13
 
14
- 1. **Fetch the GitHub issue** (if URL provided) to extract: device name, model number (e.g. T86P2), device type number, raw properties JSON, firmware version, and any reference PRs.
15
- 2. **Run the property mapping script**: Save the raw properties JSON to a temp file and run:
14
+ 1. **Fetch the GitHub issue** (if URL provided) to extract: device name, model number (e.g. T86P2), device type number, raw properties JSON, firmware version, and any reference PRs. Note: users on recent eufy-security-client versions see a structured "unknown device" debug message that includes raw properties in a format directly usable with the mapping script.
15
+ 2. **Check for existing upstream work** before starting, search for PRs that already add this device:
16
16
  ```bash
17
- node homebridge-eufy-security/.claude/skills/new-device-support/map-properties.mjs /tmp/<device>-raw-props.json
17
+ gh pr list --repo bropat/eufy-security-client --state all --search "<model> OR <device-type-number>" --limit 10
18
18
  ```
19
- This maps each raw `param_type` to its `CommandType`/`ParamType` enum name, matching property constants, and which existing device types use them. It also outputs a suggested DeviceProperties block. Use this output to identify the closest existing device and plan the property mapping.
20
- 3. **Ask the user** two questions:
19
+ If a PR exists: review its diff to see what's already implemented, what's missing (e.g. missing GenericTypeProperty label, incomplete DeviceCommands), and whether it's merged, open, or stale. If merged, the device may only need homebridge-eufy-security side changes. If open but incomplete, coordinate with the PR author or build on their work. If the PR has review comments, check for flagged issues.
20
+ 3. **Pre-flight checks** before proceeding, confirm you have:
21
+ - Device type number (required — cannot proceed without it)
22
+ - Raw device properties JSON (required for accurate property mapping — if missing, ask the user to enable debug logging and re-export diagnostics)
23
+ - Model number / display name (needed for enum naming and documentation)
24
+ - If the device type number is completely unknown (not in any existing code), investigate: check the model number prefix pattern, look at raw properties to infer capabilities (camera properties? lock properties? sensor properties?), and check Eufy's product pages for the model.
25
+ 4. **Run the pre-implementation audit**: Before writing any code, run the audit script to see what already exists:
26
+ ```bash
27
+ node homebridge-eufy-security/.claude/skills/new-device-support/check-device.mjs <type-number> [--pr-search <model>]
28
+ ```
29
+ This checks all 6 registration points in types.ts, classification methods in device.ts, device-images.js, finds the closest existing devices by property overlap, and searches upstream PRs. Use its output to understand the starting point.
30
+ 5. **Run the property mapping script**: Save the raw properties JSON to a temp file and run:
31
+ ```bash
32
+ node homebridge-eufy-security/.claude/skills/new-device-support/map-properties.mjs /tmp/<device>-raw-props.json --closest
33
+ ```
34
+ This maps each raw `param_type` to its `CommandType`/`ParamType` enum name, matching property constants, and which existing device types use them. It also outputs a suggested DeviceProperties block. The `--closest` flag ranks existing devices by property overlap to identify the best base device.
35
+ 6. **Ask the user** two questions:
21
36
  - Image naming convention (check if images already exist or need renaming)
22
37
  - Enum name for the DeviceType (e.g. `CAMERA_4G_S330`)
23
38
 
@@ -25,12 +40,32 @@ Use `$ARGUMENTS` for the issue URL or device details.
25
40
 
26
41
  Create a detailed plan covering all files that need changes. The plan must be based on the actual raw device properties — never guess which properties a device supports.
27
42
 
43
+ ### Determine device category
44
+
45
+ Before planning file changes, identify the device category — this determines which classification methods and accessory classes apply:
46
+
47
+ - **Camera** (including doorbells, floodlights, indoor cameras, solo cameras, 4G cameras): uses `CameraAccessory` in the plugin. Classification methods: `isCamera()`, and optionally `isDoorbell()`, `isFloodLight()`, `isIndoorCamera()`, `isPanAndTiltCamera()`, `isOutdoorPanAndTiltCamera()`, `isSoloCameras()`, etc. Note: `isSoloCameras()` is a composite method that includes outdoor PTZ, wall light cams, and other standalone camera types.
48
+ - **WallLightCam**: uses `CameraAccessory`. Classification: `isWallLightCam()`. This category is heavily referenced in `station.ts` (50+ references) for livestream, talkback, property handling, and command routing — expect substantial `station.ts` changes.
49
+ - **GarageCamera**: uses `CameraAccessory`. Classification: `isGarageCamera()`. Has dedicated property variants (e.g. `DeviceWatermarkGarageCameraProperty`, `DeviceMotionDetectionSensitivityGarageCameraProperty`). Also has `isGarageCameraBySn()` for serial number matching.
50
+ - **Lock** (BLE, WiFi, WiFi Video variants): uses `LockAccessory`. Classification methods: `isLock()`, `isLockWifi()`, `isLockBle()`, `isLockWifiVideo()`, etc. Locks have many specialized type guard methods (e.g. `isLockWifiT8531()`, `isLockWifiR10()`) — check existing patterns carefully. Locks may require changes in **three additional layers** beyond this skill's primary scope — flag these to the user:
51
+ - `src/mqtt/` — MQTT protocol for lock communication
52
+ - `src/p2p/session.ts` — P2P session initialization uses `Device.isLockWifi()` and `Device.isLockWifiNoFinger()` for lock sequence generation
53
+ - `src/http/station.ts` — lock-specific routing at station level
54
+ - **Sensor** (entry sensor, motion sensor, PIR sensor): uses `EntrySensorAccessory` or `MotionSensorAccessory`. Classification: `isSensor()`, `isEntrySensor()`, `isMotionSensor()`. Note: `isSensor()` is static-only — no public instance method exists.
55
+ - **SmartDrop**: uses `SmartDropAccessory`. Classification: `isSmartDrop()`. Also has `isSmartDropBySn()` for serial number matching.
56
+ - **SmartSafe**: no HomeKit accessory currently. Classification: `isSmartSafe()`.
57
+ - **Tracker/SmartTrack**: no HomeKit accessory currently. Classification: `isSmartTrack()`.
58
+ - **Keypad**: no HomeKit accessory currently. Classification: `isKeyPad()`.
59
+ - **WaterFreezeSensor**: no HomeKit accessory currently. Classification: check for `WATER_FREEZE_SENSOR_*` in DeviceType enum.
60
+ - **Siren**: no HomeKit accessory currently. Classification: check for `SIREN_SENSOR_*` in DeviceType enum.
61
+ - **New category**: if the device doesn't fit any existing category, a new accessory class is needed in `src/accessories/`, a new classification method, and a new `if` block in `register_device()`. Flag this to the user — it's a significantly larger task.
62
+
28
63
  ### Files to modify in eufy-security-client
29
64
 
30
65
  #### `src/http/types.ts` — 6 locations:
31
66
 
32
67
  1. **DeviceType enum**: Add `ENUM_NAME = <number>, //<model>` in numeric order
33
- 2. **GenericTypeProperty states**: Add `<number>: "<Display Name> (<Model>)"` in numeric order
68
+ 2. **GenericTypeProperty `states` field**: Add `<number>: "<Display Name> (<Model>)"` inside the `states` object in numeric order. **WARNING**: This is one of the most commonly forgotten steps. Missing this label causes the device to display as a raw number in downstream UIs (see upstream PR #828 where 5 device types had missing labels).
34
69
  3. **DeviceProperties**: Add `[DeviceType.ENUM_NAME]` block. Always starts with `...GenericDeviceProperties`. Map each raw param_type to its corresponding `PropertyName.*` property constant. Base on the closest existing device but only include properties that match the raw data.
35
70
  4. **StationProperties**: Add `[DeviceType.ENUM_NAME]` block if device can act as its own station (solo cameras, integrated devices). Use `...BaseStationProperties` plus station-specific properties.
36
71
  5. **DeviceCommands**: Add `[DeviceType.ENUM_NAME]` array. Commands depend on device capabilities (livestream, talkback, pan/tilt, download, snooze, preset positions, calibrate, alarm).
@@ -38,14 +73,24 @@ Create a detailed plan covering all files that need changes. The plan must be ba
38
73
 
39
74
  #### `src/http/device.ts` — Classification methods:
40
75
 
41
- Add the new device type to all applicable static classification methods. Common ones:
76
+ Add the new device type to all applicable static classification methods. There are two kinds:
77
+
78
+ **Broad classification methods** (add device to these as applicable):
42
79
  - `isCamera()` — if it's a camera/doorbell/floodlight
43
- - `hasBattery()` — if battery-powered
80
+ - `hasBattery()` — if battery-powered (critical — omitting this means no battery service in HomeKit)
44
81
  - `isPanAndTiltCamera()` — if has PTZ
45
- - `isOutdoorPanAndTiltCamera()` — if outdoor PTZ (this feeds into `isSoloCameras()`)
46
- - `isFloodLight()`, `isIndoorCamera()`, `isDoorbell()`, etc. as applicable
82
+ - `isOutdoorPanAndTiltCamera()` — if outdoor PTZ (included by `isSoloCameras()`)
83
+ - `isSoloCameras()` composite method aggregating solo/standalone camera types
84
+ - `isFloodLight()`, `isIndoorCamera()`, `isDoorbell()`, `isWallLightCam()`, `isGarageCamera()` — as applicable
85
+ - `isLock()`, `isLockWifi()`, `isLockBle()`, `isLockWifiNoFinger()` — if it's a lock variant
86
+ - `isSensor()`, `isEntrySensor()`, `isMotionSensor()` — if it's a sensor
87
+ - `isSmartDrop()`, `isSmartSafe()`, `isSmartTrack()`, `isKeyPad()` — as applicable
88
+
89
+ Note: Not all broad methods have public instance counterparts (e.g. `isSensor()` is static-only). Don't create instance methods where none exist for the pattern.
47
90
 
48
- Add a **new static type guard method** and matching **instance method**:
91
+ **`isSupported()` check**: After adding to `DeviceProperties` map, `Device.isSupported(type)` automatically returns `true` (it checks `DeviceProperties[type] !== undefined`). This is the foundation of device registration — if `DeviceProperties` entry is missing, the device is silently unsupported regardless of all other registrations.
92
+
93
+ Add a **new dedicated type guard method** (static + instance pair):
49
94
  ```typescript
50
95
  static isNewDevice(type: number): boolean {
51
96
  //<Model>
@@ -57,9 +102,12 @@ public isNewDevice(): boolean {
57
102
  }
58
103
  ```
59
104
 
60
- Update serial number checks if applicable:
105
+ Update serial number checks if applicable (all are static-only, no instance methods):
61
106
  - `isIntegratedDeviceBySn()` — add `sn.startsWith("<model>")` if the device is integrated/standalone
62
107
  - `isSoloCameraBySn()` — add `sn.startsWith("<model>")` if it's a solo camera
108
+ - `isSmartDropBySn()` — add if it's a SmartDrop variant
109
+ - `isGarageCameraBySn()` — add if it's a garage camera variant
110
+ - `isFloodlightBySn()` — add if it's a floodlight variant
63
111
 
64
112
  #### `src/http/station.ts`:
65
113
 
@@ -67,7 +115,7 @@ Update serial number checks if applicable:
67
115
 
68
116
  #### `src/push/service.ts`:
69
117
 
70
- - If the device is 4G LTE or needs special push notification handling, expand the normalization block (~line 768) to include the new type guard.
118
+ - If the device is 4G LTE or needs special push notification handling, expand the normalization block (line 768) to include the new type guard.
71
119
 
72
120
  #### `docs/supported_devices.md`:
73
121
 
@@ -79,6 +127,10 @@ Use `:wrench:` for initial support.
79
127
 
80
128
  ### Files to modify in homebridge-eufy-security
81
129
 
130
+ #### `src/platform.ts` — `register_device()`:
131
+
132
+ Verify the new device type is handled by `register_device()`. This method uses independent `if` blocks (not `else if`) to map device types to accessory classes. If the device is a camera, it falls through to the camera path. If it's a lock, sensor, or SmartDrop, it hits those specific checks. If it's a new category that doesn't match any existing check, the device won't get an accessory — flag this to the user.
133
+
82
134
  #### `homebridge-ui/public/utils/device-images.js`:
83
135
 
84
136
  Add a case in the `getImage()` switch:
@@ -95,38 +147,48 @@ case <type_number>: return '<image_large>.png';
95
147
 
96
148
  Execute the plan. Key implementation notes:
97
149
 
98
- - **Property mapping**: Use the output from `map-properties.mjs` (Phase 1) as the primary guide. When multiple property constants match the same `param_type` (e.g. `DeviceWatermarkProperty` vs `DeviceWatermarkSoloWiredDoorbellProperty`), pick the variant used by the closest existing device. Check the "Used by DeviceTypes" column in the script output.
150
+ - **Property mapping**: Use the output from `map-properties.mjs` (Phase 1) as the primary guide. Many property constants have large variant families (e.g. `DeviceMotionDetection*` has 40+ variants, `DeviceFloodlightLight*` has 12+, `DeviceVideoRecordingQuality*` has 11+). When multiple constants match the same `param_type`, pick the variant used by the closest existing device check the "Used by DeviceTypes" column in the script output and the `--closest` flag results.
151
+ - **Companion custom properties**: Some properties have required companions with `custom_*` keys that never appear in raw device data (they're populated at runtime). The script detects these and marks them with `⚠ companion`. Always include them — omitting a companion breaks functionality silently. Key pairs: `DeviceRTSPStream` → `DeviceRTSPStreamUrl`, `DeviceWifiRSSI` → `DeviceWifiSignalLevel`, `DeviceCellularRSSI` → `DeviceCellularSignalLevel`.
99
152
  - **Insert in order**: When adding to enums, switch statements, or `if` chains, maintain numeric ordering by device type number.
100
153
  - **Audio recording property**: Different device families use different audio recording property constants (e.g. `DeviceAudioRecordingProperty`, `DeviceAudioRecordingStarlight4gLTEProperty`). Match the closest existing device.
101
- - **No Co-Authored-By**: Do not add co-author lines to commits.
102
154
 
103
- ## Phase 4 — Build & Lint Verification
155
+ ### Registration verification checklist
104
156
 
105
- Run these in parallel:
157
+ After implementing all changes, run the post-implementation verification script:
106
158
  ```bash
107
- cd eufy-security-client && npm run build
108
- cd homebridge-eufy-security && npm run build
159
+ node homebridge-eufy-security/.claude/skills/new-device-support/verify-device.mjs <ENUM_NAME>
109
160
  ```
161
+ This automatically checks all required registration points and reports PASS/FAIL:
162
+
163
+ 1. `DeviceType` enum definition
164
+ 2. `GenericTypeProperty` states
165
+ 3. `DeviceProperties` map
166
+ 4. `DeviceCommands` map
167
+ 5. `StationProperties` map (if device acts as its own station — reported as WARN if missing)
168
+ 6. `StationCommands` map (if device has station properties — reported as WARN if missing)
169
+ 7. Dedicated type guard method (static + instance) in device.ts
170
+ 8. Broad classification methods (isCamera, isLock, etc.) in device.ts
171
+ 9. `docs/supported_devices.md` entry (reported as WARN if missing)
172
+ 10. `*BySn` serial number methods (reported as WARN if missing — not all devices need these)
173
+ 11. device-images.js case in homebridge-eufy-security
174
+
175
+ The script exits with code 1 if any required checks fail. A device type that is defined in the enum but missing from DeviceProperties will silently fall back to GenericDeviceProperties with only ~3 basic properties (name, model, serial). This is the most common implementation error — see upstream issue #853 where LOCK_85V0 was added to the enum but never registered in the lookup maps.
110
176
 
111
- Then verify lint:
112
- ```bash
113
- cd eufy-security-client && npm run lint
114
- cd homebridge-eufy-security && npm run lint
115
- ```
177
+ ## Phase 4 — Build & Lint Verification
116
178
 
117
- Note: eufy-security-client lint may fail due to a pre-existing `jiti` library issue unrelated to our changes. The TypeScript build succeeding is sufficient validation.
179
+ Run build and lint for both repos. Note: eufy-security-client lint may fail due to a pre-existing `jiti` library issue unrelated to our changes the TypeScript build succeeding is sufficient validation.
118
180
 
119
181
  ## Phase 5 — Git & PR
120
182
 
121
- ### eufy-security-client
183
+ Follow CLAUDE.md Git Workflow for commit messages, branch naming, and PR body format. This skill creates **two PRs** across repos:
184
+
185
+ ### eufy-security-client (cross-fork)
122
186
 
123
- 1. Discard any unrelated changes (e.g. `package-lock.json`)
124
- 2. Sync develop with upstream: `git fetch upstream && git checkout develop && git merge upstream/develop`
125
- 3. Create branch: `git checkout -b feat/<device-slug>`
126
- 4. Stage only relevant files (images (should match the `<device-slug>`), `src/http/types.ts`, `src/http/device.ts`, `src/push/service.ts`, `docs/supported_devices.md`)
127
- 5. Commit: `git commit -m "feat: add <Device Name> (<Model>, type <number>) support"`
128
- 6. Push: `git push origin feat/<device-slug>`
129
- 7. Create cross-fork PR:
187
+ 1. Discard unrelated changes (e.g. `package-lock.json`)
188
+ 2. Sync develop: `git fetch upstream && git checkout develop && git merge upstream/develop`
189
+ 3. Branch: `git checkout -b feat/<device-slug>`
190
+ 4. Stage only: images, `src/http/types.ts`, `src/http/device.ts`, `src/push/service.ts`, `docs/supported_devices.md`
191
+ 5. Cross-fork PR:
130
192
  ```bash
131
193
  gh pr create --repo bropat/eufy-security-client --base develop \
132
194
  --head lenoxys:feat/<device-slug> \
@@ -136,26 +198,15 @@ Note: eufy-security-client lint may fail due to a pre-existing `jiti` library is
136
198
 
137
199
  ### homebridge-eufy-security
138
200
 
139
- 1. Branch from the current beta branch (check with `git branch`): `git checkout -b feat/<device-slug>`
201
+ 1. Branch from current beta: `git checkout -b feat/<device-slug>`
140
202
  2. Stage: `homebridge-ui/public/utils/device-images.js` + any added image
141
- 3. Commit: `git commit -m "feat: add <Device Name> (<Model>, type <number>) device image mapping"`
142
- 4. Push: `git push origin feat/<device-slug>`
143
- 5. Create PR:
203
+ 3. PR to current beta branch:
144
204
  ```bash
145
- gh pr create --repo homebridge-plugins/homebridge-eufy-security \
146
- --base <beta-branch> \
147
- --title "feat: add <Device Name> (<Model>, type <number>) device image" \
205
+ gh pr create --base beta-<current-version> \
206
+ --title "feat: add <Device Name> (<Model>) image" \
148
207
  --body-file /tmp/pr-body-<branch>.md
149
208
  ```
150
209
 
151
- ### PR body format
152
-
153
- Write PR bodies to `/tmp/pr-body-<branch>.md` files. Include:
154
- - `## Summary` — bullet points describing the changes
155
- - Cross-references: `Closes homebridge-plugins/homebridge-eufy-security#<issue>` in the client PR, `Closes #<issue>` in the plugin PR
156
- - `Depends on bropat/eufy-security-client#<pr>` in the plugin PR
157
- - `## Test plan` — checklist of verification steps
158
-
159
- ### Link the issue
210
+ ### Cross-referencing
160
211
 
161
- After both PRs are created, update both PR bodies so they reference the issue with closing keywords.
212
+ After both PRs are created, update both bodies so they reference each other.