homebridge-deconz 0.1.4 → 0.1.7
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/cli/deconz.js +47 -43
- package/cli/ui.js +419 -0
- package/jsdoc.json +1 -0
- package/lib/Deconz/ApiClient.js +23 -23
- package/lib/Deconz/ApiError.js +2 -2
- package/lib/Deconz/ApiResponse.js +2 -2
- package/lib/Deconz/Discovery.js +6 -6
- package/lib/Deconz/Resource.js +4 -4
- package/lib/Deconz/WsClient.js +4 -4
- package/lib/DeconzAccessory/Gateway.js +26 -63
- package/lib/DeconzAccessory/Light.js +2 -4
- package/lib/DeconzAccessory/Sensor.js +6 -7
- package/lib/DeconzAccessory/Thermostat.js +2 -4
- package/lib/DeconzAccessory/WarningDevice.js +0 -3
- package/lib/DeconzAccessory/index.js +2 -3
- package/lib/DeconzPlatform.js +5 -5
- package/lib/DeconzService/Battery.js +2 -2
- package/lib/DeconzService/Button.js +2 -2
- package/lib/DeconzService/Gateway.js +2 -2
- package/lib/DeconzService/Light.js +6 -7
- package/lib/DeconzService/LightsResource.js +1 -2
- package/lib/DeconzService/WindowCovering.js +1 -3
- package/lib/DeconzService/index.js +2 -2
- package/package.json +5 -5
- package/cli/ui.sh +0 -83
package/cli/deconz.js
CHANGED
@@ -9,11 +9,13 @@
|
|
9
9
|
|
10
10
|
const fs = require('fs')
|
11
11
|
const Deconz = require('../lib/Deconz')
|
12
|
-
const
|
12
|
+
const {
|
13
|
+
CommandLineParser, CommandLineTool, JsonFormatter, OptionParser
|
14
|
+
} = require('hb-lib-tools')
|
13
15
|
const packageJson = require('../package.json')
|
14
16
|
|
15
|
-
const { b, u } =
|
16
|
-
const { UsageError } =
|
17
|
+
const { b, u } = CommandLineTool
|
18
|
+
const { UsageError } = CommandLineParser
|
17
19
|
|
18
20
|
const usage = {
|
19
21
|
deconz: `${b('deconz')} [${b('-hVDp')}] [${b('-H')} ${u('hostname')}[${b(':')}${u('port')}]] [${b('-K')} ${u('api key')}] [${b('-t')} ${u('timeout')}] ${u('command')} [${u('argument')} ...]`,
|
@@ -351,7 +353,7 @@ Parameters:
|
|
351
353
|
Print full API output.`
|
352
354
|
}
|
353
355
|
|
354
|
-
class Main extends
|
356
|
+
class Main extends CommandLineTool {
|
355
357
|
constructor () {
|
356
358
|
super({ mode: 'command', debug: false })
|
357
359
|
this.usage = usage.deconz
|
@@ -378,7 +380,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
378
380
|
}
|
379
381
|
|
380
382
|
writeGateways () {
|
381
|
-
const jsonFormatter = new
|
383
|
+
const jsonFormatter = new JsonFormatter(
|
382
384
|
{ noWhiteSpace: true, sortKeys: true }
|
383
385
|
)
|
384
386
|
const text = jsonFormatter.stringify(this.gateways)
|
@@ -386,7 +388,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
386
388
|
}
|
387
389
|
|
388
390
|
parseArguments () {
|
389
|
-
const parser = new
|
391
|
+
const parser = new CommandLineParser(packageJson)
|
390
392
|
const clargs = {
|
391
393
|
options: {
|
392
394
|
host: process.env.DECONZ_HOST || 'localhost',
|
@@ -397,11 +399,11 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
397
399
|
.help('h', 'help', help.deconz)
|
398
400
|
.version('V', 'version')
|
399
401
|
.option('H', 'host', (value) => {
|
400
|
-
|
402
|
+
OptionParser.toHost('host', value, false, true)
|
401
403
|
clargs.options.host = value
|
402
404
|
})
|
403
405
|
.option('K', 'apiKey', (value) => {
|
404
|
-
clargs.options.apiKey =
|
406
|
+
clargs.options.apiKey = OptionParser.toString(
|
405
407
|
'apiKey', value, true, true
|
406
408
|
)
|
407
409
|
})
|
@@ -416,7 +418,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
416
418
|
}
|
417
419
|
})
|
418
420
|
.option('t', 'timeout', (value) => {
|
419
|
-
clargs.options.timeout =
|
421
|
+
clargs.options.timeout = OptionParser.toInt(
|
420
422
|
'timeout', value, 1, 60, true
|
421
423
|
)
|
422
424
|
})
|
@@ -449,13 +451,17 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
449
451
|
})
|
450
452
|
this.deconzDiscovery
|
451
453
|
.on('error', (error) => {
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
454
|
+
if (error.request != null) {
|
455
|
+
this.log(
|
456
|
+
'%s: request %d: %s %s', error.request.name,
|
457
|
+
error.request.id, error.request.method, error.request.resource
|
458
|
+
)
|
459
|
+
this.warn(
|
460
|
+
'%s: request %d: %s', error.request.name, error.request.id, error
|
461
|
+
)
|
462
|
+
return
|
463
|
+
}
|
464
|
+
this.warn(error)
|
459
465
|
})
|
460
466
|
.on('request', (request) => {
|
461
467
|
this.debug(
|
@@ -586,7 +592,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
586
592
|
// ===== GET =================================================================
|
587
593
|
|
588
594
|
async get (...args) {
|
589
|
-
const parser = new
|
595
|
+
const parser = new CommandLineParser(packageJson)
|
590
596
|
const clargs = {
|
591
597
|
options: {}
|
592
598
|
}
|
@@ -605,14 +611,14 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
605
611
|
.flag('v', 'valuesOnly', () => { clargs.options.valuesOnly = true })
|
606
612
|
.remaining((list) => {
|
607
613
|
if (list.length > 1) {
|
608
|
-
throw new UsageError('too many
|
614
|
+
throw new UsageError('too many parameters')
|
609
615
|
}
|
610
616
|
clargs.resource = list.length === 0
|
611
617
|
? '/'
|
612
|
-
:
|
618
|
+
: OptionParser.toPath('resource', list[0])
|
613
619
|
})
|
614
620
|
.parse(...args)
|
615
|
-
const jsonFormatter = new
|
621
|
+
const jsonFormatter = new JsonFormatter(clargs.options)
|
616
622
|
const response = await this.client.get(clargs.resource)
|
617
623
|
this.print(jsonFormatter.stringify(response))
|
618
624
|
}
|
@@ -620,7 +626,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
620
626
|
// ===== PUT, POST, DELETE ===================================================
|
621
627
|
|
622
628
|
async resourceCommand (command, ...args) {
|
623
|
-
const parser = new
|
629
|
+
const parser = new CommandLineParser(packageJson)
|
624
630
|
const clargs = {
|
625
631
|
options: {}
|
626
632
|
}
|
@@ -628,7 +634,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
628
634
|
.help('h', 'help', help[command])
|
629
635
|
.flag('v', 'verbose', () => { clargs.options.verbose = true })
|
630
636
|
.parameter('resource', (resource) => {
|
631
|
-
clargs.resource =
|
637
|
+
clargs.resource = OptionParser.toPath('resource', resource)
|
632
638
|
if (clargs.resource === '/') {
|
633
639
|
// deCONZ will crash otherwise, see deconz-rest-plugin#2520.
|
634
640
|
throw new UsageError(`/: invalid resource for ${command}`)
|
@@ -636,7 +642,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
636
642
|
})
|
637
643
|
.remaining((list) => {
|
638
644
|
if (list.length > 1) {
|
639
|
-
throw new Error('too many
|
645
|
+
throw new Error('too many parameters')
|
640
646
|
}
|
641
647
|
if (list.length === 1) {
|
642
648
|
try {
|
@@ -648,7 +654,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
648
654
|
})
|
649
655
|
.parse(...args)
|
650
656
|
const response = await this.client[command](clargs.resource, clargs.body)
|
651
|
-
const jsonFormatter = new
|
657
|
+
const jsonFormatter = new JsonFormatter()
|
652
658
|
if (clargs.options.verbose || response.success == null) {
|
653
659
|
this.print(jsonFormatter.stringify(response.body))
|
654
660
|
return
|
@@ -688,7 +694,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
688
694
|
}
|
689
695
|
|
690
696
|
async eventlog (...args) {
|
691
|
-
const parser = new
|
697
|
+
const parser = new CommandLineParser(packageJson)
|
692
698
|
let mode = 'daemon'
|
693
699
|
const options = {}
|
694
700
|
parser
|
@@ -697,7 +703,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
697
703
|
.flag('r', 'raw', () => { options.raw = true })
|
698
704
|
.flag('s', 'service', () => { mode = 'service' })
|
699
705
|
.parse(...args)
|
700
|
-
this.jsonFormatter = new
|
706
|
+
this.jsonFormatter = new JsonFormatter(
|
701
707
|
mode === 'service' ? { noWhiteSpace: true } : {}
|
702
708
|
)
|
703
709
|
const { websocketport } = await this.client.get('/config')
|
@@ -741,7 +747,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
741
747
|
// ===========================================================================
|
742
748
|
|
743
749
|
async simpleCommand (command, ...args) {
|
744
|
-
const parser = new
|
750
|
+
const parser = new CommandLineParser(packageJson)
|
745
751
|
const clargs = {
|
746
752
|
options: {}
|
747
753
|
}
|
@@ -750,7 +756,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
750
756
|
.flag('v', 'verbose', () => { clargs.options.verbose = true })
|
751
757
|
.parse(...args)
|
752
758
|
const response = await this.client[command]()
|
753
|
-
const jsonFormatter = new
|
759
|
+
const jsonFormatter = new JsonFormatter()
|
754
760
|
for (const error of response.errors) {
|
755
761
|
this.warn('api error %d: %s', error.type, error.description)
|
756
762
|
}
|
@@ -770,45 +776,45 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
770
776
|
}
|
771
777
|
|
772
778
|
async discover (...args) {
|
773
|
-
const parser = new
|
779
|
+
const parser = new CommandLineParser(packageJson)
|
774
780
|
let stealth = false
|
775
781
|
parser
|
776
782
|
.help('h', 'help', help.discover)
|
777
783
|
.flag('S', 'stealth', () => { stealth = true })
|
778
784
|
.parse(...args)
|
779
|
-
const jsonFormatter = new
|
785
|
+
const jsonFormatter = new JsonFormatter({ sortKeys: true })
|
780
786
|
const bridges = await this.deconzDiscovery.discover(stealth)
|
781
787
|
this.print(jsonFormatter.stringify(bridges))
|
782
788
|
}
|
783
789
|
|
784
790
|
async config (...args) {
|
785
|
-
const parser = new
|
791
|
+
const parser = new CommandLineParser(packageJson)
|
786
792
|
const options = {}
|
787
793
|
parser
|
788
794
|
.help('h', 'help', help.config)
|
789
795
|
.flag('s', 'sortKeys', () => { options.sortKeys = true })
|
790
796
|
.parse(...args)
|
791
|
-
const jsonFormatter = new
|
797
|
+
const jsonFormatter = new JsonFormatter(options)
|
792
798
|
const json = jsonFormatter.stringify(this.gatewayConfig)
|
793
799
|
this.print(json)
|
794
800
|
}
|
795
801
|
|
796
802
|
async description (...args) {
|
797
|
-
const parser = new
|
803
|
+
const parser = new CommandLineParser(packageJson)
|
798
804
|
const options = {}
|
799
805
|
parser
|
800
806
|
.help('h', 'help', help.description)
|
801
807
|
.flag('s', 'sortKeys', () => { options.sortKeys = true })
|
802
808
|
.parse(...args)
|
803
809
|
const response = await this.deconzDiscovery.description(this.clargs.options.host)
|
804
|
-
const jsonFormatter = new
|
810
|
+
const jsonFormatter = new JsonFormatter(options)
|
805
811
|
const json = jsonFormatter.stringify(response)
|
806
812
|
this.print(json)
|
807
813
|
}
|
808
814
|
|
809
815
|
async getApiKey (...args) {
|
810
|
-
const parser = new
|
811
|
-
const jsonFormatter = new
|
816
|
+
const parser = new CommandLineParser(packageJson)
|
817
|
+
const jsonFormatter = new JsonFormatter(
|
812
818
|
{ noWhiteSpace: true, sortKeys: true }
|
813
819
|
)
|
814
820
|
parser
|
@@ -834,17 +840,15 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
834
840
|
// ===== LIGHTVALUES =========================================================
|
835
841
|
|
836
842
|
async probe (...args) {
|
837
|
-
const parser = new
|
843
|
+
const parser = new CommandLineParser(packageJson)
|
838
844
|
const clargs = {
|
839
845
|
maxCount: 60
|
840
846
|
}
|
841
847
|
parser
|
842
848
|
.help('h', 'help', help.probe)
|
843
849
|
.flag('v', 'verbose', () => { clargs.verbose = true })
|
844
|
-
.option('t', 'timeout', (value
|
845
|
-
|
846
|
-
'timeout', value, 1, 10, true
|
847
|
-
)
|
850
|
+
.option('t', 'timeout', (value) => {
|
851
|
+
OptionParser.toInt('timeout', value, 1, 10, true)
|
848
852
|
clargs.maxCount = value * 12
|
849
853
|
})
|
850
854
|
.parameter('light', (value) => {
|
@@ -929,7 +933,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
929
933
|
response.xy.b = await probeXy.call(this, 'blue', [zero, zero])
|
930
934
|
}
|
931
935
|
await this.client.put(clargs.light + '/state', { on: light.state.on })
|
932
|
-
this.jsonFormatter = new
|
936
|
+
this.jsonFormatter = new JsonFormatter()
|
933
937
|
const json = this.jsonFormatter.stringify(response)
|
934
938
|
this.print(json)
|
935
939
|
}
|
@@ -937,7 +941,7 @@ class Main extends homebridgeLib.CommandLineTool {
|
|
937
941
|
// ===== BRIDGE/GATEWAY DISCOVERY ==============================================
|
938
942
|
|
939
943
|
async restart (...args) {
|
940
|
-
const parser = new
|
944
|
+
const parser = new CommandLineParser(packageJson)
|
941
945
|
const clargs = {}
|
942
946
|
parser
|
943
947
|
.help('h', 'help', help.restart)
|
package/cli/ui.js
ADDED
@@ -0,0 +1,419 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
// homebridge-deconz/cli/deconz.js
|
4
|
+
// Copyright © 2018-2023 Erik Baauw. All rights reserved.
|
5
|
+
//
|
6
|
+
// Command line interface to Homebridge deCONZ UI Server.
|
7
|
+
|
8
|
+
'use strict'
|
9
|
+
|
10
|
+
const fs = require('fs').promises
|
11
|
+
// const Deconz = require('../lib/Deconz')
|
12
|
+
const {
|
13
|
+
CommandLineParser, CommandLineTool, HttpClient, JsonFormatter, OptionParser
|
14
|
+
} = require('hb-lib-tools')
|
15
|
+
const packageJson = require('../package.json')
|
16
|
+
|
17
|
+
const { b, u } = CommandLineTool
|
18
|
+
const { UsageError } = CommandLineParser
|
19
|
+
|
20
|
+
const usage = {
|
21
|
+
ui: `${b('ui')} [${b('-hVD')}] [${b('-U')} ${u('username')}] [${b('-G')} ${u('gateway')}] [${b('-t')} ${u('timeout')}] ${u('command')} [${u('argument')} ...]`,
|
22
|
+
get: `${b('get')} [${b('-hsnjuatlkv')}] [${b('/devices')}[${b('/')}${u('id')}] | ${b('/accessories')}[${b('/')}${u('id')}]]`,
|
23
|
+
put: `${b('put')} [${b('-hsnjuatlkv')}] [${b('/devices/')}${u('id')}] | ${b('/accessories/')}${u('id')}]] ${u('body')}`
|
24
|
+
}
|
25
|
+
|
26
|
+
const description = {
|
27
|
+
ui: 'Command line interface to Homebridge deCONZ UI Server for dynamic settings.',
|
28
|
+
get: 'Get dynamic settings.',
|
29
|
+
put: 'Update dynamic settings.'
|
30
|
+
}
|
31
|
+
|
32
|
+
const help = {
|
33
|
+
ui: `${description.ui}
|
34
|
+
|
35
|
+
Usage: ${usage.ui}
|
36
|
+
|
37
|
+
Parameters:
|
38
|
+
${b('-h')}, ${b('--help')}
|
39
|
+
Print this help and exit.
|
40
|
+
|
41
|
+
${b('-V')}, ${b('--version')}
|
42
|
+
Print version and exit.
|
43
|
+
|
44
|
+
${b('-D')}, ${b('--debug')}
|
45
|
+
Print debug messages for communication with the Homebridge deCONZ UI Server.
|
46
|
+
|
47
|
+
${b('-U')} ${u('username')}, ${b('--username=')}${u('username')}
|
48
|
+
Specify the username of the Homebridge instance. Default: first instance in config.json.
|
49
|
+
|
50
|
+
${b('-G')} ${u('gateway')}, ${b('--gateway=')}${u('gateway')}
|
51
|
+
Specify the id of the deCONZ gateway. Default: first gateway in cachedAccessories.
|
52
|
+
|
53
|
+
${b('-t')} ${u('timeout')}, ${b('--timeout=')}${u('timeout')}
|
54
|
+
Set timeout to ${u('timeout')} seconds instead of default ${b(5)}.
|
55
|
+
|
56
|
+
Commands:
|
57
|
+
${usage.get}
|
58
|
+
${description.get}
|
59
|
+
|
60
|
+
${usage.put}
|
61
|
+
${description.put}
|
62
|
+
|
63
|
+
For more help, issue: ${b('ui')} ${u('command')} ${b('-h')}`,
|
64
|
+
get: `${description.get}
|
65
|
+
|
66
|
+
Usage: ${b('ui')} ${usage.get}
|
67
|
+
|
68
|
+
Parameters:
|
69
|
+
${b('-h')}, ${b('--help')}
|
70
|
+
Print this help and exit.
|
71
|
+
|
72
|
+
${b('-s')}, ${b('--sortKeys')}
|
73
|
+
Sort object key/value pairs alphabetically on key.
|
74
|
+
|
75
|
+
${b('-n')}, ${b('-noWhiteSpace')}
|
76
|
+
Do not include spaces nor newlines in the output.
|
77
|
+
|
78
|
+
${b('-j')}, ${b('--jsonArray')}
|
79
|
+
Output a JSON array of objects for each key/value pair.
|
80
|
+
Each object contains two key/value pairs: key "keys" with an array
|
81
|
+
of keys as value and key "value" with the value as value.
|
82
|
+
|
83
|
+
${b('-u')}, ${b('--joinKeys')}
|
84
|
+
Output JSON array of objects for each key/value pair.
|
85
|
+
Each object contains one key/value pair: the path (concatenated
|
86
|
+
keys separated by '/') as key and the value as value.
|
87
|
+
|
88
|
+
${b('-a')}, ${b('--ascii')}
|
89
|
+
Output path:value in plain text instead of JSON.
|
90
|
+
|
91
|
+
${b('-t')}, ${b('--topOnly')}
|
92
|
+
Limit output to top-level key/values.
|
93
|
+
|
94
|
+
${b('-l')}, ${b('--leavesOnly')}
|
95
|
+
Limit output to leaf (non-array, non-object) key/values.
|
96
|
+
|
97
|
+
${b('-k')}, ${b('--keysOnly')}
|
98
|
+
Limit output to keys. With ${b('-u')}, output a JSON array of paths.
|
99
|
+
|
100
|
+
${b('-v')}, ${b('--valuesOnly')}
|
101
|
+
Limit output to values. With ${b('-u')}, output a JSON array of values.`,
|
102
|
+
put: `${description.put}
|
103
|
+
|
104
|
+
Usage: ${b('ui')} ${usage.put}
|
105
|
+
|
106
|
+
Parameters:
|
107
|
+
xxx`
|
108
|
+
}
|
109
|
+
|
110
|
+
class Main extends CommandLineTool {
|
111
|
+
constructor () {
|
112
|
+
super({ mode: 'command', debug: false })
|
113
|
+
this.usage = usage.deconz
|
114
|
+
}
|
115
|
+
|
116
|
+
parseArguments () {
|
117
|
+
const parser = new CommandLineParser(packageJson)
|
118
|
+
const clargs = {
|
119
|
+
options: {
|
120
|
+
timeout: 5
|
121
|
+
}
|
122
|
+
}
|
123
|
+
parser
|
124
|
+
.help('h', 'help', help.ui)
|
125
|
+
.version('V', 'version')
|
126
|
+
.flag('D', 'debug', () => {
|
127
|
+
if (this.debugEnabled) {
|
128
|
+
this.setOptions({ vdebug: true })
|
129
|
+
} else {
|
130
|
+
this.setOptions({ debug: true, chalk: true })
|
131
|
+
}
|
132
|
+
})
|
133
|
+
.option('U', 'username', (value) => {
|
134
|
+
clargs.username = OptionParser.toString(
|
135
|
+
'username', value, true, true
|
136
|
+
).toUpperCase()
|
137
|
+
if (!OptionParser.patterns.mac.test(clargs.username)) {
|
138
|
+
throw new UsageError(`${clargs.username}: invalid username`)
|
139
|
+
}
|
140
|
+
})
|
141
|
+
.option('G', 'gateway', (value) => {
|
142
|
+
clargs.gateway = OptionParser.toString(
|
143
|
+
'gateway', value, true, true
|
144
|
+
).toUpperCase()
|
145
|
+
})
|
146
|
+
.option('t', 'timeout', (value) => {
|
147
|
+
clargs.options.timeout = OptionParser.toInt(
|
148
|
+
'timeout', value, 1, 60, true
|
149
|
+
)
|
150
|
+
})
|
151
|
+
.parameter('command', (value) => {
|
152
|
+
if (usage[value] == null || typeof this[value] !== 'function') {
|
153
|
+
throw new UsageError(`${value}: unknown command`)
|
154
|
+
}
|
155
|
+
clargs.command = value
|
156
|
+
})
|
157
|
+
.remaining((list) => { clargs.args = list })
|
158
|
+
parser
|
159
|
+
.parse()
|
160
|
+
return clargs
|
161
|
+
}
|
162
|
+
|
163
|
+
async main () {
|
164
|
+
try {
|
165
|
+
await this._main()
|
166
|
+
} catch (error) {
|
167
|
+
if (error.request == null) {
|
168
|
+
this.error(error)
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
/** Read and parse a JSON file.
|
174
|
+
*
|
175
|
+
* @param {string} filename - The name of the JSON file.
|
176
|
+
* @returns {*} body - The contents of the JSON file as JavaScript object.
|
177
|
+
*/
|
178
|
+
async readJsonFile (filename) {
|
179
|
+
this.vdebug('reading %s', filename)
|
180
|
+
const text = await fs.readFile(filename)
|
181
|
+
this.debug('%s: %d bytes', filename, text.length)
|
182
|
+
const body = JSON.parse(text)
|
183
|
+
this.vdebug('%s: %j', filename, body)
|
184
|
+
return body
|
185
|
+
}
|
186
|
+
|
187
|
+
/** Read Homebridge's cachedAccessories.
|
188
|
+
*
|
189
|
+
* @param {string} dir - The Homebridge user directory.
|
190
|
+
* @param {string} username - The username (mac address) of the Homebridge bridge.
|
191
|
+
* @param {string} platformName - The name of the platform.
|
192
|
+
* @returns {Array<SerialisedAccessory>} accessories - The serialsed accessories.
|
193
|
+
*/
|
194
|
+
async readCachedAccessories (dir, username, platformName) {
|
195
|
+
let filename = dir + '/accessories/cachedAccessories'
|
196
|
+
if (username != null) {
|
197
|
+
filename += '.' + username.replace(/:/g, '').toUpperCase()
|
198
|
+
}
|
199
|
+
const body = await this.readJsonFile(filename)
|
200
|
+
return body.filter((accessory) => {
|
201
|
+
return accessory.platform === platformName
|
202
|
+
})
|
203
|
+
}
|
204
|
+
|
205
|
+
/** Get platform entries from Homebridge's config.json file.
|
206
|
+
*
|
207
|
+
* @param {string} dir - The Homebridge user directory.
|
208
|
+
* @param {string} platformName - The name of the platform.
|
209
|
+
* @returns {Array<PlatformInfo>} platforms - Array of matching platforms.
|
210
|
+
*/
|
211
|
+
async _getPlatforms (dir, platformName) {
|
212
|
+
const filename = dir + '/config.json'
|
213
|
+
const config = await this.readJsonFile(filename)
|
214
|
+
const gateways = []
|
215
|
+
for (const platform of config.platforms) {
|
216
|
+
if (platform.platform !== platformName) {
|
217
|
+
continue
|
218
|
+
}
|
219
|
+
try {
|
220
|
+
const username = platform._bridge == null ? null : platform._bridge.username
|
221
|
+
const cachedAccessories = await this.readCachedAccessories(dir, username, platformName)
|
222
|
+
const cachedGateways = cachedAccessories.filter((accessory) => {
|
223
|
+
return accessory.context.className === 'Gateway'
|
224
|
+
})
|
225
|
+
for (const gateway of cachedGateways) {
|
226
|
+
gateways.push({
|
227
|
+
platformName: platform.name == null ? platform.platform : platform.name,
|
228
|
+
username,
|
229
|
+
uiPort: gateway.context.uiPort,
|
230
|
+
gid: gateway.context.id,
|
231
|
+
name: gateway.context.name
|
232
|
+
})
|
233
|
+
}
|
234
|
+
} catch (error) { this.warn(error) }
|
235
|
+
}
|
236
|
+
return gateways
|
237
|
+
}
|
238
|
+
|
239
|
+
/** Get platform entries from Homebridge's config.json file.
|
240
|
+
*
|
241
|
+
* @param {string} platformName - The name of the platform.
|
242
|
+
* @returns {Array<PlatformInfo>} platforms - Array of matching platforms.
|
243
|
+
*/
|
244
|
+
async getPlatforms (platformName) {
|
245
|
+
if (process.env.HOMEBRIDGE_DIR != null) {
|
246
|
+
return this._getPlatforms(process.env.HOMEBRIDGE_DIR, platformName)
|
247
|
+
}
|
248
|
+
for (const dir of ['/var/lib/homebridge', process.env.HOME + '/.homebridge', '.']) {
|
249
|
+
try {
|
250
|
+
return await this._getPlatforms(dir, platformName)
|
251
|
+
} catch (error) {
|
252
|
+
if (error.code !== 'ENOENT') {
|
253
|
+
throw error
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
throw new Error('cannot find config.json - please set HOMEBRIDGE_DIR')
|
258
|
+
}
|
259
|
+
|
260
|
+
/** Create a client to the UI server.
|
261
|
+
*
|
262
|
+
* @param {integer} uiPort - The port for the UI server.
|
263
|
+
* @returns {HttpClient} client - The UI client
|
264
|
+
*/
|
265
|
+
async createUiClient (uiPort) {
|
266
|
+
const host = 'localhost:' + uiPort
|
267
|
+
const client = new HttpClient({
|
268
|
+
host,
|
269
|
+
json: true,
|
270
|
+
name: host,
|
271
|
+
timeout: this.clargs.options.timeout
|
272
|
+
})
|
273
|
+
client
|
274
|
+
.on('error', (error) => {
|
275
|
+
if (error.request.id !== this.requestId) {
|
276
|
+
if (error.request.body == null) {
|
277
|
+
this.log(
|
278
|
+
'%s: request %d: %s %s', error.request.name, error.request.id,
|
279
|
+
error.request.method, error.request.resource
|
280
|
+
)
|
281
|
+
} else {
|
282
|
+
this.log(
|
283
|
+
'%s: request %d: %s %s %s', error.request.name, error.request.id,
|
284
|
+
error.request.method, error.request.resource, error.request.body
|
285
|
+
)
|
286
|
+
}
|
287
|
+
this.requestId = error.request.id
|
288
|
+
}
|
289
|
+
this.error('%s: request %d: %s', error.request.name, error.request.id, error)
|
290
|
+
})
|
291
|
+
.on('request', (request) => {
|
292
|
+
if (request.body == null) {
|
293
|
+
this.debug(
|
294
|
+
'%s: request %d: %s %s', request.name, request.id,
|
295
|
+
request.method, request.resource
|
296
|
+
)
|
297
|
+
this.vdebug(
|
298
|
+
'%s: request %d: %s %s', request.name, request.id,
|
299
|
+
request.method, request.url
|
300
|
+
)
|
301
|
+
} else {
|
302
|
+
this.debug(
|
303
|
+
'%s: request %d: %s %s %s', request.name, request.id,
|
304
|
+
request.method, request.resource, request.body
|
305
|
+
)
|
306
|
+
this.vdebug(
|
307
|
+
'%s: request %d: %s %s %s', request.name, request.id,
|
308
|
+
request.method, request.url, request.body
|
309
|
+
)
|
310
|
+
}
|
311
|
+
})
|
312
|
+
.on('response', (response) => {
|
313
|
+
this.vdebug(
|
314
|
+
'%s: request %d: response: %j', response.request.name, response.request.id,
|
315
|
+
response.body
|
316
|
+
)
|
317
|
+
this.debug(
|
318
|
+
'%s: request %d: %d %s', response.request.name, response.request.id,
|
319
|
+
response.statusCode, response.statusMessage
|
320
|
+
)
|
321
|
+
})
|
322
|
+
const response = await client.get('/ping')
|
323
|
+
if (response.body !== 'pong') {
|
324
|
+
throw new Error(`${host}: cannot ping`)
|
325
|
+
}
|
326
|
+
return client
|
327
|
+
}
|
328
|
+
|
329
|
+
async _main () {
|
330
|
+
this.clargs = this.parseArguments()
|
331
|
+
this.name = 'ui ' + this.clargs.command
|
332
|
+
this.usage = `${b('ui')} ${usage[this.clargs.command]}`
|
333
|
+
let platforms = await this.getPlatforms('deCONZ')
|
334
|
+
if (platforms.length === 0) {
|
335
|
+
throw new Error('no UI server found')
|
336
|
+
}
|
337
|
+
if (this.clargs.username != null) {
|
338
|
+
platforms = platforms.filter((platform) => {
|
339
|
+
return platform.username === this.clargs.username
|
340
|
+
})
|
341
|
+
if (platforms.length === 0) {
|
342
|
+
throw new Error(`no UI server found for bridge ${this.clargs.username}`)
|
343
|
+
}
|
344
|
+
}
|
345
|
+
if (this.clargs.gateway != null) {
|
346
|
+
platforms = platforms.filter((platform) => {
|
347
|
+
return platform.gid === this.clargs.gateway
|
348
|
+
})
|
349
|
+
if (platforms.length === 0) {
|
350
|
+
throw new Error(`no UI server found for gateway ${this.clargs.gateway}`)
|
351
|
+
}
|
352
|
+
}
|
353
|
+
const { gid, uiPort } = platforms[0]
|
354
|
+
this.client = await this.createUiClient(uiPort)
|
355
|
+
this.gid = gid
|
356
|
+
return this[this.clargs.command](this.clargs.args)
|
357
|
+
}
|
358
|
+
|
359
|
+
async get (...args) {
|
360
|
+
const parser = new CommandLineParser(packageJson)
|
361
|
+
const clargs = {
|
362
|
+
options: {}
|
363
|
+
}
|
364
|
+
parser
|
365
|
+
.help('h', 'help', help.get)
|
366
|
+
.flag('s', 'sortKeys', () => { clargs.options.sortKeys = true })
|
367
|
+
.flag('n', 'noWhiteSpace', () => {
|
368
|
+
clargs.options.noWhiteSpace = true
|
369
|
+
})
|
370
|
+
.flag('j', 'jsonArray', () => { clargs.options.noWhiteSpace = true })
|
371
|
+
.flag('u', 'joinKeys', () => { clargs.options.joinKeys = true })
|
372
|
+
.flag('a', 'ascii', () => { clargs.options.ascii = true })
|
373
|
+
.flag('t', 'topOnly', () => { clargs.options.topOnly = true })
|
374
|
+
.flag('l', 'leavesOnly', () => { clargs.options.leavesOnly = true })
|
375
|
+
.flag('k', 'keysOnly', () => { clargs.options.keysOnly = true })
|
376
|
+
.flag('v', 'valuesOnly', () => { clargs.options.valuesOnly = true })
|
377
|
+
.parameter('resource', (value) => {
|
378
|
+
clargs.resource = OptionParser.toPath('resource', value)
|
379
|
+
})
|
380
|
+
.parse(...args)
|
381
|
+
if (clargs.resource === '/') {
|
382
|
+
clargs.resource = ''
|
383
|
+
}
|
384
|
+
const jsonFormatter = new JsonFormatter(clargs.options)
|
385
|
+
const { body } = await this.client.get('/gateways/' + this.gid + clargs.resource)
|
386
|
+
this.print(jsonFormatter.stringify(body))
|
387
|
+
}
|
388
|
+
|
389
|
+
async put (...args) {
|
390
|
+
const parser = new CommandLineParser(packageJson)
|
391
|
+
const clargs = {
|
392
|
+
options: {}
|
393
|
+
}
|
394
|
+
parser
|
395
|
+
.help('h', 'help', help.get)
|
396
|
+
.parameter('resource', (value) => {
|
397
|
+
clargs.resource = OptionParser.toPath('resource', value)
|
398
|
+
})
|
399
|
+
.parameter('body', (value) => {
|
400
|
+
value = OptionParser.toString('body', value, true, true)
|
401
|
+
try {
|
402
|
+
clargs.body = JSON.parse(value)
|
403
|
+
} catch (error) {
|
404
|
+
throw new UsageError(error.message) // Covert TypeError to UsageError.
|
405
|
+
}
|
406
|
+
})
|
407
|
+
.parse(...args)
|
408
|
+
if (clargs.resource === '/') {
|
409
|
+
clargs.resource = ''
|
410
|
+
}
|
411
|
+
const jsonFormatter = new JsonFormatter(clargs.options)
|
412
|
+
const { body } = await this.client.put(
|
413
|
+
'/gateways/' + this.gid + clargs.resource + '/settings', clargs.body
|
414
|
+
)
|
415
|
+
this.print(jsonFormatter.stringify(body))
|
416
|
+
}
|
417
|
+
}
|
418
|
+
|
419
|
+
new Main().main()
|