iobroker.zwavews 0.0.3

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.
@@ -0,0 +1,84 @@
1
+ /**
2
+ *
3
+ */
4
+ class StatesController {
5
+ /**
6
+ *
7
+ * @param adapter
8
+ * @param deviceCache
9
+ */
10
+ constructor(adapter) {
11
+ this.adapter = adapter;
12
+ }
13
+
14
+ /**
15
+ *
16
+ * @param stateName
17
+ * @param value
18
+ */
19
+ async setStateSafelyAsync(stateName, value) {
20
+ if (value === undefined || value === null) {
21
+ return;
22
+ }
23
+ await this.adapter.setStateAsync(stateName, value, true);
24
+ }
25
+
26
+ /**
27
+ *
28
+ * @param stateName
29
+ * @param value
30
+ */
31
+ async setStateChangedSafelyAsync(stateName, value) {
32
+ if (value === undefined || value === null) {
33
+ return;
34
+ }
35
+ await this.adapter.setStateChangedAsync(stateName, value, true);
36
+ }
37
+
38
+ /**
39
+ *
40
+ * @param deviceCache
41
+ */
42
+ async subscribeAllWritableExistsStates() {
43
+ const writableStates = {};
44
+
45
+ const res = await this.adapter.getObjectViewAsync("system", "state", {
46
+ startkey: "zwaveWS.",
47
+ endkey: "zwaveWS.\u9999",
48
+ });
49
+
50
+ for (const row of res.rows) {
51
+ const obj = row.value;
52
+ if (obj?.common?.write === true) {
53
+ writableStates[obj._id] = {
54
+ mqttId: obj.native.mqttPath,
55
+ write: true,
56
+ subst: null,
57
+ };
58
+ }
59
+ }
60
+
61
+ return writableStates;
62
+ }
63
+
64
+ /**
65
+ *
66
+ */
67
+ async setAllAvailableToFalse() {
68
+ const readyStates = await this.adapter.getStatesAsync("*.ready");
69
+ for (const readyState in readyStates) {
70
+ await this.adapter.setStateChangedAsync(readyState, false, true);
71
+ }
72
+ const availableStates = await this.adapter.getStatesAsync("*.status");
73
+ for (const availableState in availableStates) {
74
+ await this.adapter.setStateChangedAsync(availableState, "unknown", true);
75
+ }
76
+ await this.adapter.setStateChangedAsync('info.zwave_gateway_status', 'offline', true);
77
+
78
+ }
79
+
80
+ }
81
+
82
+ module.exports = {
83
+ StatesController,
84
+ };
package/lib/utils.js ADDED
@@ -0,0 +1,205 @@
1
+ /**
2
+ *
3
+ * @param ba
4
+ */
5
+ function bytesArrayToWordArray(ba) {
6
+ const wa = [];
7
+ for (let i = 0; i < ba.length; i++) {
8
+ wa[(i / 2) | 0] |= ba[i] << (8 * (i % 2));
9
+ }
10
+ return wa;
11
+ }
12
+
13
+ // If the value is greater than 1000, kelvin is assumed.
14
+ // If smaller, it is assumed to be mired.
15
+ /**
16
+ *
17
+ * @param t
18
+ */
19
+ function toMired(t) {
20
+ let miredValue = t;
21
+ if (t > 1000) {
22
+ miredValue = miredKelvinConversion(t);
23
+ }
24
+ return miredValue;
25
+ }
26
+
27
+ /**
28
+ *
29
+ * @param t
30
+ */
31
+ function miredKelvinConversion(t) {
32
+ return Math.round(1000000 / t);
33
+ }
34
+
35
+ /**
36
+ * Converts a decimal number to a hex string with zero-padding
37
+ *
38
+ * @param decimal
39
+ * @param padding
40
+ */
41
+ function decimalToHex(decimal, padding) {
42
+ let hex = Number(decimal).toString(16);
43
+ padding =
44
+ typeof padding === "undefined" || padding === null
45
+ ? (padding = 2)
46
+ : padding;
47
+
48
+ while (hex.length < padding) {
49
+ hex = `0${hex}`;
50
+ }
51
+
52
+ return hex;
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param array
58
+ */
59
+ function clearArray(array) {
60
+ while (array.length > 0) {
61
+ array.pop();
62
+ }
63
+ }
64
+
65
+ /**
66
+ *
67
+ * @param source
68
+ * @param target
69
+ */
70
+ function moveArray(source, target) {
71
+ while (source.length > 0) {
72
+ target.push(source.shift());
73
+ }
74
+ }
75
+
76
+ /**
77
+ *
78
+ * @param item
79
+ */
80
+ function isObject(item) {
81
+ return typeof item === "object" && !Array.isArray(item) && item !== null;
82
+ }
83
+
84
+ /**
85
+ *
86
+ * @param item
87
+ */
88
+ function isJson(item) {
89
+ let value = typeof item !== "string" ? JSON.stringify(item) : item;
90
+ try {
91
+ value = JSON.parse(value);
92
+ } catch (e) {
93
+ return false;
94
+ }
95
+
96
+ return typeof value === "object" && value !== null;
97
+ }
98
+
99
+ /**
100
+ *
101
+ * @param input
102
+ */
103
+ function getLastSegment(input) {
104
+ if (typeof input !== "string") {
105
+ return "";
106
+ }
107
+ const parts = input.split(/[./]/).filter(Boolean);
108
+ return parts.length ? parts[parts.length - 1] : "";
109
+ }
110
+
111
+ /**
112
+ * @param {any} value
113
+ * @returns {boolean}
114
+ */
115
+ function isNumeric(value) {
116
+ if (value === null || value === undefined) {
117
+ return false;
118
+ }
119
+ if (typeof value === "number") {
120
+ return Number.isFinite(value);
121
+ }
122
+ if (typeof value === "string") {
123
+ const s = value.trim();
124
+ if (s === "") {
125
+ return false;
126
+ }
127
+ return /^[-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?$/.test(s);
128
+ }
129
+ return false;
130
+ }
131
+
132
+ /**
133
+ *
134
+ * @param str
135
+ */
136
+ function replaceLastDot(str) {
137
+ const idx = str.lastIndexOf(".");
138
+ return idx >= 0 ? `${str.slice(0, idx)}_${str.slice(idx + 1)}` : str;
139
+ }
140
+ /**
141
+ *
142
+ * @param input
143
+ */
144
+ function formatMQTT(input) {
145
+ if (typeof input !== "string") {
146
+ return "";
147
+ }
148
+ return input.replace(/\./g, "/");
149
+ }
150
+
151
+ /**
152
+ *
153
+ * @param nodeId
154
+ * @param width
155
+ */
156
+ function padNodeId(nodeId, width = 3) {
157
+ return nodeId.replace(/(\d+)$/, (m) => m.padStart(width, "0"));
158
+ }
159
+
160
+ /**
161
+ *
162
+ * @param status
163
+ */
164
+ function getStatusText(status) {
165
+ const nodeStatus = {
166
+ 0: "Unknown",
167
+ 1: "Asleep",
168
+ 2: "Awake",
169
+ 3: "Dead",
170
+ 4: "Alive",
171
+ };
172
+
173
+ return nodeStatus[status] || "Unknown";
174
+ }
175
+
176
+ /**
177
+ *
178
+ * @param nodeIdOriginal
179
+ */
180
+ function formatNodeId(nodeIdOriginal) {
181
+ let nodeId = nodeIdOriginal;
182
+
183
+ if (this.isNumeric(nodeIdOriginal)) {
184
+ nodeId = this.padNodeId(`nodeID_${nodeIdOriginal}`);
185
+ }
186
+ return nodeId;
187
+ }
188
+
189
+ module.exports = {
190
+ bytesArrayToWordArray,
191
+ toMired,
192
+ miredKelvinConversion,
193
+ decimalToHex,
194
+ formatNodeId,
195
+ clearArray,
196
+ moveArray,
197
+ isObject,
198
+ isJson,
199
+ getLastSegment,
200
+ isNumeric,
201
+ replaceLastDot,
202
+ formatMQTT,
203
+ padNodeId,
204
+ getStatusText,
205
+ };
@@ -0,0 +1,131 @@
1
+ const WebSocket = require('ws');
2
+ let wsClient;
3
+ const wsHeartbeatIntervall = 5000;
4
+ const restartTimeout = 1000;
5
+ let ping;
6
+ let pingTimeout;
7
+ let autoRestartTimeout;
8
+
9
+ /**
10
+ *
11
+ */
12
+ class WebsocketController {
13
+ /**
14
+ *
15
+ * @param adapter
16
+ */
17
+ constructor(adapter) {
18
+ this.adapter = adapter;
19
+ }
20
+
21
+ /**
22
+ *
23
+ */
24
+ initWsClient() {
25
+ try {
26
+ let wsURL = `${this.adapter.config.wsScheme}://${this.adapter.config.wsServerIP}:${this.adapter.config.wsServerPort}/api`;
27
+
28
+ if (this.adapter.config.wsTokenEnabled == true) {
29
+ wsURL += `?token=${this.adapter.config.wsToken}`;
30
+ }
31
+
32
+ wsClient = new WebSocket(wsURL, { rejectUnauthorized: false });
33
+
34
+ wsClient.on('open', () => {
35
+ // Send ping to server
36
+ this.sendPingToServer();
37
+ // Start Heartbeat
38
+ this.wsHeartbeat();
39
+ });
40
+
41
+ wsClient.on('pong', () => {
42
+ this.wsHeartbeat();
43
+ });
44
+
45
+ wsClient.on('close', async () => {
46
+ clearTimeout(pingTimeout);
47
+ clearTimeout(ping);
48
+
49
+ if (wsClient.readyState === WebSocket.CLOSED) {
50
+ this.autoRestart();
51
+ }
52
+ });
53
+
54
+ wsClient.on('message', () => {});
55
+
56
+ wsClient.on('error', (err) => {
57
+ this.adapter.log.debug(err);
58
+ });
59
+
60
+ return wsClient;
61
+ } catch (err) {
62
+ this.adapter.log.error(err);
63
+ }
64
+ }
65
+
66
+ /**
67
+ *
68
+ * @param message
69
+ */
70
+ send(message) {
71
+ if (wsClient.readyState !== WebSocket.OPEN) {
72
+ this.adapter.log.warn('Cannot set State, no websocket connection to Zigbee2MQTT!');
73
+ return;
74
+ }
75
+ wsClient.send(message);
76
+ }
77
+
78
+ /**
79
+ *
80
+ */
81
+ sendPingToServer() {
82
+ //this.logDebug('Send ping to server');
83
+ wsClient.ping();
84
+ ping = setTimeout(() => {
85
+ this.sendPingToServer();
86
+ }, wsHeartbeatIntervall);
87
+ }
88
+
89
+ /**
90
+ *
91
+ */
92
+ wsHeartbeat() {
93
+ clearTimeout(pingTimeout);
94
+ pingTimeout = setTimeout(() => {
95
+ this.adapter.log.warn('Websocked connection timed out');
96
+ wsClient.terminate();
97
+ }, wsHeartbeatIntervall + 3000);
98
+ }
99
+
100
+ /**
101
+ *
102
+ */
103
+ async autoRestart() {
104
+ this.adapter.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
105
+ autoRestartTimeout = setTimeout(() => {
106
+ this.adapter.startWebsocket();
107
+ }, restartTimeout);
108
+ }
109
+
110
+ /**
111
+ *
112
+ */
113
+ closeConnection() {
114
+ if (wsClient && wsClient.readyState !== WebSocket.CLOSED) {
115
+ wsClient.close();
116
+ }
117
+ }
118
+
119
+ /**
120
+ *
121
+ */
122
+ async allTimerClear() {
123
+ clearTimeout(pingTimeout);
124
+ clearTimeout(ping);
125
+ clearTimeout(autoRestartTimeout);
126
+ }
127
+ }
128
+
129
+ module.exports = {
130
+ WebsocketController,
131
+ };