@mdjhnson/homebridge-yoto 0.1.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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Bret Comnes
4
+ Copyright (c) 2026 mdjhnson
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ <p align="center">
2
+
3
+ <img src="https://github.com/homebridge/branding/raw/latest/logos/homebridge-wordmark-logo-vertical.png" width="150">
4
+
5
+ <img src="./logo.png" width="150">
6
+
7
+ </p>
8
+
9
+ <span align="center">
10
+
11
+ # @mdjhnson/homebridge-yoto
12
+
13
+ </span>
14
+
15
+ <span align="center">
16
+
17
+ [![latest version](https://img.shields.io/npm/v/@mdjhnson/homebridge-yoto.svg)](https://www.npmjs.com/package/@mdjhnson/homebridge-yoto)
18
+ [![Actions Status](https://github.com/mdjhnson/homebridge-yoto/workflows/tests/badge.svg)](https://github.com/mdjhnson/homebridge-yoto/actions)
19
+ ![Types in JS](https://img.shields.io/badge/types_in_js-yes-brightgreen)
20
+ [![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-7fffff?style=flat&labelColor=ff80ff)](https://github.com/neostandard/neostandard)
21
+
22
+ </span>
23
+
24
+ Homebridge plugin that exposes Yoto players to HomeKit: playback and volume, card and shortcut buttons, battery, temperature, nightlights, and more. Updates arrive in real time over MQTT, with HTTP polling as a fallback.
25
+
26
+ This is a maintained fork of [bcomnes/homebridge-yoto](https://github.com/bcomnes/homebridge-yoto).
27
+
28
+ ## Install
29
+
30
+ Search for `@mdjhnson/homebridge-yoto` in the Homebridge UI **Plugins** tab, or run:
31
+
32
+ ```sh
33
+ npm install -g @mdjhnson/homebridge-yoto
34
+ ```
35
+
36
+ Requires Node.js 22+ and Homebridge 1.8+ or 2.x.
37
+
38
+ ## Sign in
39
+
40
+ 1. Open the plugin's **Settings** in the Homebridge UI and click **Sign in with Yoto**. Yoto's sign-in page opens in a new tab.
41
+ 2. Sign in and approve access.
42
+ 3. Your browser then shows *"This site can't be reached"* at `127.0.0.1`. That's expected. Copy the full address from the address bar, paste it into the plugin settings, and click **Finish Sign-in**.
43
+ 4. Restart Homebridge.
44
+
45
+ Using your own Yoto developer app? Make it a **Public Client**, add `http://127.0.0.1:8787/callback` as an allowed callback URL, enable the `family:devices:*`, `family:library:view`, `user:content:view` and `offline_access` scopes, and enter its client ID under **Advanced Settings** before signing in.
46
+
47
+ The plugin asks for access to view, configure and control your players, plus read-only access to your card library (used to name shortcut switches). If you signed in with an older version, sign in again so the new permissions apply.
48
+
49
+ The plugin refreshes its tokens on its own. If the login ever expires or is revoked, the Homebridge log will say so. Sign in again from the plugin settings.
50
+
51
+ ## Settings
52
+
53
+ All options live under **Accessory Services** in the plugin settings.
54
+
55
+ **Playback**
56
+ - **Playback Controls**: Adds a play/pause switch and a volume dimmer to each player's bridged accessory.
57
+ - **External Smart Speaker**: Publishes a separate Smart Speaker accessory for playback and volume.
58
+ - **TV Playback Accessory**: Publishes a separate TV-style accessory. Its inputs play your card controls and shortcuts, and you control it with the iOS remote.
59
+
60
+ External accessories must be added by hand in the Home app (**Add Accessory → More options**) using the setup code in the Homebridge log.
61
+
62
+ **Card Controls** (`services.cardControls`)
63
+ - A switch on each player that plays the card ID you configure.
64
+ - Optional **Play on All Yotos**: a separate accessory that plays the card on every online player.
65
+
66
+ **Shortcuts** (`services.shortcuts`)
67
+ - A switch for each shortcut configured on the player in the Yoto app. Turning it on plays that shortcut's card, chapter and track.
68
+ - Switches are named after the card and update when you change the shortcuts in the Yoto app.
69
+
70
+ **Service toggles**
71
+ - **Battery**, **Temperature Sensor** (v3), **Nightlight** (v3), **Card Slot**, **Day Mode**, **Sleep Timer**, **Bluetooth**, **Volume Limits**.
72
+
73
+ **Advanced**
74
+ - **HTTP Poll Interval**: How often to poll the Yoto API as a fallback to MQTT. Defaults to 60 seconds; the minimum is 10 seconds.
75
+
76
+ ## HomeKit services
77
+
78
+ **Playback (bridged)**
79
+ - **Playback**: Switch. On resumes, Off pauses.
80
+ - **Volume**: Lightbulb. On unmutes, Off mutes, and Brightness maps 0–100% to the player's volume steps.
81
+
82
+ **Smart Speaker (external)**
83
+ - Current/Target Media State, Volume, Mute, and online status. Stop pauses, so playback can be resumed.
84
+
85
+ **TV Playback (external)**
86
+ - Active is on while the player is playing. Turning it off pauses.
87
+ - Inputs: **Now Playing**, then one per card control and one per shortcut. Choosing an input plays its card.
88
+ - Remote: Play/Pause and Select toggle playback. Volume buttons step the volume.
89
+
90
+ **Card Controls and Shortcuts**
91
+ - Momentary switches that start their card and then turn back off.
92
+
93
+ **Device status**
94
+ - **Online Status**: Contact sensor. Contact Not Detected means online.
95
+ - **Battery**: Battery level, charging state, and low battery.
96
+ - **Temperature**: Temperature sensor (v3).
97
+
98
+ **Nightlight** (v3)
99
+ - **Day Nightlight / Night Nightlight**: Lightbulbs with On/Off, Brightness, Hue, and Saturation.
100
+ - **Nightlight Active / Day Nightlight Active / Night Nightlight Active**: Contact sensors for the live nightlight state.
101
+
102
+ **Other controls**
103
+ - **Card Slot**: Contact sensor for card insertion.
104
+ - **Day Mode**: Contact sensor. Contact Not Detected means day mode.
105
+ - **Sleep Timer**: Switch that turns the sleep timer on or off.
106
+ - **Bluetooth**: Switch that toggles Bluetooth.
107
+ - **Day/Night Max Volume**: Lightbulbs whose brightness sets the max volume limits.
108
+
109
+ ## Notes
110
+
111
+ - **Switching from `homebridge-yoto`:** uninstall the original plugin first. Both register the `Yoto` platform and would conflict. Your existing `Yoto` config block keeps working, but bridged accessories are re-created, so you'll need to re-add them to rooms and automations.
112
+ - **Removed external accessories:** if you turn off the Smart Speaker or TV accessory, or a player leaves your account, remove the old accessory from the Home app by hand. Homebridge can't unpublish external accessories.
113
+
114
+ ## Development
115
+
116
+ ```sh
117
+ npm install
118
+ npm test # eslint + tsc + node:test with coverage
119
+ ```
120
+
121
+ Releases are cut with the **npm bump** GitHub Action (Actions → npm bump → Run workflow). It publishes with [npm trusted publishing](https://docs.npmjs.com/trusted-publishers), so no npm token is stored in the repo.
122
+
123
+ ## License
124
+
125
+ MIT © [Bret Comnes](https://bret.io) and mdjhnson
126
+
127
+ ## Acknowledgments
128
+
129
+ - [Bret Comnes](https://github.com/bcomnes) wrote the original plugin and [yoto-nodejs-client](https://github.com/bcomnes/yoto-nodejs-client).
130
+ - Thanks to [Yoto](https://yoto.io) for their API.
131
+ - Built with [Homebridge](https://homebridge.io).
@@ -0,0 +1,11 @@
1
+ /** @type {import('./config.schema.json')} */
2
+ const configSchema = require('./config.schema.json')
3
+
4
+ const serviceSchema = configSchema.schema.properties.services.properties
5
+
6
+ /**
7
+ * @typedef {keyof typeof serviceSchema} ServiceSchemaKey
8
+ */
9
+
10
+ exports.configSchema = configSchema
11
+ exports.serviceSchema = serviceSchema
@@ -0,0 +1,231 @@
1
+ {
2
+ "pluginAlias": "Yoto",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "customUi": true,
6
+ "headerDisplay": "**Homebridge Yoto Plugin** - Control your Yoto players through HomeKit",
7
+ "footerDisplay": "For help and documentation, visit the [GitHub repository](https://github.com/mdjhnson/homebridge-yoto)",
8
+ "schema": {
9
+ "type": "object",
10
+ "properties": {
11
+ "clientId": {
12
+ "title": "OAuth Client ID",
13
+ "type": "string",
14
+ "default": "tpc_ot5BY24FLyZoCX9MnykipB",
15
+ "required": false,
16
+ "description": "OAuth client ID for Yoto API authentication. The default works for most users. Only change if you've created your own OAuth app at yoto.dev.",
17
+ "x-schema-form": {
18
+ "hidden": false
19
+ }
20
+ },
21
+ "accessToken": {
22
+ "title": "Access Token",
23
+ "type": "string",
24
+ "required": false,
25
+ "description": "Automatically managed by the authentication flow. Do not edit manually.",
26
+ "x-schema-form": {
27
+ "type": "password"
28
+ }
29
+ },
30
+ "refreshToken": {
31
+ "title": "Refresh Token",
32
+ "type": "string",
33
+ "required": false,
34
+ "description": "Automatically managed by the authentication flow. Do not edit manually.",
35
+ "x-schema-form": {
36
+ "type": "password"
37
+ }
38
+ },
39
+ "tokenExpiresAt": {
40
+ "title": "Token Expiration",
41
+ "type": "number",
42
+ "required": false,
43
+ "description": "Automatically managed by the authentication flow. Do not edit manually.",
44
+ "x-schema-form": {
45
+ "hidden": true
46
+ }
47
+ },
48
+ "httpPollIntervalMs": {
49
+ "title": "HTTP Poll Interval (ms)",
50
+ "type": "integer",
51
+ "default": 60000,
52
+ "minimum": 10000,
53
+ "required": false,
54
+ "description": "How often to poll the Yoto API for device status and config as a fallback to MQTT updates. Defaults to 60000 (1 minute)."
55
+ },
56
+ "services": {
57
+ "title": "Accessory Services",
58
+ "type": "object",
59
+ "properties": {
60
+ "playbackControls": {
61
+ "title": "Playback Controls",
62
+ "type": "boolean",
63
+ "default": false,
64
+ "description": "Expose bridged playback switch and volume dimmer controls."
65
+ },
66
+ "smartSpeaker": {
67
+ "title": "External Smart Speaker",
68
+ "type": "boolean",
69
+ "default": false,
70
+ "description": "Publish an external Smart Speaker accessory for playback and volume controls. Requires additional pairing steps and appears as a separate accessory."
71
+ },
72
+ "television": {
73
+ "title": "TV Playback Accessory",
74
+ "type": "boolean",
75
+ "default": false,
76
+ "description": "Publish an external playback accessory using the TV service with input and speaker controls. Requires additional pairing steps and appears as a separate accessory."
77
+ },
78
+ "battery": {
79
+ "title": "Battery",
80
+ "type": "boolean",
81
+ "default": true,
82
+ "description": "Expose battery status service."
83
+ },
84
+ "temperature": {
85
+ "title": "Temperature Sensor",
86
+ "type": "boolean",
87
+ "default": true,
88
+ "description": "Expose temperature sensor when supported."
89
+ },
90
+ "nightlight": {
91
+ "title": "Nightlight",
92
+ "type": "boolean",
93
+ "default": false,
94
+ "description": "Expose day/night nightlight controls and status."
95
+ },
96
+ "cardSlot": {
97
+ "title": "Card Slot",
98
+ "type": "boolean",
99
+ "default": true,
100
+ "description": "Expose card insertion status."
101
+ },
102
+ "cardControls": {
103
+ "title": "Card Controls",
104
+ "type": "array",
105
+ "description": "Add switches that play a specific card ID on each Yoto device. Optionally add an accessory that plays the card on all Yotos.",
106
+ "items": {
107
+ "title": "Card Control",
108
+ "type": "object",
109
+ "properties": {
110
+ "label": {
111
+ "title": "Label",
112
+ "type": "string",
113
+ "required": true,
114
+ "description": "Name shown in HomeKit for this card control."
115
+ },
116
+ "cardId": {
117
+ "title": "Card ID",
118
+ "type": "string",
119
+ "required": true,
120
+ "description": "The Yoto card ID to play."
121
+ },
122
+ "playOnAll": {
123
+ "title": "Play on All Yotos",
124
+ "type": "boolean",
125
+ "default": false,
126
+ "description": "Create a separate accessory that plays this card on every Yoto."
127
+ }
128
+ }
129
+ }
130
+ },
131
+ "shortcuts": {
132
+ "title": "Shortcuts",
133
+ "type": "boolean",
134
+ "default": false,
135
+ "description": "Expose a switch for each shortcut configured on the Yoto (in the Yoto app under the player's shortcut buttons). Turning a switch on plays that shortcut."
136
+ },
137
+ "dayMode": {
138
+ "title": "Day Mode",
139
+ "type": "boolean",
140
+ "default": true,
141
+ "description": "Expose day mode status."
142
+ },
143
+ "sleepTimer": {
144
+ "title": "Sleep Timer",
145
+ "type": "boolean",
146
+ "default": false,
147
+ "description": "Expose sleep timer switch."
148
+ },
149
+ "bluetooth": {
150
+ "title": "Bluetooth",
151
+ "type": "boolean",
152
+ "default": false,
153
+ "description": "Expose Bluetooth toggle."
154
+ },
155
+ "volumeLimits": {
156
+ "title": "Volume Limits",
157
+ "type": "boolean",
158
+ "default": true,
159
+ "description": "Expose day/night max volume controls."
160
+ }
161
+ }
162
+ }
163
+ }
164
+ },
165
+ "layout": [
166
+ {
167
+ "type": "section",
168
+ "title": "Authentication",
169
+ "expandable": true,
170
+ "expanded": false,
171
+ "items": [
172
+ {
173
+ "type": "help",
174
+ "helpvalue": "<p><strong>Authentication Instructions:</strong></p><ol><li>Click the <strong>\"Start Authentication\"</strong> button in the custom UI above</li><li>Follow the on-screen instructions to authorize with your Yoto account</li><li>Tokens will be saved automatically when authorization completes</li><li>Homebridge will restart and connect to your devices</li></ol><p><strong>Advanced:</strong> The default OAuth Client ID works for all users. The token fields below are automatically managed by the plugin and should not be edited manually.</p>"
175
+ },
176
+ "clientId",
177
+ "accessToken",
178
+ "refreshToken"
179
+ ]
180
+ },
181
+ {
182
+ "type": "section",
183
+ "title": "Accessory Services",
184
+ "items": [
185
+ {
186
+ "type": "help",
187
+ "helpvalue": "<p>Select which HomeKit services to expose for each Yoto device.</p>"
188
+ },
189
+ {
190
+ "type": "help",
191
+ "helpvalue": "<p><strong>Playback Controls:</strong> Enable a bridged switch + dimmer for play/pause and volume. <strong>External Smart Speaker:</strong> publishes a separate accessory and requires additional pairing steps in the Home app. <strong>TV Playback Accessory:</strong> publishes an external playback accessory with input and speaker controls.</p>"
192
+ },
193
+ "services.playbackControls",
194
+ "services.smartSpeaker",
195
+ "services.television",
196
+ "services.volumeLimits",
197
+ "services.battery",
198
+ "services.temperature",
199
+ "services.cardSlot",
200
+ "services.dayMode",
201
+ "services.nightlight",
202
+ "services.bluetooth",
203
+ "services.sleepTimer",
204
+ "services.shortcuts",
205
+ {
206
+ "type": "help",
207
+ "helpvalue": "<p><strong>Card Controls:</strong> Add switches that play a specific card ID on each Yoto. Enable \"Play on All Yotos\" to create a separate accessory.</p>"
208
+ },
209
+ {
210
+ "key": "services.cardControls",
211
+ "type": "array",
212
+ "buttonText": "Add Card Control",
213
+ "items": [
214
+ "services.cardControls[].label",
215
+ "services.cardControls[].cardId",
216
+ "services.cardControls[].playOnAll"
217
+ ]
218
+ }
219
+ ]
220
+ },
221
+ {
222
+ "type": "section",
223
+ "title": "Advanced",
224
+ "expandable": true,
225
+ "expanded": false,
226
+ "items": [
227
+ "httpPollIntervalMs"
228
+ ]
229
+ }
230
+ ]
231
+ }