homebridge-yoto 0.0.28 → 0.0.31
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/AGENTS.md +42 -157
- package/CHANGELOG.md +13 -5
- package/NOTES.md +87 -0
- package/PLAN.md +320 -504
- package/README.md +18 -314
- package/config.schema.cjs +3 -0
- package/config.schema.json +19 -155
- package/homebridge-ui/server.js +264 -0
- package/index.js +1 -1
- package/index.test.js +1 -1
- package/lib/accessory.js +1870 -0
- package/lib/constants.js +8 -149
- package/lib/platform.js +303 -363
- package/lib/sanitize-name.js +49 -0
- package/lib/settings.js +16 -0
- package/lib/sync-service-names.js +34 -0
- package/logo.png +0 -0
- package/package.json +17 -22
- package/pnpm-workspace.yaml +4 -0
- package/declaration.tsconfig.json +0 -15
- package/lib/auth.js +0 -237
- package/lib/playerAccessory.js +0 -1724
- package/lib/types.js +0 -253
- package/lib/yotoApi.js +0 -270
- package/lib/yotoMqtt.js +0 -570
package/AGENTS.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
This document contains patterns, conventions, and guidelines for developing the homebridge-yoto plugin.
|
|
4
4
|
|
|
5
|
+
## Dont write summary markdown files unless asked to do so
|
|
6
|
+
|
|
5
7
|
## JSDoc Typing Patterns
|
|
6
8
|
|
|
7
9
|
### Use TypeScript-in-JavaScript (ts-in-js)
|
|
@@ -80,174 +82,57 @@ import { EventEmitter } from 'events';
|
|
|
80
82
|
/** @import { API, PlatformAccessory } from 'homebridge' */
|
|
81
83
|
```
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Homebridge types are available from the `homebridge` package:
|
|
86
|
-
|
|
87
|
-
```javascript
|
|
88
|
-
/** @import { API, Characteristic, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service } from 'homebridge' */
|
|
89
|
-
/** @import { CharacteristicValue } from 'homebridge' */
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
For HAP (HomeKit Accessory Protocol) types:
|
|
93
|
-
|
|
94
|
-
```javascript
|
|
95
|
-
/** @import { HAPStatus } from 'homebridge' */
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* @throws {import('homebridge').HapStatusError}
|
|
99
|
-
*/
|
|
100
|
-
function throwNotResponding() {
|
|
101
|
-
throw new this.api.hap.HapStatusError(this.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Prefer Schema-Based Types
|
|
106
|
-
|
|
107
|
-
Define types for API responses and configuration objects using JSDoc typedef.
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
/**
|
|
111
|
-
* @typedef {Object} YotoDeviceStatus
|
|
112
|
-
* @property {string} deviceId
|
|
113
|
-
* @property {number} batteryLevelPercentage
|
|
114
|
-
* @property {boolean} isCharging
|
|
115
|
-
* @property {boolean} isOnline
|
|
116
|
-
* @property {string | null} activeCard
|
|
117
|
-
* @property {number} userVolumePercentage
|
|
118
|
-
* @property {number} systemVolumePercentage
|
|
119
|
-
* @property {number} temperatureCelcius
|
|
120
|
-
* @property {number} wifiStrength
|
|
121
|
-
* @property {0 | 1 | 2} cardInsertionState - 0=none, 1=physical, 2=remote
|
|
122
|
-
* @property {-1 | 0 | 1} dayMode - -1=unknown, 0=night, 1=day
|
|
123
|
-
* @property {0 | 1 | 2 | 3} powerSource - 0=battery, 1=V2 dock, 2=USB-C, 3=Qi
|
|
124
|
-
*/
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* @typedef {Object} YotoDevice
|
|
128
|
-
* @property {string} deviceId
|
|
129
|
-
* @property {string} name
|
|
130
|
-
* @property {string} description
|
|
131
|
-
* @property {boolean} online
|
|
132
|
-
* @property {string} releaseChannel
|
|
133
|
-
* @property {string} deviceType
|
|
134
|
-
* @property {string} deviceFamily
|
|
135
|
-
* @property {string} deviceGroup
|
|
136
|
-
*/
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* @typedef {Object} YotoDeviceConfig
|
|
140
|
-
* @property {string} name
|
|
141
|
-
* @property {YotoDeviceConfigSettings} config
|
|
142
|
-
*/
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @typedef {Object} YotoDeviceConfigSettings
|
|
146
|
-
* @property {any[]} alarms
|
|
147
|
-
* @property {string} ambientColour
|
|
148
|
-
* @property {string} bluetoothEnabled
|
|
149
|
-
* @property {boolean} btHeadphonesEnabled
|
|
150
|
-
* @property {string} clockFace
|
|
151
|
-
* @property {string} dayDisplayBrightness
|
|
152
|
-
* @property {string} dayTime
|
|
153
|
-
* @property {string} maxVolumeLimit
|
|
154
|
-
* @property {string} nightAmbientColour
|
|
155
|
-
* @property {string} nightDisplayBrightness
|
|
156
|
-
* @property {string} nightMaxVolumeLimit
|
|
157
|
-
* @property {string} nightTime
|
|
158
|
-
* @property {boolean} repeatAll
|
|
159
|
-
* @property {string} shutdownTimeout
|
|
160
|
-
* @property {string} volumeLevel
|
|
161
|
-
*/
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### API Response Typing
|
|
85
|
+
## Event Pattern Preference
|
|
165
86
|
|
|
166
|
-
|
|
87
|
+
### Use Aggregate Events with Exhaustive Type Narrowing
|
|
167
88
|
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* @typedef {Object} YotoApiDevicesResponse
|
|
171
|
-
* @property {YotoDevice[]} devices
|
|
172
|
-
*/
|
|
89
|
+
The `yoto-nodejs-client` library should emit **only aggregate events** with `changedFields` arrays:
|
|
173
90
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
*/
|
|
178
|
-
async function getDevices() {
|
|
179
|
-
const response = await fetch('https://api.yotoplay.com/device-v2/devices/mine', {
|
|
180
|
-
headers: { 'Authorization': `Bearer ${this.accessToken}` }
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
/** @type {YotoApiDevicesResponse} */
|
|
184
|
-
const data = await response.json();
|
|
185
|
-
return data.devices;
|
|
186
|
-
}
|
|
187
|
-
```
|
|
91
|
+
- `statusUpdate` - passes `(status, source, changedFields[])`
|
|
92
|
+
- `configUpdate` - passes `(config, changedFields[])`
|
|
93
|
+
- `playbackUpdate` - passes `(playback, changedFields[])`
|
|
188
94
|
|
|
189
|
-
|
|
95
|
+
**Do NOT create granular field events** like `volumeChanged`, `batteryLevelChanged`, etc.
|
|
190
96
|
|
|
191
|
-
|
|
97
|
+
### Homebridge Plugin Pattern
|
|
192
98
|
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* @typedef {Object} YotoAccessoryContext
|
|
196
|
-
* @property {YotoDevice} device
|
|
197
|
-
* @property {YotoDeviceStatus | null} lastStatus
|
|
198
|
-
* @property {number} lastUpdate
|
|
199
|
-
*/
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* @param {PlatformAccessory<YotoAccessoryContext>} accessory
|
|
203
|
-
*/
|
|
204
|
-
function configureAccessory(accessory) {
|
|
205
|
-
const device = accessory.context.device;
|
|
206
|
-
this.log.info('Restoring device:', device.name);
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Nullable Fields in API Responses
|
|
211
|
-
|
|
212
|
-
Use union types with `null` for fields that may be absent:
|
|
99
|
+
The plugin should use exhaustive switch statements to handle field updates:
|
|
213
100
|
|
|
214
101
|
```javascript
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
102
|
+
this.deviceModel.on('statusUpdate', (status, source, changedFields) => {
|
|
103
|
+
for (const field of changedFields) {
|
|
104
|
+
switch (field) {
|
|
105
|
+
case 'volume':
|
|
106
|
+
// Update volume characteristic
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
case 'batteryLevelPercentage':
|
|
110
|
+
// Update battery characteristic
|
|
111
|
+
break
|
|
112
|
+
|
|
113
|
+
// ... handle all fields
|
|
114
|
+
|
|
115
|
+
case 'firmwareVersion':
|
|
116
|
+
// Empty case - available but not used yet
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
default: {
|
|
120
|
+
// Exhaustive check - TypeScript error if field missed
|
|
121
|
+
const _exhaustive: never = field
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})
|
|
223
127
|
```
|
|
224
128
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
* @typedef {Object} YotoPlayerState
|
|
232
|
-
* @property {string} deviceId - Always present
|
|
233
|
-
* @property {string | null} activeCard - Present but may be null
|
|
234
|
-
* @property {string} [lastPlayedCard] - May not be present in response
|
|
235
|
-
*/
|
|
236
|
-
```
|
|
129
|
+
**Benefits:**
|
|
130
|
+
- Fewer event listeners (3 instead of 20+)
|
|
131
|
+
- Exhaustive TypeScript checking ensures all fields handled
|
|
132
|
+
- Empty cases document available fields
|
|
133
|
+
- Easy to extend - just fill in empty cases
|
|
134
|
+
- Type-safe with proper field typing
|
|
237
135
|
|
|
238
136
|
## Changelog Management
|
|
239
137
|
|
|
240
138
|
**NEVER manually edit CHANGELOG.md**
|
|
241
|
-
|
|
242
|
-
The changelog is automatically generated using `auto-changelog` during the version bump process:
|
|
243
|
-
|
|
244
|
-
- When running `npm version [patch|minor|major]`, the `version:changelog` script runs automatically
|
|
245
|
-
- It uses the keepachangelog template
|
|
246
|
-
- Detects breaking changes via `BREAKING CHANGE:` pattern in commit messages
|
|
247
|
-
- Generates entries from git commits
|
|
248
|
-
|
|
249
|
-
To ensure proper changelog generation:
|
|
250
|
-
- Write meaningful git commit messages
|
|
251
|
-
- Use conventional commit format when possible
|
|
252
|
-
- Mark breaking changes with `BREAKING CHANGE:` in commit body
|
|
253
|
-
- Let the automation handle changelog updates during `npm version`
|
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
2
3
|
All notable changes to this project will be documented in this file.
|
|
3
|
-
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
9
|
+
|
|
10
|
+
## 0.0.31
|
|
11
|
+
|
|
12
|
+
### Commits
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
14
|
+
- init [`88b35b6`](https://github.com/bcomnes/homebridge-yoto/commit/88b35b695166c6f5c3e3ec381c4afb52940a369b)
|
|
15
|
+
- WIP [`cf0e5c8`](https://github.com/bcomnes/homebridge-yoto/commit/cf0e5c85df5984eb7fe10a2118de08990d875337)
|
|
16
|
+
- Implement against client [`27d4fae`](https://github.com/bcomnes/homebridge-yoto/commit/27d4fae930a84f12a4671d3cd129c299505e398b)
|
package/NOTES.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Homebridge Notes
|
|
2
|
+
http://bee.local:8581/login
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
- Instance: https://homebridge.io
|
|
6
|
+
- Homepage: https://homebridge.io
|
|
7
|
+
- Plugin Docs: https://developers.homebridge.io/#/
|
|
8
|
+
- Wiki: https://github.com/homebridge/homebridge/wiki
|
|
9
|
+
- Homebridge package: https://github.com/homebridge/homebridge
|
|
10
|
+
- Plugins Repo: https://github.com/homebridge/plugins
|
|
11
|
+
- Plugin UI Utils: https://github.com/homebridge/plugin-ui-utils
|
|
12
|
+
- HAP Client: https://github.com/homebridge/hap-client
|
|
13
|
+
- HAP-NodeJS: https://github.com/homebridge/HAP-NodeJS
|
|
14
|
+
- HAP-NodeJS JSDocs: https://developers.homebridge.io/HAP-NodeJS/modules.html
|
|
15
|
+
- Old docs?: https://github.com/homebridge/documentation
|
|
16
|
+
- PLugin Tempalte: https://github.com/homebridge/homebridge-plugin-template
|
|
17
|
+
- Template lib helper thing: https://github.com/ebaauw/homebridge-lib
|
|
18
|
+
- Ring: https://github.com/homebridge-plugins/homebridge-meross/blob/9ea479ea94a8cfa8dce9051976fafb8308faae85/lib/platform.js#L1037
|
|
19
|
+
- Scoped Plugins (Reference) https://github.com/homebridge/plugins/wiki/Scoped-Plugins#available-plugins
|
|
20
|
+
- https://github.com/homebridge-plugins/homebridge-rainbird/blob/latest/src/platform.ts
|
|
21
|
+
- https://github.com/homebridge-plugins/homebridge-unifi-network
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Yoto Docs:
|
|
25
|
+
- https://yoto.dev/get-started/start-here/
|
|
26
|
+
- https://yoto.dev/api/
|
|
27
|
+
- https://yoto.space/developers
|
|
28
|
+
- https://yoto.space/developers/post/home-assistant-integration-Ao9OxbsMDBqaG9I
|
|
29
|
+
- https://github.com/yotoplay/examples
|
|
30
|
+
- https://github.com/yotoplay/twine-to-yoto
|
|
31
|
+
|
|
32
|
+
MQTT
|
|
33
|
+
- MQTT: https://github.com/mqttjs/MQTT.js
|
|
34
|
+
- https://mqtt.org
|
|
35
|
+
- https://www.emqx.com/en/blog/the-easiest-guide-to-getting-started-with-mqtt
|
|
36
|
+
- https://aws.amazon.com/what-is/mqtt/
|
|
37
|
+
- https://en.wikipedia.org/wiki/MQTT
|
|
38
|
+
-
|
|
39
|
+
|
|
40
|
+
- https://github.com/ebaauw/homebridge-zp/issues/10#issuecomment-270743358
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
- Implement a improved config type, and populate it.
|
|
44
|
+
- Audit the set functions in hb, make sure the client works/helps with that.
|
|
45
|
+
- Clean up unused vars and config
|
|
46
|
+
- Audit online/offline config set methods
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
[12/26/2025, 4:05:52 PM] [homebridge-yoto] Device discovered: GGs Boombox (y29sZvOdk63vN50Qed5eH4Y0)
|
|
50
|
+
[12/26/2025, 4:05:52 PM] [homebridge-yoto] Adding new accessory: GGs Boombox
|
|
51
|
+
[12/26/2025, 4:05:52 PM] [homebridge-yoto] [Accessory] Setting up GGs Boombox
|
|
52
|
+
[GGs Boombox@GGs Boombox@Status Active] Characteristic not in required or optional characteristic section for service SmartSpeaker. Adding anyway.
|
|
53
|
+
[GGs Boombox@GGs Boombox Battery@Status Active] Characteristic not in required or optional characteristic section for service Battery. Adding anyway.
|
|
54
|
+
[12/26/2025, 4:05:52 PM] [homebridge-yoto] [Accessory] ✓ GGs Boombox ready
|
|
55
|
+
[12/26/2025, 4:05:52 PM] [homebridge-yoto] Publishing new external accessory: GGs Boombox
|
|
56
|
+
[12/26/2025, 4:05:52 PM] GGs Boombox 55FC is running on port 45125.
|
|
57
|
+
[12/26/2025, 4:05:52 PM] Please add [GGs Boombox 55FC] manually in Home app. Setup Code: 547-78-744
|
|
58
|
+
[12/26/2025, 4:05:54 PM] [homebridge-yoto] Device discovered: Gigis mini (y2JVD5AxvJEeaK1g6nvBWkej)
|
|
59
|
+
[12/26/2025, 4:05:54 PM] [homebridge-yoto] Adding new accessory: Gigis mini
|
|
60
|
+
[12/26/2025, 4:05:54 PM] [homebridge-yoto] [Accessory] Setting up Gigis mini
|
|
61
|
+
[Gigis mini@Gigis mini@Status Active] Characteristic not in required or optional characteristic section for service SmartSpeaker. Adding anyway.
|
|
62
|
+
[Gigis mini@Gigis mini Battery@Status Active] Characteristic not in required or optional characteristic section for service Battery. Adding anyway.
|
|
63
|
+
[12/26/2025, 4:05:54 PM] [homebridge-yoto] [Accessory] ✓ Gigis mini ready
|
|
64
|
+
[12/26/2025, 4:05:54 PM] [homebridge-yoto] Publishing new external accessory: Gigis mini
|
|
65
|
+
[12/26/2025, 4:05:54 PM] Gigis mini 8D02 is running on port 38987.
|
|
66
|
+
[12/26/2025, 4:05:54 PM] Please add [Gigis mini 8D02] manually in Home app. Setup Code: 547-78-744
|
|
67
|
+
[12/26/2025, 4:05:55 PM] [homebridge-yoto] Device discovered: Otto's mini (y2iYT7ItkMRWbuMzTDuGjgmS)
|
|
68
|
+
[12/26/2025, 4:05:55 PM] [homebridge-yoto] Adding new accessory: Otto's mini
|
|
69
|
+
[12/26/2025, 4:05:55 PM] [homebridge-yoto] [Accessory] Setting up Otto's mini
|
|
70
|
+
[Otto's mini @Otto's mini @Status Active] Characteristic not in required or optional characteristic section for service SmartSpeaker. Adding anyway.
|
|
71
|
+
[Otto's mini @Otto's mini Battery@Status Active] Characteristic not in required or optional characteristic section for service Battery. Adding anyway.
|
|
72
|
+
[12/26/2025, 4:05:55 PM] [homebridge-yoto] [Accessory] ✓ Otto's mini ready
|
|
73
|
+
[12/26/2025, 4:05:55 PM] [homebridge-yoto] Publishing new external accessory: Otto's mini
|
|
74
|
+
[12/26/2025, 4:05:55 PM] Otto's mini 024E is running on port 33839.
|
|
75
|
+
[12/26/2025, 4:05:55 PM] Please add [Otto's mini 024E] manually in Home app. Setup Code: 547-78-744
|
|
76
|
+
[12/26/2025, 4:05:57 PM] [homebridge-yoto] Device discovered: Ottos Boombox (y2knTzvDkKVtvnhl09z9bWfe)
|
|
77
|
+
[12/26/2025, 4:05:57 PM] [homebridge-yoto] Adding new accessory: Ottos Boombox
|
|
78
|
+
[12/26/2025, 4:05:57 PM] [homebridge-yoto] [Accessory] Setting up Ottos Boombox
|
|
79
|
+
[Ottos Boombox@Ottos Boombox@Status Active] Characteristic not in required or optional characteristic section for service SmartSpeaker. Adding anyway.
|
|
80
|
+
[Ottos Boombox@Ottos Boombox Battery@Status Active] Characteristic not in required or optional characteristic section for service Battery. Adding anyway.
|
|
81
|
+
[12/26/2025, 4:05:57 PM] [homebridge-yoto] [Accessory] ✓ Ottos Boombox ready
|
|
82
|
+
[12/26/2025, 4:05:57 PM] [homebridge-yoto] Publishing new external accessory: Ottos Boombox
|
|
83
|
+
[12/26/2025, 4:05:57 PM] [homebridge-yoto] ✓ Yoto account started with 4 device(s)
|
|
84
|
+
[12/26/2025, 4:05:57 PM] Ottos Boombox 3486 is running on port 37845.
|
|
85
|
+
[12/26/2025, 4:05:57 PM] Please add [Ottos Boombox 3486] manually in Home app. Setup Code: 547-78-744
|
|
86
|
+
[12/26/2025, 4:05:59 PM] [homebridge-yoto] This plugin generated a warning from the characteristic 'Brightness': characteristic was supplied illegal value: number 255 exceeded maximum of 100. See https://homebridge.io/w/JtMGR for more info.
|
|
87
|
+
[12/26/2025, 4:05:59 PM] [homebridge-yoto] This plugin generated a warning from the characteristic 'Brightness': characteristic was supplied illegal value: number 255 exceeded maximum of 100. See https://homebridge.io/w/JtMGR for more info.
|