iobroker.statechart 0.2.0 → 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/README.md CHANGED
@@ -10,11 +10,7 @@
10
10
 
11
11
  **Tests:** ![Test and Release](https://github.com/IggePop/ioBroker.statechart/workflows/Test%20and%20Release/badge.svg)
12
12
 
13
- **[English](#english) | [Deutsch](#deutsch)**
14
-
15
- ---
16
-
17
- <a id="english"></a>
13
+ **English** | [Deutsch](docs/de/README.md)
18
14
 
19
15
  ## State machine adapter for ioBroker
20
16
 
@@ -22,23 +18,33 @@ Model your automations as **state machines** (statecharts) in a graphical editor
22
18
  chart editors known from model based development. States, transitions with events, conditions and
23
19
  actions, timers and function blocks are drawn in the ioBroker admin and executed by the adapter.
24
20
 
21
+ ![The editor with an alarm system: nested and parallel states, timers, a function block, a note and the live values](docs/screenshot_main.png)
22
+
23
+ *An alarm system as example: exit and entry delay, alarm with siren and notification in parallel, the live values
24
+ of the running chart on the top left, the variables on the right.*
25
+
25
26
  ### Features
26
27
 
27
28
  - Graphical editor as its own entry in the admin side bar
28
- - States with `entry`, `during` and `exit` actions
29
+ - States with `entry`, `during` and `exit` actions, nested and parallel states (AND), history junction
29
30
  - Transitions with labels in the well-known syntax `event[condition]{action}`
30
- - Timers: `after(5, sec)`
31
+ - Temporal logic like in Stateflow: `after(5, sec)`, `before`, `at`, `every`, counting inputs, `after(5, min, condition)`, `elapsed()`, `duration()`, and every day at a time or an astro event: `at("sunset", -30)`
31
32
  - Function blocks with your own JavaScript functions, usable in all conditions and actions
32
33
  - Inputs and outputs mapped to any ioBroker states
33
34
  - Live view: the active state is highlighted in the editor, the current values of the variables are shown, taken
34
35
  transitions light up, and a history shows when which transition was taken and why
36
+ - Simulation: run the chart from the editor without switching anything, set the inputs by hand, advance the time,
37
+ breakpoints and single steps
35
38
  - Code editor with syntax highlighting, error checking (e.g. unknown variables) and completion
36
- - Several independent charts per instance
37
- - Import and export of charts as JSON files
38
- - Comments on states, transitions and function blocks, and notes on the canvas
39
+ - Static check of the chart: states which never become active, transitions which are never taken, unused variables
40
+ - Several independent charts per instance, which can be paused, reset and woken up by states (e.g. from vis)
41
+ - Import and export of charts as JSON files, export of the whole chart as PNG image (e.g. for the forum)
42
+ - Comments on states, transitions and function blocks, notes on the canvas, and a color per state
39
43
  - Per input: wake up on every update or only on changes, filter by ack, debounce
40
- - Helper functions: setState, setStateDelayed, sendTo, isTimeBetween, isDaytime, astroTime, change detection (hasChanged...)
41
- - Undo/redo (Ctrl+Z, Ctrl+Y), copy/cut/paste of states and function blocks (Ctrl+C, Ctrl+X, Ctrl+V), also between charts, and a warning about unsaved changes
44
+ - Helper functions: getState (any state), setState, setStateDelayed, sendTo, isTimeBetween, isDaytime, astroTime, change detection (hasChanged...)
45
+ - Align and distribute the selected elements, snap to the grid (can be switched off)
46
+ - The list of charts and the properties panel can be resized and collapsed, for more space for the chart
47
+ - Undo/redo (Ctrl+Z, Ctrl+Y), cut/copy/paste/delete of states, function blocks and notes (Ctrl+X, Ctrl+C, Ctrl+V, Del), also between charts, and a warning about unsaved changes
42
48
  - **Show code**: the JavaScript generated from the chart, showing exactly how the adapter executes it
43
49
 
44
50
  ### Installation
@@ -55,13 +61,14 @@ The adapter is not in the ioBroker repositories yet, but can be installed direct
55
61
  Then add an instance of **State Machine**. This installs the current development version, which may
56
62
  still change incompatibly.
57
63
 
58
- Requirements: Node.js 22 or newer, js-controller 6.0.11 or newer, admin 7.6.17 or newer.
64
+ Requirements: Node.js 22 or newer, js-controller 6.0.11 or newer, admin 7.6.20 or newer.
59
65
 
60
66
  ### Getting started
61
67
 
62
68
  1. Open **State Machine** in the admin side bar.
63
69
  2. Create a chart with **+** in the list on the left.
64
- 3. Define the variables in the tab **Variables** on the right:
70
+ 3. Define the variables in the tab **Variables** on the right, with **+** next to their group. The variables are
71
+ grouped by kind, each one is a line with its state and current value, a click on it shows its settings:
65
72
  - **Input**: read from an ioBroker state, every change wakes up the chart
66
73
  - **Output**: written to an ioBroker state when an action changes it
67
74
  - **Local**: only exists inside the chart, with an initial value
@@ -71,6 +78,7 @@ Requirements: Node.js 22 or newer, js-controller 6.0.11 or newer, admin 7.6.17 o
71
78
  end where the states face each other and follow them when they are moved. To fix an end at another place,
72
79
  select the transition and drag the round grip at the end along the border (double click: automatic again). To
73
80
  move a transition to another state, drop the grip on that state: its label and all other properties are kept.
81
+ Drag a label to move it, e.g. if it covers another one; a moved label stays connected to its arrow by a thin line.
74
82
  6. Select a state or transition (or double click it) to edit its actions or label on the right.
75
83
  7. Click **Save**. New charts are paused: start the chart with ▶ in the chart list. After saving, the adapter
76
84
  restarts a running chart immediately.
@@ -90,525 +98,17 @@ Requirements: Node.js 22 or newer, js-controller 6.0.11 or newer, admin 7.6.17 o
90
98
  | Transition | Label |
91
99
  |---|---|
92
100
  | Off → On | `motion[motion == true]` |
93
- | On → Off | `after(2, min)[!motion]` |
94
-
95
- ### Transition labels
96
-
97
- ```
98
- event[condition]{action}
99
- ```
100
-
101
- All parts are optional and may span several lines.
102
-
103
- | Part | Meaning | Example |
104
- |---|---|---|
105
- | `event` | Name of an input: the transition is only checked when this input changes | `motion` |
106
- | `after(n, unit)` | Instead of an event: valid from *n* units after entering the state on. Then a timer wakes up the chart, and the condition is checked again on every following wakeup. Units: `msec`, `sec`, `min` | `after(30, sec)` |
107
- | `[condition]` | JavaScript expression, the transition is only taken if it is true | `[temp > 21 && !window]` |
108
- | `{action}` | JavaScript, executed when the transition is taken | `{count++; light = true;}` |
109
-
110
- Without event, a transition is checked whenever any input changes. If a state has several outgoing
111
- transitions, they are checked in the order of their **priority** (shown at the arrow).
112
-
113
- ### When inputs wake up the chart
114
-
115
- Every input has three settings in the tab **Variables**:
116
-
117
- | Setting | Values | Default |
118
- |---|---|---|
119
- | **Wake up on** | every update / value changes only | every update |
120
- | **Acknowledged (ack)** | any / confirmed values only (`ack=true`) / commands only (`ack=false`) | any |
121
- | **Debounce (ms)** | the chart wakes up only after the input had no update for this time, with its last value | 0 (off) |
122
-
123
- Updates filtered out by ack do not change the variable, so it e.g. only contains confirmed values.
124
-
125
- ### Actions
126
-
127
- Actions and conditions are JavaScript. All variables of the chart can be used directly by their name,
128
- plus `Math`, `Date`, `JSON` etc. and `log("text")` to write to the ioBroker log.
129
-
130
- | Action | Executed |
131
- |---|---|
132
- | `entry` | when the state is entered |
133
- | `during` | on every wakeup of the chart while the state is active and none of its outgoing transitions is taken |
134
- | `exit` | when the state is left |
135
-
136
- ### Nested states
137
-
138
- Drop a state into another state to make it a **sub state**, drag it out again to undo it. While dragging,
139
- the state it would be moved into is highlighted. Every level has its own initial state (dot with arrow).
140
-
141
- - Entering a super state also enters its initial sub state (or the sub state the transition points to).
142
- Leaving it leaves the active sub state first.
143
- - On every wakeup, each active state is processed in this order, starting with the outermost one:
144
- 1. transitions leaving the state, 2. its `during` action, 3. its **inner transitions**, 4. its active sub state.
145
- So the transitions of a super state have priority over those of its sub states.
146
- - A transition from a state to one of its own sub states is an **inner transition** (dot at its start):
147
- the state stays active, only its active sub state is left, its own `entry`/`exit` are not executed.
148
- With the option **Leave and enter the parent state again**, it is left and entered again instead.
149
-
150
- ### Helper functions
151
-
152
- In conditions, actions and function blocks:
153
-
154
- | Function | Purpose |
155
- |---|---|
156
- | `log(message, level)` | writes to the ioBroker log, level `"debug"`, `"info"` (default), `"warn"` or `"error"` |
157
- | `setState(id, value, ack)` | writes any ioBroker state, `ack` is `false` by default |
158
- | `setStateDelayed(id, value, delayMs, ack)` | writes after a delay, cancelled when the chart stops |
159
- | `sendTo(instance, command, message)` | sends a message to an adapter, e.g. `sendTo("telegram.0", "send", { text: "Window open" })` |
160
- | `isTimeBetween("22:00", "06:00")` | true if the current time is in the range, also over midnight |
161
- | `isDaytime()` | true between sunrise and sunset |
162
- | `astroTime("sunset", offsetMinutes)` | time of an astro event today as `Date`: `sunrise`, `sunset`, `dawn`, `dusk`, `solarNoon`, ... |
163
- | `hasChanged(variable)` | true if the variable has changed, see below |
164
- | `hasChangedFrom(variable, value)` | true if the variable has changed and its previous value was `value` |
165
- | `hasChangedTo(variable, value)` | true if the variable has changed to `value`, e.g. `hasChangedTo(button, true)` for a rising edge |
101
+ | On → Off | `after(2, min, !motion)` |
166
102
 
167
- `isDaytime()` and `astroTime()` use the location of the ioBroker system settings. To read other states, add
168
- them as inputs: their variables are always up to date and wake up the chart. Time based functions do not wake up
169
- the chart by themselves, they are evaluated when it wakes up.
103
+ The light goes on with motion and off after 2 minutes without motion: `after(2, min, !motion)` waits until
104
+ `motion` has been false for 2 minutes, every new motion starts the time again.
170
105
 
171
- **Change detection** works like in Stateflow: it compares the value of the variable at the start of the current
172
- wakeup with its value at the start of the previous wakeup. Inputs, outputs and locals can be checked. An update
173
- with the same value is no change, neither is a wakeup by another input or a timer, so `[hasChangedTo(button, true)]`
174
- is only true once per press of the button. On the wakeup after the start nothing has changed yet. A change of a
175
- local by an action is seen on the next wakeup, also in a super step. Objects and arrays are compared by their
176
- contents.
106
+ ### Documentation
177
107
 
178
- ### Function blocks
179
-
180
- **Add function** creates a block for your own JavaScript. It is executed once when the chart starts,
181
- all functions and constants declared in it can be used in every condition and action of the chart:
182
-
183
- ```js
184
- const LIMIT = 22;
185
-
186
- function tooWarm(temp) {
187
- return temp > LIMIT;
188
- }
189
- ```
190
-
191
- Transition label: `[tooWarm(temperature)]{heating = false;}`
192
-
193
- ### Comments and notes
194
-
195
- - **Comment:** states, transitions, function blocks and the chart itself have a field **Comment** in their properties.
196
- A speech bubble on the canvas shows that there is a comment, the comment is shown as tooltip.
197
- - **Notes** (icon **Add note** in the toolbar) are free texts on the canvas, like sticky notes. Drop a note into a
198
- state, so it moves together with it.
199
-
200
- Comments and notes have no influence on the execution. They are included in the generated code and in exported
201
- files, and changing them does not prevent restoring the state after a restart.
202
-
203
- ### Watching a running chart
204
-
205
- While a chart runs, the editor shows what it does:
206
-
207
- - The **active states** have a green border.
208
- - **Live values** (panel at the top left of the canvas, switched on and off with the last icon in the toolbar):
209
- the current values of all variables. They are also shown below each variable in the tab **Variables**.
210
- - A **transition lights up** for a moment when the chart takes it.
211
- - The tab **History** lists the starts of the chart and the transitions taken, newest first: when, from which state
212
- to which, triggered by which input or by a timer (`after`), and which states were active afterwards. Click a
213
- transition to select it in the chart. The adapter keeps the last 200 events, also when the editor is closed and
214
- after a restart. **Clear history** removes them.
215
-
216
- The live values and the history come from the running chart, so they refer to the saved chart, not to unsaved
217
- changes in the editor.
218
-
219
- ### Import and export
220
-
221
- - **Export chart** (last icon in the toolbar of the editor) saves the chart as JSON file, including unsaved changes.
222
- - **Export all charts** (icon above the chart list) saves all saved charts in one file.
223
- - **Import charts** (icon above the chart list) adds the charts of such a file as new charts, existing charts are not
224
- changed. The file is checked before (structure, IDs, references between states and transitions).
225
-
226
- Imported charts are **paused**: their variables may refer to states of another system. Check them in the tab
227
- **Variables**, then start the chart with the button in the chart list.
228
-
229
- ### States created by the adapter
230
-
231
- For every chart, the adapter creates below `statechart.<instance>.charts.<chart>`:
232
-
233
- | State | Content |
234
- |---|---|
235
- | `activeState` | Name of the active state, nested states as `Parent.Child` |
236
- | `activeStateIds` | IDs of all active states as JSON |
237
- | `snapshot` | Stored state of the chart (active states, when they were entered, locals), see below |
238
- | `values` | Current values of all variables as JSON, `{}` if the chart does not run |
239
- | `history` | The last 200 events (starts and transitions taken) as JSON. Writing it without ack clears it |
240
-
241
- The chart itself is stored in the object `statechart.<instance>.charts.<chart>`.
242
-
243
- ### Restart of the adapter
244
-
245
- After a restart of the adapter or of ioBroker, every chart continues in the states which were active
246
- before, with its local variables. The entry actions are not executed again, and timers continue with
247
- their remaining time. A timer which expired while the adapter was stopped is taken right after the start.
248
- Like after every start, the chart then wakes up once, so changes of the inputs during the stop are
249
- noticed.
250
-
251
- The chart starts in its initial state instead if it was changed meanwhile (positions in the editor do not
252
- count), after saving it in the editor, after pausing it, and if the option **Restore the state after a
253
- restart** in the chart properties is switched off. Variables declared in function blocks are not restored.
254
-
255
- ### Execution model and differences to other statechart tools
256
-
257
- - The adapter uses [XState](https://stately.ai/docs/xstate) to execute the charts.
258
- - Every change of an input wakes up the chart once, as well as an expiring `after(...)` and the start of the chart
259
- (also after restoring it). Transitions without event, e.g. without label, are checked on every wakeup, so they
260
- are taken right after the start if their condition is true.
261
- - By default, only one transition is taken per wakeup, a following one waits for the next wakeup. With the
262
- chart option **Take transitions until none is valid (super step)**, the chart is checked again right after a
263
- transition, until no transition is valid anymore, like the super step semantics of Stateflow. The event of an
264
- input only counts for the first transition of a wakeup, during actions may run several times. To protect against
265
- loops, at most 100 transitions are taken per wakeup, then a warning is logged.
266
- - `{action}` is executed while the transition is taken, i.e. after `exit` of the old and before `entry` of
267
- the new state. A separate condition action and `/action` are not supported (yet).
268
- - Transitions are selected like in Stateflow (outer before inner, see above), XState only executes them.
269
- Pure XState would check the transitions of sub states first.
270
- - Temporal logic is limited to `after(n, msec|sec|min)`, measured in real time.
271
- - Conditions and actions use JavaScript, not MATLAB/C syntax.
272
-
273
- ### Limitations
274
-
275
- - Parallel states (AND), history junctions and connective junctions are not supported yet.
276
- - Deleting a super state also deletes its sub states.
277
- - Unsaved changes are lost when you select another chart.
278
- - Conditions and actions run in a separate JavaScript context with a time limit of 1 second, but this
279
- is **no security sandbox**: everyone who may edit charts can execute any code in the adapter,
280
- just like with the JavaScript adapter.
281
-
282
- ---
283
-
284
- <a id="deutsch"></a>
285
-
286
- ## Zustandsautomaten-Adapter für ioBroker
287
-
288
- Bilde deine Automatisierungen als **Zustandsautomaten** (Statecharts) in einem grafischen Editor ab,
289
- ähnlich den Chart-Editoren aus der modellbasierten Entwicklung. Zustände, Übergänge mit Events,
290
- Bedingungen und Aktionen, Timer und Funktionsblöcke werden im ioBroker-Admin gezeichnet und vom
291
- Adapter ausgeführt.
292
-
293
- ### Funktionen
294
-
295
- - Grafischer Editor als eigener Eintrag in der Seitenleiste des Admins
296
- - Zustände mit `entry`-, `during`- und `exit`-Aktionen
297
- - Übergänge mit Beschriftungen in der bekannten Syntax `event[bedingung]{aktion}`
298
- - Timer: `after(5, sec)`
299
- - Funktionsblöcke mit eigenen JavaScript-Funktionen, nutzbar in allen Bedingungen und Aktionen
300
- - Eingänge und Ausgänge, die mit beliebigen ioBroker-Datenpunkten verbunden sind
301
- - Live-Ansicht: Der aktive Zustand wird im Editor hervorgehoben, die aktuellen Werte der Variablen werden angezeigt,
302
- genommene Übergänge leuchten auf, und ein Verlauf zeigt, wann welcher Übergang genommen wurde und warum
303
- - Code-Editor mit Syntax-Hervorhebung, Fehlerprüfung (z.B. unbekannte Variablen) und Autovervollständigung
304
- - Mehrere unabhängige Charts pro Instanz
305
- - Import und Export von Charts als JSON-Dateien
306
- - Kommentare zu Zuständen, Übergängen und Funktionsblöcken sowie Notizen auf der Zeichenfläche
307
- - Pro Eingang: Aufwecken bei jeder Aktualisierung oder nur bei Änderung, Filter nach ack, Entprellen
308
- - Hilfsfunktionen: setState, setStateDelayed, sendTo, isTimeBetween, isDaytime, astroTime, Änderungserkennung (hasChanged...)
309
- - Rückgängig/Wiederholen (Strg+Z, Strg+Y), Kopieren/Ausschneiden/Einfügen von Zuständen und Funktionsblöcken (Strg+C, Strg+X, Strg+V), auch zwischen Charts, und eine Warnung bei ungespeicherten Änderungen
310
- - **Code anzeigen**: das aus dem Chart generierte JavaScript, das genau zeigt, wie der Adapter es ausführt
311
-
312
- ### Installation
313
-
314
- Der Adapter ist noch nicht in den ioBroker-Repositories, lässt sich aber direkt von GitHub installieren:
315
-
316
- - **Admin:** Reiter **Adapter** → Symbol **Aus eigener URL installieren** (GitHub-Katze) → Reiter
317
- **Benutzerdefiniert** → URL `https://github.com/IggePop/ioBroker.statechart` → **Installieren**
318
- - **oder auf der Konsole** des ioBroker-Systems:
319
- ```bash
320
- iobroker url https://github.com/IggePop/ioBroker.statechart
321
- ```
322
-
323
- Danach eine Instanz von **Zustandsmaschine** hinzufügen. Installiert wird der aktuelle
324
- Entwicklungsstand, der sich noch inkompatibel ändern kann.
325
-
326
- Voraussetzungen: Node.js 22 oder neuer, js-controller 6.0.11 oder neuer, Admin 7.6.17 oder neuer.
327
-
328
- ### Erste Schritte
329
-
330
- 1. In der Seitenleiste des Admins **Zustandsmaschine** öffnen.
331
- 2. Links in der Liste mit **+** ein Chart anlegen.
332
- 3. Rechts im Reiter **Variablen** die Variablen festlegen:
333
- - **Eingang**: wird aus einem ioBroker-Datenpunkt gelesen, jede Änderung weckt das Chart auf
334
- - **Ausgang**: wird in einen ioBroker-Datenpunkt geschrieben, wenn eine Aktion ihn ändert
335
- - **Lokal**: gibt es nur im Chart, mit einem Startwert
336
- 4. Mit **Zustand hinzufügen** Zustände anlegen. Der Zustand mit Punkt und Pfeil ist der Startzustand
337
- (änderbar in den Eigenschaften eines Zustands).
338
- 5. Übergänge zeichnen: vom Rand eines Zustands zu einem anderen Zustand (oder zu sich selbst) ziehen. Die Pfeile
339
- beginnen und enden dort, wo sich die Zustände gegenüberliegen, und folgen ihnen beim Verschieben. Um ein Ende
340
- an einer anderen Stelle festzulegen, den Übergang auswählen und den runden Griff am Ende am Rand entlang ziehen
341
- (Doppelklick: wieder automatisch). Um einen Übergang an einen anderen Zustand umzuhängen, den Griff auf diesen
342
- Zustand ziehen: Beschriftung und alle anderen Eigenschaften bleiben erhalten.
343
- 6. Einen Zustand oder Übergang anklicken (oder doppelklicken), um rechts seine Aktionen bzw. seine
344
- Beschriftung zu bearbeiten.
345
- 7. **Speichern** klicken. Neue Charts sind angehalten: Starte das Chart mit ▶ in der Chart-Liste. Nach dem
346
- Speichern startet der Adapter ein laufendes Chart sofort neu.
347
-
348
- ### Beispiel: Licht mit Bewegungsmelder
349
-
350
- | Variable | Art | Datenpunkt |
351
- |---|---|---|
352
- | `motion` | Eingang | z.B. `hm-rpc.0.ABC123.1.MOTION` |
353
- | `light` | Ausgang | z.B. `hue.0.flur.on` |
354
-
355
- | Zustand | entry |
356
- |---|---|
357
- | Aus (Startzustand) | `light = false;` |
358
- | An | `light = true;` |
359
-
360
- | Übergang | Beschriftung |
361
- |---|---|
362
- | Aus → An | `motion[motion == true]` |
363
- | An → Aus | `after(2, min)[!motion]` |
364
-
365
- ### Beschriftung von Übergängen
366
-
367
- ```
368
- event[bedingung]{aktion}
369
- ```
370
-
371
- Alle Teile sind optional und dürfen über mehrere Zeilen gehen.
372
-
373
- | Teil | Bedeutung | Beispiel |
374
- |---|---|---|
375
- | `event` | Name eines Eingangs: Der Übergang wird nur geprüft, wenn sich dieser Eingang ändert | `motion` |
376
- | `after(n, einheit)` | Statt eines Events: gültig ab *n* Einheiten nach Betreten des Zustands. Dann weckt ein Timer das Chart, und die Bedingung wird bei jedem weiteren Aufwecken erneut geprüft. Einheiten: `msec`, `sec`, `min` | `after(30, sec)` |
377
- | `[bedingung]` | JavaScript-Ausdruck, der Übergang wird nur genommen, wenn er wahr ist | `[temp > 21 && !fenster]` |
378
- | `{aktion}` | JavaScript, wird ausgeführt, wenn der Übergang genommen wird | `{zaehler++; licht = true;}` |
379
-
380
- Ohne Event wird ein Übergang bei jeder Änderung eines beliebigen Eingangs geprüft. Hat ein Zustand
381
- mehrere ausgehende Übergänge, werden sie in der Reihenfolge ihrer **Priorität** geprüft (steht am Pfeil).
382
-
383
- ### Wann Eingänge das Chart aufwecken
384
-
385
- Jeder Eingang hat im Reiter **Variablen** drei Einstellungen:
386
-
387
- | Einstellung | Werte | Standard |
388
- |---|---|---|
389
- | **Aufwecken bei** | jeder Aktualisierung / nur bei Wertänderung | jeder Aktualisierung |
390
- | **Bestätigt (ack)** | egal / nur bestätigte Werte (`ack=true`) / nur Befehle (`ack=false`) | egal |
391
- | **Entprellen (ms)** | das Chart wird erst geweckt, wenn der Eingang so lange keine Aktualisierung bekommen hat, mit dem letzten Wert | 0 (aus) |
392
-
393
- Aktualisierungen, die wegen ack aussortiert werden, ändern die Variable nicht. Sie enthält dann z.B. nur bestätigte Werte.
394
-
395
- ### Aktionen
396
-
397
- Aktionen und Bedingungen sind JavaScript. Alle Variablen des Charts sind direkt über ihren Namen
398
- verfügbar, dazu `Math`, `Date`, `JSON` usw. sowie `log("Text")` zum Schreiben ins ioBroker-Log.
399
-
400
- | Aktion | Wird ausgeführt |
401
- |---|---|
402
- | `entry` | beim Betreten des Zustands |
403
- | `during` | bei jedem Aufwecken des Charts, solange der Zustand aktiv ist und keiner seiner ausgehenden Übergänge genommen wird |
404
- | `exit` | beim Verlassen des Zustands |
405
-
406
- ### Verschachtelte Zustände
407
-
408
- Lass einen Zustand über einem anderen los, um ihn zum **Unterzustand** zu machen, und zieh ihn wieder
409
- heraus, um das rückgängig zu machen. Beim Ziehen wird der Zustand hervorgehoben, in den er verschoben
410
- würde. Jede Ebene hat ihren eigenen Startzustand (Punkt mit Pfeil).
411
-
412
- - Beim Betreten eines Superstates wird auch sein Start-Unterzustand betreten (oder der Unterzustand,
413
- auf den der Übergang zeigt). Beim Verlassen wird zuerst der aktive Unterzustand verlassen.
414
- - Bei jedem Aufwecken wird jeder aktive Zustand in dieser Reihenfolge bearbeitet, beginnend mit dem äußersten:
415
- 1. Übergänge, die den Zustand verlassen, 2. seine `during`-Aktion, 3. seine **inneren Übergänge**,
416
- 4. sein aktiver Unterzustand. Die Übergänge eines Superstates haben also Vorrang vor denen seiner Unterzustände.
417
- - Ein Übergang von einem Zustand zu einem seiner eigenen Unterzustände ist ein **innerer Übergang**
418
- (Punkt am Anfang): Der Zustand bleibt aktiv, nur sein aktiver Unterzustand wird verlassen, seine
419
- eigenen `entry`/`exit` laufen nicht. Mit der Option **Elternzustand verlassen und neu betreten** wird
420
- er stattdessen verlassen und neu betreten.
421
-
422
- ### Hilfsfunktionen
423
-
424
- In Bedingungen, Aktionen und Funktionsblöcken:
425
-
426
- | Funktion | Zweck |
427
- |---|---|
428
- | `log(text, stufe)` | schreibt ins ioBroker-Log, Stufe `"debug"`, `"info"` (Standard), `"warn"` oder `"error"` |
429
- | `setState(id, wert, ack)` | schreibt einen beliebigen Datenpunkt, `ack` ist standardmäßig `false` |
430
- | `setStateDelayed(id, wert, ms, ack)` | schreibt verzögert, wird beim Anhalten des Charts verworfen |
431
- | `sendTo(instanz, befehl, nachricht)` | sendet eine Nachricht an einen Adapter, z.B. `sendTo("telegram.0", "send", { text: "Fenster offen" })` |
432
- | `isTimeBetween("22:00", "06:00")` | wahr, wenn die aktuelle Uhrzeit im Bereich liegt, auch über Mitternacht |
433
- | `isDaytime()` | wahr zwischen Sonnenaufgang und Sonnenuntergang |
434
- | `astroTime("sunset", minuten)` | Zeitpunkt eines Astro-Ereignisses heute als `Date`: `sunrise`, `sunset`, `dawn`, `dusk`, `solarNoon`, ... |
435
- | `hasChanged(variable)` | wahr, wenn sich die Variable geändert hat, siehe unten |
436
- | `hasChangedFrom(variable, wert)` | wahr, wenn sich die Variable geändert hat und vorher `wert` war |
437
- | `hasChangedTo(variable, wert)` | wahr, wenn sich die Variable auf `wert` geändert hat, z.B. `hasChangedTo(taster, true)` für eine steigende Flanke |
438
-
439
- `isDaytime()` und `astroTime()` nutzen den Standort aus den ioBroker-Systemeinstellungen. Andere Datenpunkte liest
440
- man über Eingänge: Ihre Variablen sind immer aktuell und wecken das Chart. Zeitabhängige Funktionen wecken das
441
- Chart nicht von selbst, sie werden beim Aufwecken ausgewertet.
442
-
443
- Die **Änderungserkennung** funktioniert wie in Stateflow: Sie vergleicht den Wert der Variablen zu Beginn des
444
- aktuellen Aufweckens mit ihrem Wert zu Beginn des vorherigen. Eingänge, Ausgänge und lokale Variablen lassen sich
445
- prüfen. Eine Aktualisierung mit demselben Wert ist keine Änderung, ebenso wenig ein Aufwecken durch einen anderen
446
- Eingang oder einen Timer. `[hasChangedTo(taster, true)]` ist also nur einmal pro Tastendruck wahr. Beim Aufwecken
447
- nach dem Start hat sich noch nichts geändert. Ändert eine Aktion eine lokale Variable, sieht man das erst beim
448
- nächsten Aufwecken, auch im Super-Step. Objekte und Arrays werden nach ihrem Inhalt verglichen.
449
-
450
- ### Funktionsblöcke
451
-
452
- **Funktion hinzufügen** legt einen Block für eigenes JavaScript an. Er wird beim Start des Charts
453
- einmal ausgeführt. Alle darin deklarierten Funktionen und Konstanten können in jeder Bedingung und
454
- Aktion des Charts verwendet werden:
455
-
456
- ```js
457
- const LIMIT = 22;
458
-
459
- function zuWarm(temp) {
460
- return temp > LIMIT;
461
- }
462
- ```
463
-
464
- Beschriftung eines Übergangs: `[zuWarm(temperatur)]{heizung = false;}`
465
-
466
- ### Kommentare und Notizen
467
-
468
- - **Kommentar:** Zustände, Übergänge, Funktionsblöcke und das Chart selbst haben in ihren Eigenschaften ein Feld
469
- **Kommentar**. Eine Sprechblase auf der Zeichenfläche zeigt, dass es einen Kommentar gibt, der Kommentar erscheint
470
- als Tooltip.
471
- - **Notizen** (Symbol **Notiz hinzufügen** in der Werkzeugleiste) sind freie Texte auf der Zeichenfläche, wie
472
- Haftzettel. Lass eine Notiz über einem Zustand los, dann bewegt sie sich mit ihm.
473
-
474
- Kommentare und Notizen haben keinen Einfluss auf die Ausführung. Sie stehen im generierten Code und in exportierten
475
- Dateien, und wenn sie sich ändern, wird der Zustand nach einem Neustart trotzdem wiederhergestellt.
476
-
477
- ### Ein laufendes Chart beobachten
478
-
479
- Während ein Chart läuft, zeigt der Editor, was es tut:
480
-
481
- - Die **aktiven Zustände** haben einen grünen Rahmen.
482
- - **Live-Werte** (Tafel oben links auf der Zeichenfläche, ein- und ausgeschaltet mit dem letzten Symbol der
483
- Werkzeugleiste): die aktuellen Werte aller Variablen. Sie stehen auch unter jeder Variable im Reiter **Variablen**.
484
- - Ein **Übergang leuchtet kurz auf**, wenn das Chart ihn nimmt.
485
- - Der Reiter **Verlauf** listet die Starts des Charts und die genommenen Übergänge, der neueste zuerst: wann, von
486
- welchem Zustand zu welchem, ausgelöst durch welchen Eingang oder einen Timer (`after`), und welche Zustände danach
487
- aktiv waren. Ein Klick auf einen Übergang wählt ihn im Chart aus. Der Adapter behält die letzten 200 Ereignisse,
488
- auch bei geschlossenem Editor und über einen Neustart hinweg. **Verlauf leeren** löscht sie.
489
-
490
- Live-Werte und Verlauf kommen vom laufenden Chart. Sie beziehen sich also auf das gespeicherte Chart, nicht auf
491
- ungespeicherte Änderungen im Editor.
492
-
493
- ### Import und Export
494
-
495
- - **Chart exportieren** (letztes Symbol in der Werkzeugleiste des Editors) speichert das Chart als JSON-Datei, mit
496
- ungespeicherten Änderungen.
497
- - **Alle Charts exportieren** (Symbol über der Chart-Liste) speichert alle gespeicherten Charts in einer Datei.
498
- - **Charts importieren** (Symbol über der Chart-Liste) fügt die Charts einer solchen Datei als neue Charts hinzu,
499
- vorhandene Charts werden nicht verändert. Die Datei wird vorher geprüft (Aufbau, IDs, Verweise zwischen Zuständen
500
- und Übergängen).
501
-
502
- Importierte Charts sind **angehalten**: Ihre Variablen können auf Datenpunkte eines anderen Systems verweisen. Prüfe sie
503
- im Reiter **Variablen** und starte das Chart dann mit dem Knopf in der Chart-Liste.
504
-
505
- ### Vom Adapter angelegte Datenpunkte
506
-
507
- Für jedes Chart legt der Adapter unter `statechart.<instanz>.charts.<chart>` an:
508
-
509
- | Datenpunkt | Inhalt |
510
- |---|---|
511
- | `activeState` | Name des aktiven Zustands, verschachtelte Zustände als `Eltern.Kind` |
512
- | `activeStateIds` | IDs aller aktiven Zustände als JSON |
513
- | `snapshot` | Gespeicherter Zustand des Charts (aktive Zustände, wann sie betreten wurden, lokale Variablen), siehe unten |
514
- | `values` | Aktuelle Werte aller Variablen als JSON, `{}` wenn das Chart nicht läuft |
515
- | `history` | Die letzten 200 Ereignisse (Starts und genommene Übergänge) als JSON. Schreiben ohne ack leert ihn |
516
-
517
- Das Chart selbst ist im Objekt `statechart.<instanz>.charts.<chart>` gespeichert.
518
-
519
- ### Neustart des Adapters
520
-
521
- Nach einem Neustart des Adapters oder von ioBroker macht jedes Chart in den Zuständen weiter, die
522
- vorher aktiv waren, mit seinen lokalen Variablen. Die entry-Aktionen laufen nicht noch einmal, und Timer
523
- laufen mit ihrer Restzeit weiter. Ein Timer, der abgelaufen ist, während der Adapter gestoppt war, greift
524
- gleich nach dem Start. Wie nach jedem Start wacht das Chart dann einmal auf, so werden Änderungen der
525
- Eingänge während des Stopps bemerkt.
526
-
527
- Im Startzustand beginnt das Chart stattdessen, wenn es zwischendurch geändert wurde (Positionen im
528
- Editor zählen nicht), nach dem Speichern im Editor, nach dem Anhalten und wenn die Option **Zustand nach
529
- Neustart wiederherstellen** in den Chart-Eigenschaften ausgeschaltet ist. Variablen, die in
530
- Funktionsblöcken deklariert sind, werden nicht wiederhergestellt.
531
-
532
- ### Ausführung und Unterschiede zu anderen Statechart-Werkzeugen
533
-
534
- - Der Adapter führt die Charts mit [XState](https://stately.ai/docs/xstate) aus.
535
- - Jede Änderung eines Eingangs weckt das Chart einmal auf, ebenso ein ablaufendes `after(...)` und der Start des
536
- Charts (auch nach dem Wiederherstellen). Übergänge ohne Event, z.B. ohne Beschriftung, werden bei jedem Aufwecken
537
- geprüft und greifen deshalb direkt nach dem Start, wenn ihre Bedingung wahr ist.
538
- - Standardmäßig wird pro Aufwecken nur ein Übergang genommen, ein folgender wartet auf das nächste Aufwecken. Mit der
539
- Chart-Option **Weiterschalten, bis kein Übergang mehr gilt (Super-Step)** wird das Chart nach einem Übergang sofort
540
- erneut geprüft, bis kein Übergang mehr gilt, wie bei der Super-Step-Semantik von Stateflow. Das Event eines
541
- Eingangs zählt nur für den ersten Übergang eines Aufweckens, during-Aktionen können mehrmals laufen. Zum Schutz vor
542
- Schleifen werden pro Aufwecken höchstens 100 Übergänge genommen, danach wird eine Warnung geloggt.
543
- - `{aktion}` wird während des Übergangs ausgeführt, also nach `exit` des alten und vor `entry` des
544
- neuen Zustands. Eine eigene Condition Action und `/aktion` werden (noch) nicht unterstützt.
545
- - Übergänge werden wie bei Stateflow ausgewählt (außen vor innen, siehe oben), XState führt sie nur aus.
546
- Reines XState würde die Übergänge der Unterzustände zuerst prüfen.
547
- - Zeitlogik gibt es nur als `after(n, msec|sec|min)`, gemessen in Echtzeit.
548
- - Bedingungen und Aktionen sind JavaScript, nicht MATLAB- oder C-Syntax.
549
-
550
- ### Einschränkungen
551
-
552
- - Parallele Zustände (AND), History- und Verbindungspunkte werden noch nicht unterstützt.
553
- - Beim Löschen eines Superstates werden auch seine Unterzustände gelöscht.
554
- - Ungespeicherte Änderungen gehen verloren, wenn ein anderes Chart ausgewählt wird.
555
- - Bedingungen und Aktionen laufen in einem eigenen JavaScript-Kontext mit 1 Sekunde Zeitlimit, das
556
- ist aber **keine Sicherheits-Sandbox**: Wer Charts bearbeiten darf, kann im Adapter beliebigen Code
557
- ausführen, wie beim JavaScript-Adapter auch.
558
-
559
- ---
560
-
561
- ## Development / Entwicklung
562
-
563
- ```bash
564
- npm install
565
- npm run build # build the adapter (src/ to build/) and the admin tab
566
- npm run dev-server setup # once: local ioBroker test instance in .dev-server/
567
- npm run dev-server watch # run it, admin on http://127.0.0.1:8081/, rebuilds the adapter on changes
568
- npm run upload:tab # in a second terminal: upload the tab into the test instance
569
- ```
570
-
571
- The adapter and the editor are written in TypeScript. The editor imports the chart model, the validation
572
- and the code generator directly from `src/lib/`, so they are shared by both.
573
-
574
- | Script | Description |
575
- |---|---|
576
- | `dev-server` | Runs [`dev-server`](https://github.com/ioBroker/dev-server) with the given command. Before, it applies the patches in `patches/`: the dev-server uses `wmic.exe` to find the adapter process, which no longer exists in Windows 11 25H2 |
577
- | `build` / `watch` | Build the adapter (`src` to `build`) and the tab (`admin/src` to `admin/build`), with type check |
578
- | `build:ts` / `watch:ts` | Build only the adapter |
579
- | `build:react` / `watch:react` | Build only the tab |
580
- | `upload:tab` | Build the tab and write the admin files into the running dev-server instance (works while `dev-server watch` runs) |
581
- | `test` | Unit tests (`src/**/*.test.ts`) and package tests |
582
- | `check` | Type check of the adapter and the tab |
583
- | `lint` | ESLint |
584
- | `translate` | Translate the texts of the adapter |
585
- | `release` | Create a new release |
586
-
587
- After changes of `io-package.json`, run `npm run dev-server upload` and restart `npm run dev-server watch`.
588
-
589
- **The built adapter (`build`) and tab (`admin/build`) are committed**, because installations from GitHub do
590
- not run a build. After changes in `src` or `admin/src`, run `npm run build` and commit `build` and
591
- `admin/build` as well. The workflow job `check-build` fails if they are outdated. The source maps are not
592
- committed.
593
-
594
- **Dependabot** proposes updates monthly. Patch updates (and minor updates of development dependencies) are
595
- merged automatically when the checks pass, which are required for `main`. Dependabot cannot rebuild the
596
- committed build, so `check-build` does not compare it in Dependabot pull requests. Instead, after every push to
597
- `main`, it rebuilds `build` and `admin/build` and commits them if they changed. The workflows need the secret
598
- `AUTO_MERGE_TOKEN`, a fine-grained personal access token for this repository with the permissions
599
- "Contents" and "Pull requests" (read and write). Major updates of MUI, React and
600
- `@types/node` are ignored: `@iobroker/adapter-react-v5` requires MUI 6 and React 18, and the adapter supports
601
- Node.js 22.
602
-
603
- | Path | Content |
604
- |---|---|
605
- | `src/main.ts` | Adapter: loads the charts, subscribes the inputs, restarts a chart when it is saved |
606
- | `src/lib/chart-model.ts` | Chart model (shared by adapter and editor) and parser of the transition labels |
607
- | `src/lib/chart-runtime.ts` | Execution of a chart with XState |
608
- | `src/lib/chart-validation.ts` | Checks of conditions, actions and function blocks |
609
- | `src/lib/chart-codegen.ts` | Code shown by "Show code" in the editor |
610
- | `admin/src/` | Editor (React, React Flow, CodeMirror) |
611
- | `admin/jsonConfig.json` | Instance settings |
108
+ - [Reference](docs/en/reference.md): the editor and its toolbar, transition labels and temporal logic, when inputs wake up the chart, actions,
109
+ nested and parallel states, helper functions, function blocks, watching a running chart, simulation, import and
110
+ export, states created by the adapter, restart, execution model and limitations
111
+ - [Development](docs/development.md): build, dev-server, tests and the structure of the project
612
112
 
613
113
  ## Disclaimer
614
114
 
@@ -620,6 +120,18 @@ with, endorsed or sponsored by The MathWorks, Inc.
620
120
  Placeholder for the next version (at the beginning of the line):
621
121
  ### **WORK IN PROGRESS**
622
122
  -->
123
+ ### 0.3.0 (2026-11-14)
124
+ * (Igge) simulation: the chart runs in the editor without switching anything, the inputs are set by hand, advance the time, pause, single steps and breakpoints on states and transitions
125
+ * (Igge) parallel states (AND) with an execution order, and history junctions: entering a state returns to its last active sub state
126
+ * (Igge) more temporal logic: `before`, `at`, `every`, counting the wakeups by an input (`after(3, button)`), `after(5, min, condition)`, `elapsed()`, `duration()`, and every day at a time or astro event (`at("22:30")`, `at("sunset", -30)`)
127
+ * (Igge) `getState("id")` reads any state in conditions and actions, control states per chart: `enabled`, `reset` and `event`
128
+ * (Igge) static check of the chart: states which never become active or are never left, transitions which are never taken, conditions which are never true, unused variables
129
+ * (Igge) editor: color of each state (every second level of nested states darker), right-angled transitions as option, snap to the grid, align and distribute, labels of transitions can be moved
130
+ * (Igge) editor: toolbar grouped by tasks with new buttons to cut and delete, collapsible side panels, colors and icons for each kind of element, variables grouped by kind with a search
131
+ * (Igge) editor: transitions are black, their labels have no background
132
+ * (Igge) export of the whole chart as PNG image
133
+ * (Igge) fixed: labels of transitions inside a superstate were hidden, labels of several transitions between the same states and the line to a moved label covered text, self transitions on the top or right side were drawn twisted
134
+
623
135
  ### 0.2.0 (2026-11-01)
624
136
  * (Igge) the adapter and the editor are written in TypeScript now, no changes of the behavior
625
137
  * (Igge) live values of the variables in the editor