iobroker.statechart 0.1.1 → 0.2.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 +130 -21
- package/admin/build/tab.js +132 -130
- package/admin/statechart.png +0 -0
- package/build/lib/chart-codegen.js +338 -0
- package/build/lib/chart-codegen.js.map +7 -0
- package/build/lib/chart-helpers.js +90 -0
- package/build/lib/chart-helpers.js.map +7 -0
- package/build/lib/chart-model.js +353 -0
- package/build/lib/chart-model.js.map +7 -0
- package/build/lib/chart-runtime.js +876 -0
- package/build/lib/chart-runtime.js.map +7 -0
- package/build/lib/chart-structure.js +101 -0
- package/build/lib/chart-structure.js.map +7 -0
- package/build/lib/chart-validation.js +303 -0
- package/build/lib/chart-validation.js.map +7 -0
- package/build/main.js +342 -0
- package/build/main.js.map +7 -0
- package/io-package.json +14 -1
- package/package.json +20 -17
- 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/build/main.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var utils = __toESM(require("@iobroker/adapter-core"));
|
|
25
|
+
var import_chart_runtime = require("./lib/chart-runtime");
|
|
26
|
+
const HISTORY_LENGTH = 200;
|
|
27
|
+
class Statechart extends utils.Adapter {
|
|
28
|
+
/** running charts by chart ID */
|
|
29
|
+
runtimes = /* @__PURE__ */ new Map();
|
|
30
|
+
/** foreign states we are subscribed to */
|
|
31
|
+
subscribedInputs = /* @__PURE__ */ new Set();
|
|
32
|
+
/** chart (re)starts are executed one after another */
|
|
33
|
+
chartQueue = Promise.resolve();
|
|
34
|
+
/** for the astro helper functions */
|
|
35
|
+
location = null;
|
|
36
|
+
/** the last events of each chart by chart ID, newest last, also stored in charts.<chart>.history */
|
|
37
|
+
histories = /* @__PURE__ */ new Map();
|
|
38
|
+
constructor(options = {}) {
|
|
39
|
+
super({
|
|
40
|
+
...options,
|
|
41
|
+
name: "statechart"
|
|
42
|
+
});
|
|
43
|
+
this.on("ready", this.onReady.bind(this));
|
|
44
|
+
this.on("stateChange", this.onStateChange.bind(this));
|
|
45
|
+
this.on("objectChange", this.onObjectChange.bind(this));
|
|
46
|
+
this.on("unload", this.onUnload.bind(this));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Is called when databases are connected and adapter received configuration.
|
|
50
|
+
*/
|
|
51
|
+
async onReady() {
|
|
52
|
+
await this.setObjectNotExistsAsync("charts", {
|
|
53
|
+
type: "folder",
|
|
54
|
+
common: { name: "Charts" },
|
|
55
|
+
native: {}
|
|
56
|
+
});
|
|
57
|
+
await this.loadLocation();
|
|
58
|
+
const objects = await this.getForeignObjectsAsync(`${this.namespace}.charts.*`, "channel");
|
|
59
|
+
for (const [id, obj] of Object.entries(objects)) {
|
|
60
|
+
const chartId = this.getChartId(id);
|
|
61
|
+
if (chartId) {
|
|
62
|
+
await this.startChart(chartId, obj, true);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
this.subscribeObjects("charts.*");
|
|
66
|
+
this.subscribeStates("charts.*.history");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reads the location of the ioBroker system settings, used by isDaytime() and astroTime()
|
|
70
|
+
*/
|
|
71
|
+
async loadLocation() {
|
|
72
|
+
const config = await this.getForeignObjectAsync("system.config");
|
|
73
|
+
const latitude = parseFloat(String(config?.common?.latitude ?? ""));
|
|
74
|
+
const longitude = parseFloat(String(config?.common?.longitude ?? ""));
|
|
75
|
+
if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
|
|
76
|
+
this.location = { latitude, longitude };
|
|
77
|
+
} else {
|
|
78
|
+
this.log.info("No location configured in the system settings, isDaytime() and astroTime() do not work");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extracts the chart ID from the ID of a chart object
|
|
83
|
+
*
|
|
84
|
+
* @param id Object ID
|
|
85
|
+
* @returns undefined if the ID is no chart object
|
|
86
|
+
*/
|
|
87
|
+
getChartId(id) {
|
|
88
|
+
const prefix = `${this.namespace}.charts.`;
|
|
89
|
+
if (!id.startsWith(prefix)) {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
const chartId = id.substring(prefix.length);
|
|
93
|
+
return chartId.includes(".") ? void 0 : chartId;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @param id State ID
|
|
97
|
+
* @returns the chart ID if the state is the history of a chart
|
|
98
|
+
*/
|
|
99
|
+
getHistoryChartId(id) {
|
|
100
|
+
const match = /^charts.([^.]+).history$/.exec(id.substring(this.namespace.length + 1));
|
|
101
|
+
return id.startsWith(`${this.namespace}.`) && match ? match[1] : void 0;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @param chartId Chart ID
|
|
105
|
+
* @param obj Chart object
|
|
106
|
+
* @param restore Continue with the persisted state instead of the initial state
|
|
107
|
+
*/
|
|
108
|
+
async startChart(chartId, obj, restore = false) {
|
|
109
|
+
const chart = obj?.type === "channel" ? obj.native?.chart : void 0;
|
|
110
|
+
if (!chart) {
|
|
111
|
+
this.histories.delete(chartId);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
await this.createChartStates(chartId);
|
|
115
|
+
await this.loadHistory(chartId);
|
|
116
|
+
const empty = !chart.states?.length;
|
|
117
|
+
if (!chart.enabled || empty) {
|
|
118
|
+
if (empty) {
|
|
119
|
+
this.log.warn(`Chart "${chart.name}" has no states, it is not started`);
|
|
120
|
+
} else {
|
|
121
|
+
this.log.info(`Chart "${chart.name}" is paused`);
|
|
122
|
+
}
|
|
123
|
+
await this.setState(`charts.${chartId}.activeState`, "", true);
|
|
124
|
+
await this.setState(`charts.${chartId}.activeStateIds`, "[]", true);
|
|
125
|
+
await this.setState(`charts.${chartId}.values`, "{}", true);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const runtime = new import_chart_runtime.ChartRuntime(chartId, chart, {
|
|
129
|
+
readState: async (id) => (await this.getForeignStateAsync(id))?.val ?? null,
|
|
130
|
+
writeState: async (id, val, ack = false) => {
|
|
131
|
+
await this.setForeignStateAsync(id, val, ack);
|
|
132
|
+
},
|
|
133
|
+
sendTo: (instance, command, message) => this.sendTo(instance, command, message),
|
|
134
|
+
getLocation: () => this.location,
|
|
135
|
+
onActiveChange: ({ names, ids }) => {
|
|
136
|
+
void this.setState(`charts.${chartId}.activeState`, names, true);
|
|
137
|
+
void this.setState(`charts.${chartId}.activeStateIds`, JSON.stringify(ids), true);
|
|
138
|
+
},
|
|
139
|
+
persist: (data) => {
|
|
140
|
+
void this.setState(`charts.${chartId}.snapshot`, JSON.stringify(data), true);
|
|
141
|
+
},
|
|
142
|
+
onValues: (values) => {
|
|
143
|
+
void this.setState(`charts.${chartId}.values`, JSON.stringify(values), true);
|
|
144
|
+
},
|
|
145
|
+
onHistory: (entries) => this.addHistory(chartId, entries),
|
|
146
|
+
loadPersisted: async () => {
|
|
147
|
+
const state = await this.getStateAsync(`charts.${chartId}.snapshot`);
|
|
148
|
+
return typeof state?.val === "string" && state.val ? JSON.parse(state.val) : null;
|
|
149
|
+
},
|
|
150
|
+
log: this.log
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
await runtime.start({ restore });
|
|
154
|
+
} catch (err) {
|
|
155
|
+
this.log.error(`Cannot start chart "${chart.name}": ${err.message}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.runtimes.set(chartId, runtime);
|
|
159
|
+
this.updateSubscriptions();
|
|
160
|
+
this.log.info(`Chart "${chart.name}" started`);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Reads the stored history of a chart, if it is not known yet
|
|
164
|
+
*
|
|
165
|
+
* @param chartId Chart ID
|
|
166
|
+
*/
|
|
167
|
+
async loadHistory(chartId) {
|
|
168
|
+
if (this.histories.has(chartId)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
let history = [];
|
|
172
|
+
try {
|
|
173
|
+
const state = await this.getStateAsync(`charts.${chartId}.history`);
|
|
174
|
+
const parsed = typeof state?.val === "string" && state.val ? JSON.parse(state.val) : [];
|
|
175
|
+
history = Array.isArray(parsed) ? parsed : [];
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
178
|
+
this.histories.set(chartId, history);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Appends events to the history of a chart and stores it
|
|
182
|
+
*
|
|
183
|
+
* @param chartId Chart ID
|
|
184
|
+
* @param entries The new events
|
|
185
|
+
*/
|
|
186
|
+
addHistory(chartId, entries) {
|
|
187
|
+
const history = [...this.histories.get(chartId) || [], ...entries].slice(-HISTORY_LENGTH);
|
|
188
|
+
this.histories.set(chartId, history);
|
|
189
|
+
void this.setState(`charts.${chartId}.history`, JSON.stringify(history), true);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* @param chartId Chart ID
|
|
193
|
+
*/
|
|
194
|
+
stopChart(chartId) {
|
|
195
|
+
const runtime = this.runtimes.get(chartId);
|
|
196
|
+
if (runtime) {
|
|
197
|
+
runtime.stop();
|
|
198
|
+
this.runtimes.delete(chartId);
|
|
199
|
+
this.updateSubscriptions();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Creates the states which show the current status of a chart
|
|
204
|
+
*
|
|
205
|
+
* @param chartId Chart ID
|
|
206
|
+
*/
|
|
207
|
+
async createChartStates(chartId) {
|
|
208
|
+
await this.setObjectNotExistsAsync(`charts.${chartId}.activeState`, {
|
|
209
|
+
type: "state",
|
|
210
|
+
common: {
|
|
211
|
+
name: "Active state",
|
|
212
|
+
type: "string",
|
|
213
|
+
role: "text",
|
|
214
|
+
read: true,
|
|
215
|
+
write: false
|
|
216
|
+
},
|
|
217
|
+
native: {}
|
|
218
|
+
});
|
|
219
|
+
await this.setObjectNotExistsAsync(`charts.${chartId}.snapshot`, {
|
|
220
|
+
type: "state",
|
|
221
|
+
common: {
|
|
222
|
+
name: "Stored state of the chart, restored after a restart of the adapter",
|
|
223
|
+
type: "string",
|
|
224
|
+
role: "json",
|
|
225
|
+
read: true,
|
|
226
|
+
write: false
|
|
227
|
+
},
|
|
228
|
+
native: {}
|
|
229
|
+
});
|
|
230
|
+
await this.setObjectNotExistsAsync(`charts.${chartId}.activeStateIds`, {
|
|
231
|
+
type: "state",
|
|
232
|
+
common: {
|
|
233
|
+
name: "IDs of all active states",
|
|
234
|
+
type: "string",
|
|
235
|
+
role: "json",
|
|
236
|
+
read: true,
|
|
237
|
+
write: false
|
|
238
|
+
},
|
|
239
|
+
native: {}
|
|
240
|
+
});
|
|
241
|
+
await this.setObjectNotExistsAsync(`charts.${chartId}.values`, {
|
|
242
|
+
type: "state",
|
|
243
|
+
common: {
|
|
244
|
+
name: "Current values of all variables",
|
|
245
|
+
type: "string",
|
|
246
|
+
role: "json",
|
|
247
|
+
read: true,
|
|
248
|
+
write: false
|
|
249
|
+
},
|
|
250
|
+
native: {}
|
|
251
|
+
});
|
|
252
|
+
await this.setObjectNotExistsAsync(`charts.${chartId}.history`, {
|
|
253
|
+
type: "state",
|
|
254
|
+
common: {
|
|
255
|
+
name: `Last ${HISTORY_LENGTH} events: starts and transitions taken, write anything to clear it`,
|
|
256
|
+
type: "string",
|
|
257
|
+
role: "json",
|
|
258
|
+
read: true,
|
|
259
|
+
write: true
|
|
260
|
+
},
|
|
261
|
+
native: {}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Subscribes to all inputs of the running charts and unsubscribes from the ones no longer used
|
|
266
|
+
*/
|
|
267
|
+
updateSubscriptions() {
|
|
268
|
+
const needed = new Set([...this.runtimes.values()].flatMap((runtime) => runtime.inputStateIds));
|
|
269
|
+
for (const id of needed) {
|
|
270
|
+
if (!this.subscribedInputs.has(id)) {
|
|
271
|
+
this.subscribeForeignStates(id);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
for (const id of this.subscribedInputs) {
|
|
275
|
+
if (!needed.has(id)) {
|
|
276
|
+
this.unsubscribeForeignStates(id);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
this.subscribedInputs = needed;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Is called when adapter shuts down - callback has to be called under any circumstances!
|
|
283
|
+
*
|
|
284
|
+
* @param callback Callback function
|
|
285
|
+
*/
|
|
286
|
+
onUnload(callback) {
|
|
287
|
+
try {
|
|
288
|
+
for (const runtime of this.runtimes.values()) {
|
|
289
|
+
runtime.stop();
|
|
290
|
+
}
|
|
291
|
+
this.runtimes.clear();
|
|
292
|
+
callback();
|
|
293
|
+
} catch (error) {
|
|
294
|
+
this.log.error(`Error during unloading: ${error.message}`);
|
|
295
|
+
callback();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Is called if a subscribed object changes
|
|
300
|
+
*
|
|
301
|
+
* @param id Object ID
|
|
302
|
+
* @param obj Object
|
|
303
|
+
*/
|
|
304
|
+
onObjectChange(id, obj) {
|
|
305
|
+
const chartId = this.getChartId(id);
|
|
306
|
+
if (!chartId) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
this.chartQueue = this.chartQueue.then(async () => {
|
|
310
|
+
this.stopChart(chartId);
|
|
311
|
+
await this.startChart(chartId, obj);
|
|
312
|
+
}).catch((err) => this.log.error(`Cannot restart chart ${chartId}: ${err.message}`));
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Is called if a subscribed state changes
|
|
316
|
+
*
|
|
317
|
+
* @param id State ID
|
|
318
|
+
* @param state State object
|
|
319
|
+
*/
|
|
320
|
+
onStateChange(id, state) {
|
|
321
|
+
if (!state) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const historyOf = this.getHistoryChartId(id);
|
|
325
|
+
if (historyOf) {
|
|
326
|
+
if (!state.ack) {
|
|
327
|
+
this.histories.set(historyOf, []);
|
|
328
|
+
void this.setState(`charts.${historyOf}.history`, "[]", true);
|
|
329
|
+
}
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
for (const runtime of this.runtimes.values()) {
|
|
333
|
+
runtime.handleInput(id, state.val, { ack: state.ack });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (require.main !== module) {
|
|
338
|
+
module.exports = (options) => new Statechart(options);
|
|
339
|
+
} else {
|
|
340
|
+
(() => new Statechart())();
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/main.ts"],
|
|
4
|
+
"sourcesContent": ["/*\n * Created with @iobroker/create-adapter v3.1.5\n */\n\n// The adapter-core module gives you access to the core ioBroker functions\n// you need to create an adapter\nimport * as utils from \"@iobroker/adapter-core\";\nimport type { Chart } from \"./lib/chart-model\";\nimport { ChartRuntime } from \"./lib/chart-runtime\";\nimport type { HistoryEntry, Location } from \"./lib/chart-runtime\";\n\n/** Maximum number of entries in the history of a chart (state charts.<chart>.history) */\nconst HISTORY_LENGTH = 200;\n\nclass Statechart extends utils.Adapter {\n /** running charts by chart ID */\n private runtimes = new Map<string, ChartRuntime>();\n /** foreign states we are subscribed to */\n private subscribedInputs = new Set<string>();\n /** chart (re)starts are executed one after another */\n private chartQueue = Promise.resolve();\n /** for the astro helper functions */\n private location: Location | null = null;\n /** the last events of each chart by chart ID, newest last, also stored in charts.<chart>.history */\n private histories = new Map<string, HistoryEntry[]>();\n\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"statechart\",\n });\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"stateChange\", this.onStateChange.bind(this));\n this.on(\"objectChange\", this.onObjectChange.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n }\n\n /**\n * Is called when databases are connected and adapter received configuration.\n */\n private async onReady(): Promise<void> {\n await this.setObjectNotExistsAsync(\"charts\", {\n type: \"folder\",\n common: { name: \"Charts\" },\n native: {},\n });\n await this.loadLocation();\n\n const objects = await this.getForeignObjectsAsync(`${this.namespace}.charts.*`, \"channel\");\n for (const [id, obj] of Object.entries(objects)) {\n const chartId = this.getChartId(id);\n if (chartId) {\n // continue where the chart was before the adapter stopped\n await this.startChart(chartId, obj, true);\n }\n }\n\n // the editor saves charts as objects, restart a chart (in its initial state) whenever it changes\n this.subscribeObjects(\"charts.*\");\n // the editor clears the history by writing to it\n this.subscribeStates(\"charts.*.history\");\n }\n\n /**\n * Reads the location of the ioBroker system settings, used by isDaytime() and astroTime()\n */\n private async loadLocation(): Promise<void> {\n const config = await this.getForeignObjectAsync(\"system.config\");\n const latitude = parseFloat(String(config?.common?.latitude ?? \"\"));\n const longitude = parseFloat(String(config?.common?.longitude ?? \"\"));\n if (Number.isFinite(latitude) && Number.isFinite(longitude)) {\n this.location = { latitude, longitude };\n } else {\n this.log.info(\"No location configured in the system settings, isDaytime() and astroTime() do not work\");\n }\n }\n\n /**\n * Extracts the chart ID from the ID of a chart object\n *\n * @param id Object ID\n * @returns undefined if the ID is no chart object\n */\n private getChartId(id: string): string | undefined {\n const prefix = `${this.namespace}.charts.`;\n if (!id.startsWith(prefix)) {\n return undefined;\n }\n const chartId = id.substring(prefix.length);\n return chartId.includes(\".\") ? undefined : chartId;\n }\n\n /**\n * @param id State ID\n * @returns the chart ID if the state is the history of a chart\n */\n private getHistoryChartId(id: string): string | undefined {\n const match = /^charts.([^.]+).history$/.exec(id.substring(this.namespace.length + 1));\n return id.startsWith(`${this.namespace}.`) && match ? match[1] : undefined;\n }\n\n /**\n * @param chartId Chart ID\n * @param obj Chart object\n * @param restore Continue with the persisted state instead of the initial state\n */\n private async startChart(chartId: string, obj: ioBroker.Object | null | undefined, restore = false): Promise<void> {\n const chart: Chart | undefined = obj?.type === \"channel\" ? obj.native?.chart : undefined;\n if (!chart) {\n // e.g. the chart was deleted\n this.histories.delete(chartId);\n return;\n }\n\n await this.createChartStates(chartId);\n await this.loadHistory(chartId);\n // e.g. a new chart, which was just created in the editor: nothing to execute, but no error either\n const empty = !chart.states?.length;\n if (!chart.enabled || empty) {\n if (empty) {\n this.log.warn(`Chart \"${chart.name}\" has no states, it is not started`);\n } else {\n this.log.info(`Chart \"${chart.name}\" is paused`);\n }\n // no state is active, so the editor does not highlight the last active state anymore\n await this.setState(`charts.${chartId}.activeState`, \"\", true);\n await this.setState(`charts.${chartId}.activeStateIds`, \"[]\", true);\n // the editor shows no values of a chart which does not run\n await this.setState(`charts.${chartId}.values`, \"{}\", true);\n return;\n }\n\n const runtime = new ChartRuntime(chartId, chart, {\n readState: async id => (await this.getForeignStateAsync(id))?.val ?? null,\n writeState: async (id, val, ack = false) => {\n await this.setForeignStateAsync(id, val, ack);\n },\n sendTo: (instance, command, message) => this.sendTo(instance, command, message),\n getLocation: () => this.location,\n onActiveChange: ({ names, ids }) => {\n void this.setState(`charts.${chartId}.activeState`, names, true);\n void this.setState(`charts.${chartId}.activeStateIds`, JSON.stringify(ids), true);\n },\n persist: data => {\n void this.setState(`charts.${chartId}.snapshot`, JSON.stringify(data), true);\n },\n onValues: values => {\n void this.setState(`charts.${chartId}.values`, JSON.stringify(values), true);\n },\n onHistory: entries => this.addHistory(chartId, entries),\n loadPersisted: async () => {\n const state = await this.getStateAsync(`charts.${chartId}.snapshot`);\n return typeof state?.val === \"string\" && state.val ? JSON.parse(state.val) : null;\n },\n log: this.log,\n });\n try {\n await runtime.start({ restore });\n } catch (err) {\n this.log.error(`Cannot start chart \"${chart.name}\": ${err.message}`);\n return;\n }\n this.runtimes.set(chartId, runtime);\n this.updateSubscriptions();\n this.log.info(`Chart \"${chart.name}\" started`);\n }\n\n /**\n * Reads the stored history of a chart, if it is not known yet\n *\n * @param chartId Chart ID\n */\n private async loadHistory(chartId: string): Promise<void> {\n if (this.histories.has(chartId)) {\n return;\n }\n let history: HistoryEntry[] = [];\n try {\n const state = await this.getStateAsync(`charts.${chartId}.history`);\n const parsed = typeof state?.val === \"string\" && state.val ? JSON.parse(state.val) : [];\n history = Array.isArray(parsed) ? parsed : [];\n } catch {\n // an invalid history is replaced by a new one\n }\n this.histories.set(chartId, history);\n }\n\n /**\n * Appends events to the history of a chart and stores it\n *\n * @param chartId Chart ID\n * @param entries The new events\n */\n private addHistory(chartId: string, entries: HistoryEntry[]): void {\n const history = [...(this.histories.get(chartId) || []), ...entries].slice(-HISTORY_LENGTH);\n this.histories.set(chartId, history);\n void this.setState(`charts.${chartId}.history`, JSON.stringify(history), true);\n }\n\n /**\n * @param chartId Chart ID\n */\n private stopChart(chartId: string): void {\n const runtime = this.runtimes.get(chartId);\n if (runtime) {\n runtime.stop();\n this.runtimes.delete(chartId);\n this.updateSubscriptions();\n }\n }\n\n /**\n * Creates the states which show the current status of a chart\n *\n * @param chartId Chart ID\n */\n private async createChartStates(chartId: string): Promise<void> {\n await this.setObjectNotExistsAsync(`charts.${chartId}.activeState`, {\n type: \"state\",\n common: {\n name: \"Active state\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n },\n native: {},\n });\n await this.setObjectNotExistsAsync(`charts.${chartId}.snapshot`, {\n type: \"state\",\n common: {\n name: \"Stored state of the chart, restored after a restart of the adapter\",\n type: \"string\",\n role: \"json\",\n read: true,\n write: false,\n },\n native: {},\n });\n await this.setObjectNotExistsAsync(`charts.${chartId}.activeStateIds`, {\n type: \"state\",\n common: {\n name: \"IDs of all active states\",\n type: \"string\",\n role: \"json\",\n read: true,\n write: false,\n },\n native: {},\n });\n await this.setObjectNotExistsAsync(`charts.${chartId}.values`, {\n type: \"state\",\n common: {\n name: \"Current values of all variables\",\n type: \"string\",\n role: \"json\",\n read: true,\n write: false,\n },\n native: {},\n });\n await this.setObjectNotExistsAsync(`charts.${chartId}.history`, {\n type: \"state\",\n common: {\n name: `Last ${HISTORY_LENGTH} events: starts and transitions taken, write anything to clear it`,\n type: \"string\",\n role: \"json\",\n read: true,\n write: true,\n },\n native: {},\n });\n }\n\n /**\n * Subscribes to all inputs of the running charts and unsubscribes from the ones no longer used\n */\n private updateSubscriptions(): void {\n const needed = new Set([...this.runtimes.values()].flatMap(runtime => runtime.inputStateIds));\n for (const id of needed) {\n if (!this.subscribedInputs.has(id)) {\n this.subscribeForeignStates(id);\n }\n }\n for (const id of this.subscribedInputs) {\n if (!needed.has(id)) {\n this.unsubscribeForeignStates(id);\n }\n }\n this.subscribedInputs = needed;\n }\n\n /**\n * Is called when adapter shuts down - callback has to be called under any circumstances!\n *\n * @param callback Callback function\n */\n private onUnload(callback: () => void): void {\n try {\n for (const runtime of this.runtimes.values()) {\n runtime.stop();\n }\n this.runtimes.clear();\n callback();\n } catch (error) {\n this.log.error(`Error during unloading: ${error.message}`);\n callback();\n }\n }\n\n /**\n * Is called if a subscribed object changes\n *\n * @param id Object ID\n * @param obj Object\n */\n private onObjectChange(id: string, obj: ioBroker.Object | null | undefined): void {\n const chartId = this.getChartId(id);\n if (!chartId) {\n return;\n }\n this.chartQueue = this.chartQueue\n .then(async () => {\n this.stopChart(chartId);\n await this.startChart(chartId, obj);\n })\n .catch(err => this.log.error(`Cannot restart chart ${chartId}: ${err.message}`));\n }\n\n /**\n * Is called if a subscribed state changes\n *\n * @param id State ID\n * @param state State object\n */\n private onStateChange(id: string, state: ioBroker.State | null | undefined): void {\n if (!state) {\n return;\n }\n const historyOf = this.getHistoryChartId(id);\n if (historyOf) {\n // written by the editor (\"clear\"), the adapter's own updates are acknowledged\n if (!state.ack) {\n this.histories.set(historyOf, []);\n void this.setState(`charts.${historyOf}.history`, \"[]\", true);\n }\n return;\n }\n for (const runtime of this.runtimes.values()) {\n runtime.handleInput(id, state.val, { ack: state.ack });\n }\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new Statechart(options);\n} else {\n // otherwise start the instance directly\n (() => new Statechart())();\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "statechart",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.2.0": {
|
|
7
|
+
"en": "the adapter and the editor are written in TypeScript now, no changes of the behavior\nlive values of the variables in the editor\ntaken transitions light up in the editor\nhistory of the starts and the transitions taken (tab \"History\", state `charts.<chart>.history`)\na chart wakes up once right after the start, so transitions without label are taken without waiting for a change of an input\nchart option \"super step\": take transitions until none is valid anymore\nthe arrows of transitions start and end where the states face each other (floating edges), their ends can be dragged along the border of the states or onto another state to move the transition there\nchange detection: `hasChanged`, `hasChangedFrom` and `hasChangedTo`, e.g. `hasChangedTo(button, true)` for edges\nsuncalc 2: `isDaytime()` is true all day during the midnight sun, `astroTime()` reports events which do not happen on a day (polar regions)",
|
|
8
|
+
"de": "Adapter und Editor sind jetzt in TypeScript geschrieben, das Verhalten bleibt gleich\nLive-Werte der Variablen im Editor\nGenommene Übergänge leuchten im Editor auf\nVerlauf der Starts und der genommenen Übergänge (Reiter „Verlauf“, Datenpunkt `charts.<chart>.history`)\nEin Chart wacht direkt nach dem Start einmal auf, Übergänge ohne Beschriftung werden also genommen, ohne auf die Änderung eines Eingangs zu warten\nChart-Option „Super-Step“: weiterschalten, bis kein Übergang mehr gilt\nDie Pfeile der Übergänge beginnen und enden dort, wo sich die Zustände gegenüberliegen (Floating Edges). Ihre Enden lassen sich am Rand der Zustände verschieben oder auf einen anderen Zustand ziehen, um den Übergang dorthin zu verlegen\nÄnderungserkennung: `hasChanged`, `hasChangedFrom` und `hasChangedTo`, z.B. `hasChangedTo(taster, true)` für Flanken\nsuncalc 2: `isDaytime()` ist während der Mitternachtssonne den ganzen Tag wahr, `astroTime()` meldet Ereignisse, die an einem Tag nicht stattfinden (Polargebiete)",
|
|
9
|
+
"ru": "адаптер и редактор теперь написаны на TypeScript, никаких изменений в поведении\nживые значения переменных в редакторе\nснятые переходы загораются в редакторе\nистория стартов и сделанных переходов (вкладка «История», состояние `графики). <chart>.история`)\nграфик просыпается один раз сразу после старта, поэтому переходы без метки выполняются, не дожидаясь изменения входных данных\nопция диаграммы «super step»: делайте переходы до тех пор, пока ни один из них не станет действительным\nстрелки переходов начинаются и заканчиваются там, где состояния обращены друг к другу (плавающие ребра), их концы можно перетащить вдоль границы состояний или в другое состояние, чтобы переместить переход туда\nобнаружение изменений: «изменилось», «изменилось с» и «изменилось на», например, «изменилось на» (кнопка, истина) для ребер\nsuncalc 2: `isDaytime () `действует весь день при полуночном солнце, `AstroTime ()` сообщает о событиях, которые не происходят в один день (полярные регионы)",
|
|
10
|
+
"pt": "o adaptador e o editor agora são escritos em TypeScript, sem mudanças\nvalores reais das variáveis no editor\ntransições feitas acendem no editor\nhistórico dos inícios e transições (aba “Histórico”, estado `charts. <chart>.história`)\num gráfico acorda uma vez logo após o início, então transições sem rótulo são feitas sem a troca de uma entrada\nopção de gráfico “super step”: faça transições até não valer mais\nas setas das transições começam e terminam onde os estados se enfrentam (bordas flutuantes) e podem ser arrastadas pela borda dos estados ou para outro estado para mover a transição até lá\ndetecção de mudanças: `hasChanged`, `hasChangedFrom` e `hasChangedTo`, ex: `hasChangedTo (button, true) `para bordas\nsuncalc 2: `isDayTime () `vale todo o dia do sol da meia-noite, `astroTime ()` relata eventos que não ocorrem em um dia (polares)",
|
|
11
|
+
"nl": "de adapter en de editor zijn nu in TypeScript geschreven, geen wijzigingen in het gedrag\nactuele waarden van de variabelen in de editor\ngenomen overgangen lichten op in de editor\ngeschiedenis van de starts en de gemaakte overgangen (tab „Geschiedenis”, status `diagrammen. <chart>.geschiedenis`)\neen grafiek wordt één keer wakker, direct na de start, zodat overgangen zonder label worden gemaakt zonder te wachten op een wijziging van een invoer\ngrafiekoptie „superstap”: overgangen uitvoeren tot ze niet meer geldig zijn\nde pijlen van overgangen beginnen en eindigen waar de staten tegenover elkaar staan (zwevende randen), waarvan de uiteinden langs de rand van de staten kunnen worden gesleept of naar een andere staat om de overgang daarheen te verplaatsen\ndetectie van wijzigingen: `HasChanged`, `HasChangedFrom` en `HasChangedTo`, bijvoorbeeld `HasChangedTo (button, true) `voor randen\nsuncalc 2: `IsDaytime () `geldt de hele dag tijdens de middernacht; `AstroTime ()` rapporteert gebeurtenissen die zich niet op een dag voordoen (poolgebieden)",
|
|
12
|
+
"fr": "l'adaptateur et l'éditeur sont écrits en TypeScript maintenant, rien à voir\nvaleurs en direct des variables en éditeur\nles transitions prises s'allument\nhistorique des départs et des transitions (onglet « Historique », état « charts »). <chart>.history`)\nun graphique apparaît juste après le début, donc les transitions sans étiquette se font sans attendre de modification\noption graphique « super étape » : faites des transitions jusqu'à ce que rien ne soit valide\nles flèches des transitions commencent et se terminent là où les états se font face (bords flottants), leur fin peut être glissée le long de la frontière des états ou sur un autre état pour y déplacer la transition\ndétection des changements : hasChanged`, `hasChangedFrom` et `hasChangedTo`, par exemple `hasChangedTo (button, true) `pour les bords\nsuncalc 2 : `isDaytime () `est vrai toute la journée à minuit, `AstroTime ()` signale des événements qui ne se produisent pas un jour (régions polaires)",
|
|
13
|
+
"it": "l'adattatore e l'editor sono scritti in TypeScript ora, nessun cambio\nvalori live delle variabili in editor\nle transizioni prese si accendono\ncronologia delle partenze e transizioni (scheda «Cronologia», «grafici»). <chart>.storia`)\nun grafico si attiva subito dopo l'inizio, quindi le transizioni senza etichetta avvengono senza cambiare input\nopzione grafica «super step»: transizioni finché non è più valida\nle frecce iniziano e finiscono dove gli stati si fronteggiano (bordi mobili), le loro estremità possono essere trascinate lungo i confini degli stati o in un altro stato\nrilevamento modifiche: `hasChanged`, `hasChangedFrom` e `hasChangedTo (button, true) `per i bordi\nsuncalc 2: `isDayTime () `è vero tutto il giorno a mezzanotte, `astroTime ()` segnala eventi non diurni (regioni polari)",
|
|
14
|
+
"es": "el adaptador y el editor ahora están escritos en TypeScript, sin cambios de comportamiento\nvalores en vivo de las variables en el editor\nlas transiciones tomadas se iluminan en el editor\nhistorial de los inicios y las transiciones tomadas (pestaña «Historia»), listas estatales. <chart>.historia`)\nun gráfico se activa una vez al principio, por lo que las transiciones sin etiqueta se hacen sin esperar a que se cambie la entrada\nopción de gráfico «superpaso»: haga transiciones hasta que ninguna sea válida\nlas flechas de las transiciones comienzan y terminan donde los estados están uno frente al otro (bordes flotantes), sus extremos se pueden arrastrar a lo largo de la frontera de los estados o a otro estado para moverla\ndetección de cambios: `HasChanged`, `HasChangedFrom` y `HasChangedTo`, p. ej., `hasChangedTo (button, true) `para bordes\nsuncalc 2: `isDaytime () `dura todo el día durante el sol de medianoche, `AstroTime ()` informa de eventos que no ocurren en un día (regiones polares)",
|
|
15
|
+
"pl": "adapter i edytor są teraz zapisane w TypeScript, bez zmian zachowania\nwartości na żywo zmiennych w edytorze\nwykonane przejścia świecą się w edytorze\nhistoria startów i wykonanych przejść (zakładka „Historia”, wykresy stanów. <chart>.historia`)\nwykres budzi się raz zaraz po rozpoczęciu, więc przejścia bez etykiety są wykonywane bez czekania na zmianę danych wejściowych\nopcja wykresu „super krok”: wykonuj przejścia, dopóki żaden nie będzie już ważny\nstrzałki przejść zaczynają się i kończą tam, gdzie stany są zwrócone do siebie (pływające krawędzie), ich końce można przeciągnąć wzdłuż granicy stanów lub do innego stanu, aby przenieść tam przejście\nwykrywanie zmian: `hasChanged`, `HasChangedFrom` i `HasChangedTo`, np. `HasChangedTo (przycisk, true) `dla krawędzi\nsuncalc 2: `IsDaytime () `jest prawdziwy przez cały dzień podczas słońca o północy, `AstroTime ()` zgłasza zdarzenia, które nie zdarzają się w ciągu dnia (regiony polarne)",
|
|
16
|
+
"uk": "адаптер і редактор тепер написані в TypeScript, ніяких змін у поведінці\nживі значення змінних в редакторі\nзроблені переходи загоряються в редакторі\nісторія стартів і пройдених переходів (вкладка «Історія», діаграми стану. <chart>.історія`)\nдіаграма прокидається один раз відразу після початку, тому переходи без мітки приймаються без очікування зміни вхідних даних\nпараметр діаграми «суперкрок»: робіть переходи, поки жоден більше не буде дійсним\nстрілки переходів починаються і закінчуються там, де стани звернені один до одного (плаваючі краї), їх кінці можна перетягнути вздовж кордону станів або в інший стан, щоб переміщати перехід туди\nвиявлення змін: `hasChanged`, `hasChangedFrom` і `hasChangedTo`, наприклад `hasChangedTo (кнопка, true) `для країв\nsuncalc 2: `isDaytime () `вірний цілий день під час опівнічного сонця, `AstroTime ()` повідомляє про події, які не відбуваються в день (полярні регіони)",
|
|
17
|
+
"zh-cn": "适配器和编辑器现在是用 TypeScript 编写的,行为没有变化\n编辑器中变量的实时值\n已拍摄的过渡效果在编辑器中亮起\n开始和过渡的历史记录(“历史” 选项卡,显示 “图表”。<chart>.history`)\n图表在开始后立即醒来一次,因此无需等待输入更改即可进行没有标签的过渡\n图表选项 “超级步骤”:进行过渡,直到所有选项都不再有效\n过渡的箭头在状态相互面对的地方(浮动边缘)开始和结束,它们的末端可以沿着状态的边界拖动或拖到另一个状态上以将过渡移动到那里\n变化检测:`hasChanged`、`hasChangedFrom` 和 `hasChangedTo`,例如用于边缘的 `hasChangedTo (button, true) `\nsuncalc 2: `isDayTime () `在午夜阳光下整天都是正确的,`AstroTime () `报告一天不发生的事件(极地区域)"
|
|
18
|
+
},
|
|
6
19
|
"0.1.1": {
|
|
7
20
|
"en": "renamed from \"state-machine\" to \"statechart\", because npm does not accept the old name (too similar to an existing package)",
|
|
8
21
|
"de": "umbenannt von „state-machine“ in „statechart“, weil npm den alten Namen nicht akzeptiert (zu ähnlich einem Paket)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.statechart",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Statecharts (hierarchical state machines) with a graphical editor for ioBroker",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igge",
|
|
@@ -27,22 +27,22 @@
|
|
|
27
27
|
"@iobroker/adapter-core": "^3.4.3",
|
|
28
28
|
"acorn": "^8.19.0",
|
|
29
29
|
"acorn-walk": "^8.3.5",
|
|
30
|
-
"suncalc": "^1.
|
|
31
|
-
"xstate": "^5.33.
|
|
30
|
+
"suncalc": "^2.1.1",
|
|
31
|
+
"xstate": "^5.33.3"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@alcalzone/release-script": "^5.2.1",
|
|
35
35
|
"@alcalzone/release-script-plugin-iobroker": "^5.2.0",
|
|
36
36
|
"@alcalzone/release-script-plugin-license": "^5.2.2",
|
|
37
37
|
"@alcalzone/release-script-plugin-manual-review": "^5.2.0",
|
|
38
|
-
"@codemirror/autocomplete": "^6.20.
|
|
38
|
+
"@codemirror/autocomplete": "^6.20.4",
|
|
39
39
|
"@codemirror/commands": "^6.11.1",
|
|
40
40
|
"@codemirror/lang-javascript": "^6.2.5",
|
|
41
|
-
"@codemirror/language": "^6.
|
|
41
|
+
"@codemirror/language": "^6.13.1",
|
|
42
42
|
"@codemirror/lint": "^6.9.7",
|
|
43
43
|
"@codemirror/state": "^6.7.6",
|
|
44
44
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
45
|
-
"@codemirror/view": "^6.43.
|
|
45
|
+
"@codemirror/view": "^6.43.15",
|
|
46
46
|
"@emotion/react": "^11.14.0",
|
|
47
47
|
"@emotion/styled": "^11.14.1",
|
|
48
48
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
@@ -61,36 +61,39 @@
|
|
|
61
61
|
"patch-package": "^8.0.1",
|
|
62
62
|
"react": "^18.3.1",
|
|
63
63
|
"react-dom": "^18.3.1",
|
|
64
|
+
"tsx": "^4.23.15",
|
|
64
65
|
"typescript": "~5.9.3"
|
|
65
66
|
},
|
|
66
|
-
"main": "main.js",
|
|
67
|
+
"main": "build/main.js",
|
|
67
68
|
"files": [
|
|
68
69
|
"admin{,/!(src)/**}/!(tsconfig|tsconfig.*|.eslintrc).{json,json5}",
|
|
69
70
|
"admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}",
|
|
70
71
|
"admin/build/*.{js,css}",
|
|
71
|
-
"
|
|
72
|
+
"build/",
|
|
72
73
|
"www/",
|
|
73
74
|
"io-package.json",
|
|
74
|
-
"LICENSE"
|
|
75
|
-
"main.js"
|
|
75
|
+
"LICENSE"
|
|
76
76
|
],
|
|
77
77
|
"scripts": {
|
|
78
|
-
"prebuild": "rimraf admin/build",
|
|
79
|
-
"build": "build-adapter
|
|
80
|
-
"watch": "build-adapter
|
|
78
|
+
"prebuild": "rimraf build admin/build",
|
|
79
|
+
"build": "build-adapter all --typescriptCompileTarget node22",
|
|
80
|
+
"watch": "build-adapter all --watch --typescriptCompileTarget node22",
|
|
81
|
+
"prebuild:ts": "rimraf build",
|
|
82
|
+
"build:ts": "build-adapter ts --typescriptCompileTarget node22",
|
|
83
|
+
"watch:ts": "build-adapter ts --watch --typescriptCompileTarget node22",
|
|
81
84
|
"prebuild:react": "rimraf admin/build",
|
|
82
85
|
"build:react": "build-adapter react",
|
|
83
86
|
"watch:react": "build-adapter react --watch",
|
|
84
|
-
"test:
|
|
87
|
+
"test:ts": "mocha --config test/mocharc.custom.json \"{src,admin/src}/**/*.test.ts\"",
|
|
85
88
|
"test:package": "mocha test/package --exit",
|
|
86
89
|
"test:integration": "mocha test/integration --exit",
|
|
87
|
-
"test": "npm run test:
|
|
88
|
-
"check": "tsc --noEmit -p tsconfig.
|
|
90
|
+
"test": "npm run test:ts && npm run test:package",
|
|
91
|
+
"check": "tsc --noEmit && tsc --noEmit -p admin/tsconfig.json",
|
|
89
92
|
"lint": "eslint -c eslint.config.mjs .",
|
|
90
93
|
"translate": "translate-adapter",
|
|
91
94
|
"release": "release-script",
|
|
92
95
|
"dev-server": "patch-package && dev-server",
|
|
93
|
-
"upload:tab": "npm run build && node tools/upload-admin.js"
|
|
96
|
+
"upload:tab": "npm run build:react && node tools/upload-admin.js"
|
|
94
97
|
},
|
|
95
98
|
"bugs": {
|
|
96
99
|
"url": "https://github.com/IggePop/ioBroker.statechart/issues"
|
package/lib/adapter-config.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// This file extends the AdapterConfig type from "@iobroker/types"
|
|
2
|
-
// using the actual properties present in io-package.json
|
|
3
|
-
// in order to provide typings for adapter.config properties
|
|
4
|
-
|
|
5
|
-
import { native } from "../io-package.json";
|
|
6
|
-
|
|
7
|
-
type _AdapterConfig = typeof native;
|
|
8
|
-
|
|
9
|
-
// Augment the globally declared type ioBroker.AdapterConfig
|
|
10
|
-
declare global {
|
|
11
|
-
namespace ioBroker {
|
|
12
|
-
interface AdapterConfig extends _AdapterConfig {
|
|
13
|
-
// Do not enter anything here!
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// this is required so the above AdapterConfig is found by TypeScript / type checking
|
|
19
|
-
export {};
|