hb-zp-tools 2.0.0-0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/ZpClient.js +42 -17
- package/lib/ZpTool.js +80 -108
- package/lib/ZpXmlParser.js +35 -22
- package/package.json +2 -2
package/lib/ZpClient.js
CHANGED
|
@@ -155,10 +155,10 @@ class ZpClientRequest extends HttpClient.HttpRequest {
|
|
|
155
155
|
get body () {}
|
|
156
156
|
|
|
157
157
|
/** The (parsed) request body (in JavaScript).
|
|
158
|
-
* @type {
|
|
158
|
+
* @type {?string}
|
|
159
159
|
* @readonly
|
|
160
160
|
*/
|
|
161
|
-
get
|
|
161
|
+
get jsonBody () {}
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/** ZpClient response.
|
|
@@ -331,6 +331,7 @@ class ZpClient extends HttpClient {
|
|
|
331
331
|
.intKey('timeout', 1, 60) // seconds
|
|
332
332
|
.intKey('subscriptionTimeout', 1, 1440) // minutes
|
|
333
333
|
.instanceKey('listener', ZpListener)
|
|
334
|
+
.instanceKey('logger')
|
|
334
335
|
.parse(params)
|
|
335
336
|
_params.subscriptionTimeout *= 60 // minutes -> seconds
|
|
336
337
|
|
|
@@ -340,6 +341,7 @@ class ZpClient extends HttpClient {
|
|
|
340
341
|
keepAlive: true,
|
|
341
342
|
maxSockets: 1,
|
|
342
343
|
name: _params.hostname,
|
|
344
|
+
logger: _params.logger,
|
|
343
345
|
timeout: _params.timeout,
|
|
344
346
|
xmlParser: parser.parse.bind(parser)
|
|
345
347
|
}
|
|
@@ -373,6 +375,7 @@ class ZpClient extends HttpClient {
|
|
|
373
375
|
// _request().
|
|
374
376
|
error (error) {
|
|
375
377
|
if (error.request == null) {
|
|
378
|
+
this.warn(error)
|
|
376
379
|
this.emit('error', error)
|
|
377
380
|
}
|
|
378
381
|
}
|
|
@@ -486,7 +489,7 @@ class ZpClient extends HttpClient {
|
|
|
486
489
|
props.household = household
|
|
487
490
|
props.zoneGroup = group.coordinator
|
|
488
491
|
if (props.id === this._params.id) {
|
|
489
|
-
this
|
|
492
|
+
this.#checkAddress(props.address)
|
|
490
493
|
await this._checkBootSeq(props.bootSeq)
|
|
491
494
|
if (props.bootSeq < this._props.bootSeq) {
|
|
492
495
|
props.bootSeq = this._props.bootSeq
|
|
@@ -981,7 +984,7 @@ class ZpClient extends HttpClient {
|
|
|
981
984
|
this._params.callbackUrl = await this._zpListener.addClient(this)
|
|
982
985
|
this._zpListener.on(this.id, async (message) => {
|
|
983
986
|
try {
|
|
984
|
-
await this
|
|
987
|
+
await this.#updateLastSeen()
|
|
985
988
|
message.name = this.name
|
|
986
989
|
if (message.body != null) {
|
|
987
990
|
message.rawBody = message.body
|
|
@@ -994,6 +997,16 @@ class ZpClient extends HttpClient {
|
|
|
994
997
|
this._zoneGroupState = message.body
|
|
995
998
|
await this.initTopology(this)
|
|
996
999
|
}
|
|
1000
|
+
const notify = message.device === 'ZonePlayer'
|
|
1001
|
+
? message.service
|
|
1002
|
+
: message.device + '/' + message.service
|
|
1003
|
+
this.vvdebug(
|
|
1004
|
+
'%s: notify %s/Event: %j', this.name, notify, message.rawBody
|
|
1005
|
+
)
|
|
1006
|
+
this.vdebug(
|
|
1007
|
+
'%s: notify %s/Event: %j', this.name, notify, message.body
|
|
1008
|
+
)
|
|
1009
|
+
this.debug('%s: notify %s/Event', this.name, notify)
|
|
997
1010
|
/** Emitted when a push notification has been received from the zone player.
|
|
998
1011
|
* @event ZpClient#message
|
|
999
1012
|
* @param {ZpClient.ZpClientNotification} message - The message.
|
|
@@ -1049,7 +1062,7 @@ class ZpClient extends HttpClient {
|
|
|
1049
1062
|
}
|
|
1050
1063
|
}
|
|
1051
1064
|
try {
|
|
1052
|
-
const response = await this
|
|
1065
|
+
const response = await this.#request('SUBSCRIBE', url, undefined, headers)
|
|
1053
1066
|
this._subscriptions[url].sid = response.headers.sid
|
|
1054
1067
|
} catch (error) {
|
|
1055
1068
|
if (error.statusCode === 412) {
|
|
@@ -1084,7 +1097,7 @@ class ZpClient extends HttpClient {
|
|
|
1084
1097
|
delete this._subscriptions[url]
|
|
1085
1098
|
if (sid != null) {
|
|
1086
1099
|
try {
|
|
1087
|
-
await this
|
|
1100
|
+
await this.#request('UNSUBSCRIBE', url, undefined, { SID: sid })
|
|
1088
1101
|
} catch (error) { this.error(error) }
|
|
1089
1102
|
}
|
|
1090
1103
|
if (Object.keys(this._subscriptions).length === 0) {
|
|
@@ -1112,6 +1125,10 @@ class ZpClient extends HttpClient {
|
|
|
1112
1125
|
await this.subscribe(url)
|
|
1113
1126
|
} catch (error) { this.error(error) }
|
|
1114
1127
|
}
|
|
1128
|
+
this.debug(
|
|
1129
|
+
'%s: rebooted (%d -> %d)', this.name,
|
|
1130
|
+
oldBootSeq, this.bootSeq
|
|
1131
|
+
)
|
|
1115
1132
|
/** Emitted when the zone player has rebooted.
|
|
1116
1133
|
* @event ZpClient#rebooted
|
|
1117
1134
|
* @param {integer} oldBootSeq - The old
|
|
@@ -1120,7 +1137,7 @@ class ZpClient extends HttpClient {
|
|
|
1120
1137
|
this.emit('rebooted', oldBootSeq)
|
|
1121
1138
|
}
|
|
1122
1139
|
|
|
1123
|
-
|
|
1140
|
+
#checkAddress (address) {
|
|
1124
1141
|
if (address === this._props.address) {
|
|
1125
1142
|
return
|
|
1126
1143
|
}
|
|
@@ -1128,6 +1145,11 @@ class ZpClient extends HttpClient {
|
|
|
1128
1145
|
this._props.address = address
|
|
1129
1146
|
this.host = this._props.address + ':1400'
|
|
1130
1147
|
this._params.hostname = this._props.address
|
|
1148
|
+
|
|
1149
|
+
this.debug(
|
|
1150
|
+
'%s: IP address changed (%s -> %s)', this.name,
|
|
1151
|
+
oldAddress, this.address
|
|
1152
|
+
)
|
|
1131
1153
|
/** Emitted when the zone player has a new IP address.
|
|
1132
1154
|
* @event ZpClient#addressChanged
|
|
1133
1155
|
* @param {string} oldAddress - The old
|
|
@@ -1136,7 +1158,7 @@ class ZpClient extends HttpClient {
|
|
|
1136
1158
|
this.emit('addressChanged', oldAddress)
|
|
1137
1159
|
}
|
|
1138
1160
|
|
|
1139
|
-
async
|
|
1161
|
+
async #updateLastSeen () {
|
|
1140
1162
|
if (this._reSubscribing) {
|
|
1141
1163
|
return
|
|
1142
1164
|
}
|
|
@@ -1175,9 +1197,9 @@ class ZpClient extends HttpClient {
|
|
|
1175
1197
|
if (this._props.household !== message.household) {
|
|
1176
1198
|
this._props.household = message.household
|
|
1177
1199
|
}
|
|
1178
|
-
this
|
|
1200
|
+
this.#checkAddress(message.address)
|
|
1179
1201
|
await this._checkBootSeq(message.bootseq)
|
|
1180
|
-
await this
|
|
1202
|
+
await this.#updateLastSeen()
|
|
1181
1203
|
}
|
|
1182
1204
|
|
|
1183
1205
|
// ***** Control *************************************************************
|
|
@@ -1207,6 +1229,9 @@ class ZpClient extends HttpClient {
|
|
|
1207
1229
|
PlayMode: alarm.playMode,
|
|
1208
1230
|
Volume: alarm.volume,
|
|
1209
1231
|
IncludeLinkedZones: alarm.includeLinkedZones
|
|
1232
|
+
// Note that when set from the Sonos iOS app, the alarm includes a
|
|
1233
|
+
// <Content type="..." .../> tag. This tag is lost when updating the
|
|
1234
|
+
// alarm here, but also when updating from the Sonos macOS app.
|
|
1210
1235
|
})
|
|
1211
1236
|
}
|
|
1212
1237
|
|
|
@@ -1411,7 +1436,7 @@ class ZpClient extends HttpClient {
|
|
|
1411
1436
|
let meta = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">'
|
|
1412
1437
|
meta += `<item id="${metaData.id}" parentID="${metaData.parentId}" restricted="${metaData.restricted}">`
|
|
1413
1438
|
meta += `<dc:title>${metaData.title}</dc:title>`
|
|
1414
|
-
meta += `<upnp:class>${metaData.class}
|
|
1439
|
+
meta += `<upnp:class>${metaData.class}</upnp:class>`
|
|
1415
1440
|
if (albumArtUri != null) {
|
|
1416
1441
|
for (const uri of albumArtUri) {
|
|
1417
1442
|
meta += `<upnp:albumArtURI>${he.escape(uri)}</upnp:albumArtURI>`
|
|
@@ -1712,10 +1737,10 @@ class ZpClient extends HttpClient {
|
|
|
1712
1737
|
}
|
|
1713
1738
|
const info = {
|
|
1714
1739
|
action,
|
|
1715
|
-
|
|
1740
|
+
jsonBody: JSON.stringify(options)
|
|
1716
1741
|
}
|
|
1717
|
-
const response = await this
|
|
1718
|
-
await this
|
|
1742
|
+
const response = await this.#request('POST', url, body, headers, info)
|
|
1743
|
+
await this.#updateLastSeen()
|
|
1719
1744
|
return response.body
|
|
1720
1745
|
}
|
|
1721
1746
|
|
|
@@ -1724,12 +1749,12 @@ class ZpClient extends HttpClient {
|
|
|
1724
1749
|
* @returns {?*} - The parsed response body as JavaScript object.
|
|
1725
1750
|
*/
|
|
1726
1751
|
async get (url = '/xml/device_description.xml') {
|
|
1727
|
-
const response = await this
|
|
1728
|
-
await this
|
|
1752
|
+
const response = await this.#request('GET', url)
|
|
1753
|
+
await this.#updateLastSeen()
|
|
1729
1754
|
return response.body
|
|
1730
1755
|
}
|
|
1731
1756
|
|
|
1732
|
-
async
|
|
1757
|
+
async #request (method, resource, body, headers, info) {
|
|
1733
1758
|
const response = await super.request(
|
|
1734
1759
|
method, resource, body, headers, '', info
|
|
1735
1760
|
)
|
package/lib/ZpTool.js
CHANGED
|
@@ -52,7 +52,8 @@ const usage = {
|
|
|
52
52
|
subLevel: `${b('subLevel')} [${b('-h')}] [${b('--')}] [${u('level')}]`,
|
|
53
53
|
led: `${b('led')} [${b('-h')}] [${b('on')}|${b('off')}]`,
|
|
54
54
|
buttonLock: `${b('buttonLock')} [${b('-h')}] [${b('on')}|${b('off')}]`,
|
|
55
|
-
listAlarms: `${b('listAlarms')} [${b('-
|
|
55
|
+
listAlarms: `${b('listAlarms')} [${b('-hns')}]`,
|
|
56
|
+
alarm: `${b('alarm')} [${b('-h')}] ${u('id')} [${b('on')}|${b('off')}]`
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
const description = {
|
|
@@ -93,7 +94,8 @@ const description = {
|
|
|
93
94
|
subLevel: 'Get/set Sub level.',
|
|
94
95
|
led: 'Get/set/clear LED state.',
|
|
95
96
|
buttonLock: 'Get/set/clear button lock state.',
|
|
96
|
-
listAlarms: 'Get alarms.'
|
|
97
|
+
listAlarms: 'Get alarms.',
|
|
98
|
+
alarm: 'Get/enable/disable alarm.'
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
const help = {
|
|
@@ -224,6 +226,9 @@ Commands:
|
|
|
224
226
|
${usage.listAlarms}
|
|
225
227
|
${description.listAlarms}
|
|
226
228
|
|
|
229
|
+
${usage.alarm}
|
|
230
|
+
${description.alarm}
|
|
231
|
+
|
|
227
232
|
For more help, issue: ${b('zp')} ${u('command')} ${b('-h')}`,
|
|
228
233
|
info: `${description.info}
|
|
229
234
|
|
|
@@ -661,7 +666,29 @@ Usage: ${b('zp')} ${usage.listAlarms}
|
|
|
661
666
|
|
|
662
667
|
Parameters:
|
|
663
668
|
${b('-h')}, ${b('--help')}
|
|
664
|
-
Print this help and exit
|
|
669
|
+
Print this help and exit.
|
|
670
|
+
|
|
671
|
+
${b('-n')}, ${b('--noWhiteSpace')}
|
|
672
|
+
Do not include spaces nor newlines in JSON output.
|
|
673
|
+
|
|
674
|
+
${b('-s')}, ${b('--sortKeys')}
|
|
675
|
+
Sort JSON keys alphabetically.`,
|
|
676
|
+
alarm: `${description.alarm}
|
|
677
|
+
|
|
678
|
+
Usage: ${b('zp')} ${usage.alarm}
|
|
679
|
+
|
|
680
|
+
Parameters:
|
|
681
|
+
${b('-h')}, ${b('--help')}
|
|
682
|
+
Print this help and exit.
|
|
683
|
+
|
|
684
|
+
${u('id')}
|
|
685
|
+
Alarm ID (from ${b('listAlarms')}).
|
|
686
|
+
|
|
687
|
+
${b('on')}
|
|
688
|
+
Enable alarm.
|
|
689
|
+
|
|
690
|
+
${b('off')}
|
|
691
|
+
Disable alarm.`
|
|
665
692
|
}
|
|
666
693
|
|
|
667
694
|
const unsupportedServices = [
|
|
@@ -679,105 +706,6 @@ class ZpTool extends CommandLineTool {
|
|
|
679
706
|
this.clients = []
|
|
680
707
|
}
|
|
681
708
|
|
|
682
|
-
createZpClient (options) {
|
|
683
|
-
const zpClient = new ZpClient(options)
|
|
684
|
-
zpClient
|
|
685
|
-
.on('error', (error) => {
|
|
686
|
-
if (error.request == null) {
|
|
687
|
-
this.warn(error)
|
|
688
|
-
return
|
|
689
|
-
}
|
|
690
|
-
if (error.request.id !== this.requestId) {
|
|
691
|
-
if (error.request.body == null) {
|
|
692
|
-
this.log(
|
|
693
|
-
'%s: request %d: %s %s', error.request.name, error.request.id,
|
|
694
|
-
error.request.method, error.request.resource
|
|
695
|
-
)
|
|
696
|
-
} else {
|
|
697
|
-
this.log(
|
|
698
|
-
'%s: request %d: %s %s', error.request.name, error.request.id,
|
|
699
|
-
error.request.method, error.request.resource, error.request.action
|
|
700
|
-
)
|
|
701
|
-
}
|
|
702
|
-
this.requestId = error.request.id
|
|
703
|
-
}
|
|
704
|
-
this.warn(
|
|
705
|
-
'%s: request %d: %s', error.request.name, error.request.id, error
|
|
706
|
-
)
|
|
707
|
-
})
|
|
708
|
-
// TODO: never called, since no UPnP listener is active
|
|
709
|
-
.on('rebooted', (oldBootSeq) => {
|
|
710
|
-
this.debug(
|
|
711
|
-
'%s: rebooted (%d -> %d)', zpClient.name,
|
|
712
|
-
oldBootSeq, zpClient.bootSeq
|
|
713
|
-
)
|
|
714
|
-
})
|
|
715
|
-
.on('addressChanged', (oldAddress) => {
|
|
716
|
-
this.debug(
|
|
717
|
-
'%s: IP address changed (%s -> %s)', zpClient.name,
|
|
718
|
-
oldAddress, zpClient.address
|
|
719
|
-
)
|
|
720
|
-
})
|
|
721
|
-
.on('request', (request) => {
|
|
722
|
-
this.debug(
|
|
723
|
-
'%s: request %s: %s %s%s', request.name,
|
|
724
|
-
request.id, request.method, request.resource,
|
|
725
|
-
request.action == null ? '' : ' ' + request.action
|
|
726
|
-
)
|
|
727
|
-
if (request.parsedBody != null) {
|
|
728
|
-
this.vdebug(
|
|
729
|
-
'%s: request %s: %s %s %j', request.name,
|
|
730
|
-
request.id, request.method, request.url, request.parsedBody
|
|
731
|
-
)
|
|
732
|
-
this.vvdebug(
|
|
733
|
-
'%s: request %s: %s %s (headers: %j) %j', request.name,
|
|
734
|
-
request.id, request.method, request.url,
|
|
735
|
-
request.headers, request.body
|
|
736
|
-
)
|
|
737
|
-
} else {
|
|
738
|
-
this.vdebug(
|
|
739
|
-
'%s: request %s: %s %s', request.name,
|
|
740
|
-
request.id, request.method, request.url
|
|
741
|
-
)
|
|
742
|
-
this.vvdebug(
|
|
743
|
-
'%s: request %s: %s %s (headers: %j)', request.name,
|
|
744
|
-
request.id, request.method, request.url, request.headers
|
|
745
|
-
)
|
|
746
|
-
}
|
|
747
|
-
})
|
|
748
|
-
.on('response', (response) => {
|
|
749
|
-
this.debug(
|
|
750
|
-
'%s: request %d: %d %s', response.request.name,
|
|
751
|
-
response.request.id, response.statusCode, response.statusMessage
|
|
752
|
-
)
|
|
753
|
-
if (response.rawBody != null) {
|
|
754
|
-
this.vvdebug(
|
|
755
|
-
'%s: request %d: response (headers: %j): %j', response.request.name,
|
|
756
|
-
response.request.id, response.headers, response.rawBody
|
|
757
|
-
)
|
|
758
|
-
this.vdebug(
|
|
759
|
-
'%s: request %d: response: %j', response.request.name,
|
|
760
|
-
response.request.id, response.body
|
|
761
|
-
)
|
|
762
|
-
}
|
|
763
|
-
})
|
|
764
|
-
.on('message', (message) => {
|
|
765
|
-
const notify = message.device === 'ZonePlayer'
|
|
766
|
-
? message.service
|
|
767
|
-
: message.device + '/' + message.service
|
|
768
|
-
this.vvdebug(
|
|
769
|
-
'%s: notify %s/Event: %j', this._clargs.options.host,
|
|
770
|
-
notify, message.rawBody
|
|
771
|
-
)
|
|
772
|
-
this.vdebug(
|
|
773
|
-
'%s: notify %s/Event: %j', this._clargs.options.host,
|
|
774
|
-
notify, message.body
|
|
775
|
-
)
|
|
776
|
-
this.debug('%s: notify %s/Event', this._clargs.options.host, notify)
|
|
777
|
-
})
|
|
778
|
-
return zpClient
|
|
779
|
-
}
|
|
780
|
-
|
|
781
709
|
async main () {
|
|
782
710
|
try {
|
|
783
711
|
this._clargs = this.parseArguments()
|
|
@@ -790,7 +718,8 @@ class ZpTool extends CommandLineTool {
|
|
|
790
718
|
.on('close', (url) => { this.debug('closed %s', url) })
|
|
791
719
|
.on('error', (error) => { this.warn(error) })
|
|
792
720
|
this._clargs.options.listener = this.zpListener
|
|
793
|
-
this.
|
|
721
|
+
this._clargs.options.logger = this
|
|
722
|
+
this.zpClient = new ZpClient(this._clargs.options)
|
|
794
723
|
await this.zpClient.init()
|
|
795
724
|
this.debug(
|
|
796
725
|
'%s: reached using local address %s', this._clargs.options.host,
|
|
@@ -801,7 +730,9 @@ class ZpTool extends CommandLineTool {
|
|
|
801
730
|
this.help = help[this._clargs.command]
|
|
802
731
|
await this[this._clargs.command](this._clargs.args)
|
|
803
732
|
} catch (error) {
|
|
804
|
-
|
|
733
|
+
if (error.request == null) {
|
|
734
|
+
this.error(error)
|
|
735
|
+
}
|
|
805
736
|
}
|
|
806
737
|
}
|
|
807
738
|
|
|
@@ -898,9 +829,10 @@ class ZpTool extends CommandLineTool {
|
|
|
898
829
|
}
|
|
899
830
|
|
|
900
831
|
async _getInfo (zonePlayer) {
|
|
901
|
-
const zpClient =
|
|
832
|
+
const zpClient = new ZpClient({
|
|
902
833
|
host: zonePlayer.address,
|
|
903
834
|
id: zonePlayer.id,
|
|
835
|
+
logger: this,
|
|
904
836
|
timeout: this._clargs.options.timeout
|
|
905
837
|
})
|
|
906
838
|
await zpClient.init()
|
|
@@ -1225,9 +1157,49 @@ class ZpTool extends CommandLineTool {
|
|
|
1225
1157
|
|
|
1226
1158
|
async listAlarms (...args) {
|
|
1227
1159
|
const parser = new CommandLineParser(this.pkgJson)
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1160
|
+
const clargs = {
|
|
1161
|
+
options: {}
|
|
1162
|
+
}
|
|
1163
|
+
parser
|
|
1164
|
+
.help('h', 'help', this.help)
|
|
1165
|
+
.flag('n', 'noWhiteSpace', () => { clargs.options.noWhiteSpace = true })
|
|
1166
|
+
.flag('s', 'sortKeys', () => { clargs.options.sortKeys = true })
|
|
1167
|
+
.parse(...args)
|
|
1168
|
+
const jsonFormatter = new JsonFormatter(clargs.options)
|
|
1169
|
+
const result = await this.zpClient.listAlarms()
|
|
1170
|
+
const json = jsonFormatter.stringify(result)
|
|
1171
|
+
this.print(json)
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
async alarm (...args) {
|
|
1175
|
+
const parser = new CommandLineParser(this.pkgJson)
|
|
1176
|
+
const clargs = {}
|
|
1177
|
+
parser
|
|
1178
|
+
.help('h', 'help', this.help)
|
|
1179
|
+
.parameter('id', (value) => {
|
|
1180
|
+
clargs.id = OptionParser.toInt('id', value, 0, Infinity, true)
|
|
1181
|
+
})
|
|
1182
|
+
.remaining((list) => {
|
|
1183
|
+
if (list.length > 1) {
|
|
1184
|
+
throw new UsageError('too many arguments')
|
|
1185
|
+
}
|
|
1186
|
+
if (list.length === 1) {
|
|
1187
|
+
clargs.enabled = OptionParser.toBool('enabled', list[0], true)
|
|
1188
|
+
}
|
|
1189
|
+
})
|
|
1190
|
+
.parse(...args)
|
|
1191
|
+
const response = await this.zpClient.listAlarms()
|
|
1192
|
+
const alarm = response?.currentAlarmList?.filter(
|
|
1193
|
+
(alarm) => { return alarm.id === clargs.id }
|
|
1194
|
+
)?.[0]
|
|
1195
|
+
if (alarm == null) {
|
|
1196
|
+
throw new UsageError(`${clargs.id}: alarm not found`)
|
|
1197
|
+
}
|
|
1198
|
+
if (clargs.enabled != null) {
|
|
1199
|
+
alarm.enabled = clargs.enabled ? 1 : 0
|
|
1200
|
+
await this.zpClient.updateAlarm(alarm)
|
|
1201
|
+
}
|
|
1202
|
+
this.print(alarm.enabled ? 'on' : 'off')
|
|
1231
1203
|
}
|
|
1232
1204
|
|
|
1233
1205
|
async simpleCommand (command, ...args) {
|
package/lib/ZpXmlParser.js
CHANGED
|
@@ -109,25 +109,25 @@ class ZpXmlParser {
|
|
|
109
109
|
const xml2jsParserOptions = {
|
|
110
110
|
explicitArray: false,
|
|
111
111
|
mergeAttrs: true,
|
|
112
|
-
attrNameProcessors: [this.
|
|
113
|
-
attrValueProcessors: [this.
|
|
114
|
-
tagNameProcessors: [this.
|
|
115
|
-
valueProcessors: [this.
|
|
112
|
+
attrNameProcessors: [this.#processKey.bind(this)],
|
|
113
|
+
attrValueProcessors: [this.#processValue.bind(this)],
|
|
114
|
+
tagNameProcessors: [this.#processKey.bind(this)],
|
|
115
|
+
valueProcessors: [this.#processValue.bind(this)]
|
|
116
116
|
}
|
|
117
117
|
this._parser = new Parser(xml2jsParserOptions)
|
|
118
118
|
// this._debug = debug('ZpXmlParser')
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
async parse (xml) {
|
|
122
|
-
|
|
122
|
+
this._debug?.('parse(%j)', xml)
|
|
123
123
|
const result = await this._parser.parseStringPromise(xml)
|
|
124
|
-
return this
|
|
124
|
+
return this.#process(result)
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// Convert key to javascript standard key.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
#processKey (key) {
|
|
129
|
+
const oldKey = key
|
|
130
|
+
this._debug?.('processKey(%j)', oldKey)
|
|
131
131
|
|
|
132
132
|
// Get rid of schema tags.
|
|
133
133
|
const a = key.split(':')
|
|
@@ -152,14 +152,14 @@ class ZpXmlParser {
|
|
|
152
152
|
key = replacementKeys[key]
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
this._debug?.('processKey(%j) => %j', oldKey, key)
|
|
156
156
|
return key
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// Convert value to proper javascript value.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
#processValue (value, key) {
|
|
161
|
+
const oldValue = value
|
|
162
|
+
this._debug?.('processValue(%j, %j)', oldValue, key)
|
|
163
163
|
|
|
164
164
|
// Convert integer string to integer
|
|
165
165
|
if (/^[0-9]+$/.test(value) && !stringKeys.includes(key)) {
|
|
@@ -170,12 +170,25 @@ class ZpXmlParser {
|
|
|
170
170
|
value = he.decode(value)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
this._debug?.('processValue(%j, %j) => %j', oldValue, key, value)
|
|
174
174
|
return value
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
// Post-process converted XML.
|
|
178
|
-
async
|
|
178
|
+
async #process (value) {
|
|
179
|
+
const oldValue = value
|
|
180
|
+
this._debug?.('process(%j)', value)
|
|
181
|
+
try {
|
|
182
|
+
value = await this.#tryProcess(value)
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this._debug?.('process(%j) => error: %j', oldValue, error.message)
|
|
185
|
+
}
|
|
186
|
+
this._debug?.('process(%j) => %j', oldValue, value)
|
|
187
|
+
return value
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Post-process converted XML.
|
|
191
|
+
async #tryProcess (value) {
|
|
179
192
|
// Recursively parse XML strings.
|
|
180
193
|
if (typeof value === 'string' && /^<.*>$/.test(value)) {
|
|
181
194
|
return this.parse(value)
|
|
@@ -185,7 +198,7 @@ class ZpXmlParser {
|
|
|
185
198
|
if (Array.isArray(value)) {
|
|
186
199
|
const list = []
|
|
187
200
|
for (const elt of value) {
|
|
188
|
-
list.push(await this
|
|
201
|
+
list.push(await this.#process(elt))
|
|
189
202
|
}
|
|
190
203
|
return list
|
|
191
204
|
}
|
|
@@ -202,7 +215,7 @@ class ZpXmlParser {
|
|
|
202
215
|
const keys = Object.keys(value)
|
|
203
216
|
if (keys.length === 1) {
|
|
204
217
|
if (rootKeys.includes(keys[0])) {
|
|
205
|
-
return this
|
|
218
|
+
return this.#process(value[keys[0]])
|
|
206
219
|
}
|
|
207
220
|
}
|
|
208
221
|
// Recursively post-process key/value pairs.
|
|
@@ -212,7 +225,7 @@ class ZpXmlParser {
|
|
|
212
225
|
const a = key.match(/^(.+)List$/)
|
|
213
226
|
if (a != null || arrayKeys[key] != null) {
|
|
214
227
|
const childKey = arrayKeys[key] == null ? a[1] : arrayKeys[key]
|
|
215
|
-
let newValue = await this
|
|
228
|
+
let newValue = await this.#process(value[key])
|
|
216
229
|
const listKeys = Object.keys(newValue)
|
|
217
230
|
if (listKeys.length === 1 && listKeys[0] === childKey) {
|
|
218
231
|
newValue = newValue[childKey]
|
|
@@ -230,7 +243,7 @@ class ZpXmlParser {
|
|
|
230
243
|
}
|
|
231
244
|
// Handle e:property.
|
|
232
245
|
if (key === 'e:property') {
|
|
233
|
-
const newValue = await this
|
|
246
|
+
const newValue = await this.#process(value[key])
|
|
234
247
|
if (Array.isArray(newValue)) {
|
|
235
248
|
for (const property of newValue) {
|
|
236
249
|
for (const key in property) {
|
|
@@ -248,7 +261,7 @@ class ZpXmlParser {
|
|
|
248
261
|
if (key === 's:Envelope') {
|
|
249
262
|
if (value[key] != null && value[key]['s:Body'] != null) {
|
|
250
263
|
const keys = Object.keys(value[key]['s:Body'])
|
|
251
|
-
const newValue = await this
|
|
264
|
+
const newValue = await this.#process(
|
|
252
265
|
keys.length === 1
|
|
253
266
|
? value[key]['s:Body'][keys[0]]
|
|
254
267
|
: value[key]['s:Body']
|
|
@@ -273,13 +286,13 @@ class ZpXmlParser {
|
|
|
273
286
|
// Handle keys (like volume) with multiple channels.
|
|
274
287
|
if (channelKeys.includes(key) && typeof value[key] === 'object') {
|
|
275
288
|
obj[key] = {}
|
|
276
|
-
const newValue = [].concat(await this
|
|
289
|
+
const newValue = [].concat(await this.#process(value[key]))
|
|
277
290
|
for (const elt of newValue) {
|
|
278
291
|
obj[key][this._processKey(elt.channel)] = elt.val
|
|
279
292
|
}
|
|
280
293
|
continue
|
|
281
294
|
}
|
|
282
|
-
obj[key] = await this
|
|
295
|
+
obj[key] = await this.#process(value[key])
|
|
283
296
|
}
|
|
284
297
|
return obj
|
|
285
298
|
}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"ebaauw"
|
|
7
7
|
],
|
|
8
8
|
"license": "Apache-2.0",
|
|
9
|
-
"version": "2.0.0
|
|
9
|
+
"version": "2.0.0",
|
|
10
10
|
"type": "module",
|
|
11
11
|
"exports": {
|
|
12
12
|
"./*": "./lib/*.js"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"node": "^24||^22||^20"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"hb-lib-tools": "~3.0.0
|
|
32
|
+
"hb-lib-tools": "~3.0.0",
|
|
33
33
|
"he": "^1.2.0",
|
|
34
34
|
"xml2js": "~0.6.2"
|
|
35
35
|
},
|