iobroker.statechart 0.1.1 → 0.3.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/LICENSE +21 -21
- package/README.md +59 -438
- package/admin/build/tab.js +133 -130
- package/admin/statechart.png +0 -0
- package/admin/{style.css → tab.css} +0 -2
- package/admin/tab_m.html +1 -1
- package/build/lib/chart-analysis.js +367 -0
- package/build/lib/chart-analysis.js.map +7 -0
- package/build/lib/chart-codegen.js +405 -0
- package/build/lib/chart-codegen.js.map +7 -0
- package/build/lib/chart-helpers.js +102 -0
- package/build/lib/chart-helpers.js.map +7 -0
- package/build/lib/chart-model.js +477 -0
- package/build/lib/chart-model.js.map +7 -0
- package/build/lib/chart-runtime.js +1521 -0
- package/build/lib/chart-runtime.js.map +7 -0
- package/build/lib/chart-structure.js +172 -0
- package/build/lib/chart-structure.js.map +7 -0
- package/build/lib/chart-validation.js +372 -0
- package/build/lib/chart-validation.js.map +7 -0
- package/build/main.js +638 -0
- package/build/main.js.map +7 -0
- package/io-package.json +31 -34
- package/package.json +21 -17
- package/admin/i18n/de.json +0 -4
- package/admin/i18n/en.json +0 -4
- package/admin/i18n/es.json +0 -4
- package/admin/i18n/fr.json +0 -4
- package/admin/i18n/it.json +0 -4
- package/admin/i18n/nl.json +0 -4
- package/admin/i18n/pl.json +0 -4
- package/admin/i18n/pt.json +0 -4
- package/admin/i18n/ru.json +0 -4
- package/admin/i18n/uk.json +0 -4
- package/admin/i18n/zh-cn.json +0 -4
- package/admin/jsonConfig.json +0 -19
- package/admin/statechart.svg +0 -38
- package/lib/adapter-config.d.ts +0 -19
- package/lib/chart-codegen.js +0 -436
- package/lib/chart-codegen.test.js +0 -132
- package/lib/chart-helpers.js +0 -74
- package/lib/chart-helpers.test.js +0 -38
- package/lib/chart-model.js +0 -577
- package/lib/chart-model.test.js +0 -189
- package/lib/chart-runtime.js +0 -797
- package/lib/chart-runtime.test.js +0 -699
- package/lib/chart-structure.js +0 -106
- package/lib/chart-validation.js +0 -328
- package/lib/chart-validation.test.js +0 -149
- package/main.js +0 -279
package/main.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
* Created with @iobroker/create-adapter v3.1.5
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// The adapter-core module gives you access to the core ioBroker functions
|
|
8
|
-
// you need to create an adapter
|
|
9
|
-
const utils = require("@iobroker/adapter-core");
|
|
10
|
-
const { ChartRuntime } = require("./lib/chart-runtime");
|
|
11
|
-
|
|
12
|
-
class Statechart extends utils.Adapter {
|
|
13
|
-
/**
|
|
14
|
-
* @param {Partial<utils.AdapterOptions>} [options] - Adapter options
|
|
15
|
-
*/
|
|
16
|
-
constructor(options) {
|
|
17
|
-
super({
|
|
18
|
-
...options,
|
|
19
|
-
name: "statechart",
|
|
20
|
-
});
|
|
21
|
-
/** @type {Map<string, ChartRuntime>} running charts by chart ID */
|
|
22
|
-
this.runtimes = new Map();
|
|
23
|
-
/** @type {Set<string>} foreign states we are subscribed to */
|
|
24
|
-
this.subscribedInputs = new Set();
|
|
25
|
-
/** chart (re)starts are executed one after another */
|
|
26
|
-
this.chartQueue = Promise.resolve();
|
|
27
|
-
/** @type {{ latitude: number; longitude: number } | null} for the astro helper functions */
|
|
28
|
-
this.location = null;
|
|
29
|
-
|
|
30
|
-
this.on("ready", this.onReady.bind(this));
|
|
31
|
-
this.on("stateChange", this.onStateChange.bind(this));
|
|
32
|
-
this.on("objectChange", this.onObjectChange.bind(this));
|
|
33
|
-
this.on("unload", this.onUnload.bind(this));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Is called when databases are connected and adapter received configuration.
|
|
38
|
-
*/
|
|
39
|
-
async onReady() {
|
|
40
|
-
await this.setObjectNotExistsAsync("charts", {
|
|
41
|
-
type: "folder",
|
|
42
|
-
common: { name: "Charts" },
|
|
43
|
-
native: {},
|
|
44
|
-
});
|
|
45
|
-
await this.loadLocation();
|
|
46
|
-
|
|
47
|
-
const objects = await this.getForeignObjectsAsync(`${this.namespace}.charts.*`, "channel");
|
|
48
|
-
for (const [id, obj] of Object.entries(objects)) {
|
|
49
|
-
const chartId = this.getChartId(id);
|
|
50
|
-
if (chartId) {
|
|
51
|
-
// continue where the chart was before the adapter stopped
|
|
52
|
-
await this.startChart(chartId, obj, true);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// the editor saves charts as objects, restart a chart (in its initial state) whenever it changes
|
|
57
|
-
this.subscribeObjects("charts.*");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Reads the location of the ioBroker system settings, used by isDaytime() and astroTime()
|
|
62
|
-
*/
|
|
63
|
-
async loadLocation() {
|
|
64
|
-
const config = await this.getForeignObjectAsync("system.config");
|
|
65
|
-
const latitude = parseFloat(String(config?.common?.latitude ?? ""));
|
|
66
|
-
const longitude = parseFloat(String(config?.common?.longitude ?? ""));
|
|
67
|
-
if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
|
|
68
|
-
this.location = { latitude, longitude };
|
|
69
|
-
} else {
|
|
70
|
-
this.log.info("No location configured in the system settings, isDaytime() and astroTime() do not work");
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Extracts the chart ID from the ID of a chart object
|
|
76
|
-
*
|
|
77
|
-
* @param {string} id - Object ID
|
|
78
|
-
* @returns {string | undefined} undefined if the ID is no chart object
|
|
79
|
-
*/
|
|
80
|
-
getChartId(id) {
|
|
81
|
-
const prefix = `${this.namespace}.charts.`;
|
|
82
|
-
if (!id.startsWith(prefix)) {
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
const chartId = id.substring(prefix.length);
|
|
86
|
-
return chartId.includes(".") ? undefined : chartId;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @param {string} chartId - Chart ID
|
|
91
|
-
* @param {ioBroker.Object | null | undefined} obj - Chart object
|
|
92
|
-
* @param {boolean} [restore] - Continue with the persisted state instead of the initial state
|
|
93
|
-
*/
|
|
94
|
-
async startChart(chartId, obj, restore = false) {
|
|
95
|
-
const chart = obj?.type === "channel" ? obj.native?.chart : undefined;
|
|
96
|
-
if (!chart) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await this.createChartStates(chartId);
|
|
101
|
-
// e.g. a new chart, which was just created in the editor: nothing to execute, but no error either
|
|
102
|
-
const empty = !chart.states?.length;
|
|
103
|
-
if (!chart.enabled || empty) {
|
|
104
|
-
if (empty) {
|
|
105
|
-
this.log.warn(`Chart "${chart.name}" has no states, it is not started`);
|
|
106
|
-
} else {
|
|
107
|
-
this.log.info(`Chart "${chart.name}" is paused`);
|
|
108
|
-
}
|
|
109
|
-
// no state is active, so the editor does not highlight the last active state anymore
|
|
110
|
-
await this.setState(`charts.${chartId}.activeState`, "", true);
|
|
111
|
-
await this.setState(`charts.${chartId}.activeStateIds`, "[]", true);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const runtime = new ChartRuntime(chartId, chart, {
|
|
116
|
-
readState: async id => (await this.getForeignStateAsync(id))?.val ?? null,
|
|
117
|
-
writeState: async (id, val, ack = false) => {
|
|
118
|
-
await this.setForeignStateAsync(id, val, ack);
|
|
119
|
-
},
|
|
120
|
-
sendTo: (instance, command, message) => this.sendTo(instance, command, message),
|
|
121
|
-
getLocation: () => this.location,
|
|
122
|
-
onActiveChange: ({ names, ids }) => {
|
|
123
|
-
this.setState(`charts.${chartId}.activeState`, names, true);
|
|
124
|
-
this.setState(`charts.${chartId}.activeStateIds`, JSON.stringify(ids), true);
|
|
125
|
-
},
|
|
126
|
-
persist: data => {
|
|
127
|
-
this.setState(`charts.${chartId}.snapshot`, JSON.stringify(data), true);
|
|
128
|
-
},
|
|
129
|
-
loadPersisted: async () => {
|
|
130
|
-
const state = await this.getStateAsync(`charts.${chartId}.snapshot`);
|
|
131
|
-
return typeof state?.val === "string" && state.val ? JSON.parse(state.val) : null;
|
|
132
|
-
},
|
|
133
|
-
log: this.log,
|
|
134
|
-
});
|
|
135
|
-
try {
|
|
136
|
-
await runtime.start({ restore });
|
|
137
|
-
} catch (err) {
|
|
138
|
-
this.log.error(`Cannot start chart "${chart.name}": ${err.message}`);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
this.runtimes.set(chartId, runtime);
|
|
142
|
-
this.updateSubscriptions();
|
|
143
|
-
this.log.info(`Chart "${chart.name}" started`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* @param {string} chartId - Chart ID
|
|
148
|
-
*/
|
|
149
|
-
stopChart(chartId) {
|
|
150
|
-
const runtime = this.runtimes.get(chartId);
|
|
151
|
-
if (runtime) {
|
|
152
|
-
runtime.stop();
|
|
153
|
-
this.runtimes.delete(chartId);
|
|
154
|
-
this.updateSubscriptions();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Creates the states which show the current status of a chart
|
|
160
|
-
*
|
|
161
|
-
* @param {string} chartId - Chart ID
|
|
162
|
-
*/
|
|
163
|
-
async createChartStates(chartId) {
|
|
164
|
-
await this.setObjectNotExistsAsync(`charts.${chartId}.activeState`, {
|
|
165
|
-
type: "state",
|
|
166
|
-
common: {
|
|
167
|
-
name: "Active state",
|
|
168
|
-
type: "string",
|
|
169
|
-
role: "text",
|
|
170
|
-
read: true,
|
|
171
|
-
write: false,
|
|
172
|
-
},
|
|
173
|
-
native: {},
|
|
174
|
-
});
|
|
175
|
-
await this.setObjectNotExistsAsync(`charts.${chartId}.snapshot`, {
|
|
176
|
-
type: "state",
|
|
177
|
-
common: {
|
|
178
|
-
name: "Stored state of the chart, restored after a restart of the adapter",
|
|
179
|
-
type: "string",
|
|
180
|
-
role: "json",
|
|
181
|
-
read: true,
|
|
182
|
-
write: false,
|
|
183
|
-
},
|
|
184
|
-
native: {},
|
|
185
|
-
});
|
|
186
|
-
await this.setObjectNotExistsAsync(`charts.${chartId}.activeStateIds`, {
|
|
187
|
-
type: "state",
|
|
188
|
-
common: {
|
|
189
|
-
name: "IDs of all active states",
|
|
190
|
-
type: "string",
|
|
191
|
-
role: "json",
|
|
192
|
-
read: true,
|
|
193
|
-
write: false,
|
|
194
|
-
},
|
|
195
|
-
native: {},
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Subscribes to all inputs of the running charts and unsubscribes from the ones no longer used
|
|
201
|
-
*/
|
|
202
|
-
updateSubscriptions() {
|
|
203
|
-
const needed = new Set([...this.runtimes.values()].flatMap(runtime => runtime.inputStateIds));
|
|
204
|
-
for (const id of needed) {
|
|
205
|
-
if (!this.subscribedInputs.has(id)) {
|
|
206
|
-
this.subscribeForeignStates(id);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
for (const id of this.subscribedInputs) {
|
|
210
|
-
if (!needed.has(id)) {
|
|
211
|
-
this.unsubscribeForeignStates(id);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
this.subscribedInputs = needed;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Is called when adapter shuts down - callback has to be called under any circumstances!
|
|
219
|
-
*
|
|
220
|
-
* @param {() => void} callback - Callback function
|
|
221
|
-
*/
|
|
222
|
-
onUnload(callback) {
|
|
223
|
-
try {
|
|
224
|
-
for (const runtime of this.runtimes.values()) {
|
|
225
|
-
runtime.stop();
|
|
226
|
-
}
|
|
227
|
-
this.runtimes.clear();
|
|
228
|
-
callback();
|
|
229
|
-
} catch (error) {
|
|
230
|
-
this.log.error(`Error during unloading: ${error.message}`);
|
|
231
|
-
callback();
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Is called if a subscribed object changes
|
|
237
|
-
*
|
|
238
|
-
* @param {string} id - Object ID
|
|
239
|
-
* @param {ioBroker.Object | null | undefined} obj - Object
|
|
240
|
-
*/
|
|
241
|
-
onObjectChange(id, obj) {
|
|
242
|
-
const chartId = this.getChartId(id);
|
|
243
|
-
if (!chartId) {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
this.chartQueue = this.chartQueue
|
|
247
|
-
.then(async () => {
|
|
248
|
-
this.stopChart(chartId);
|
|
249
|
-
await this.startChart(chartId, obj);
|
|
250
|
-
})
|
|
251
|
-
.catch(err => this.log.error(`Cannot restart chart ${chartId}: ${err.message}`));
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Is called if a subscribed state changes
|
|
256
|
-
*
|
|
257
|
-
* @param {string} id - State ID
|
|
258
|
-
* @param {ioBroker.State | null | undefined} state - State object
|
|
259
|
-
*/
|
|
260
|
-
onStateChange(id, state) {
|
|
261
|
-
if (!state) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
for (const runtime of this.runtimes.values()) {
|
|
265
|
-
runtime.handleInput(id, state.val, { ack: state.ack });
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (require.main !== module) {
|
|
271
|
-
// Export the constructor in compact mode
|
|
272
|
-
/**
|
|
273
|
-
* @param {Partial<utils.AdapterOptions>} [options] - Adapter options
|
|
274
|
-
*/
|
|
275
|
-
module.exports = options => new Statechart(options);
|
|
276
|
-
} else {
|
|
277
|
-
// otherwise start the instance directly
|
|
278
|
-
new Statechart();
|
|
279
|
-
}
|