homebridge-yoto 0.0.31 → 0.0.32

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/PLAN.md DELETED
@@ -1,425 +0,0 @@
1
- # Homebridge Yoto Plugin - Implementation Status
2
-
3
- ## ✅ What's Implemented
4
-
5
- ### Architecture
6
- - ✅ Platform plugin using `yoto-nodejs-client` for device management
7
- - ✅ One accessory per Yoto device (published as external accessory for SmartSpeaker support)
8
- - ✅ Real-time updates via MQTT + periodic HTTP polling fallback
9
- - ✅ Offline detection and "No Response" status handling
10
- - ✅ Capability-based service registration (v2/v3/mini device support)
11
-
12
- ### Core Services (Always Present)
13
- - ✅ **AccessoryInformation** - Device metadata (manufacturer, model, serial, firmware)
14
- - ✅ **SmartSpeaker** (PRIMARY) - Playback control and volume
15
- - ✅ **Battery** - Battery level, charging state, low battery indicator
16
- - ✅ **ContactSensor (CardSlot)** - Card insertion detection
17
- - ✅ **OccupancySensor (NightModeStatus)** - Day/night mode indicator
18
- - ✅ **Switch (SleepTimer)** - Toggle sleep timer (30 min default)
19
- - ✅ **Switch (Bluetooth)** - Toggle Bluetooth on/off
20
- - ✅ **Fanv2 (DayMaxVolume)** - Day mode max volume limit control
21
- - ✅ **Fanv2 (NightMaxVolume)** - Night mode max volume limit control
22
-
23
- ### Optional Services (Capability-Based)
24
- - ✅ **TemperatureSensor** - Temperature reading (v3 only)
25
- - ✅ **Lightbulb (DayNightlight)** - Day nightlight color/brightness control (v3 only)
26
- - ✅ **Lightbulb (NightNightlight)** - Night nightlight color/brightness control (v3 only)
27
- - ✅ **ContactSensor (NightlightActive)** - Live nightlight status (v3 only)
28
- - ✅ **ContactSensor (DayNightlightActive)** - Day nightlight status (v3 only)
29
- - ✅ **ContactSensor (NightNightlightActive)** - Night nightlight status (v3 only)
30
-
31
- ## Service & Characteristic Reference
32
-
33
- All services are named consistently using `generateServiceName()` helper: `"[Device Name] [Service Name]"`
34
-
35
- ### Yoto Player Accessory
36
-
37
- Each Yoto device is represented as a single HomeKit accessory with multiple services.
38
-
39
- **Category**: `SPEAKER` (required for SmartSpeaker service)
40
-
41
- ---
42
-
43
- #### Service: AccessoryInformation (Required)
44
-
45
- Standard HomeKit service providing device identification.
46
-
47
- **Characteristics:**
48
- - `Manufacturer` (GET) - "Yoto Inc."
49
- - `Model` (GET) - Device family (e.g., "v3", "v2", "mini")
50
- - `SerialNumber` (GET) - Device ID
51
- - `HardwareRevision` (GET) - Generation and form factor
52
- - `FirmwareRevision` (GET) - Firmware version from device status
53
-
54
- **Source:** `device` metadata + `status.firmwareVersion`
55
-
56
- ---
57
-
58
- #### Service: SmartSpeaker (PRIMARY)
59
-
60
- Controls playback and volume. Marked as primary service for the accessory.
61
-
62
- **Characteristics:**
63
- - `CurrentMediaState` (GET) - PLAY/PAUSE/STOP based on playback status
64
- - `TargetMediaState` (GET/SET) - Control playback (play/pause/stop)
65
- - `Volume` (GET/SET) - Volume level (0-16 native scale, dynamic max based on volume limit)
66
- - `Mute` (GET/SET) - Mute state (derived from volume === 0)
67
- - `StatusActive` (GET) - Online/offline indicator
68
-
69
- **Source:** `status.volume`, `playback.playbackStatus`
70
- **Control:** `sendCommand({ action: 'play' | 'pause' | 'stop' | 'volume', volume: N })`
71
-
72
- ---
73
-
74
- #### Service: Battery
75
-
76
- Battery status information.
77
-
78
- **Characteristics:**
79
- - `BatteryLevel` (GET) - Battery percentage (0-100)
80
- - `ChargingState` (GET) - CHARGING or NOT_CHARGING
81
- - `StatusLowBattery` (GET) - LOW when ≤20%, NORMAL otherwise
82
- - `StatusActive` (GET) - Online/offline indicator
83
-
84
- **Source:** `status.batteryLevelPercentage`, `status.isCharging`
85
-
86
- ---
87
-
88
- #### Service: TemperatureSensor (OPTIONAL - v3 only)
89
-
90
- Temperature reading from device sensor.
91
-
92
- **Characteristics:**
93
- - `CurrentTemperature` (GET) - Temperature in Celsius
94
- - `StatusFault` (GET) - NO_FAULT (only shown when temperature available)
95
- - `StatusActive` (GET) - Online/offline indicator
96
-
97
- **Source:** `status.temperatureCelsius`
98
- **Availability:** `deviceModel.capabilities.hasTemperatureSensor`
99
-
100
- ---
101
-
102
- #### Service: Lightbulb (subtype: "DayNightlight") - OPTIONAL - v3 only
103
-
104
- Day mode nightlight color and brightness control (config-based).
105
-
106
- **Characteristics:**
107
- - `On` (GET/SET) - Turn nightlight on/off (off states: '0x000000', 'off')
108
- - `Brightness` (GET/SET) - Screen brightness 0-100% (or 'auto')
109
- - `Hue` (GET/SET) - Color hue 0-360° (derived from hex color)
110
- - `Saturation` (GET/SET) - Color saturation 0-100% (derived from hex color)
111
-
112
- **Source:** `config.ambientColour`, `config.dayDisplayBrightness`
113
- **Control:** `updateConfig({ ambientColour: '0xRRGGBB', dayDisplayBrightness: 'N' })`
114
- **Availability:** `deviceModel.capabilities.hasColoredNightlight`
115
-
116
- **Color Conversion:** Uses `color-convert` library for hex ↔ HSV conversion
117
-
118
- ---
119
-
120
- #### Service: Lightbulb (subtype: "NightNightlight") - OPTIONAL - v3 only
121
-
122
- Night mode nightlight color and brightness control (config-based).
123
-
124
- **Characteristics:**
125
- - `On` (GET/SET) - Turn nightlight on/off (off states: '0x000000', 'off')
126
- - `Brightness` (GET/SET) - Screen brightness 0-100% (or 'auto')
127
- - `Hue` (GET/SET) - Color hue 0-360° (derived from hex color)
128
- - `Saturation` (GET/SET) - Color saturation 0-100% (derived from hex color)
129
-
130
- **Source:** `config.nightAmbientColour`, `config.nightDisplayBrightness`
131
- **Control:** `updateConfig({ nightAmbientColour: '0xRRGGBB', nightDisplayBrightness: 'N' })`
132
- **Availability:** `deviceModel.capabilities.hasColoredNightlight`
133
-
134
- ---
135
-
136
- #### Service: ContactSensor (subtype: "NightlightActive") - OPTIONAL - v3 only
137
-
138
- Shows if nightlight is currently active (live device state).
139
-
140
- **Characteristics:**
141
- - `ContactSensorState` (GET) - CONTACT_DETECTED when nightlight on
142
- - `StatusActive` (GET) - Online/offline indicator
143
-
144
- **Source:** `status.nightlightMode !== 'off'`
145
- **Availability:** `deviceModel.capabilities.hasColoredNightlight`
146
-
147
- ---
148
-
149
- #### Service: ContactSensor (subtype: "DayNightlightActive") - OPTIONAL - v3 only
150
-
151
- Shows if day nightlight is currently active and showing.
152
-
153
- **Characteristics:**
154
- - `ContactSensorState` (GET) - CONTACT_DETECTED when day mode + nightlight on
155
- - `StatusActive` (GET) - Online/offline indicator
156
-
157
- **Source:** `status.dayMode === 'day' && status.nightlightMode !== 'off'`
158
- **Availability:** `deviceModel.capabilities.hasColoredNightlight`
159
-
160
- ---
161
-
162
- #### Service: ContactSensor (subtype: "NightNightlightActive") - OPTIONAL - v3 only
163
-
164
- Shows if night nightlight is currently active and showing.
165
-
166
- **Characteristics:**
167
- - `ContactSensorState` (GET) - CONTACT_DETECTED when night mode + nightlight on
168
- - `StatusActive` (GET) - Online/offline indicator
169
-
170
- **Source:** `status.dayMode === 'night' && status.nightlightMode !== 'off'`
171
- **Availability:** `deviceModel.capabilities.hasColoredNightlight`
172
-
173
- ---
174
-
175
- #### Service: ContactSensor (subtype: "CardSlot")
176
-
177
- Shows if a card is currently inserted.
178
-
179
- **Characteristics:**
180
- - `ContactSensorState` (GET) - CONTACT_DETECTED when card present
181
- - `StatusActive` (GET) - Online/offline indicator
182
-
183
- **Source:** `status.cardInsertionState !== 'none'`
184
-
185
- ---
186
-
187
- #### Service: OccupancySensor (subtype: "NightModeStatus")
188
-
189
- Shows if device is in night mode (vs day mode).
190
-
191
- **Characteristics:**
192
- - `OccupancyDetected` (GET) - OCCUPANCY_DETECTED when in night mode
193
- - `StatusActive` (GET) - Online/offline indicator
194
-
195
- **Source:** `status.dayMode === 'night'`
196
-
197
- **Use Case:** Trigger automations based on device's day/night schedule
198
-
199
- ---
200
-
201
- #### Service: Switch (subtype: "SleepTimer")
202
-
203
- Toggle sleep timer on/off.
204
-
205
- **Characteristics:**
206
- - `On` (GET/SET) - Sleep timer active state
207
- - `StatusActive` (GET) - Online/offline indicator
208
-
209
- **Source:** `playback.sleepTimerActive`
210
- **Control:** `sendCommand({ action: 'sleep-timer', minutes: 30 })` (on) or `minutes: 0` (off)
211
-
212
- ---
213
-
214
- #### Service: Switch (subtype: "Bluetooth")
215
-
216
- Toggle Bluetooth on/off (config-based, works offline).
217
-
218
- **Characteristics:**
219
- - `On` (GET/SET) - Bluetooth enabled state
220
-
221
- **Source:** `config.bluetoothEnabled` (string '0' or '1')
222
- **Control:** `updateConfig({ bluetoothEnabled: '1' | '0' })`
223
-
224
- **Note:** No StatusActive - config-based services work offline
225
-
226
- ---
227
-
228
- #### Service: Fanv2 (subtype: "DayMaxVolume")
229
-
230
- Control day mode maximum volume limit (config-based, works offline).
231
-
232
- **Characteristics:**
233
- - `Active` (GET) - Always ACTIVE
234
- - `RotationSpeed` (GET/SET) - Volume limit 0-100%
235
-
236
- **Source:** `config.maxVolumeLimit` (device: 0-16, HomeKit: 0-100%)
237
- **Control:** `updateConfig({ maxVolumeLimit: 'N' })`
238
- **Conversion:** `percentage = (limit / 16) * 100`
239
-
240
- ---
241
-
242
- #### Service: Fanv2 (subtype: "NightMaxVolume")
243
-
244
- Control night mode maximum volume limit (config-based, works offline).
245
-
246
- **Characteristics:**
247
- - `Active` (GET) - Always ACTIVE
248
- - `RotationSpeed` (GET/SET) - Volume limit 0-100%
249
-
250
- **Source:** `config.nightMaxVolumeLimit` (device: 0-16, HomeKit: 0-100%)
251
- **Control:** `updateConfig({ nightMaxVolumeLimit: 'N' })`
252
- **Conversion:** `percentage = (limit / 16) * 100`
253
-
254
- ---
255
-
256
- #### Service: StatelessProgrammableSwitch (DYNAMIC) - NOT YET IMPLEMENTED
257
-
258
- One service per shortcut configured on device. Trigger shortcuts from HomeKit.
259
-
260
- **Characteristics:**
261
- - `ProgrammableSwitchEvent` (READ/NOTIFY) - SINGLE_PRESS event
262
- - `ServiceLabelIndex` (GET) - Index in shortcuts array
263
-
264
- **Source:** `config.shortcuts.modes.day.content[]` and `config.shortcuts.modes.night.content[]`
265
- **Control:** `sendCommand({ action: 'play', shortcut: X })`
266
-
267
- **Note:** Dynamic services - created based on device configuration
268
-
269
- ---
270
-
271
- ### Service Availability Matrix
272
-
273
- | Service | v2 | v3 | mini |
274
- |---------|----|----|------|
275
- | AccessoryInformation | ✅ | ✅ | ✅ |
276
- | SmartSpeaker | ✅ | ✅ | ✅ |
277
- | Battery | ✅ | ✅ | ✅ |
278
- | ContactSensor (CardSlot) | ✅ | ✅ | ✅ |
279
- | OccupancySensor (NightMode) | ✅ | ✅ | ✅ |
280
- | Switch (SleepTimer) | ✅ | ✅ | ✅ |
281
- | Switch (Bluetooth) | ✅ | ✅ | ✅ |
282
- | Fanv2 (Volume Limits) | ✅ | ✅ | ✅ |
283
- | TemperatureSensor | ❌ | ✅ | ❌ |
284
- | Lightbulb (Nightlights) | ❌ | ✅ | ❌ |
285
- | ContactSensor (Nightlight Status) | ❌ | ✅ | ❌ |
286
- | StatelessProgrammableSwitch | 🚧 | 🚧 | 🚧 |
287
-
288
- ---
289
-
290
- ## Offline Behavior
291
-
292
- ### What Gets Marked as "No Response"
293
-
294
- Services are categorized by data source:
295
-
296
- **Device State Services** (require online connection):
297
- - SmartSpeaker (playback, volume)
298
- - Battery (level, charging)
299
- - TemperatureSensor (temperature)
300
- - ContactSensor - all variants (card, nightlight status)
301
- - OccupancySensor (night mode)
302
- - Switch (SleepTimer) - reads playback state
303
-
304
- **Config-Based Services** (work offline):
305
- - Lightbulb (nightlight color/brightness)
306
- - Fanv2 (volume limits)
307
- - Switch (Bluetooth)
308
- - StatelessProgrammableSwitch (shortcuts)
309
-
310
- ### Implementation
311
-
312
- Device state services use `StatusActive` characteristic to indicate offline status. When `StatusActive` is false, HomeKit shows "No Response" for the service.
313
-
314
- Config-based services do NOT have `StatusActive` and remain accessible when offline since they only read/write device configuration (cached in `deviceModel.config`).
315
-
316
- ---
317
-
318
- ## ❌ What's Left to Implement
319
-
320
- ### StatelessProgrammableSwitch Services (Shortcuts)
321
-
322
- Dynamic services created based on `config.shortcuts` configuration.
323
-
324
- **Implementation Notes:**
325
- - Parse `config.shortcuts.modes.day.content[]` and `config.shortcuts.modes.night.content[]`
326
- - Create one service per unique shortcut
327
- - Handle shortcuts refresh when config changes
328
- - Trigger with `sendCommand({ action: 'play', shortcut: X })`
329
-
330
- **Complexity:** Dynamic service lifecycle management, shortcut identification
331
-
332
- ---
333
-
334
- ## API Reference Documentation
335
-
336
- ### YotoDeviceModel - State & Events
337
-
338
- **State Properties:**
339
- - `device` - Device metadata (deviceId, name, deviceType, etc.)
340
- - `status` - Live device status (volume, battery, temperature, etc.)
341
- - `config` - Device configuration (nightlight colors, volume limits, etc.)
342
- - `shortcuts` - Configured shortcuts
343
- - `playback` - Playback state (status, track, sleep timer, etc.)
344
-
345
- **Events:**
346
- - `statusUpdate(status, source, changedFields)` - Device status changed
347
- - `configUpdate(config, changedFields)` - Configuration changed
348
- - `playbackUpdate(playback, changedFields)` - Playback state changed
349
- - `online({ reason })` - Device came online
350
- - `offline({ reason })` - Device went offline
351
-
352
- ### Device API Endpoints
353
-
354
- - `GET /devices` - List all devices
355
- - `GET /devices/:deviceId/config` - Get device config
356
- - `GET /devices/:deviceId/status` - Get device status
357
- - `POST /devices/:deviceId/config` - Update device config
358
- - `POST /devices/:deviceId/mq` - Send device command
359
-
360
- ### Command Examples
361
-
362
- ```javascript
363
- // Playback control
364
- await deviceModel.sendCommand({ action: 'play' })
365
- await deviceModel.sendCommand({ action: 'pause' })
366
- await deviceModel.sendCommand({ action: 'stop' })
367
-
368
- // Volume control
369
- await deviceModel.sendCommand({ action: 'volume', volume: 10 })
370
-
371
- // Sleep timer
372
- await deviceModel.sendCommand({ action: 'sleep-timer', minutes: 30 })
373
- ```
374
-
375
- ### Config Update Examples
376
-
377
- ```javascript
378
- // Nightlight colors
379
- await deviceModel.updateConfig({ ambientColour: '0xff5733' })
380
- await deviceModel.updateConfig({ nightAmbientColour: '0x3366ff' })
381
-
382
- // Volume limits
383
- await deviceModel.updateConfig({ maxVolumeLimit: '12' })
384
- await deviceModel.updateConfig({ nightMaxVolumeLimit: '8' })
385
-
386
- // Bluetooth
387
- await deviceModel.updateConfig({ bluetoothEnabled: '1' })
388
- ```
389
-
390
- ---
391
-
392
- ## Testing Checklist
393
-
394
- ### Basic Functionality
395
- - [ ] Device discovery and accessory creation
396
- - [ ] Playback control (play/pause/stop)
397
- - [ ] Volume control (0-16 range)
398
- - [ ] Battery status updates
399
- - [ ] Online/offline detection
400
- - [ ] Firmware version display
401
-
402
- ### Optional Services
403
- - [ ] Temperature sensor (v3 only)
404
- - [ ] Nightlight color control (v3 only)
405
- - [ ] Nightlight brightness control (v3 only)
406
- - [ ] Nightlight status sensors (v3 only)
407
- - [ ] Card slot detection (all devices)
408
- - [ ] Night mode detection (all devices)
409
- - [ ] Sleep timer control (all devices)
410
- - [ ] Bluetooth toggle (all devices)
411
- - [ ] Volume limit controls (all devices)
412
-
413
- ### Edge Cases
414
- - [ ] Device goes offline during operation
415
- - [ ] MQTT disconnection with HTTP fallback
416
- - [ ] Multiple devices in one account
417
- - [ ] Device rename handling
418
- - [ ] Config changes from Yoto app
419
-
420
- ### Automations
421
- - [ ] Trigger on card insertion
422
- - [ ] Trigger on night mode change
423
- - [ ] Trigger on nightlight activation
424
- - [ ] Volume limit changes based on time
425
- - [ ] Sleep timer activation
package/eslint.config.js DELETED
@@ -1,7 +0,0 @@
1
- import neostandard, { resolveIgnoresFromGitignore } from 'neostandard'
2
-
3
- export default neostandard({
4
- ignores: [
5
- ...resolveIgnoresFromGitignore(),
6
- ],
7
- })
package/index.test.js DELETED
@@ -1,7 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import homebridgeYoto from './index.js'
4
-
5
- test('exports default function', () => {
6
- assert.strictEqual(typeof homebridgeYoto, 'function')
7
- })
package/logo.png DELETED
Binary file
@@ -1,4 +0,0 @@
1
- strictDepBuilds: true
2
-
3
- ignoredBuiltDependencies:
4
- - unrs-resolver
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "extends": "@voxpelli/tsconfig/node20.json",
3
- "compilerOptions": {
4
- "skipLibCheck": true
5
- },
6
- "include": [
7
- "**/*"
8
- ],
9
- "exclude": [
10
- "node_modules",
11
- "coverage",
12
- ".github"
13
- ]
14
- }