homebridge-yoto 0.0.28 → 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/README.md +18 -314
- package/config.schema.cjs +3 -0
- package/config.schema.json +19 -155
- package/homebridge-ui/public/client.js +428 -0
- package/homebridge-ui/public/index.html +138 -0
- package/homebridge-ui/server.js +264 -0
- package/index.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/package.json +28 -22
- package/.github/dependabot.yml +0 -18
- package/.github/funding.yml +0 -4
- package/.github/workflows/release.yml +0 -41
- package/.github/workflows/tests.yml +0 -37
- package/AGENTS.md +0 -253
- package/CHANGELOG.md +0 -8
- package/CONTRIBUTING.md +0 -34
- package/PLAN.md +0 -609
- package/declaration.tsconfig.json +0 -15
- package/eslint.config.js +0 -7
- package/index.test.js +0 -7
- 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/tsconfig.json +0 -14
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utility functions for the plugin
|
|
3
|
+
*
|
|
4
|
+
* Includes code adapted from `homebridge-plugin-utils`:
|
|
5
|
+
* - Source: https://github.com/hjdhjd/homebridge-plugin-utils/blob/main/src/util.ts
|
|
6
|
+
*
|
|
7
|
+
* ISC License
|
|
8
|
+
* ===========
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2017-2025, HJD https://github.com/hjdhjd
|
|
11
|
+
*
|
|
12
|
+
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
13
|
+
* with or without fee is hereby granted, provided that the above copyright notice
|
|
14
|
+
* and this permission notice appear in all copies.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
17
|
+
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
18
|
+
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
19
|
+
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
20
|
+
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
21
|
+
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
22
|
+
* THIS SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sanitize an accessory/service name according to HomeKit naming conventions.
|
|
27
|
+
*
|
|
28
|
+
* Starts and ends with a letter or number. Exception: may end with a period.
|
|
29
|
+
* May have the following special characters: -"',.#&.
|
|
30
|
+
* Must not include emojis.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} name - The name to sanitize
|
|
33
|
+
* @returns {string} The HomeKit-sanitized version of the name
|
|
34
|
+
*/
|
|
35
|
+
export function sanitizeName (name) {
|
|
36
|
+
return name
|
|
37
|
+
// Replace any disallowed char (including emojis) with a space.
|
|
38
|
+
.replace(/[^\p{L}\p{N}\-"'.,#&\s]/gu, ' ')
|
|
39
|
+
// Collapse multiple spaces to one.
|
|
40
|
+
.replace(/\s+/g, ' ')
|
|
41
|
+
// Trim spaces at the beginning and end of the string.
|
|
42
|
+
.trim()
|
|
43
|
+
// Strip any leading non-letter/number.
|
|
44
|
+
.replace(/^[^\p{L}\p{N}]+/u, '')
|
|
45
|
+
// Collapse two or more trailing periods into one.
|
|
46
|
+
.replace(/\.{2,}$/g, '.')
|
|
47
|
+
// Remove any other trailing char that's not letter/number/period.
|
|
48
|
+
.replace(/[^\p{L}\p{N}.]$/u, '')
|
|
49
|
+
}
|
package/lib/settings.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { configSchema } from '../config.schema.cjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
5
|
+
*/
|
|
6
|
+
export const PLATFORM_NAME = 'Yoto'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This must match the name of your plugin as defined the package.json `name` property
|
|
10
|
+
*/
|
|
11
|
+
export const PLUGIN_NAME = 'homebridge-yoto'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Default OAuth Client ID from config schema
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_CLIENT_ID = configSchema.schema.properties.clientId.default
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** @import { Service, Characteristic } from 'homebridge' */
|
|
2
|
+
import { sanitizeName } from './sanitize-name.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Apply HomeKit-visible naming to a service.
|
|
6
|
+
*
|
|
7
|
+
* We set both `Name` and `ConfiguredName` on every service we manage so HomeKit tiles are consistently labeled.
|
|
8
|
+
*
|
|
9
|
+
* @param {object} params
|
|
10
|
+
* @param {Service} params.service
|
|
11
|
+
* @param {string} params.name
|
|
12
|
+
* @param {typeof Characteristic} params.Characteristic
|
|
13
|
+
* @returns {void}
|
|
14
|
+
*/
|
|
15
|
+
export function syncServiceNames ({
|
|
16
|
+
Characteristic,
|
|
17
|
+
service,
|
|
18
|
+
name
|
|
19
|
+
}) {
|
|
20
|
+
const sanitizedName = sanitizeName(name)
|
|
21
|
+
service.displayName = sanitizedName
|
|
22
|
+
|
|
23
|
+
service.updateCharacteristic(Characteristic.Name, sanitizedName)
|
|
24
|
+
|
|
25
|
+
// Set ConfiguredName on all services, not just ones that say they support it.
|
|
26
|
+
// This is the only way to set the service name inside an accessory.
|
|
27
|
+
// const hasConfiguredNameCharacteristic = service.characteristics.some(c => c.UUID === Characteristic.ConfiguredName.UUID)
|
|
28
|
+
// const hasConfiguredNameOptional = service.optionalCharacteristics.some(c => c.UUID === Characteristic.ConfiguredName.UUID)
|
|
29
|
+
// if (!hasConfiguredNameCharacteristic && !hasConfiguredNameOptional) {
|
|
30
|
+
// service.addOptionalCharacteristic(Characteristic.ConfiguredName)
|
|
31
|
+
// }
|
|
32
|
+
|
|
33
|
+
service.updateCharacteristic(Characteristic.ConfiguredName, sanitizedName)
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-yoto",
|
|
3
3
|
"description": "Control your Yoto players through Apple HomeKit with real-time MQTT updates",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.32",
|
|
5
5
|
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/bcomnes/homebridge-yoto/issues"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"homebridge-
|
|
11
|
-
"
|
|
10
|
+
"@homebridge/plugin-ui-utils": "^2.1.2",
|
|
11
|
+
"color-convert": "3.1.3",
|
|
12
|
+
"yoto-nodejs-client": "^0.0.7"
|
|
12
13
|
},
|
|
13
14
|
"devDependencies": {
|
|
14
15
|
"@types/node": "^25.0.0",
|
|
15
16
|
"@voxpelli/tsconfig": "^16.1.0",
|
|
16
17
|
"auto-changelog": "^2.0.0",
|
|
17
18
|
"c8": "^10.0.0",
|
|
19
|
+
"eslint": "^9.39.2",
|
|
18
20
|
"gh-release": "^7.0.0",
|
|
19
|
-
"homebridge": "^
|
|
21
|
+
"homebridge": "^2.0.0-beta.66",
|
|
20
22
|
"neostandard": "^0.12.0",
|
|
21
23
|
"npm-run-all2": "^8.0.1",
|
|
22
24
|
"typescript": "~5.9.3"
|
|
23
25
|
},
|
|
24
26
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
27
|
+
"node": ">=22",
|
|
26
28
|
"npm": ">=10",
|
|
27
29
|
"homebridge": "^1.8.0 || ^2.0.0-beta.0"
|
|
28
30
|
},
|
|
@@ -36,26 +38,21 @@
|
|
|
36
38
|
"module": "index.js",
|
|
37
39
|
"main": "index.js",
|
|
38
40
|
"types": "index.d.ts",
|
|
41
|
+
"files": [
|
|
42
|
+
"index.js",
|
|
43
|
+
"index.d.ts",
|
|
44
|
+
"index.d.ts.map",
|
|
45
|
+
"config.schema.json",
|
|
46
|
+
"config.schema.cjs",
|
|
47
|
+
"lib/",
|
|
48
|
+
"homebridge-ui/",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
39
52
|
"repository": {
|
|
40
53
|
"type": "git",
|
|
41
54
|
"url": "https://github.com/bcomnes/homebridge-yoto.git"
|
|
42
55
|
},
|
|
43
|
-
"scripts": {
|
|
44
|
-
"prepublishOnly": "npm run build && git push --follow-tags && gh-release -y",
|
|
45
|
-
"postpublish": "npm run clean",
|
|
46
|
-
"test": "run-s test:*",
|
|
47
|
-
"test:lint": "eslint",
|
|
48
|
-
"test:tsc": "tsc",
|
|
49
|
-
"test:node-test": "c8 node --test --test-reporter spec",
|
|
50
|
-
"version": "run-s version:*",
|
|
51
|
-
"version:changelog": "auto-changelog -p --template keepachangelog auto-changelog --breaking-pattern 'BREAKING CHANGE:'",
|
|
52
|
-
"version:git": "git add CHANGELOG.md",
|
|
53
|
-
"build": "npm run clean && run-p build:*",
|
|
54
|
-
"build:declaration": "tsc -p declaration.tsconfig.json",
|
|
55
|
-
"clean": "run-p clean:*",
|
|
56
|
-
"clean:declarations-top": "rm -rf $(find . -maxdepth 1 -type f -name '*.d.ts*')",
|
|
57
|
-
"clean:declarations-lib": "rm -rf $(find lib -type f -name '*.d.ts*' ! -name '*-types.d.ts')"
|
|
58
|
-
},
|
|
59
56
|
"funding": {
|
|
60
57
|
"type": "individual",
|
|
61
58
|
"url": "https://github.com/sponsors/bcomnes"
|
|
@@ -65,5 +62,14 @@
|
|
|
65
62
|
"lcov",
|
|
66
63
|
"text"
|
|
67
64
|
]
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"test": "run-s test:*",
|
|
68
|
+
"test:lint": "eslint",
|
|
69
|
+
"test:tsc": "tsc",
|
|
70
|
+
"test:node-test": "c8 node --test --test-reporter spec",
|
|
71
|
+
"version": "run-s version:*",
|
|
72
|
+
"version:changelog": "auto-changelog -p --template keepachangelog auto-changelog --breaking-pattern 'BREAKING CHANGE:'",
|
|
73
|
+
"version:git": "git add CHANGELOG.md"
|
|
68
74
|
}
|
|
69
|
-
}
|
|
75
|
+
}
|
package/.github/dependabot.yml
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
2
|
-
version: 2
|
|
3
|
-
updates:
|
|
4
|
-
- package-ecosystem: "npm"
|
|
5
|
-
directory: "/"
|
|
6
|
-
# Check the npm registry for updates every day (weekdays)
|
|
7
|
-
schedule:
|
|
8
|
-
interval: "daily"
|
|
9
|
-
groups:
|
|
10
|
-
typescript:
|
|
11
|
-
patterns:
|
|
12
|
-
- "typescript"
|
|
13
|
-
- "@types/node"
|
|
14
|
-
- "@voxpelli/tsconfig"
|
|
15
|
-
- package-ecosystem: "github-actions"
|
|
16
|
-
directory: "/"
|
|
17
|
-
schedule:
|
|
18
|
-
interval: "daily"
|
package/.github/funding.yml
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
name: npm bump
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
inputs:
|
|
6
|
-
newversion:
|
|
7
|
-
description: 'npm version {major,minor,patch}'
|
|
8
|
-
required: true
|
|
9
|
-
|
|
10
|
-
env:
|
|
11
|
-
FORCE_COLOR: 1
|
|
12
|
-
|
|
13
|
-
concurrency: # prevent concurrent releases
|
|
14
|
-
group: npm-bump
|
|
15
|
-
cancel-in-progress: true
|
|
16
|
-
|
|
17
|
-
jobs:
|
|
18
|
-
version_and_release:
|
|
19
|
-
runs-on: ubuntu-latest
|
|
20
|
-
steps:
|
|
21
|
-
- uses: actions/checkout@v6
|
|
22
|
-
with:
|
|
23
|
-
# fetch full history so things like auto-changelog work properly
|
|
24
|
-
fetch-depth: 0
|
|
25
|
-
- name: Use Node.js ${{ env.node }}
|
|
26
|
-
uses: actions/setup-node@v6
|
|
27
|
-
with:
|
|
28
|
-
node-version-file: package.json
|
|
29
|
-
# setting a registry enables the NODE_AUTH_TOKEN env variable where we can set an npm token. REQUIRED
|
|
30
|
-
registry-url: 'https://registry.npmjs.org'
|
|
31
|
-
- run: npm i
|
|
32
|
-
- run: npm test
|
|
33
|
-
- name: npm version && npm publish
|
|
34
|
-
uses: bcomnes/npm-bump@v2
|
|
35
|
-
with:
|
|
36
|
-
git_email: bcomnes@gmail.com
|
|
37
|
-
git_username: ${{ github.actor }}
|
|
38
|
-
newversion: ${{ github.event.inputs.newversion }}
|
|
39
|
-
github_token: ${{ secrets.GITHUB_TOKEN }} # built in actions token. Passed tp gh-release if in use.
|
|
40
|
-
npm_token: ${{ secrets.NPM_TOKEN }} # user set secret token generated at npm
|
|
41
|
-
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
name: tests
|
|
2
|
-
|
|
3
|
-
on: [pull_request, push]
|
|
4
|
-
|
|
5
|
-
env:
|
|
6
|
-
FORCE_COLOR: 1
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
test:
|
|
10
|
-
runs-on: ${{ matrix.os }}
|
|
11
|
-
|
|
12
|
-
strategy:
|
|
13
|
-
fail-fast: false
|
|
14
|
-
matrix:
|
|
15
|
-
os: [ubuntu-latest]
|
|
16
|
-
node: ['lts/*']
|
|
17
|
-
|
|
18
|
-
steps:
|
|
19
|
-
- uses: actions/checkout@v6
|
|
20
|
-
- name: Use Node.js ${{ matrix.node }}
|
|
21
|
-
uses: actions/setup-node@v6
|
|
22
|
-
with:
|
|
23
|
-
node-version: ${{ matrix.node }}
|
|
24
|
-
- run: npm i
|
|
25
|
-
- run: npm test --color=always
|
|
26
|
-
|
|
27
|
-
automerge:
|
|
28
|
-
needs: test
|
|
29
|
-
runs-on: ubuntu-latest
|
|
30
|
-
permissions:
|
|
31
|
-
pull-requests: write
|
|
32
|
-
contents: write
|
|
33
|
-
steps:
|
|
34
|
-
- uses: fastify/github-action-merge-dependabot@v3
|
|
35
|
-
if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request' && contains(github.head_ref, 'dependabot/github_actions') }}
|
|
36
|
-
with:
|
|
37
|
-
github-token: ${{secrets.github_token}}
|
package/AGENTS.md
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
# Agent Development Notes
|
|
2
|
-
|
|
3
|
-
This document contains patterns, conventions, and guidelines for developing the homebridge-yoto plugin.
|
|
4
|
-
|
|
5
|
-
## JSDoc Typing Patterns
|
|
6
|
-
|
|
7
|
-
### Use TypeScript-in-JavaScript (ts-in-js)
|
|
8
|
-
|
|
9
|
-
All source files use `.js` extensions with JSDoc comments for type safety. This provides type checking without TypeScript compilation overhead.
|
|
10
|
-
|
|
11
|
-
### Avoid `any` types
|
|
12
|
-
|
|
13
|
-
Always provide specific types. Use `unknown` when the type is truly unknown, then narrow it with type guards.
|
|
14
|
-
|
|
15
|
-
**Bad:**
|
|
16
|
-
```javascript
|
|
17
|
-
/**
|
|
18
|
-
* @param {any} data
|
|
19
|
-
*/
|
|
20
|
-
function processData(data) {
|
|
21
|
-
return data.value;
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Good:**
|
|
26
|
-
```javascript
|
|
27
|
-
/**
|
|
28
|
-
* @param {YotoDeviceStatus} status
|
|
29
|
-
* @returns {number}
|
|
30
|
-
*/
|
|
31
|
-
function getBatteryLevel(status) {
|
|
32
|
-
return status.batteryLevelPercentage;
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Use @ts-expect-error over @ts-ignore
|
|
37
|
-
|
|
38
|
-
When you must suppress a TypeScript error, use `@ts-expect-error` with a comment explaining why. This will error if the issue is fixed, prompting cleanup.
|
|
39
|
-
|
|
40
|
-
**Bad:**
|
|
41
|
-
```javascript
|
|
42
|
-
// @ts-ignore
|
|
43
|
-
const value = accessory.context.device.unknownProperty;
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Good:**
|
|
47
|
-
```javascript
|
|
48
|
-
// @ts-expect-error - API may return undefined for offline devices
|
|
49
|
-
const lastSeen = accessory.context.device.lastSeenAt;
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Use newer @import syntax in jsdoc/ts-in-js for types only
|
|
53
|
-
|
|
54
|
-
Import types using the `@import` JSDoc tag to avoid runtime imports of type-only dependencies.
|
|
55
|
-
|
|
56
|
-
```javascript
|
|
57
|
-
/** @import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge' */
|
|
58
|
-
/** @import { YotoDevice, YotoDeviceStatus, YotoDeviceConfig } from './types.js' */
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @param {Logger} log
|
|
62
|
-
* @param {PlatformConfig} config
|
|
63
|
-
* @param {API} api
|
|
64
|
-
*/
|
|
65
|
-
export function YotoPlatform(log, config, api) {
|
|
66
|
-
this.log = log;
|
|
67
|
-
this.config = config;
|
|
68
|
-
this.api = api;
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Import Consolidation
|
|
73
|
-
|
|
74
|
-
Keep regular imports and type imports separate. Use single-line imports for types when possible.
|
|
75
|
-
|
|
76
|
-
```javascript
|
|
77
|
-
import { EventEmitter } from 'events';
|
|
78
|
-
|
|
79
|
-
/** @import { YotoDevice } from './types.js' */
|
|
80
|
-
/** @import { API, PlatformAccessory } from 'homebridge' */
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Homebridge Type Import Patterns
|
|
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
|
|
165
|
-
|
|
166
|
-
Type API responses explicitly:
|
|
167
|
-
|
|
168
|
-
```javascript
|
|
169
|
-
/**
|
|
170
|
-
* @typedef {Object} YotoApiDevicesResponse
|
|
171
|
-
* @property {YotoDevice[]} devices
|
|
172
|
-
*/
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Get all devices for authenticated user
|
|
176
|
-
* @returns {Promise<YotoDevice[]>}
|
|
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
|
-
```
|
|
188
|
-
|
|
189
|
-
### Platform Accessory Context Typing
|
|
190
|
-
|
|
191
|
-
Define the context object structure stored in accessories:
|
|
192
|
-
|
|
193
|
-
```javascript
|
|
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:
|
|
213
|
-
|
|
214
|
-
```javascript
|
|
215
|
-
/**
|
|
216
|
-
* @typedef {Object} YotoCardContent
|
|
217
|
-
* @property {string} cardId
|
|
218
|
-
* @property {string} title
|
|
219
|
-
* @property {string | null} author
|
|
220
|
-
* @property {string | null} description
|
|
221
|
-
* @property {YotoChapter[] | null} chapters
|
|
222
|
-
*/
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Optional vs Nullable
|
|
226
|
-
|
|
227
|
-
Distinguish between optional fields (may not exist) and nullable fields (exists but may be null):
|
|
228
|
-
|
|
229
|
-
```javascript
|
|
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
|
-
```
|
|
237
|
-
|
|
238
|
-
## Changelog Management
|
|
239
|
-
|
|
240
|
-
**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
DELETED
package/CONTRIBUTING.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# Contributing
|
|
2
|
-
|
|
3
|
-
## Releasing
|
|
4
|
-
|
|
5
|
-
Changelog and releasing are automated with npm scripts and actions. To create a release:
|
|
6
|
-
|
|
7
|
-
- Navigate to the Actions tab.
|
|
8
|
-
- Select the `npm bump` action.
|
|
9
|
-
- Trigger the action, specifying the semantic version bump that is needed.
|
|
10
|
-
- Changelog, GitHub release, and npm publish are handled by the action.
|
|
11
|
-
- An in-depth review of this system is documented here: [bret.io/projects/package-automation](https://bret.io/projects/package-automation/).
|
|
12
|
-
|
|
13
|
-
If for some reason this isn't working or a local release is preferred, follow these steps:
|
|
14
|
-
|
|
15
|
-
- Ensure a clean working git workspace.
|
|
16
|
-
- Run `npm version {patch, minor, major}`.
|
|
17
|
-
- This will update the version number and generate the changelog.
|
|
18
|
-
- Run `npm publish`.
|
|
19
|
-
- This will push your local git branch and tags to the default remote, perform a [gh-release](https://ghub.io/gh-release), and create an npm publication.
|
|
20
|
-
|
|
21
|
-
## Guidelines
|
|
22
|
-
|
|
23
|
-
- Patches, ideas, and changes are welcome.
|
|
24
|
-
- Fixes are almost always welcome.
|
|
25
|
-
- Features are sometimes welcome.
|
|
26
|
-
- Please open an issue to discuss the idea prior to spending lots of time on the problem.
|
|
27
|
-
- It may be rejected.
|
|
28
|
-
- If you don't want to wait for the discussion to commence and you really want to jump into the implementation work, be prepared to fork the project if the idea is respectfully declined.
|
|
29
|
-
- Try to stay within the style of the existing code.
|
|
30
|
-
- All tests must pass.
|
|
31
|
-
- Additional features or code paths must be tested.
|
|
32
|
-
- Aim for 100% test coverage.
|
|
33
|
-
- Questions are welcome. However, unless there is an official support contract established between the maintainers and the requester, support is not guaranteed.
|
|
34
|
-
- Contributors reserve the right to walk away from this project at any moment, with or without notice.
|