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 CHANGED
@@ -9,11 +9,13 @@
9
9
 
10
10
  const fs = require('fs')
11
11
  const Deconz = require('../lib/Deconz')
12
- const homebridgeLib = require('homebridge-lib')
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 } = homebridgeLib.CommandLineTool
16
- const { UsageError } = homebridgeLib.CommandLineParser
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 homebridgeLib.CommandLineTool {
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 homebridgeLib.JsonFormatter(
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 homebridgeLib.CommandLineParser(packageJson)
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
- homebridgeLib.OptionParser.toHost('host', value, false, true)
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 = homebridgeLib.OptionParser.toString(
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 = homebridgeLib.OptionParser.toInt(
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
- this.log(
453
- '%s: request %d: %s %s', error.request.name,
454
- error.request.id, error.request.method, error.request.resource
455
- )
456
- this.warn(
457
- '%s: request %d: %s', error.request.name, error.request.id, error
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 homebridgeLib.CommandLineParser(packageJson)
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 paramters')
614
+ throw new UsageError('too many parameters')
609
615
  }
610
616
  clargs.resource = list.length === 0
611
617
  ? '/'
612
- : homebridgeLib.OptionParser.toPath('resource', list[0])
618
+ : OptionParser.toPath('resource', list[0])
613
619
  })
614
620
  .parse(...args)
615
- const jsonFormatter = new homebridgeLib.JsonFormatter(clargs.options)
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 homebridgeLib.CommandLineParser(packageJson)
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 = homebridgeLib.OptionParser.toPath('resource', 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 paramters')
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 homebridgeLib.JsonFormatter()
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 homebridgeLib.CommandLineParser(packageJson)
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 homebridgeLib.JsonFormatter(
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 homebridgeLib.CommandLineParser(packageJson)
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 homebridgeLib.JsonFormatter()
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 homebridgeLib.CommandLineParser(packageJson)
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 homebridgeLib.JsonFormatter({ sortKeys: true })
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 homebridgeLib.CommandLineParser(packageJson)
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 homebridgeLib.JsonFormatter(options)
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 homebridgeLib.CommandLineParser(packageJson)
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 homebridgeLib.JsonFormatter(options)
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 homebridgeLib.CommandLineParser(packageJson)
811
- const jsonFormatter = new homebridgeLib.JsonFormatter(
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 homebridgeLib.CommandLineParser(packageJson)
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, key) => {
845
- homebridgeLib.OptionParser.toInt(
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 homebridgeLib.JsonFormatter()
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 homebridgeLib.CommandLineParser(packageJson)
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()