signalk-shelly2 1.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/dist/device.js ADDED
@@ -0,0 +1,804 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2025 Scott Bender <scott@scottbender.net>
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.Device = void 0;
21
+ const ws_1 = __importDefault(require("ws"));
22
+ const camelcase_1 = __importDefault(require("camelcase"));
23
+ const MAX_INPUTS = 10;
24
+ class Device {
25
+ id = null;
26
+ connected = false;
27
+ numSwitches = 0;
28
+ address;
29
+ name = null;
30
+ model = null;
31
+ gen = null;
32
+ ws = null;
33
+ next_id = 1;
34
+ pendingRequests = {};
35
+ deviceSettings;
36
+ sentMeta = false;
37
+ app;
38
+ plugin;
39
+ constructor(app, plugin, deviceSettings, address) {
40
+ this.address = address;
41
+ this.deviceSettings = deviceSettings;
42
+ this.app = app;
43
+ this.plugin = plugin;
44
+ }
45
+ debug(...args) {
46
+ this.app.debug(...args);
47
+ }
48
+ async connect() {
49
+ return new Promise((resolve, reject) => {
50
+ try {
51
+ this.ws = new ws_1.default(`ws://${this.address}/rpc`);
52
+ }
53
+ catch (error) {
54
+ reject(`Failed to connect to device ${this.id}: ${error}`);
55
+ return;
56
+ }
57
+ this.debug(`Connecting to device at ${this.address}`);
58
+ this.ws.on('open', () => {
59
+ this.debug(`Connected to device at ${this.address}`);
60
+ this.send("Shelly.GetDeviceInfo")
61
+ .catch((error) => {
62
+ this.app.error(`Error getting initial device information from ${this.address}: ${error}`);
63
+ reject(error);
64
+ })
65
+ .then((deviceInfo) => {
66
+ this.id = deviceInfo.id;
67
+ this.name = deviceInfo.name || null;
68
+ this.model = deviceInfo.model;
69
+ this.gen = deviceInfo.gen;
70
+ this.debug(`Initial device information retrieved successfully from ${this.address}: ${this.id} (${this.model}, Gen ${this.gen})`);
71
+ this.debug(JSON.stringify(deviceInfo, null, 2));
72
+ this.send("Shelly.GetStatus")
73
+ .then((result) => {
74
+ this.debug(`Initial device status retrieved successfully from ${this.id}`);
75
+ this.debug(JSON.stringify(result, null, 2));
76
+ this.getCapabilities(result);
77
+ this.registerForPuts(result);
78
+ this.sendDeltas(result);
79
+ this.connected = true;
80
+ resolve(this);
81
+ })
82
+ .catch((error) => {
83
+ this.debug(`Error getting initial device status: ${error}`);
84
+ reject(error);
85
+ });
86
+ });
87
+ });
88
+ this.ws.on('message', (message) => {
89
+ let parsedMessage = JSON.parse(message.toString());
90
+ //this.debug(`Received message from device ${this.id}: ${JSON.stringify(parsedMessage)}`)
91
+ if (parsedMessage.method === 'NotifyStatus') {
92
+ this.sendDeltas(parsedMessage.params);
93
+ }
94
+ else {
95
+ const pendingRequest = this.pendingRequests[parsedMessage.id];
96
+ if (pendingRequest) {
97
+ clearTimeout(pendingRequest.timeout);
98
+ if (parsedMessage.error) {
99
+ pendingRequest.reject(parsedMessage.error);
100
+ }
101
+ else {
102
+ pendingRequest.resolve(parsedMessage.result);
103
+ }
104
+ delete this.pendingRequests[parsedMessage.id];
105
+ }
106
+ }
107
+ });
108
+ this.ws.on('error', (error) => {
109
+ //console.error(`Error occurred while connecting to device ${this.id}: ${error}`)
110
+ reject(error);
111
+ });
112
+ this.ws.on('close', () => {
113
+ //console.log(`Connection to device ${this.id} closed`)
114
+ });
115
+ });
116
+ }
117
+ disconnect() {
118
+ if (this.ws) {
119
+ this.connected = false;
120
+ this.ws.close();
121
+ this.ws = null;
122
+ }
123
+ }
124
+ async send(method, params = {}) {
125
+ if (!this.ws) {
126
+ throw new Error(`WebSocket is not connected`);
127
+ }
128
+ const id = this.next_id++;
129
+ const message = JSON.stringify({
130
+ jsonrpc: "2.0",
131
+ src: "signalk-shelly2",
132
+ id,
133
+ method,
134
+ params
135
+ });
136
+ this.ws.send(message);
137
+ return new Promise((resolve, reject) => {
138
+ const timeout = setTimeout(() => {
139
+ delete this.pendingRequests[id];
140
+ reject(new Error(`Request ${id} for ${method} timed out`));
141
+ }, 5000);
142
+ this.pendingRequests[id] = {
143
+ resolve,
144
+ reject,
145
+ timeout
146
+ };
147
+ });
148
+ }
149
+ async setSwitch(value, switchIdx) {
150
+ const expected = value === 1 || value === 'on' || value === 'true' || value === true;
151
+ await this.send("Switch.Set", { id: switchIdx, on: expected });
152
+ const status = await this.getSwitch(switchIdx);
153
+ if (status.output !== expected) {
154
+ throw new Error(`Failed to set switch ${switchIdx} to ${expected}`);
155
+ }
156
+ this.sendDeltas({ [`switch:${switchIdx}`]: status });
157
+ }
158
+ async getSwitch(switchIdx) {
159
+ const res = await this.send("Switch.GetStatus", { id: switchIdx });
160
+ return res;
161
+ }
162
+ getCapabilities(status) {
163
+ this.numSwitches = 0;
164
+ for (let i = 0; i < 10; i++) {
165
+ if (status[`switch:${i}`]) {
166
+ this.numSwitches++;
167
+ }
168
+ }
169
+ }
170
+ getDevicePath(key) {
171
+ let name = this.deviceSettings?.devicePath;
172
+ if (name === undefined) {
173
+ name = this.name ? (0, camelcase_1.default)(this.name) : this.id;
174
+ }
175
+ return `electrical.switches.${name}${key ? '.' + key : ''}`;
176
+ }
177
+ getSwitchProps(relay) {
178
+ return this.deviceSettings ? this.deviceSettings[`switch${relay}`] : undefined;
179
+ }
180
+ getSwitchPath(relay, key = 'state') {
181
+ const switchProps = this.getSwitchProps(relay);
182
+ let path = this.getDevicePath();
183
+ if (this.numSwitches > 1) {
184
+ path = path + `.${switchProps?.switchPath || relay}`;
185
+ }
186
+ return path + (key ? '.' + key : '');
187
+ }
188
+ sendDeltas(status) {
189
+ let values = [];
190
+ if (this.deviceSettings?.enabled === false) {
191
+ return;
192
+ }
193
+ if (!this.sentMeta) {
194
+ this.sendMeta(status);
195
+ this.sentMeta = true;
196
+ }
197
+ if (this.name) {
198
+ values.push({
199
+ path: this.getDevicePath('name'),
200
+ value: this.name
201
+ });
202
+ }
203
+ values.push({
204
+ path: this.getDevicePath('model'),
205
+ value: this.model
206
+ });
207
+ if (this.numSwitches > 0) {
208
+ for (let i = 0; i < this.numSwitches; i++) {
209
+ const switchProps = this.getSwitchProps(i);
210
+ if (switchProps?.enabled === false) {
211
+ continue;
212
+ }
213
+ const switchStatus = status[`switch:${i}`];
214
+ if (switchStatus !== undefined) {
215
+ values.push({
216
+ path: this.getSwitchPath(i),
217
+ value: switchStatus?.output ? true : false
218
+ });
219
+ let readPaths = switchReadPaths();
220
+ readPaths?.forEach((p) => {
221
+ const path = p.path || p.key;
222
+ const converter = p.converter;
223
+ const val = switchStatus[p.key];
224
+ if (val !== undefined) {
225
+ values.push({
226
+ path: this.getSwitchPath(i, path),
227
+ value: converter ? converter(val) : val
228
+ });
229
+ }
230
+ });
231
+ }
232
+ /*
233
+ if (info.isDimmable) {
234
+ const dimmerKey = `brightness${i}`
235
+ values.push({
236
+ path: getSwitchPath(device, i, 'dimmingLevel'),
237
+ value: Number((device[dimmerKey] / 100).toFixed(2))
238
+ })
239
+ }
240
+ const powerKey = `power${i}`
241
+ if (typeof device[powerKey] !== 'undefined') {
242
+ values.push({
243
+ path: getSwitchPath(device, i, 'power'),
244
+ value: device[powerKey]
245
+ })
246
+ }
247
+ */
248
+ }
249
+ }
250
+ readKeys.forEach((p) => {
251
+ for (let i = 0; i < MAX_INPUTS; i++) {
252
+ const key = p.key;
253
+ const converter = p.converter;
254
+ const val = status[`${key}:${i}`];
255
+ if (val !== undefined) {
256
+ values.push({
257
+ path: this.getSwitchPath(i, `${key}${i}`),
258
+ value: converter ? converter(val) : val
259
+ });
260
+ }
261
+ }
262
+ });
263
+ /*
264
+ if ( this.numInputs > 0) {
265
+ for (let i = 0; i < this.numInputs; i++) {
266
+ const inputStatus = status[`input:${i}`]
267
+ if (inputStatus !== undefined) {
268
+ values.push({
269
+ path: this.getSwitchPath(i, `input${i}`),
270
+ value: inputStatus?.state ? true : false
271
+ })
272
+ }
273
+ }
274
+ }
275
+ */
276
+ /*
277
+ info.putPaths?.forEach((prop: any) => {
278
+ const path = `${getDevicePath(device)}.${prop.name || prop.deviceProp}`
279
+ let value
280
+ if (!prop.deviceProp) {
281
+ value = prop.getter(device)
282
+ } else {
283
+ value = prop.convertFrom
284
+ ? prop.convertFrom(device[prop.deviceProp], device)
285
+ : device[prop.deviceProp]
286
+ }
287
+ values.push({
288
+ path,
289
+ value
290
+ })
291
+ })
292
+
293
+ info.readPaths?.forEach((info: any) => {
294
+ let path, key, converter
295
+ if (typeof info === 'string') {
296
+ path = key = info
297
+ } else {
298
+ key = info.key
299
+ path = info.path ? info.path : info.key
300
+ converter = info.converter
301
+ }
302
+ let val: any
303
+ if ( key.indexOf('.') !== -1 ) {
304
+ let split = key.split('.')
305
+ val = device
306
+ split.forEach((k: any) => {
307
+ if ( typeof val === 'undefined' ) {
308
+ val = undefined
309
+ } else {
310
+ val = val[k]
311
+ }
312
+ })
313
+ } else {
314
+ val = device[key]
315
+ }
316
+ if (val != null) {
317
+ values.push({
318
+ path: `${getDevicePath(device)}.${path}`,
319
+ value: converter ? converter(val) : val
320
+ })
321
+ if ( info.notification && (typeof deviceProps?.sendNotifications === 'undefined' || deviceProps?.sendNotifications) ) {
322
+ let state, message
323
+ if ( info.notification.handler(val) ) {
324
+ state = 'alarm'
325
+ message = info.notification.messageOn
326
+ } else {
327
+ state = 'normal'
328
+ message = info.notification.messageOff
329
+ }
330
+ values.push({
331
+ path: `notifications.${getDevicePath(device)}.${path}`,
332
+ value: {
333
+ state,
334
+ message: `${deviceProps?.devicePath || deviceKey(device)} ${message}`,
335
+ method: [ 'sound', 'visual']
336
+ }
337
+ })
338
+ }
339
+ }
340
+ })
341
+ */
342
+ if (values.length > 0) {
343
+ this.debug('sending deltas %j', values);
344
+ this.app.handleMessage(this.plugin.id, {
345
+ updates: [
346
+ {
347
+ values
348
+ }
349
+ ]
350
+ });
351
+ }
352
+ }
353
+ sendMeta(status) {
354
+ let meta = [];
355
+ const devicePath = this.getDevicePath();
356
+ if (this.deviceSettings?.enabled === false) {
357
+ return;
358
+ }
359
+ if (this.deviceSettings?.displayName || this.name) {
360
+ meta.push({
361
+ path: devicePath,
362
+ value: {
363
+ displayName: this.deviceSettings?.displayName || this.name
364
+ }
365
+ });
366
+ }
367
+ if (this.numSwitches > 1) {
368
+ for (let i = 0; i < this.numSwitches; i++) {
369
+ const switchProps = this.getSwitchProps(i);
370
+ if (switchProps?.enabled === false) {
371
+ continue;
372
+ }
373
+ meta.push({
374
+ path: this.getSwitchPath(i),
375
+ value: {
376
+ units: 'bool',
377
+ displayName: switchProps?.displayName,
378
+ //timeout: this.ttl ? (this.ttl / 1000) : undefined
379
+ }
380
+ });
381
+ let readPaths = switchReadPaths();
382
+ readPaths?.forEach((p) => {
383
+ if (p.meta && status[`switch:${i}`][p.key] !== undefined) {
384
+ meta.push({
385
+ path: this.getSwitchPath(i, p.path || p.key),
386
+ value: p.meta
387
+ });
388
+ }
389
+ });
390
+ /*
391
+ if ( info.bankReadPaths ) {
392
+ let readPaths = info.bankReadPaths(`${info.switchKey}${i}`)
393
+ readPaths?.forEach((p: any) => {
394
+ if ( p.meta ) {
395
+ meta.push({
396
+ path: getSwitchPath(device, i, p.path),
397
+ value: p.meta
398
+ })
399
+ }
400
+ })
401
+ }
402
+ if (info.isDimmable) {
403
+ meta.push({
404
+ path: getSwitchPath(device, i, 'dimmingLevel'),
405
+ value: {
406
+ units: 'ratio',
407
+ displayName: switchProps?.displayName,
408
+ type: 'dimmer',
409
+ canDimWhenOff: info.canDimWhenOff
410
+ }
411
+ })
412
+ }
413
+ */
414
+ if (switchProps?.displayName) {
415
+ meta.push({
416
+ path: this.getSwitchPath(i, null),
417
+ value: {
418
+ displayName: switchProps?.displayName
419
+ }
420
+ });
421
+ }
422
+ const powerKey = `power${i}`;
423
+ if (status[powerKey] !== 'undefined') {
424
+ meta.push({
425
+ path: this.getSwitchPath(i, 'power'),
426
+ value: {
427
+ units: 'W'
428
+ }
429
+ });
430
+ }
431
+ }
432
+ }
433
+ else {
434
+ meta.push({
435
+ path: this.getSwitchPath(0),
436
+ value: {
437
+ units: 'bool',
438
+ displayName: this.deviceSettings?.displayName || this.name,
439
+ }
440
+ });
441
+ let readPaths = switchReadPaths();
442
+ readPaths?.forEach((p) => {
443
+ if (p.meta && status[`switch:${0}`][p.key] !== undefined) {
444
+ meta.push({
445
+ path: this.getSwitchPath(0, p.path || p.key),
446
+ value: p.meta,
447
+ displayName: this.deviceSettings?.displayName || this.name
448
+ });
449
+ }
450
+ });
451
+ }
452
+ /*
453
+ if (this.numInputs > 0) {
454
+ for (let i = 0; i < this.numInputs; i++) {
455
+ const inputStatus = status[`input:${i}`]
456
+ if (inputStatus !== undefined) {
457
+ meta.push({
458
+ path: this.getSwitchPath(i, `input${i}`),
459
+ value: {units: 'bool'}
460
+ })
461
+ }
462
+ }
463
+ }
464
+ */
465
+ /*
466
+ info.putPaths?.forEach((prop: any) => {
467
+ if ( device.ttl ) {
468
+ meta.push({
469
+ path: `${devicePath}.${prop.name || prop.deviceProp}`,
470
+ value: {
471
+ timeout: device.ttl / 1000
472
+ }
473
+ })
474
+ }
475
+ if (deviceProps?.displayName || prop.meta) {
476
+ meta.push({
477
+ path: `${devicePath}.${prop.name || prop.deviceProp}`,
478
+ value: {
479
+ ...prop.meta,
480
+ displayName: deviceProps?.displayName
481
+ }
482
+ })
483
+ if (deviceProps?.displayName) {
484
+ meta.push({
485
+ path: devicePath,
486
+ value: {
487
+ displayName: deviceProps?.displayName
488
+ }
489
+ })
490
+ }
491
+ if (deviceProps?.presets && deviceProps.presets.length > 0) {
492
+ meta.push({
493
+ path: `${devicePath}.preset`,
494
+ value: {
495
+ displayName: deviceProps?.displayName,
496
+ possibleValues: [
497
+ ...deviceProps.presets.map((preset: any) => {
498
+ return {
499
+ title: preset.name,
500
+ value: preset.name
501
+ }
502
+ })
503
+ ],
504
+ enum: [...deviceProps.presets.map((preset: any) => preset.name)]
505
+ }
506
+ })
507
+ }
508
+ }
509
+ })
510
+
511
+ info.readPaths?.forEach((prop: any) => {
512
+ let key, path
513
+
514
+ if (typeof prop === 'string') {
515
+ path = key = prop
516
+ } else {
517
+ key = prop.key
518
+ path = prop.path ? prop.path : prop.key
519
+ }
520
+
521
+ let split = key.split('.')
522
+ key = split[split.length-1]
523
+
524
+ if ( device.ttl ) {
525
+ meta.push({
526
+ path: `${devicePath}.${path}`,
527
+ value: {
528
+ timeout: device.ttl / 1000
529
+ }
530
+ })
531
+ }
532
+ if ( typeof prop !== 'string' && prop.meta) {
533
+ meta.push({
534
+ path: `${devicePath}.${path}`,
535
+ value: prop.meta
536
+ })
537
+ }
538
+ if (key.startsWith('power')) {
539
+ meta.push({
540
+ path: `${devicePath}.${path}`,
541
+ value: {
542
+ units: 'W'
543
+ }
544
+ })
545
+ }
546
+ if (key.startsWith('externalTemperature') || key.startsWith('temperature') || key === 'deviceTemperature') {
547
+ meta.push({
548
+ path: `${devicePath}.${path}`,
549
+ value: {
550
+ units: 'K'
551
+ }
552
+ })
553
+ }
554
+ })
555
+ */
556
+ if (meta.length) {
557
+ this.debug('sending meta: %j', meta);
558
+ this.app.handleMessage(this.plugin.id, {
559
+ updates: [
560
+ {
561
+ meta
562
+ }
563
+ ]
564
+ });
565
+ }
566
+ }
567
+ registerForPuts(status) {
568
+ if (this.numSwitches > 0) {
569
+ for (let i = 0; i < this.numSwitches; i++) {
570
+ const switchProps = this.getSwitchProps(i);
571
+ if (switchProps?.enabled === false) {
572
+ continue;
573
+ }
574
+ const path = this.getSwitchPath(i);
575
+ this.app.registerPutHandler('vessels.self', path, (context, path, value, cb) => {
576
+ return this.valueHandler(context, path, value, (device, value) => {
577
+ return device.setSwitch(value, i);
578
+ }, cb);
579
+ });
580
+ /*
581
+ if ( info.bankReadPaths ) {
582
+ let readPaths = info.bankReadPaths(`${info.switchKey}${i}`)
583
+ readPaths?.forEach((prop: any) => {
584
+ let key, path
585
+
586
+ if (typeof prop === 'string') {
587
+ path = key = info
588
+ } else {
589
+ key = prop.key
590
+ path = prop.path ? prop.path : prop.key
591
+ }
592
+
593
+ let split = key.split('.')
594
+ let attrName = split[split.length-1]
595
+
596
+ if ( split.length > 1 ) {
597
+ if ( device[split[0]] ) {
598
+ device[split[0]].on(`change:${attrName}`, (newValue: any) => {
599
+ if ( !stopped ) {
600
+ debug(
601
+ `${device.id} ${key} changed to ${JSON.stringify(newValue)}`
602
+ )
603
+ sendDeltas(device)
604
+ }
605
+ })
606
+ }
607
+ } else {
608
+ device.on(`change:${attrName}`, (newValue: any) => {
609
+ if ( !stopped ) {
610
+ debug(
611
+ `${device.id} ${key} changed to ${newValue}`
612
+ )
613
+ sendDeltas(device)
614
+ }
615
+ })
616
+ }
617
+ })
618
+ }
619
+
620
+ if (info.isDimmable) {
621
+ const dimmerPath = getSwitchPath(device, i, 'dimmingLevel')
622
+
623
+ app.registerPutHandler(
624
+ 'vessels.self',
625
+ dimmerPath,
626
+ (context: string, path: string, value: any, cb: any) => {
627
+ return valueHandler(
628
+ context,
629
+ path,
630
+ value,
631
+ device,
632
+ (device: any, value: any) => {
633
+ return info.dimmerSetter(device, value, i)
634
+ },
635
+ cb
636
+ )
637
+ }
638
+ )
639
+ }
640
+ */
641
+ }
642
+ }
643
+ /*
644
+ info.putPaths?.forEach((prop: any) => {
645
+ const path = `${getDevicePath(device)}.${prop.name || prop.deviceProp}`
646
+ app.registerPutHandler(
647
+ 'vessels.self',
648
+ path,
649
+ (context: string, path: string, value: any, cb: any) => {
650
+ return valueHandler(context, path, value, device, prop.setter, cb)
651
+ }
652
+ )
653
+ if ( info.nextGen ) {
654
+ device[prop.deviceProp].on('change:output', (newValue:any) => {
655
+ if ( !stopped ) {
656
+ debug(
657
+ `${device.id} ${prop.deviceProp} changed to ${newValue}`
658
+ )
659
+ sendDeltas(device)
660
+ }
661
+ })
662
+ }
663
+ })
664
+
665
+ info.readPaths?.forEach((prop: any) => {
666
+ if ( info.nextGen ) {
667
+ let key, path
668
+
669
+ if (typeof prop === 'string') {
670
+ path = key = info
671
+ } else {
672
+ key = prop.key
673
+ path = prop.path ? prop.path : prop.key
674
+ }
675
+
676
+ let split = key.split('.')
677
+ let attrName = split[split.length-1]
678
+
679
+ if ( split.length > 1 ) {
680
+ if ( device[split[0]] ) {
681
+ device[split[0]].on(`change:${attrName}`, (newValue: any) => {
682
+ if ( !stopped ) {
683
+ debug(
684
+ `${device.id} ${key} changed to ${newValue}`
685
+ )
686
+ sendDeltas(device)
687
+ }
688
+ })
689
+ }
690
+ } else {
691
+ device.on(`change:${attrName}`, (newValue: any) => {
692
+ if ( !stopped ) {
693
+ debug(
694
+ `${device.id} ${key} changed to ${newValue}`
695
+ )
696
+ sendDeltas(device)
697
+ }
698
+ })
699
+ }
700
+ }
701
+
702
+ })*/
703
+ return true;
704
+ }
705
+ valueHandler(context, path, value, func, cb, validator) {
706
+ func(this, value)
707
+ .then((status) => {
708
+ let code = validator === undefined || validator(status) ? 200 : 400;
709
+ cb({
710
+ state: 'COMPLETED',
711
+ statusCode: code
712
+ });
713
+ })
714
+ .catch((err) => {
715
+ this.app.error(err.message);
716
+ this.app.setPluginError(err.message);
717
+ cb({ state: 'COMPLETED', statusCode: 400, message: err.message });
718
+ });
719
+ return { state: 'PENDING' };
720
+ }
721
+ }
722
+ exports.Device = Device;
723
+ const switchReadPaths = () => {
724
+ return [
725
+ {
726
+ key: `voltage`,
727
+ meta: {
728
+ units: 'V'
729
+ }
730
+ },
731
+ {
732
+ key: `temperature`,
733
+ converter: temperatureConverter,
734
+ meta: {
735
+ units: 'K'
736
+ }
737
+ },
738
+ {
739
+ key: `source`,
740
+ /*meta: {
741
+ units: 'string'
742
+ }*/
743
+ },
744
+ {
745
+ key: `apower`,
746
+ meta: {
747
+ units: 'W'
748
+ }
749
+ },
750
+ {
751
+ key: `current`,
752
+ meta: {
753
+ units: 'A'
754
+ }
755
+ },
756
+ {
757
+ key: `pf`,
758
+ path: 'powerFactor',
759
+ converter: (val) => {
760
+ return val * 1000;
761
+ },
762
+ meta: {
763
+ units: 'W'
764
+ }
765
+ }
766
+ ];
767
+ };
768
+ const temperatureConverter = (value) => {
769
+ return value?.tC + 273.15;
770
+ };
771
+ const humidityConverter = (value) => {
772
+ return value / 100;
773
+ };
774
+ const readKeys = [
775
+ {
776
+ key: 'input',
777
+ converter: (v) => v.state,
778
+ meta: {
779
+ units: 'bool'
780
+ }
781
+ },
782
+ {
783
+ key: 'temperature',
784
+ converter: (v) => temperatureConverter(v.tC),
785
+ meta: {
786
+ units: 'K'
787
+ }
788
+ },
789
+ {
790
+ key: 'humidity',
791
+ converter: (v) => humidityConverter(v.rh),
792
+ meta: {
793
+ units: 'K'
794
+ }
795
+ },
796
+ {
797
+ key: 'voltmeter',
798
+ converter: (v) => v.voltage,
799
+ meta: {
800
+ units: 'K'
801
+ }
802
+ }
803
+ ];
804
+ //# sourceMappingURL=device.js.map