yoto-nodejs-client 0.0.1 → 0.0.3
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 +523 -30
- package/bin/auth.js +36 -46
- package/bin/content.js +0 -0
- package/bin/device-model.d.ts +3 -0
- package/bin/device-model.d.ts.map +1 -0
- package/bin/device-model.js +360 -0
- package/bin/device-tui.TODO.md +125 -0
- package/bin/device-tui.d.ts +31 -0
- package/bin/device-tui.d.ts.map +1 -0
- package/bin/device-tui.js +1123 -0
- package/bin/devices.js +166 -28
- package/bin/groups.js +0 -0
- package/bin/icons.js +0 -0
- package/bin/lib/cli-helpers.d.ts +33 -1
- package/bin/lib/cli-helpers.d.ts.map +1 -1
- package/bin/lib/cli-helpers.js +5 -5
- package/bin/lib/token-helpers.d.ts +32 -0
- package/bin/lib/token-helpers.d.ts.map +1 -1
- package/bin/refresh-token.js +6 -6
- package/bin/token-info.js +3 -3
- package/index.d.ts +4 -217
- package/index.d.ts.map +1 -1
- package/index.js +11 -689
- package/lib/api-client.d.ts +576 -0
- package/lib/api-client.d.ts.map +1 -0
- package/lib/api-client.js +681 -0
- package/lib/api-endpoints/auth.d.ts +280 -4
- package/lib/api-endpoints/auth.d.ts.map +1 -1
- package/lib/api-endpoints/auth.js +224 -7
- package/lib/api-endpoints/auth.test.js +54 -2
- package/lib/api-endpoints/constants.d.ts +30 -2
- package/lib/api-endpoints/constants.d.ts.map +1 -1
- package/lib/api-endpoints/constants.js +17 -10
- package/lib/api-endpoints/content.d.ts +760 -0
- package/lib/api-endpoints/content.d.ts.map +1 -1
- package/lib/api-endpoints/content.test.js +1 -1
- package/lib/api-endpoints/devices.d.ts +917 -48
- package/lib/api-endpoints/devices.d.ts.map +1 -1
- package/lib/api-endpoints/devices.js +114 -52
- package/lib/api-endpoints/devices.test.js +1 -1
- package/lib/api-endpoints/endpoint-test-helpers.d.ts +28 -0
- package/lib/api-endpoints/endpoint-test-helpers.d.ts.map +1 -0
- package/lib/api-endpoints/family-library-groups.d.ts +187 -0
- package/lib/api-endpoints/family-library-groups.d.ts.map +1 -1
- package/lib/api-endpoints/family-library-groups.test.js +1 -1
- package/lib/api-endpoints/family.d.ts +88 -0
- package/lib/api-endpoints/family.d.ts.map +1 -1
- package/lib/api-endpoints/family.test.js +1 -1
- package/lib/api-endpoints/helpers.d.ts +37 -3
- package/lib/api-endpoints/helpers.d.ts.map +1 -1
- package/lib/api-endpoints/icons.d.ts +196 -0
- package/lib/api-endpoints/icons.d.ts.map +1 -1
- package/lib/api-endpoints/icons.test.js +1 -1
- package/lib/api-endpoints/media.d.ts +83 -0
- package/lib/api-endpoints/media.d.ts.map +1 -1
- package/lib/helpers/power-state.d.ts +53 -0
- package/lib/helpers/power-state.d.ts.map +1 -0
- package/lib/helpers/power-state.js +73 -0
- package/lib/helpers/power-state.test.js +100 -0
- package/lib/helpers/temperature.d.ts +24 -0
- package/lib/helpers/temperature.d.ts.map +1 -0
- package/lib/helpers/temperature.js +61 -0
- package/lib/helpers/temperature.test.js +58 -0
- package/lib/helpers/typed-keys.d.ts +7 -0
- package/lib/helpers/typed-keys.d.ts.map +1 -0
- package/lib/helpers/typed-keys.js +8 -0
- package/lib/mqtt/client.d.ts +610 -7
- package/lib/mqtt/client.d.ts.map +1 -1
- package/lib/mqtt/client.js +213 -31
- package/lib/mqtt/commands.d.ts +195 -0
- package/lib/mqtt/commands.d.ts.map +1 -1
- package/lib/mqtt/factory.d.ts +62 -1
- package/lib/mqtt/factory.d.ts.map +1 -1
- package/lib/mqtt/factory.js +27 -5
- package/lib/mqtt/mqtt.test.js +85 -28
- package/lib/mqtt/topics.d.ts +186 -1
- package/lib/mqtt/topics.d.ts.map +1 -1
- package/lib/mqtt/topics.js +54 -20
- package/lib/pkg.d.cts +9 -0
- package/lib/token.d.ts +106 -3
- package/lib/token.d.ts.map +1 -1
- package/lib/token.js +30 -23
- package/lib/yoto-account.d.ts +163 -0
- package/lib/yoto-account.d.ts.map +1 -0
- package/lib/yoto-account.js +340 -0
- package/lib/yoto-device.d.ts +656 -0
- package/lib/yoto-device.d.ts.map +1 -0
- package/lib/yoto-device.js +2850 -0
- package/package.json +22 -15
- package/lib/api-endpoints/test-helpers.d.ts +0 -7
- package/lib/api-endpoints/test-helpers.d.ts.map +0 -1
- /package/lib/api-endpoints/{test-helpers.js → endpoint-test-helpers.js} +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Power state detection helper for Yoto device status
|
|
3
|
+
*
|
|
4
|
+
* Analyzes shutdown field and uptime to determine device power state changes
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Power state detection result
|
|
9
|
+
* @typedef {Object} PowerStateResult
|
|
10
|
+
* @property {'running' | 'shutdown' | 'startup'} state - Device power state
|
|
11
|
+
* @property {string | null} shutDownReason - Shutdown reason if state is 'shutdown'
|
|
12
|
+
* @property {number | null} upTime - Device uptime in seconds if state is 'startup'
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detect device power state from legacy status
|
|
17
|
+
*
|
|
18
|
+
* @param {string | null | undefined} shutDown - shutDown field from legacy status
|
|
19
|
+
* @param {number | null | undefined} upTime - upTime field from legacy status in seconds
|
|
20
|
+
* @returns {PowerStateResult}
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Device running normally
|
|
24
|
+
* detectPowerState('nA', 3600)
|
|
25
|
+
* // { state: 'running', shutDownReason: null, upTime: null }
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Device just started (low uptime)
|
|
29
|
+
* detectPowerState('nA', 45)
|
|
30
|
+
* // { state: 'startup', shutDownReason: null, upTime: 45 }
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Device shutting down
|
|
34
|
+
* detectPowerState('userShutdown', 3600)
|
|
35
|
+
* // { state: 'shutdown', shutDownReason: 'userShutdown', upTime: null }
|
|
36
|
+
*/
|
|
37
|
+
export function detectPowerState (shutDown, upTime) {
|
|
38
|
+
// No shutdown field - assume running
|
|
39
|
+
if (shutDown == null) {
|
|
40
|
+
return {
|
|
41
|
+
state: 'running',
|
|
42
|
+
shutDownReason: null,
|
|
43
|
+
upTime: null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// shutDown: 'nA' means "not applicable" - device is running
|
|
48
|
+
if (shutDown === 'nA') {
|
|
49
|
+
// Low uptime indicates recent startup (< 2 minutes)
|
|
50
|
+
if (upTime != null && upTime < 120) {
|
|
51
|
+
return {
|
|
52
|
+
state: 'startup',
|
|
53
|
+
shutDownReason: null,
|
|
54
|
+
upTime
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Normal running state
|
|
59
|
+
return {
|
|
60
|
+
state: 'running',
|
|
61
|
+
shutDownReason: null,
|
|
62
|
+
upTime: null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Any other value means device is shutting down or has shut down
|
|
67
|
+
// Examples: 'userShutdown', 'lowBattery', 'timeout', etc.
|
|
68
|
+
return {
|
|
69
|
+
state: 'shutdown',
|
|
70
|
+
shutDownReason: shutDown,
|
|
71
|
+
upTime: null
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import test from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { detectPowerState } from './power-state.js'
|
|
4
|
+
|
|
5
|
+
test('detectPowerState', async (t) => {
|
|
6
|
+
await t.test('should detect normal running state', () => {
|
|
7
|
+
const result = detectPowerState('nA', 3600)
|
|
8
|
+
assert.strictEqual(result.state, 'running')
|
|
9
|
+
assert.strictEqual(result.shutDownReason, null)
|
|
10
|
+
assert.strictEqual(result.upTime, null)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
await t.test('should detect startup state with low uptime', () => {
|
|
14
|
+
const result = detectPowerState('nA', 45)
|
|
15
|
+
assert.strictEqual(result.state, 'startup')
|
|
16
|
+
assert.strictEqual(result.shutDownReason, null)
|
|
17
|
+
assert.strictEqual(result.upTime, 45)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
await t.test('should detect startup at exactly 119 seconds', () => {
|
|
21
|
+
const result = detectPowerState('nA', 119)
|
|
22
|
+
assert.strictEqual(result.state, 'startup')
|
|
23
|
+
assert.strictEqual(result.upTime, 119)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
await t.test('should detect running at exactly 120 seconds (threshold)', () => {
|
|
27
|
+
const result = detectPowerState('nA', 120)
|
|
28
|
+
assert.strictEqual(result.state, 'running')
|
|
29
|
+
assert.strictEqual(result.upTime, null)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
await t.test('should detect user shutdown', () => {
|
|
33
|
+
const result = detectPowerState('userShutdown', 3600)
|
|
34
|
+
assert.strictEqual(result.state, 'shutdown')
|
|
35
|
+
assert.strictEqual(result.shutDownReason, 'userShutdown')
|
|
36
|
+
assert.strictEqual(result.upTime, null)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
await t.test('should detect any non-nA value as shutdown', () => {
|
|
40
|
+
const testCases = [
|
|
41
|
+
'lowBattery',
|
|
42
|
+
'timeout',
|
|
43
|
+
'powerLoss',
|
|
44
|
+
'unknown',
|
|
45
|
+
'anyOtherValue'
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
for (const shutDownValue of testCases) {
|
|
49
|
+
const result = detectPowerState(shutDownValue, 1000)
|
|
50
|
+
assert.strictEqual(result.state, 'shutdown')
|
|
51
|
+
assert.strictEqual(result.shutDownReason, shutDownValue)
|
|
52
|
+
assert.strictEqual(result.upTime, null)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
await t.test('should handle null shutDown as running', () => {
|
|
57
|
+
const result = detectPowerState(null, 500)
|
|
58
|
+
assert.strictEqual(result.state, 'running')
|
|
59
|
+
assert.strictEqual(result.shutDownReason, null)
|
|
60
|
+
assert.strictEqual(result.upTime, null)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
await t.test('should handle undefined shutDown as running', () => {
|
|
64
|
+
const result = detectPowerState(undefined, 500)
|
|
65
|
+
assert.strictEqual(result.state, 'running')
|
|
66
|
+
assert.strictEqual(result.shutDownReason, null)
|
|
67
|
+
assert.strictEqual(result.upTime, null)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
await t.test('should handle null upTime with nA', () => {
|
|
71
|
+
const result = detectPowerState('nA', null)
|
|
72
|
+
assert.strictEqual(result.state, 'running')
|
|
73
|
+
assert.strictEqual(result.upTime, null)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
await t.test('should handle undefined upTime with nA', () => {
|
|
77
|
+
const result = detectPowerState('nA', undefined)
|
|
78
|
+
assert.strictEqual(result.state, 'running')
|
|
79
|
+
assert.strictEqual(result.upTime, null)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
await t.test('should detect startup with uptime 0', () => {
|
|
83
|
+
const result = detectPowerState('nA', 0)
|
|
84
|
+
assert.strictEqual(result.state, 'startup')
|
|
85
|
+
assert.strictEqual(result.upTime, 0)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
await t.test('should detect startup with uptime 1', () => {
|
|
89
|
+
const result = detectPowerState('nA', 1)
|
|
90
|
+
assert.strictEqual(result.state, 'startup')
|
|
91
|
+
assert.strictEqual(result.upTime, 1)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
await t.test('should ignore upTime when shutting down', () => {
|
|
95
|
+
const result = detectPowerState('userShutdown', 10)
|
|
96
|
+
assert.strictEqual(result.state, 'shutdown')
|
|
97
|
+
assert.strictEqual(result.shutDownReason, 'userShutdown')
|
|
98
|
+
assert.strictEqual(result.upTime, null)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temperature parsing helper for Yoto device status
|
|
3
|
+
*
|
|
4
|
+
* Temperature field format: colon-separated string where second value (index [1]) is temperature in Celsius
|
|
5
|
+
* Examples: '0:19' → 19°C, '12:20:23' → 20°C, '0:0' → 0°C, '0:unavailable' → null
|
|
6
|
+
* Plain string format: '19' → 19°C, '0' → 0°C
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Parse temperature from Yoto status message
|
|
10
|
+
*
|
|
11
|
+
* @param {string | number | null | undefined} tempValue - Temperature value from status message
|
|
12
|
+
* @returns {number | null} Temperature in Celsius, or null if unavailable/invalid
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* parseTemperature('0:19') // 19
|
|
16
|
+
* parseTemperature('12:20:23') // 20
|
|
17
|
+
* parseTemperature('0:0') // 0
|
|
18
|
+
* parseTemperature('0:unavailable') // null
|
|
19
|
+
* parseTemperature('0') // 0
|
|
20
|
+
* parseTemperature('19') // 19
|
|
21
|
+
* parseTemperature(null) // null
|
|
22
|
+
*/
|
|
23
|
+
export function parseTemperature(tempValue: string | number | null | undefined): number | null;
|
|
24
|
+
//# sourceMappingURL=temperature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temperature.d.ts","sourceRoot":"","sources":["temperature.js"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;GAcG;AACH,4CAZW,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,MAAM,GAAG,IAAI,CAgDzB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temperature parsing helper for Yoto device status
|
|
3
|
+
*
|
|
4
|
+
* Temperature field format: colon-separated string where second value (index [1]) is temperature in Celsius
|
|
5
|
+
* Examples: '0:19' → 19°C, '12:20:23' → 20°C, '0:0' → 0°C, '0:unavailable' → null
|
|
6
|
+
* Plain string format: '19' → 19°C, '0' → 0°C
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parse temperature from Yoto status message
|
|
11
|
+
*
|
|
12
|
+
* @param {string | number | null | undefined} tempValue - Temperature value from status message
|
|
13
|
+
* @returns {number | null} Temperature in Celsius, or null if unavailable/invalid
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* parseTemperature('0:19') // 19
|
|
17
|
+
* parseTemperature('12:20:23') // 20
|
|
18
|
+
* parseTemperature('0:0') // 0
|
|
19
|
+
* parseTemperature('0:unavailable') // null
|
|
20
|
+
* parseTemperature('0') // 0
|
|
21
|
+
* parseTemperature('19') // 19
|
|
22
|
+
* parseTemperature(null) // null
|
|
23
|
+
*/
|
|
24
|
+
export function parseTemperature (tempValue) {
|
|
25
|
+
// Handle null/undefined
|
|
26
|
+
if (tempValue == null) {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Convert to string
|
|
31
|
+
const tempStr = String(tempValue)
|
|
32
|
+
|
|
33
|
+
// Empty string
|
|
34
|
+
if (!tempStr) {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Colon-separated format (e.g., '0:19', '12:20:23', '0:unavailable')
|
|
39
|
+
if (tempStr.includes(':')) {
|
|
40
|
+
const parts = tempStr.split(':')
|
|
41
|
+
const secondValue = parts[1] ?? ''
|
|
42
|
+
|
|
43
|
+
// Parse second value as number
|
|
44
|
+
const parsed = parseFloat(secondValue)
|
|
45
|
+
|
|
46
|
+
// Return null if not a valid number (e.g., 'unavailable', 'notSupported')
|
|
47
|
+
if (isNaN(parsed)) {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return parsed
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fallback: try to parse as plain number (e.g., '19' or '0')
|
|
55
|
+
const parsed = parseFloat(tempStr)
|
|
56
|
+
if (isNaN(parsed)) {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return parsed
|
|
61
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import test from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { parseTemperature } from './temperature.js'
|
|
4
|
+
|
|
5
|
+
test('parseTemperature', async (t) => {
|
|
6
|
+
await t.test('should parse standard two-part format', () => {
|
|
7
|
+
assert.strictEqual(parseTemperature('0:19'), 19)
|
|
8
|
+
assert.strictEqual(parseTemperature('0:25'), 25)
|
|
9
|
+
assert.strictEqual(parseTemperature('0:0'), 0)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
await t.test('should parse three-part format using middle value', () => {
|
|
13
|
+
assert.strictEqual(parseTemperature('12:20:23'), 20)
|
|
14
|
+
assert.strictEqual(parseTemperature('10:15:20'), 15)
|
|
15
|
+
assert.strictEqual(parseTemperature('0:42:100'), 42)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
await t.test('should return null for unavailable/unsupported', () => {
|
|
19
|
+
assert.strictEqual(parseTemperature('0:unavailable'), null)
|
|
20
|
+
assert.strictEqual(parseTemperature('0:notSupported'), null)
|
|
21
|
+
assert.strictEqual(parseTemperature('notSupported'), null)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
await t.test('should handle null and undefined', () => {
|
|
25
|
+
assert.strictEqual(parseTemperature(null), null)
|
|
26
|
+
assert.strictEqual(parseTemperature(undefined), null)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
await t.test('should handle empty string', () => {
|
|
30
|
+
assert.strictEqual(parseTemperature(''), null)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
await t.test('should parse plain numbers as fallback', () => {
|
|
34
|
+
assert.strictEqual(parseTemperature('25'), 25)
|
|
35
|
+
assert.strictEqual(parseTemperature('0'), 0)
|
|
36
|
+
assert.strictEqual(parseTemperature(25), 25)
|
|
37
|
+
assert.strictEqual(parseTemperature(0), 0)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
await t.test('should handle decimal temperatures', () => {
|
|
41
|
+
assert.strictEqual(parseTemperature('0:19.5'), 19.5)
|
|
42
|
+
assert.strictEqual(parseTemperature('12:20.75:23'), 20.75)
|
|
43
|
+
assert.strictEqual(parseTemperature('19.5'), 19.5)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await t.test('should handle negative temperatures', () => {
|
|
47
|
+
assert.strictEqual(parseTemperature('0:-5'), -5)
|
|
48
|
+
assert.strictEqual(parseTemperature('10:-2:15'), -2)
|
|
49
|
+
assert.strictEqual(parseTemperature('-5'), -5)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
await t.test('should return null for invalid formats', () => {
|
|
53
|
+
assert.strictEqual(parseTemperature('invalid'), null)
|
|
54
|
+
assert.strictEqual(parseTemperature('abc:xyz'), null)
|
|
55
|
+
assert.strictEqual(parseTemperature(':'), null)
|
|
56
|
+
assert.strictEqual(parseTemperature('::'), null)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed-keys.d.ts","sourceRoot":"","sources":["typed-keys.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,0BAJ4C,CAAC,SAA/B,MAAM,CAAC,WAAW,EAAE,OAAO,CAAE,OAChC,CAAC,GACC,CAAC,MAAM,CAAC,CAAC,EAAE,CAIvB"}
|