homematic-manager 3.0.0-beta.25 → 3.0.0-beta.27
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/node_modules/@homematic-manager/backend/dist/api/backend.js +97 -26
- package/node_modules/@homematic-manager/backend/dist/rega/scripts.d.ts +19 -7
- package/node_modules/@homematic-manager/backend/dist/rega/scripts.js +30 -8
- package/node_modules/@homematic-manager/backend/package.json +1 -1
- package/node_modules/@homematic-manager/core/dist/api/types.d.ts +30 -1
- package/node_modules/@homematic-manager/core/package.json +1 -1
- package/package.json +3 -3
- package/ui/assets/{index-DEU9sglJ.css → index-BsgPpKOE.css} +1 -1
- package/ui/assets/index-D-Soh6Ni.js +20 -0
- package/ui/index.html +2 -2
- package/ui/assets/index-_ilAhuHR.js +0 -20
|
@@ -99,8 +99,8 @@ export class Backend {
|
|
|
99
99
|
* swallows packets takes 30 s); a store that has not started by then never will.
|
|
100
100
|
*/
|
|
101
101
|
#metaStarting;
|
|
102
|
-
/** Task 36: ReGa's
|
|
103
|
-
#
|
|
102
|
+
/** Task 36, B-74: ReGa's pending alarms per `<interface>|<address>|<datapoint>` - the CCU's own list. */
|
|
103
|
+
#regaAlarms = new Map();
|
|
104
104
|
/** Task 64: `getParamsetId` answers by `<interface>|<address>`; emptied on every connect. */
|
|
105
105
|
#paramsetIds = new Map();
|
|
106
106
|
#serviceMessageTimer;
|
|
@@ -392,6 +392,14 @@ export class Backend {
|
|
|
392
392
|
return this.#setInstallMode(p[0], p[1], p[2]);
|
|
393
393
|
case 'devices.installMode.get':
|
|
394
394
|
return Number(await this.#read(p[0], 'getInstallMode', []));
|
|
395
|
+
case 'devices.installMode.keyMismatch': {
|
|
396
|
+
// rfd answers the serial as a string; anything else means "none"
|
|
397
|
+
const serial = await this.#read(p[0], 'getKeyMismatchDevice', [true]);
|
|
398
|
+
return typeof serial === 'string' ? serial : '';
|
|
399
|
+
}
|
|
400
|
+
case 'devices.installMode.tempKey':
|
|
401
|
+
await this.#write(p[0], 'setTempKey', [p[1]]);
|
|
402
|
+
return null;
|
|
395
403
|
case 'devices.replaceable':
|
|
396
404
|
return asDescriptions(await this.#read(p[0], 'listReplaceableDevices', [p[1]]));
|
|
397
405
|
case 'names.get':
|
|
@@ -665,7 +673,7 @@ export class Backend {
|
|
|
665
673
|
});
|
|
666
674
|
// B-54: the names are waited for only so long; the store starts after them either way,
|
|
667
675
|
// so its names still land over ReGa's as before (task 27, D-40)
|
|
668
|
-
this.#
|
|
676
|
+
this.#regaAlarms.clear();
|
|
669
677
|
const names = this.#rega.refreshNames().then((changed) => {
|
|
670
678
|
if (changed && this.#manager === manager) {
|
|
671
679
|
this.#caches.saveNames();
|
|
@@ -1085,7 +1093,8 @@ export class Backend {
|
|
|
1085
1093
|
*/
|
|
1086
1094
|
#stickyUnreachMessages() {
|
|
1087
1095
|
const states = this.#manager?.states() ?? [];
|
|
1088
|
-
|
|
1096
|
+
// B-74: with the messages only ReGa lists - the WebUI's "war gestört" the interface forgot
|
|
1097
|
+
return this.#serviceMessages().filter((message) => {
|
|
1089
1098
|
if (message.datapoint !== 'STICKY_UNREACH' || message.value === false) {
|
|
1090
1099
|
return false;
|
|
1091
1100
|
}
|
|
@@ -1102,9 +1111,7 @@ export class Backend {
|
|
|
1102
1111
|
*/
|
|
1103
1112
|
async #acknowledgeExisting(messages) {
|
|
1104
1113
|
for (const message of messages) {
|
|
1105
|
-
const listed = this.#
|
|
1106
|
-
.listServiceMessages(message.interfaceName)
|
|
1107
|
-
.some((entry) => entry.address === message.address && entry.datapoint === message.datapoint);
|
|
1114
|
+
const listed = this.#serviceMessages(message.interfaceName).some((entry) => entry.address === message.address && entry.datapoint === message.datapoint);
|
|
1108
1115
|
if (!listed || this.#stopped) {
|
|
1109
1116
|
continue;
|
|
1110
1117
|
}
|
|
@@ -1432,20 +1439,56 @@ export class Backend {
|
|
|
1432
1439
|
return matrix;
|
|
1433
1440
|
}
|
|
1434
1441
|
#serviceMessages(interfaceName) {
|
|
1435
|
-
return this.#
|
|
1442
|
+
return this.#withRegaAlarms(this.#caches.listServiceMessages(interfaceName), interfaceName);
|
|
1436
1443
|
}
|
|
1437
1444
|
/**
|
|
1438
1445
|
* Task 36: where ReGa knows a message, its first report (the WebUI's *Erste Meldung*) replaces
|
|
1439
1446
|
* the time this application first saw it, and says so.
|
|
1447
|
+
*
|
|
1448
|
+
* B-74 (#150): and a pending alarm whose datapoint the interface process does not list becomes
|
|
1449
|
+
* a row of its own, as the WebUI shows it - value and first report from ReGa, marked as listed
|
|
1450
|
+
* by the CCU only. rfd forgets a `STICKY_UNREACH` at a restart while ReGa keeps the alarm until
|
|
1451
|
+
* it is receipted, so the WebUI had nine messages this list lacked. Only the connection's own
|
|
1452
|
+
* interfaces and the datapoints that are service messages here: an alarm on a system variable
|
|
1453
|
+
* or a CUxD datapoint stays the WebUI's.
|
|
1440
1454
|
*/
|
|
1441
|
-
#
|
|
1442
|
-
if (this.#
|
|
1455
|
+
#withRegaAlarms(messages, interfaceName) {
|
|
1456
|
+
if (this.#regaAlarms.size === 0) {
|
|
1443
1457
|
return messages;
|
|
1444
1458
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1459
|
+
const listed = new Set();
|
|
1460
|
+
const result = messages.map((message) => {
|
|
1461
|
+
const key = `${message.interfaceName}|${message.address}|${message.datapoint}`;
|
|
1462
|
+
listed.add(key);
|
|
1463
|
+
const alarm = this.#regaAlarms.get(key);
|
|
1464
|
+
return alarm === undefined ? message : { ...message, since: alarm.first, sinceSource: 'rega' };
|
|
1448
1465
|
});
|
|
1466
|
+
const known = new Set(this.#manager?.names() ?? []);
|
|
1467
|
+
for (const [key, alarm] of this.#regaAlarms) {
|
|
1468
|
+
if (listed.has(key) ||
|
|
1469
|
+
!known.has(alarm.interfaceName) ||
|
|
1470
|
+
(interfaceName !== undefined && alarm.interfaceName !== interfaceName) ||
|
|
1471
|
+
!countsAsServiceMessage(alarm.datapoint, alarm.value)) {
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
result.push({
|
|
1475
|
+
interfaceName: alarm.interfaceName,
|
|
1476
|
+
address: alarm.address,
|
|
1477
|
+
datapoint: alarm.datapoint,
|
|
1478
|
+
value: alarm.value,
|
|
1479
|
+
since: alarm.first,
|
|
1480
|
+
sinceSource: 'rega',
|
|
1481
|
+
source: 'rega',
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
return result;
|
|
1485
|
+
}
|
|
1486
|
+
/** B-74: is this message one only ReGa lists - a pending alarm without the interface's datapoint? */
|
|
1487
|
+
#isRegaOnly(interfaceName, address, datapoint) {
|
|
1488
|
+
return (this.#regaAlarms.has(`${interfaceName}|${address}|${datapoint}`) &&
|
|
1489
|
+
!this.#caches
|
|
1490
|
+
.listServiceMessages(interfaceName)
|
|
1491
|
+
.some((message) => message.address === address && message.datapoint === datapoint));
|
|
1449
1492
|
}
|
|
1450
1493
|
/**
|
|
1451
1494
|
* Task 36: reads ReGa's pending service messages (one short script, read-only) and keeps their
|
|
@@ -1457,8 +1500,8 @@ export class Backend {
|
|
|
1457
1500
|
async #refreshRegaAlarms() {
|
|
1458
1501
|
const rega = this.#rega;
|
|
1459
1502
|
if (!rega?.state.enabled) {
|
|
1460
|
-
if (this.#
|
|
1461
|
-
this.#
|
|
1503
|
+
if (this.#regaAlarms.size > 0) {
|
|
1504
|
+
this.#regaAlarms.clear();
|
|
1462
1505
|
this.events.emit('serviceMessages.changed', this.#serviceMessages());
|
|
1463
1506
|
}
|
|
1464
1507
|
return;
|
|
@@ -1467,12 +1510,15 @@ export class Backend {
|
|
|
1467
1510
|
if (alarms === undefined || this.#rega !== rega) {
|
|
1468
1511
|
return;
|
|
1469
1512
|
}
|
|
1470
|
-
const next = new Map(alarms.map((alarm) => [`${alarm.interfaceName}|${alarm.address}|${alarm.datapoint}`, alarm
|
|
1471
|
-
const changed = next.size !== this.#
|
|
1472
|
-
[...next].some(([key,
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1513
|
+
const next = new Map(alarms.map((alarm) => [`${alarm.interfaceName}|${alarm.address}|${alarm.datapoint}`, alarm]));
|
|
1514
|
+
const changed = next.size !== this.#regaAlarms.size ||
|
|
1515
|
+
[...next].some(([key, alarm]) => {
|
|
1516
|
+
const before = this.#regaAlarms.get(key);
|
|
1517
|
+
return before === undefined || before.first !== alarm.first || before.value !== alarm.value;
|
|
1518
|
+
});
|
|
1519
|
+
this.#regaAlarms.clear();
|
|
1520
|
+
for (const [key, alarm] of next) {
|
|
1521
|
+
this.#regaAlarms.set(key, alarm);
|
|
1476
1522
|
}
|
|
1477
1523
|
if (changed) {
|
|
1478
1524
|
this.events.emit('serviceMessages.changed', this.#serviceMessages());
|
|
@@ -1705,21 +1751,44 @@ export class Backend {
|
|
|
1705
1751
|
if (!isAcknowledgeable(datapoint)) {
|
|
1706
1752
|
throw validationError(`${datapoint} cannot be acknowledged`);
|
|
1707
1753
|
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1754
|
+
// B-74: a message only ReGa lists has no flag left to clear on the interface; the write is
|
|
1755
|
+
// still sent (rfd takes a `false` for a flag it does not hold), but its failure - a device
|
|
1756
|
+
// the interface no longer knows, say - must not keep the alarm standing in the WebUI
|
|
1757
|
+
const regaOnly = this.#isRegaOnly(interfaceName, address, datapoint);
|
|
1758
|
+
try {
|
|
1759
|
+
const description = await this.#describe(interfaceName, address, 'VALUES');
|
|
1760
|
+
const parameter = description[datapoint];
|
|
1761
|
+
const value = parameter?.TYPE === 'ACTION';
|
|
1762
|
+
await this.#writer.setValue(interfaceName, address, datapoint, value);
|
|
1763
|
+
}
|
|
1764
|
+
catch (error) {
|
|
1765
|
+
if (!regaOnly) {
|
|
1766
|
+
throw error;
|
|
1767
|
+
}
|
|
1768
|
+
this.#notice('info', `${address}: ${datapoint} is listed by the CCU only; the interface did not take the write (${errorMessage(error)}), the alarm is receipted in ReGa`, interfaceName);
|
|
1769
|
+
}
|
|
1712
1770
|
if (this.#caches.serviceMessages.clear(interfaceName, address, datapoint)) {
|
|
1713
1771
|
this.events.emit('serviceMessages.changed', this.#serviceMessages());
|
|
1714
1772
|
}
|
|
1715
1773
|
// Issue #94: the datapoint write above is the acknowledgement that matters, and it happened
|
|
1716
1774
|
// whether ReGa exists or not. This clears the CCU's own alarm on top, so the WebUI stops
|
|
1717
1775
|
// showing a message the user has already dealt with here. D-2: silent when ReGa is off.
|
|
1718
|
-
|
|
1776
|
+
// B-74: for a message only ReGa lists the receipt *is* the acknowledgement, so it is waited
|
|
1777
|
+
// for, and the row goes with the list that follows it.
|
|
1778
|
+
const receipt = (this.#rega?.acknowledgeAlarm(interfaceName, address, datapoint) ?? Promise.resolve(false)).then(async (receipted) => {
|
|
1719
1779
|
if (receipted) {
|
|
1720
1780
|
await this.#refreshRegaAlarms();
|
|
1721
1781
|
}
|
|
1782
|
+
else if (regaOnly) {
|
|
1783
|
+
this.#notice('warn', `${address}: ${datapoint} is listed by the CCU only, and ReGa did not receipt it`, interfaceName);
|
|
1784
|
+
}
|
|
1722
1785
|
});
|
|
1786
|
+
if (regaOnly) {
|
|
1787
|
+
await receipt;
|
|
1788
|
+
}
|
|
1789
|
+
else {
|
|
1790
|
+
void receipt;
|
|
1791
|
+
}
|
|
1723
1792
|
return null;
|
|
1724
1793
|
}
|
|
1725
1794
|
#recentEvents(interfaceName, limit) {
|
|
@@ -1915,6 +1984,8 @@ export const API_METHOD_NAMES = [
|
|
|
1915
1984
|
'devices.installFirmware',
|
|
1916
1985
|
'devices.installMode.set',
|
|
1917
1986
|
'devices.installMode.get',
|
|
1987
|
+
'devices.installMode.keyMismatch',
|
|
1988
|
+
'devices.installMode.tempKey',
|
|
1918
1989
|
'devices.replaceable',
|
|
1919
1990
|
'names.get',
|
|
1920
1991
|
'names.set',
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* the interface process, or is a convenience; a system without ReGa loses nothing but the CCU-side
|
|
21
21
|
* bookkeeping.
|
|
22
22
|
*/
|
|
23
|
+
import type { ParamsetValue } from '@homematic-manager/core';
|
|
23
24
|
/** Escapes a string for the ReGa string literal it is written into. */
|
|
24
25
|
export declare function escapeRegaString(value: string): string;
|
|
25
26
|
/** One entry of a rename: the ReGa object id and the name it should carry. */
|
|
@@ -72,24 +73,35 @@ export declare function parseConfirmedDevices(output: string): ConfirmedDevice[]
|
|
|
72
73
|
*/
|
|
73
74
|
export declare function acknowledgeAlarmScript(interfaceName: string, address: string, datapoint: string): string | undefined;
|
|
74
75
|
/**
|
|
75
|
-
* Task 36: the CCU's pending service messages with their times, as the WebUI's
|
|
76
|
-
*
|
|
77
|
-
*
|
|
76
|
+
* Task 36, B-74: the CCU's pending service messages with their times and values, as the WebUI's
|
|
77
|
+
* service-message page lists them: every `OT_ALARMDP` of `ID_SERVICES` that is `Used()`,
|
|
78
|
+
* `Enabled()` and `asOncoming` (`serviceMessages.htm` of OpenCCU 3.89.8), one line per alarm with
|
|
79
|
+
* the trigger datapoint's name, `AlOccurrenceTime()` (the WebUI's *Erste Meldung*), `Timestamp()`
|
|
78
80
|
* (*Letzte Meldung*), both as Unix seconds (`ToInteger()`, checked against `date +%s` on the lab's
|
|
79
|
-
* OpenCCU)
|
|
81
|
+
* OpenCCU), and the trigger's `Value()` - ReGa's own last value of the datapoint, which is what
|
|
82
|
+
* the WebUI shows for the message (`true`/`false`, an ENUM as its index). Read-only.
|
|
83
|
+
*
|
|
84
|
+
* B-74 (#150): an alarm stays oncoming until it is receipted, whatever the interface process
|
|
85
|
+
* reports meanwhile - rfd forgets a `STICKY_UNREACH` at a restart, ReGa does not - so the
|
|
86
|
+
* WebUI lists messages `getServiceMessages` does not. The value is what such a message is shown
|
|
87
|
+
* with.
|
|
80
88
|
*/
|
|
81
|
-
export declare const REGA_ALARMS_SCRIPT = "string sId;\nforeach (sId, dom.GetObject(ID_SERVICES).EnumIDs()) {\n object oAlarm = dom.GetObject(sId);\n if (oAlarm) {\n if (oAlarm.IsTypeOf(OT_ALARMDP) && (oAlarm.AlState() == asOncoming)) {\n object oTrigger = dom.GetObject(oAlarm.AlTriggerDP());\n if (oTrigger) {\n Write(oTrigger.Name() # \"\\t\" # oAlarm.AlOccurrenceTime().ToInteger() # \"\\t\" # oAlarm.Timestamp().ToInteger() # \"\\n\");\n }\n }\n }\n}\n";
|
|
82
|
-
/** One pending ReGa service message: whose datapoint, first and last reported (epoch ms). */
|
|
89
|
+
export declare const REGA_ALARMS_SCRIPT = "string sId;\nforeach (sId, dom.GetObject(ID_SERVICES).EnumIDs()) {\n object oAlarm = dom.GetObject(sId);\n if (oAlarm) {\n if (oAlarm.IsTypeOf(OT_ALARMDP) && (oAlarm.Used() == true) && (oAlarm.Enabled() == true) && (oAlarm.AlState() == asOncoming)) {\n object oTrigger = dom.GetObject(oAlarm.AlTriggerDP());\n if (oTrigger) {\n Write(oTrigger.Name() # \"\\t\" # oAlarm.AlOccurrenceTime().ToInteger() # \"\\t\" # oAlarm.Timestamp().ToInteger() # \"\\t\" # oTrigger.Value() # \"\\n\");\n }\n }\n }\n}\n";
|
|
90
|
+
/** One pending ReGa service message: whose datapoint, its value, first and last reported (epoch ms). */
|
|
83
91
|
export interface RegaAlarm {
|
|
84
92
|
readonly interfaceName: string;
|
|
85
93
|
readonly address: string;
|
|
86
94
|
readonly datapoint: string;
|
|
95
|
+
/** ReGa's last value of the trigger datapoint; `true` when the line carries none (older script). */
|
|
96
|
+
readonly value: ParamsetValue;
|
|
87
97
|
readonly first: number;
|
|
88
98
|
readonly last: number;
|
|
89
99
|
}
|
|
90
100
|
/**
|
|
91
101
|
* Reads what {@link REGA_ALARMS_SCRIPT} wrote. A line whose name is not `<interface>.<channel>.<datapoint>`
|
|
92
|
-
* (a system alarm, a CUxD name with more dots) or whose time is no positive number is skipped.
|
|
102
|
+
* (a system alarm, a CUxD name with more dots) or whose time is no positive number is skipped. The
|
|
103
|
+
* value column is ReGa's text of the value: `true`/`false`, a number as it prints it (an ENUM by
|
|
104
|
+
* its index), anything else stays a string; a line without it means `true`.
|
|
93
105
|
*/
|
|
94
106
|
export declare function parseRegaAlarms(output: string): RegaAlarm[];
|
|
95
107
|
/**
|
|
@@ -124,20 +124,27 @@ Write(iDone);
|
|
|
124
124
|
`;
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
|
-
* Task 36: the CCU's pending service messages with their times, as the WebUI's
|
|
128
|
-
*
|
|
129
|
-
*
|
|
127
|
+
* Task 36, B-74: the CCU's pending service messages with their times and values, as the WebUI's
|
|
128
|
+
* service-message page lists them: every `OT_ALARMDP` of `ID_SERVICES` that is `Used()`,
|
|
129
|
+
* `Enabled()` and `asOncoming` (`serviceMessages.htm` of OpenCCU 3.89.8), one line per alarm with
|
|
130
|
+
* the trigger datapoint's name, `AlOccurrenceTime()` (the WebUI's *Erste Meldung*), `Timestamp()`
|
|
130
131
|
* (*Letzte Meldung*), both as Unix seconds (`ToInteger()`, checked against `date +%s` on the lab's
|
|
131
|
-
* OpenCCU)
|
|
132
|
+
* OpenCCU), and the trigger's `Value()` - ReGa's own last value of the datapoint, which is what
|
|
133
|
+
* the WebUI shows for the message (`true`/`false`, an ENUM as its index). Read-only.
|
|
134
|
+
*
|
|
135
|
+
* B-74 (#150): an alarm stays oncoming until it is receipted, whatever the interface process
|
|
136
|
+
* reports meanwhile - rfd forgets a `STICKY_UNREACH` at a restart, ReGa does not - so the
|
|
137
|
+
* WebUI lists messages `getServiceMessages` does not. The value is what such a message is shown
|
|
138
|
+
* with.
|
|
132
139
|
*/
|
|
133
140
|
export const REGA_ALARMS_SCRIPT = `string sId;
|
|
134
141
|
foreach (sId, dom.GetObject(ID_SERVICES).EnumIDs()) {
|
|
135
142
|
object oAlarm = dom.GetObject(sId);
|
|
136
143
|
if (oAlarm) {
|
|
137
|
-
if (oAlarm.IsTypeOf(OT_ALARMDP) && (oAlarm.AlState() == asOncoming)) {
|
|
144
|
+
if (oAlarm.IsTypeOf(OT_ALARMDP) && (oAlarm.Used() == true) && (oAlarm.Enabled() == true) && (oAlarm.AlState() == asOncoming)) {
|
|
138
145
|
object oTrigger = dom.GetObject(oAlarm.AlTriggerDP());
|
|
139
146
|
if (oTrigger) {
|
|
140
|
-
Write(oTrigger.Name() # "\\t" # oAlarm.AlOccurrenceTime().ToInteger() # "\\t" # oAlarm.Timestamp().ToInteger() # "\\n");
|
|
147
|
+
Write(oTrigger.Name() # "\\t" # oAlarm.AlOccurrenceTime().ToInteger() # "\\t" # oAlarm.Timestamp().ToInteger() # "\\t" # oTrigger.Value() # "\\n");
|
|
141
148
|
}
|
|
142
149
|
}
|
|
143
150
|
}
|
|
@@ -145,12 +152,14 @@ foreach (sId, dom.GetObject(ID_SERVICES).EnumIDs()) {
|
|
|
145
152
|
`;
|
|
146
153
|
/**
|
|
147
154
|
* Reads what {@link REGA_ALARMS_SCRIPT} wrote. A line whose name is not `<interface>.<channel>.<datapoint>`
|
|
148
|
-
* (a system alarm, a CUxD name with more dots) or whose time is no positive number is skipped.
|
|
155
|
+
* (a system alarm, a CUxD name with more dots) or whose time is no positive number is skipped. The
|
|
156
|
+
* value column is ReGa's text of the value: `true`/`false`, a number as it prints it (an ENUM by
|
|
157
|
+
* its index), anything else stays a string; a line without it means `true`.
|
|
149
158
|
*/
|
|
150
159
|
export function parseRegaAlarms(output) {
|
|
151
160
|
const alarms = [];
|
|
152
161
|
for (const line of output.split('\n')) {
|
|
153
|
-
const [name, first, last] = line.split('\t');
|
|
162
|
+
const [name, first, last, value] = line.split('\t');
|
|
154
163
|
const match = /^([^.\s]+)\.([^.\s]+)\.([^.\s]+)$/u.exec(name?.trim() ?? '');
|
|
155
164
|
const firstSeconds = Number(first);
|
|
156
165
|
const lastSeconds = Number(last);
|
|
@@ -161,12 +170,25 @@ export function parseRegaAlarms(output) {
|
|
|
161
170
|
interfaceName: match[1] ?? '',
|
|
162
171
|
address: match[2] ?? '',
|
|
163
172
|
datapoint: match[3] ?? '',
|
|
173
|
+
value: parseRegaValue(value),
|
|
164
174
|
first: firstSeconds * 1000,
|
|
165
175
|
last: Number.isFinite(lastSeconds) && lastSeconds > 0 ? lastSeconds * 1000 : firstSeconds * 1000,
|
|
166
176
|
});
|
|
167
177
|
}
|
|
168
178
|
return alarms;
|
|
169
179
|
}
|
|
180
|
+
/** ReGa's text of a datapoint value, as `Write(oDP.Value())` prints it. */
|
|
181
|
+
function parseRegaValue(text) {
|
|
182
|
+
const trimmed = text?.trim() ?? '';
|
|
183
|
+
if (trimmed === '' || trimmed === 'true') {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
if (trimmed === 'false') {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
const number = Number(trimmed);
|
|
190
|
+
return Number.isFinite(number) ? number : trimmed;
|
|
191
|
+
}
|
|
170
192
|
/**
|
|
171
193
|
* Is this safe to put inside a ReGa string literal *unescaped*?
|
|
172
194
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homematic-manager/backend",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Homematic Manager backend: XML-RPC/BIN-RPC clients and callback servers, caches, optional ReGa",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -589,6 +589,13 @@ export interface ServiceMessage {
|
|
|
589
589
|
* WebUI's *Erste Meldung*); absent when it is when this application first saw it.
|
|
590
590
|
*/
|
|
591
591
|
sinceSource?: 'rega';
|
|
592
|
+
/**
|
|
593
|
+
* B-74 (#150): `rega` when only the CCU's ReGa lists this message - its alarm is pending while
|
|
594
|
+
* the interface process no longer reports the datapoint (rfd forgets a `STICKY_UNREACH` at a
|
|
595
|
+
* restart, ReGa keeps the alarm until it is receipted). The WebUI shows such a message; so does
|
|
596
|
+
* this list. Absent when the interface reports it.
|
|
597
|
+
*/
|
|
598
|
+
source?: 'rega';
|
|
592
599
|
}
|
|
593
600
|
export interface EventRecord {
|
|
594
601
|
/** Milliseconds since epoch. */
|
|
@@ -741,7 +748,10 @@ export interface InstallModeOptions {
|
|
|
741
748
|
* seconds)` with exactly two arguments, `hmipKey` ignored.
|
|
742
749
|
*/
|
|
743
750
|
hmipKeyMode?: 'KEY' | 'SGTIN' | 'ANY';
|
|
744
|
-
/**
|
|
751
|
+
/**
|
|
752
|
+
* BidCos: temporary key for `setTempKey` - the security key (passphrase) of the system the
|
|
753
|
+
* device was paired to, sent before the install mode or `addDevice`.
|
|
754
|
+
*/
|
|
745
755
|
tempKey?: string;
|
|
746
756
|
/** Restrict to a device address (BidCos `setInstallMode` with address). */
|
|
747
757
|
address?: string;
|
|
@@ -892,6 +902,25 @@ export interface ApiMethods {
|
|
|
892
902
|
params: [interfaceName: string];
|
|
893
903
|
result: number;
|
|
894
904
|
};
|
|
905
|
+
/**
|
|
906
|
+
* Task 76: the serial of the last BidCos device rfd refused to pair because it holds a security
|
|
907
|
+
* key this system does not have (`getKeyMismatchDevice(true)`: read once, then cleared in the
|
|
908
|
+
* interface process), or `''` when there is none. rfd reports it to nobody, so a client polls
|
|
909
|
+
* it while its install mode is open, as the CCU WebUI does every five seconds.
|
|
910
|
+
*/
|
|
911
|
+
'devices.installMode.keyMismatch': {
|
|
912
|
+
params: [interfaceName: string];
|
|
913
|
+
result: string;
|
|
914
|
+
};
|
|
915
|
+
/**
|
|
916
|
+
* Task 76: `setTempKey(passphrase)` on its own - the security key of the system a device was
|
|
917
|
+
* paired to, which rfd uses for the pairings that follow; `''` clears it, so a mistyped key does
|
|
918
|
+
* not linger for the next pairing. `devices.installMode.set` sends it too when `tempKey` is set.
|
|
919
|
+
*/
|
|
920
|
+
'devices.installMode.tempKey': {
|
|
921
|
+
params: [interfaceName: string, key: string];
|
|
922
|
+
result: null;
|
|
923
|
+
};
|
|
895
924
|
'devices.replaceable': {
|
|
896
925
|
params: [interfaceName: string, address: string];
|
|
897
926
|
result: DeviceDescription[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homematic-manager",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Configure and administer Homematic and HomematicIP devices - devices and channels, direct links, paramsets, RSSI, service messages, events and an RPC console - from a local HTTP/WebSocket server that also serves the web UI",
|
|
6
6
|
"keywords": [
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"hm-simulator": "^1.0.0"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@homematic-manager/backend": "3.0.0-beta.
|
|
71
|
-
"@homematic-manager/core": "3.0.0-beta.
|
|
70
|
+
"@homematic-manager/backend": "3.0.0-beta.27",
|
|
71
|
+
"@homematic-manager/core": "3.0.0-beta.27",
|
|
72
72
|
"binrpc": "^4.2.0",
|
|
73
73
|
"homematic-rega": "^2.0.0",
|
|
74
74
|
"homematic-xmlrpc": "^2.0.0",
|