hm2mqtt 2.5.0 → 3.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/README.md +191 -99
- package/config.js +112 -58
- package/example-names.json +6 -0
- package/index.js +595 -974
- package/lib/cast.js +105 -0
- package/lib/compare.js +125 -0
- package/lib/install.js +17 -0
- package/lib/interfaces.js +110 -0
- package/lib/metadata.js +360 -0
- package/lib/rega.js +380 -0
- package/lib/rpc.js +497 -0
- package/lib/topics.js +211 -0
- package/lib/values.js +283 -0
- package/names.schema.json +8 -0
- package/package.json +54 -36
- package/paramsets.json +428781 -0
- package/scripts/clean-retained.js +69 -0
- package/scripts/compare-trees.js +46 -0
- package/.npmignore +0 -4
- package/.travis.yml +0 -40
- package/Dockerfile +0 -10
- package/Dockerfile.armhf +0 -11
- package/discover.js +0 -24
- package/firstip.js +0 -16
- package/regascripts/devices.fn +0 -43
- package/regascripts/programs.fn +0 -27
- package/regascripts/variables.fn +0 -78
- package/test.js +0 -201
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lists (and with --yes clears) retained messages matching topic patterns — for the cutover
|
|
5
|
+
* (ROADMAP §9): the Node-RED flow left retained topics for unnamed channels (`hm/status//X`)
|
|
6
|
+
* and for devices and names that no longer exist.
|
|
7
|
+
*
|
|
8
|
+
* node scripts/clean-retained.js mqtt://broker 'hm/status//#' # list only
|
|
9
|
+
* node scripts/clean-retained.js mqtt://broker 'hm/status//#' 'hm3/#' --yes
|
|
10
|
+
* node scripts/clean-retained.js mqtts://broker:8883 'hm/#' --ca ca.pem --grep 'Alte Lampe'
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import mqtt from 'mqtt';
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const take = (flag) => {
|
|
18
|
+
const i = args.indexOf(flag);
|
|
19
|
+
return i === -1 ? undefined : args.splice(i, 2)[1];
|
|
20
|
+
};
|
|
21
|
+
const ca = take('--ca');
|
|
22
|
+
const grep = take('--grep');
|
|
23
|
+
const seconds = Number(take('--seconds') || 5);
|
|
24
|
+
const yes = args.includes('--yes');
|
|
25
|
+
const patterns = args.filter((a) => !a.startsWith('--') && a !== args[0]);
|
|
26
|
+
const url = args[0];
|
|
27
|
+
if (!url || patterns.length === 0) {
|
|
28
|
+
console.error(
|
|
29
|
+
'usage: clean-retained.js <mqtt-url> <pattern>... [--grep <substring>] [--seconds 5] [--ca file] [--yes]',
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const found = new Map();
|
|
35
|
+
const client = mqtt.connect(url, {ca: ca ? fs.readFileSync(ca) : undefined, rejectUnauthorized: Boolean(ca)});
|
|
36
|
+
client.on('connect', () => {
|
|
37
|
+
client.subscribe(patterns);
|
|
38
|
+
console.error(`collecting retained messages for ${patterns.join(', ')} (${seconds} s) …`);
|
|
39
|
+
});
|
|
40
|
+
client.on('message', (topic, payload, packet) => {
|
|
41
|
+
if (packet.retain && payload.length > 0 && (!grep || topic.includes(grep))) {
|
|
42
|
+
found.set(topic, payload.toString().slice(0, 80));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
const topics = [...found.keys()].sort();
|
|
47
|
+
for (const t of topics) {
|
|
48
|
+
console.log(`${t} ${found.get(t)}`);
|
|
49
|
+
}
|
|
50
|
+
console.error(`${topics.length} retained topics`);
|
|
51
|
+
if (!yes) {
|
|
52
|
+
console.error('dry run — add --yes to clear them');
|
|
53
|
+
client.end(true);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let n = 0;
|
|
57
|
+
for (const t of topics) {
|
|
58
|
+
client.publish(t, '', {retain: true}, () => {
|
|
59
|
+
n += 1;
|
|
60
|
+
if (n === topics.length) {
|
|
61
|
+
console.error(`cleared ${n}`);
|
|
62
|
+
client.end(true);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (topics.length === 0) {
|
|
67
|
+
client.end(true);
|
|
68
|
+
}
|
|
69
|
+
}, seconds * 1000);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parallel-run check before the cutover (ROADMAP §9): subscribes to two status trees on the same
|
|
5
|
+
* broker, collects the retained state plus everything published while running, and prints
|
|
6
|
+
* what differs. Ignores the fields that legitimately differ (timestamps, cache flags).
|
|
7
|
+
*
|
|
8
|
+
* node scripts/compare-trees.js mqtt://broker hm hm3 [seconds]
|
|
9
|
+
* node scripts/compare-trees.js mqtts://user:pass@broker:8883 hm hm3 3600 --ca ca.pem
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import mqtt from 'mqtt';
|
|
14
|
+
import {compareTrees, formatReport} from '../lib/compare.js';
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const caIndex = args.indexOf('--ca');
|
|
18
|
+
const ca = caIndex === -1 ? undefined : fs.readFileSync(args.splice(caIndex, 2)[1]);
|
|
19
|
+
const [url, leftName, rightName, seconds = '60'] = args;
|
|
20
|
+
if (!url || !leftName || !rightName) {
|
|
21
|
+
console.error('usage: compare-trees.js <mqtt-url> <left-prefix> <right-prefix> [seconds] [--ca file]');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const left = new Map();
|
|
26
|
+
const right = new Map();
|
|
27
|
+
const client = mqtt.connect(url, {ca, rejectUnauthorized: Boolean(ca)});
|
|
28
|
+
client.on('connect', () => {
|
|
29
|
+
client.subscribe([`${leftName}/status/#`, `${rightName}/status/#`]);
|
|
30
|
+
console.error(`collecting ${leftName}/status/# and ${rightName}/status/# for ${seconds} s …`);
|
|
31
|
+
});
|
|
32
|
+
client.on('message', (topic, payload) => {
|
|
33
|
+
const raw = payload.toString();
|
|
34
|
+
if (topic.startsWith(`${leftName}/status/`)) {
|
|
35
|
+
left.set(topic.slice(leftName.length + 8), raw);
|
|
36
|
+
} else if (topic.startsWith(`${rightName}/status/`)) {
|
|
37
|
+
right.set(topic.slice(rightName.length + 8), raw);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
setTimeout(
|
|
41
|
+
() => {
|
|
42
|
+
client.end(true);
|
|
43
|
+
console.log(formatReport(compareTrees(left, right), {leftName, rightName}));
|
|
44
|
+
},
|
|
45
|
+
Number(seconds) * 1000,
|
|
46
|
+
);
|
package/.npmignore
DELETED
package/.travis.yml
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
sudo: true
|
|
2
|
-
|
|
3
|
-
before_install:
|
|
4
|
-
- sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa -y
|
|
5
|
-
- sudo apt-get update -y
|
|
6
|
-
- sudo apt-get install mosquitto -y
|
|
7
|
-
|
|
8
|
-
services:
|
|
9
|
-
- docker
|
|
10
|
-
|
|
11
|
-
before_script:
|
|
12
|
-
- sudo apt-get --yes --no-install-recommends install binfmt-support qemu-user-static
|
|
13
|
-
- echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:' | sudo tee -a /proc/sys/fs/binfmt_misc/register
|
|
14
|
-
|
|
15
|
-
jobs:
|
|
16
|
-
include:
|
|
17
|
-
- stage: test
|
|
18
|
-
script:
|
|
19
|
-
- nvm install 6
|
|
20
|
-
- npm install
|
|
21
|
-
- npm test
|
|
22
|
-
after_success: skip
|
|
23
|
-
- stage: build docker image
|
|
24
|
-
script:
|
|
25
|
-
- docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
|
|
26
|
-
- docker build -f Dockerfile.armhf -t hm2mqtt_armhf .
|
|
27
|
-
- docker images
|
|
28
|
-
- export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo $(npm view hm2mqtt version); else echo -$TRAVIS_BRANCH ; fi`
|
|
29
|
-
- docker tag hm2mqtt_armhf mqttsmarthome/hm2mqtt_armhf:$TAG
|
|
30
|
-
- docker tag hm2mqtt_armhf mqttsmarthome/hm2mqtt_armhf:latest
|
|
31
|
-
- stage: test docker image
|
|
32
|
-
script:
|
|
33
|
-
- export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo $(npm view hm2mqtt version); else echo -$TRAVIS_BRANCH ; fi`
|
|
34
|
-
- docker run -d mqttsmarthome/hm2mqtt_armhf:$TAG
|
|
35
|
-
- docker ps | grep armhf
|
|
36
|
-
after_success:
|
|
37
|
-
- export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo $(npm view hm2mqtt version); else echo -$TRAVIS_BRANCH ; fi`
|
|
38
|
-
- docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
|
|
39
|
-
- docker push mqttsmarthome/hm2mqtt_armhf:$TAG
|
|
40
|
-
- docker push mqttsmarthome/hm2mqtt_armhf:latest
|
package/Dockerfile
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
FROM node:slim
|
|
2
|
-
LABEL maintainer="Holger Imbery <contact@connectedobjects.cloud>" \
|
|
3
|
-
version="1.1a" \
|
|
4
|
-
description="HM2MQTT (hm2mqtt.js) dockerized version of https://github.com/hobbyquaker/hm2mqtt.js"
|
|
5
|
-
|
|
6
|
-
RUN npm config set unsafe-perm true && npm install -g hm2mqtt
|
|
7
|
-
|
|
8
|
-
EXPOSE 2126
|
|
9
|
-
EXPOSE 2127
|
|
10
|
-
ENTRYPOINT ["hm2mqtt"]
|
package/Dockerfile.armhf
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
FROM hypriot/rpi-node:slim
|
|
2
|
-
LABEL maintainer="Holger Imbery <contact@connectedobjects.cloud>" \
|
|
3
|
-
version="1.1a" \
|
|
4
|
-
description="HM2MQTT (hm2mqtt.js) dockerized version of https://github.com/hobbyquaker/hm2mqtt.js"
|
|
5
|
-
|
|
6
|
-
RUN npm config set unsafe-perm true
|
|
7
|
-
RUN npm install -g hm2mqtt
|
|
8
|
-
|
|
9
|
-
EXPOSE 2126
|
|
10
|
-
EXPOSE 2127
|
|
11
|
-
ENTRYPOINT ["hm2mqtt"]
|
package/discover.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const net = require('net');
|
|
2
|
-
|
|
3
|
-
module.exports = (host, interfaces, callback) => {
|
|
4
|
-
const sum = Object.keys(interfaces).length;
|
|
5
|
-
const connections = {};
|
|
6
|
-
let count = 0;
|
|
7
|
-
|
|
8
|
-
Object.keys(interfaces).forEach(service => {
|
|
9
|
-
connections[service] = net.connect({host, port: interfaces[service].port}, () => {
|
|
10
|
-
connections[service].end();
|
|
11
|
-
checkDone();
|
|
12
|
-
});
|
|
13
|
-
connections[service].on('error', () => {
|
|
14
|
-
delete interfaces[service];
|
|
15
|
-
checkDone();
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
function checkDone() {
|
|
20
|
-
if (++count >= sum) {
|
|
21
|
-
callback(interfaces);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
};
|
package/firstip.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
const os = require('os');
|
|
2
|
-
|
|
3
|
-
const interfaces = os.networkInterfaces();
|
|
4
|
-
const addresses = [];
|
|
5
|
-
Object.keys(interfaces).forEach(i => {
|
|
6
|
-
Object.keys(interfaces[i]).forEach(a => {
|
|
7
|
-
const address = interfaces[i][a];
|
|
8
|
-
if (address.family === 'IPv4' && !address.internal) {
|
|
9
|
-
addresses.push(address.address);
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const [firstAddress] = addresses;
|
|
15
|
-
|
|
16
|
-
module.exports = firstAddress;
|
package/regascripts/devices.fn
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
!# devices.fn hm2mqtt
|
|
2
|
-
!#
|
|
3
|
-
!# Dieses Homematic-Script gibt eine Liste aller Geraete/Kanaele im JSON Format aus
|
|
4
|
-
!#
|
|
5
|
-
!# 3'2013-4'2017 hobbyquaker https://github.com/hobbyquaker
|
|
6
|
-
!#
|
|
7
|
-
|
|
8
|
-
string sDevId;
|
|
9
|
-
string sChnId;
|
|
10
|
-
|
|
11
|
-
Write('{');
|
|
12
|
-
|
|
13
|
-
boolean dFirst = true;
|
|
14
|
-
|
|
15
|
-
foreach (sDevId, root.Devices().EnumUsedIDs()) {
|
|
16
|
-
|
|
17
|
-
object oDevice = dom.GetObject(sDevId);
|
|
18
|
-
boolean bDevReady = oDevice.ReadyConfig();
|
|
19
|
-
|
|
20
|
-
if (bDevReady) {
|
|
21
|
-
|
|
22
|
-
if (dFirst) {
|
|
23
|
-
dFirst = false;
|
|
24
|
-
} else {
|
|
25
|
-
WriteLine(',');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
Write('"' # oDevice.Address() # '":"');
|
|
29
|
-
WriteURL(oDevice.Name());
|
|
30
|
-
Write('"');
|
|
31
|
-
|
|
32
|
-
foreach(sChnId, oDevice.Channels()) {
|
|
33
|
-
object oChannel = dom.GetObject(sChnId);
|
|
34
|
-
WriteLine(',');
|
|
35
|
-
Write('"' # oChannel.Address() # '":"');
|
|
36
|
-
WriteURL(oChannel.Name());
|
|
37
|
-
Write('"');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
Write('}');
|
package/regascripts/programs.fn
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
!# programs.fn hm2mqtt
|
|
2
|
-
!# Dieses Script gibt eine Liste der Programme im JSON Format aus
|
|
3
|
-
!#
|
|
4
|
-
!# 3'2013-4'2017 hobbyquaker https://github.com/hobbyquaker
|
|
5
|
-
!#
|
|
6
|
-
|
|
7
|
-
string oPrgID;
|
|
8
|
-
object oProgram;
|
|
9
|
-
boolean first = true;
|
|
10
|
-
Write('{');
|
|
11
|
-
foreach (oPrgID, dom.GetObject(ID_PROGRAMS).EnumUsedIDs()) {
|
|
12
|
-
oProgram = dom.GetObject(oPrgID);
|
|
13
|
-
if(oProgram != null) {
|
|
14
|
-
if (first == false) {
|
|
15
|
-
WriteLine(',');
|
|
16
|
-
} else {
|
|
17
|
-
first = false;
|
|
18
|
-
}
|
|
19
|
-
Write('"' # oPrgID # '":{"name":"');
|
|
20
|
-
WriteURL(oProgram.Name());
|
|
21
|
-
Write('", "active":' # oProgram.Active());
|
|
22
|
-
Write(',"ts":"' # oProgram.ProgramLastExecuteTime());
|
|
23
|
-
Write('"}');
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
Write('}');
|
package/regascripts/variables.fn
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
!# variables.fn hm2mqtt
|
|
2
|
-
!#
|
|
3
|
-
!# Dieses Script gibt die Systemvariablen im JSON Format aus
|
|
4
|
-
!#
|
|
5
|
-
!# 3'2013-4'2017 hobbyquaker https://github.com/hobbyquaker
|
|
6
|
-
!#
|
|
7
|
-
|
|
8
|
-
object oSysVar;
|
|
9
|
-
string sSysVarId;
|
|
10
|
-
string sValueType;
|
|
11
|
-
string sValue;
|
|
12
|
-
boolean bFirst = true;
|
|
13
|
-
|
|
14
|
-
Write('{');
|
|
15
|
-
|
|
16
|
-
WriteLine('"40":{"name":"Alarmmeldungen","val":' # dom.GetObject(40).Value() # ',"min":0,"max":65000,"unit":"","type":16,"enum":""},');
|
|
17
|
-
Write('"41":{"name":"Servicemeldungen","val":' # dom.GetObject(41).Value() # ',"min":0,"max":65000,"unit":"","type":16,"enum":""}');
|
|
18
|
-
|
|
19
|
-
foreach (sSysVarId, dom.GetObject(ID_SYSTEM_VARIABLES).EnumUsedIDs()) {
|
|
20
|
-
|
|
21
|
-
WriteLine(',');
|
|
22
|
-
|
|
23
|
-
oSysVar = dom.GetObject(sSysVarId);
|
|
24
|
-
sValueType = oSysVar.ValueType();
|
|
25
|
-
|
|
26
|
-
Write('"' # sSysVarId # '":{"name":"');
|
|
27
|
-
WriteURL(oSysVar.Name());
|
|
28
|
-
Write('","val":');
|
|
29
|
-
if (sValueType == 20) {
|
|
30
|
-
Write('"');
|
|
31
|
-
WriteURL(oSysVar.Value());
|
|
32
|
-
Write('"');
|
|
33
|
-
} else {
|
|
34
|
-
sValue = oSysVar.Value();
|
|
35
|
-
if (sValueType == 2) {
|
|
36
|
-
if (sValue) {
|
|
37
|
-
Write("true");
|
|
38
|
-
} else {
|
|
39
|
-
Write("false");
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
if (sValue == "") {
|
|
43
|
-
Write("0");
|
|
44
|
-
} else {
|
|
45
|
-
Write(sValue);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
string sValueMin = oSysVar.ValueMin();
|
|
50
|
-
|
|
51
|
-
if (sValueMin == '') {
|
|
52
|
-
sValueMin = 'null';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
string sValueMax = oSysVar.ValueMax();
|
|
56
|
-
|
|
57
|
-
if (sValueMax == '') {
|
|
58
|
-
sValueMax = 'null';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
Write(',"ts":"' # oSysVar.Timestamp());
|
|
62
|
-
Write('","min":' # sValueMin # ',"max":' # sValueMax # ',"unit":"');
|
|
63
|
-
WriteURL(oSysVar.ValueUnit());
|
|
64
|
-
Write('","type":' # sValueType);
|
|
65
|
-
|
|
66
|
-
if (sValueType == 2) {
|
|
67
|
-
Write(',"enum":"');
|
|
68
|
-
WriteURL(oSysVar.ValueName0() # ';' # oSysVar.ValueName1());
|
|
69
|
-
} else {
|
|
70
|
-
Write(',"enum":"');
|
|
71
|
-
WriteURL(oSysVar.ValueList());
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
Write('"}');
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
Write('}');
|
package/test.js
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
require('should');
|
|
4
|
-
|
|
5
|
-
const cp = require('child_process');
|
|
6
|
-
const request = require('request');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const streamSplitter = require('stream-splitter');
|
|
9
|
-
const Mqtt = require('mqtt');
|
|
10
|
-
mqtt = Mqtt.connect('mqtt://127.0.0.1');
|
|
11
|
-
|
|
12
|
-
const simCmd = path.join(__dirname, '/node_modules/.bin/hm-simulator');
|
|
13
|
-
const simArgs = [];
|
|
14
|
-
let sim;
|
|
15
|
-
let simPipeOut;
|
|
16
|
-
let simPipeErr;
|
|
17
|
-
const simSubscriptions = {};
|
|
18
|
-
const simBuffer = [];
|
|
19
|
-
|
|
20
|
-
const hmCmd = path.join(__dirname, '/index.js');
|
|
21
|
-
const hmArgs = ['--mqtt-url', 'mqtt://127.0.0.1:1883', '--ccu-address', '127.0.0.1', '-v', 'debug'];
|
|
22
|
-
let hm;
|
|
23
|
-
let hmPipeOut;
|
|
24
|
-
let hmPipeErr;
|
|
25
|
-
const hmSubscriptions = {};
|
|
26
|
-
const hmBuffer = [];
|
|
27
|
-
|
|
28
|
-
let subIndex = 0;
|
|
29
|
-
|
|
30
|
-
function subscribe(type, rx, cb) {
|
|
31
|
-
subIndex += 1;
|
|
32
|
-
if (type === 'sim') {
|
|
33
|
-
simSubscriptions[subIndex] = {rx, cb};
|
|
34
|
-
} else if (type === 'hm') {
|
|
35
|
-
hmSubscriptions[subIndex] = {rx, cb};
|
|
36
|
-
}
|
|
37
|
-
matchSubscriptions(type);
|
|
38
|
-
return subIndex;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function unsubscribe(type, subIndex) {
|
|
42
|
-
if (type === 'sim') {
|
|
43
|
-
delete simSubscriptions[subIndex];
|
|
44
|
-
} else if (type === 'hm') {
|
|
45
|
-
delete hmSubscriptions[subIndex];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function matchSubscriptions(type, data) {
|
|
50
|
-
let subs;
|
|
51
|
-
let buf;
|
|
52
|
-
if (type === 'sim') {
|
|
53
|
-
subs = simSubscriptions;
|
|
54
|
-
buf = simBuffer;
|
|
55
|
-
} else if (type === 'hm') {
|
|
56
|
-
subs = hmSubscriptions;
|
|
57
|
-
buf = hmBuffer;
|
|
58
|
-
}
|
|
59
|
-
if (data) {
|
|
60
|
-
buf.push(data);
|
|
61
|
-
}
|
|
62
|
-
buf.forEach((line, index) => {
|
|
63
|
-
Object.keys(subs).forEach(key => {
|
|
64
|
-
const sub = subs[key];
|
|
65
|
-
if (line.match(sub.rx)) {
|
|
66
|
-
sub.cb(line);
|
|
67
|
-
delete subs[key];
|
|
68
|
-
buf.splice(index, 1);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function startHm() {
|
|
75
|
-
hm = cp.spawn(hmCmd, hmArgs);
|
|
76
|
-
hmPipeOut = hm.stdout.pipe(streamSplitter('\n'));
|
|
77
|
-
hmPipeErr = hm.stderr.pipe(streamSplitter('\n'));
|
|
78
|
-
hmPipeOut.on('token', data => {
|
|
79
|
-
console.log('hm', data.toString());
|
|
80
|
-
matchSubscriptions('hm', data.toString());
|
|
81
|
-
});
|
|
82
|
-
hmPipeErr.on('token', data => {
|
|
83
|
-
console.log('hm', data.toString());
|
|
84
|
-
matchSubscriptions('hm', data.toString());
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function startSim() {
|
|
89
|
-
sim = cp.spawn(simCmd, simArgs);
|
|
90
|
-
simPipeOut = sim.stdout.pipe(streamSplitter('\n'));
|
|
91
|
-
simPipeErr = sim.stderr.pipe(streamSplitter('\n'));
|
|
92
|
-
simPipeOut.on('token', data => {
|
|
93
|
-
console.log('sim', data.toString());
|
|
94
|
-
matchSubscriptions('sim', data.toString());
|
|
95
|
-
});
|
|
96
|
-
simPipeErr.on('token', data => {
|
|
97
|
-
console.log('sim', data.toString());
|
|
98
|
-
matchSubscriptions('sim', data.toString());
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function end(code) {
|
|
103
|
-
if (hm.kill) {
|
|
104
|
-
hm.kill();
|
|
105
|
-
}
|
|
106
|
-
if (sim.kill) {
|
|
107
|
-
sim.kill();
|
|
108
|
-
}
|
|
109
|
-
if (typeof code !== 'undefined') {
|
|
110
|
-
process.exit(code);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
process.on('SIGINT', () => {
|
|
115
|
-
end(1);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
process.on('exit', () => {
|
|
119
|
-
end();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('start daemons', () => {
|
|
123
|
-
it('hm-simulator should start without error', function (done) {
|
|
124
|
-
this.timeout(20000);
|
|
125
|
-
subscribe('sim', /binrpc server listening/, data => {
|
|
126
|
-
done();
|
|
127
|
-
});
|
|
128
|
-
startSim();
|
|
129
|
-
|
|
130
|
-
});
|
|
131
|
-
it('hm2mqtt should start without error', function (done) {
|
|
132
|
-
this.timeout(20000);
|
|
133
|
-
subscribe('hm', /hm2mqtt [0-9.]+ starting/, data => {
|
|
134
|
-
done();
|
|
135
|
-
});
|
|
136
|
-
startHm();
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe('hm2mqtt - mqtt connection', () => {
|
|
141
|
-
it('hm2mqtt should connect to the mqtt broker', function (done) {
|
|
142
|
-
this.timeout(12000);
|
|
143
|
-
subscribe('hm', /mqtt connected/, data => {
|
|
144
|
-
done();
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe('hm2mqtt - hm-simulator connection', () => {
|
|
150
|
-
it('hm2mqtt should have rfd devices', function (done) {
|
|
151
|
-
this.timeout(12000);
|
|
152
|
-
subscribe('hm', /rfd got [0-9]+ devices and channels/, data => {
|
|
153
|
-
done();
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
it('hm2mqtt should have hmip devices', function (done) {
|
|
157
|
-
this.timeout(12000);
|
|
158
|
-
subscribe('hm', /hmip got [0-9]+ devices and channels/, data => {
|
|
159
|
-
done();
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
it('hm2mqtt should save paramsetDescriptions (1)', function (done) {
|
|
163
|
-
this.timeout(180000);
|
|
164
|
-
subscribe('hm', /saving paramsetDescriptions/, data => {
|
|
165
|
-
done();
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
it('hm2mqtt should save paramsetDescriptions (2)', function (done) {
|
|
169
|
-
this.timeout(180000);
|
|
170
|
-
subscribe('hm', /saving paramsetDescriptions/, data => {
|
|
171
|
-
done();
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('hm2mqtt - hm-simulator - mqtt', () => {
|
|
177
|
-
it('hm2mqtt should receive a BidCoS-RF:1 PRESS_SHORT event', function (done) {
|
|
178
|
-
this.timeout(12000);
|
|
179
|
-
subscribe('hm', /rpc < event \["hm2mqtt_rfd","BidCoS-RF:1","PRESS_SHORT",true\]/, data => {
|
|
180
|
-
done();
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
it('hm2mqtt should publish a mqtt message on hm/status/BidCoS-RF:1/PRESS_SHORT', function (done) {
|
|
184
|
-
this.timeout(12000);
|
|
185
|
-
subscribe('hm', /mqtt > hm\/status\/BidCoS-RF:1\/PRESS_SHORT/, data => {
|
|
186
|
-
done();
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
it('hm2mqtt should publish a BidCoS-RF:2 PRESS_SHORT event', function (done) {
|
|
190
|
-
this.timeout(12000);
|
|
191
|
-
subscribe('hm', /mqtt > hm\/status\/BidCoS-RF:2\/PRESS_SHORT/, data => {
|
|
192
|
-
done();
|
|
193
|
-
});
|
|
194
|
-
mqtt.publish('hm/set/BidCoS-RF:2/PRESS_SHORT', 'true');
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
setTimeout(() => {
|
|
199
|
-
hm2mqtt.kill();
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}, 30000);
|