This manual explains how to use the web interface of the myIO smart-home controller. myIO is a standalone (cloud-independent) controller: it handles relay outputs, dimmers, push-button inputs, temperature and humidity sensors, with timers, a thermostat function and external integrations (Shelly, BroadLink, another myIO). Everything is configured on the built-in web page – no internet connection or installed software is needed.
In myIO every controllable thing is an element with its own identifier (ID):
| Element | ID range | What is it? |
|---|---|---|
| Relay | 1–64 | An output that can be switched on/off (lamp, pump, shutter motor…) |
| Group | 501–520 | Several outputs handled together (e.g. “all lights”) |
| Output | from 2001 | Dimmer (brightness control) or plain on/off channel |
| Output fade | from 4001 | The same channel, but with gradual ramp-up/ramp-down |
| Input | 1–64 | Wall push button / switch |
| Sensor | see chapter 15 | Thermometer, humidity sensor, energy meter, network sensor |
You can give elements a name (description) – the interface shows only named elements, unnamed ones stay hidden. So the first step is always to name the outputs and inputs you use on the settings pages.
You reach the controller from a browser at its IP address (e.g. http://192.168.1.50) or by its
network name (e.g. http://myio-nappali.local). The browser asks for a user name and password.
| Level | What can it do? |
|---|---|
| Admin | Everything: settings, users, files, updates. Account 1 is always an admin (factory default: admin/admin – change it as your very first step!). |
| Editor (editor) | All settings pages, except system-level operations. |
| User (user) | Only the home page: sees/switches the elements the admin has granted access to (chapter 22). |
iPhone / iPad (Safari): open the controller in Safari
(it also works with an http://… address), log in, then tap the Share icon →
Add to Home Screen. A myIO icon with the device name is placed on the home screen, and it
opens like an app, full screen (without the address bar). On an unencrypted (http) connection this is the
recommended way – iOS does not need https for it.
Android: Chrome rewrites the typed address to https, which the controller
does not speak. Solutions: the myIO mobile app (recommended), or Firefox with an
http:// address, or in Chrome turn off the Privacy and security → Always use
secure connections switch (if needed, also go to
chrome://flags and disable HTTPS-First Mode and HTTPS Upgrades).
An installable (PWA) version over http is not possible on Android – that would need an https proxy in front of the
controller.
apple-touch-icon.png file must be in the root of the controller's
storage (it is part of the web file package). If it is missing, installation still works,
but the icon will be a screenshot.The same two-row menu bar appears at the top of every settings page. The pages are arranged into four group menus: Control (Outputs, Logic outputs, Input, Groups, Sensors), Link (myIO, Shelly, Broadlink), Apps (Timer, eManager, Dictionary, Files, System diagnostics) and System (General, Network, Users, Log). On older (3.x) controllers the 4.x-only pages do not appear in the menus. On a narrow (mobile) display the menu bar switches to a single row: Auto + Refresh on the left, the Home icon in the middle, the ☰ menu on the right — the menu holds the switches, the four groups and saving. The home page has a simpler row: Refresh, Booster, Home / Chart / Settings buttons, Log Out and the Theme list (home page themes).
| Element | Function |
|---|---|
| Auto switch | Automatic refresh: reloads the page after every change. When off, changes are sent in the background (faster, more convenient when changing many settings). |
| ↻ Refresh button | Reloads the page manually with fresh data from the controller (the icon next to the Auto switch). |
| Language selector | The language of the interface (in the top row). |
| Booster switch | Loads the interface files from an online host (faster display on old devices). When on, the host address is shown next to it (default: https://okoslak.hu/ext2/). If the host is unreachable or serves broken files, the interface automatically falls back to the device (the page then reloads once), and after several consecutive failures Booster turns off permanently. Turning the switch back on or editing the host address clears the failure record, and the next load tries the host again – the field always shows the saved address. Emergency switch from the browser: append #booster=0 (off), #booster=1 (back on) or #booster=reset (default state, clearing the host address) to the address – no need to delete cookies. |
| Supervisor switch | Service view: the settings pages also show the unnamed (hidden) rows. To add a new element, turn it on, name the element, then turn it off. The change takes effect immediately, without a reload. |
| ?Help switch | Help mode on/off – see chapter 4. The hint bubble appears on the menu bar buttons and on the page controls (not on the items of opened menus). |
| ☀ / ☾ Theme button 4.x | Switches between the dark and light interface. The button icon always shows what it switches to: sun = light theme, moon = dark theme. The change takes effect immediately, without a reload, and the browser remembers it (every further page loads with the chosen theme). The 3.x controller's own (blue) theme cannot be switched. On mobile the button is in the ☰ menu. On the home page a Theme list takes the button's place – see Home page themes. |
| 💾 Save center | The floppy menu on the far right: Save Immediately (small panel: slot + category — the list depends on the page: “All” + the categories of the current page, with the page's own category preselected), Save and Load (full pages) – see chapter 24. On the Broadlink page this is also where Discovery is; on the myIO Link page the immediate save saves directly, without a slot. |
| Message / clock | The right end of the 2nd row shows the controller's feedback (e.g. the result of a save); when there is no message, the date and time. |
After you turn on the ?Help checkbox in the menu bar:
The wizard guides you through setting up the controller: it asks, you answer, and at the end it shows what it is going to write – it touches nothing until you approve. Start it with the Wizard button in the menu bar. On a new, not yet configured controller it offers itself automatically.
At the bottom of every step it tells you where the same can be set manually – so along the way you also learn the interface. The wizard does not replace the settings pages: it sets up what is most common; fine-tuning stays on the respective page.
The order is deliberate: every chapter builds on what the previous one named. Any chapter can be started on its own and stopped at any time – settings already written are kept.
| # | Chapter | What it does |
|---|---|---|
| 1 | Basic settings | Device name, time zone, location – the latter provides sunrise/sunset |
| 2 | Sensors | Naming the detected thermometers and humidity sensors |
| 3 | Outputs | Naming, type (plain / pulse / timed), discovery |
| 4 | Protection | Pairs that must never be on together |
| 5 | Joiner | One output pulls the other |
| 6 | Groups | Name and members |
| 7 | Inputs | Naming a wall switch and all three of its events (short/long press, release) – with learn mode |
| 8 | Logic | Condition outputs: “Daylight”, “Floor is warm” – and blocking with them |
| 9 | Saving | Slot name, operating modes |
| 10 | Automation | Timers and scenes – hands over to the Timer page |
The wizard writes nothing along the way. At the end of the chapter it shows a summary: what it changes, from what, to what. Clicking a row takes you back to the question that produced it, and by removing the tick you can skip individual rows. Before writing – if there is a free slot – it saves the current settings to a separate slot named Before wizard, so you can return to them at any time on the Load page.
Whatever the wizard finds needs no change, it does not write at all: if you run the same chapter twice with the same answers, the second time there is nothing left to do.
If you don't know which number is which load, the wizard switches on the selected output and asks what you see (it can also blink it if you are watching from further away). Before that, however, it asks – and this is not a formality:
If any answer is no or uncertain, the wizard switches nothing – you choose from a list. It always skips alarm-flag and just-ON (pulse, typically gate) outputs, and lists which ones it skipped. The switched-on output turns off by itself if you don't touch it, and also when you close the wizard.
In the input chapter you don't need to know the switch number: press it on the wall, and the wizard shows which one it was. This is harmless – the wizard only watches, it switches nothing.
On newer firmware (3.12.7+ / 4.3.12+) it reads the live state of the digital
inputs (sens_IO.json): the list continuously shows which
input is currently closed, and the state change is the recognition. Since this is a state and not
an event, the 2-second polling can miss a very brief button press – that's why the
wizard asks you to press and hold the switch. With a (bistable) switch this happens naturally.
On older firmware it shows the input that triggered most recently. Which path it takes is not decided by the version number but by trying: if the controller does not provide the live state, the wizard switches over by itself – so it also works correctly when the inputs are not visible due to permissions.
For the selected switch, all three events can then be set in separate sections: short press (hit), long press (press) and release. The target can be an output, a group or loading a saved state – with a search by name. A section you don't touch stays unchanged; the “Nothing” item clears an existing event. At the end of the chapter you can also turn on the NC contact and Block (shutter stop).
The controller has no rule editor, but it has something that can do the same: a logic output that is not physically wired, switched by a sensor. This is the flag.
| Flag | Setting |
|---|---|
| “Daylight” | Sensor = Sun, ON = sunrise, OFF = sunset |
| “Floor is warm” | Sensor = floor thermometer, ON = 28 °C, OFF = 26 °C (ON is the higher one) |
The flag becomes a block through a protection pair: the first member of the pair is the flag, the second is the output to be blocked, and with priority turned on the flag wins. As long as the flag is on, the blocked output cannot stay on.
What you should know: protection switches off afterwards, so on the first attempt the blocked output may flash on for a moment; during the cooldown further attempts do not even start. Good for blocking, but it is not “never even tries” semantics.
Example: “The garden lights may only switch on after dark” → create a “Daylight” flag and block the garden lighting relay with it. Likewise: a “Floor is warm” flag blocks the underfloor heating circuit so that it does not overheat.
On a settings page, right-click any row (on a phone, use a long press) to open the Links panel. It answers the question you ask most often while configuring: “what is connected to this element?” – without having to browse through three pages for it.
The panel is available on the Output, Dimmer, Input and Groups pages. On the name, number and drop-down fields the browser's own menu is kept (copy/paste) – to open the panel, right-click anywhere else in the row.
| Section | What it shows |
|---|---|
| Switched by these inputs | Every button that refers to this output, and whether by short press, long press or release. A disabled input gets a separate mark: the assignment exists but has no effect. |
| Shelly 4.x | For an output: which channel of which Shelly device drives it. For an input: which Shelly input triggers it, and in which mode (button / toggle / hit only). A disabled device gets a mark. See chapter 19. |
| myIO Link 4.2+ | Whether the output is mirrored to another controller: to which server and to which of its outputs. See chapter 20. |
| Group membership | Which groups the element is a member of. |
| Protection / lockout | The other member of the interlock pair, and which one is stronger: blocks this, blocked by this, or without priority mutually exclusive (see chapter 9). |
| Joiner (follow) | Which element it pulls along, or which element controls it (see chapter 10). |
| Controlled by sensor | The thermostat activator and the on/off threshold (see chapter 8). |
| Timers | Every timer that switches this element: the time (for sunrise/sunset with its symbol and the 4.3.8+ offset), the days, and whether it switches on or off. Clicking the row opens the Timer page at the row in question. |
| Own timing | The element's auto-off and switch-on delay, if set. (This is not a timer entry but the output's own setting.) |
| Energy management | Which consumption level of which phase switches it on or off (see chapter 18). |
| What it switches | On an input: the targets assigned to the three events (short/long press, release). |
On an input the real question is usually not what it switches – that is right there in the row – but whether anything reaches it from outside. That's why on an input the panel lists the Shelly link first: the settings table does not show it, because it lives on another page.
Clicking any row of the list takes you to the referenced setting and highlights that row for a few seconds – so from a group or a protection pair you get with one click to the place where you can edit it.
timer.csv file itself once and remembers it until the end of the
session. This file is the same on every firmware version, so the list is complete on older controllers too.
In the same way it reads the Shelly and myIO Link assignments once – but
only if that service is enabled at all, so on an older
controller not a single unnecessary request is sent. Until these arrive, the panel does not show the
“Not assigned” warning.The Assistant button in the menu bar gathers the learning aids in one place: the Setup wizard, Tutor mode and the Tours. All three are available on both 3.12+ (Due) and 4.x (GIGA/Opta) controllers.
Tutor mode teaches through game-like missions: it starts from zero, and every mission walks you through a real,
useful setting on the real pages (not on a separate tutorial screen). The mission steps
highlight the field or button needed right now on the page, explain what we do and why, and
watch whether the step has been completed – for example the typed name, the ticked checkbox, or pressing
the input and the lamp switching over. It polls the input state from the controller every second
(sens_IO.json, 3.12.7 / 4.3.12+), so you need to hold the button for about 2 seconds; meanwhile the row of the pressed
input is highlighted without a refresh. On older firmware it recognises the last pressed input.
The tutor does not enter anything for you: you fill in every field and press every button yourself, exactly as you will later in everyday use. So at the end of the mission not only is the setting done, you have the routine too.
At the bottom of the screen a mission bar shows the mission name, the step number and the current task; the 🔍 button highlights the target again, ⏭ skips the step (the mission will then not be complete), ☰ opens the mission list, ✕ exits the tutor (progress is kept, the menu is full again; you can continue any time under Assistant › Tutor). When you need to change pages (e.g. Outputs → Input), the bar shows where to go, and the Go button takes you there; the mission continues automatically on the new page.
The missions are divided into three levels: Beginner (the menu bar, a lamp on the Outputs page, push button, LED strip, brightness button), Advanced (shutter, shutter buttons, groups, temperature sensors, thermostats) and Pro (joiners, delay, pulse gate, “Daylight” logic output, time zone, winter/summer saves). In the list a completed mission gets a tick; a locked (🔒) mission still lacks a prerequisite – the tile shows which one. A completed mission can be started again. Progress is stored in the browser (separately for each controller); Clear progress clears only the ticks, not the settings.
When the first mission (introducing the menu bar) starts, the settings menu switches to progressive mode: only the menu items that a mission has already taught are visible (plus System › General), and fields not yet learned are dimmed on the pages. This way beginners are not overwhelmed by dozens of settings. You can turn it off any time with the “Show only the menus learned so far” switch in the mission list; progress is kept.
The first step of the missions is turning on the service view (Supervisor): the rows of still unnamed relays, dimmers and inputs are visible only there – this is the first thing to learn on a new controller.
Some missions switch a real output (the lamp, the LED strip). Before the first such step the tutor asks which outputs must not be switched (boiler, pump, gate…), and from then on it does not allow them. Outputs marked as alarm flag and just-ON (pulse) outputs are excluded from the start. If you are unsure, you can skip the step with the “Not switching now” button. It is worth playing through the tutor during wiring, on site, when you can see what starts.
The Tour changes nothing: it highlights the buttons of a page one by one and explains with the help text what they are for. The tour of the menu bar and the home page is meant for the very first login; in the step bubble the “details” link jumps to the matching chapter of the manual.
The home page (Home) is where everyday use happens: here you see the named relays, dimmers, groups and the current values of the sensors.
If you only have read permission for an element, you see its state but cannot switch it.
The Theme list in the home page menu bar (on mobile: in the ☰ menu) selects the look of the home page. The choice is remembered by the browser (separately per browser / device) – it does not touch the controller's settings.
| Theme | What it means |
|---|---|
| Dark / Light 4.x | The two colour schemes of the classic interface. Switches instantly, without a reload, and also applies to the settings pages (where the ☀ / ☾ button switches it). |
| Classic | The 3.x / 2.x controller's own (blue / green) interface; its colour scheme cannot be switched. |
| Cards | A modern card-based home page: collapsible and reorderable sections, favourites, thermostat dial, live refresh without page reloads, its own ☰ menu (Booster, auto refresh, language, theme, zoom, export / import of the settings stored in the browser, log out). Offered only while Booster is on, because its files load from the Booster host (they are not stored on the controller). If the host is unreachable or Booster is off, the home page automatically appears with the classic look; the choice is kept, and the cards view loads again next time. To switch back, use the ☰ Menu → Theme list of the cards home page. The Chart and the settings pages keep their look. |
The Chart page shows daily charts of the logged sensor values (temperature, humidity, consumption, energy meter). Use the date picker to call up earlier days. The logging interval can be set in the settings; without an SD card the controller logs less often to spare the internal storage (chapter 25).
The Output page has one row per relay, with the following settings:
| Setting | Operation |
|---|---|
| Name | The element's name. Only relays with a name appear on the home page and in the lists. |
| Invert | Reversed operation for relays wired as normally closed (NC) instead of normally open (NO): the “on” state releases the relay. |
| Alarm | Alarm-flag output: when any alarm relay switches on, the system enters the alarm state (the mobile app signals it). |
| Hold (Just ON) | Buttons can only switch it on, not off – for motion-sensor / staircase lights where the timer does the switching off. It also stays on when its group is switched off. |
| Timer | Switches off automatically this many seconds after switching on. 0 = no timing. On a just-ON relay, pressing again restarts the time. |
| Delay | Actually switches on this many seconds after the switch-on command. Can be combined with the timer (delayed on, then timed off). |
| Sensor / ON, OFF threshold | Thermostat operation – see chapter 8. |
| Protection, Joiner, Group | See chapters 9, 10 and 12. |
On a Finder Opta controller two pages get a separate section:
On Opta the wall-switch range belongs entirely to the RS485 expanders (see chapter 20/C): the inputs of the expander with address 1 = switches 1–16.
Button,LONG-NETRESET line is written to the event log. The serial btnlock
command disables/enables this (EEPROM 45); when disabled, the button only logs after 5 s./forcefm file manager run (every page redirects there). Use it to repair a damaged configuration or web tree;
afterwards use Restart or a short button press. Bootloader mode still requires a double press of the small recessed RST button,
not the USER button.Any relay (and output/PWM output) can be switched automatically based on a sensor value. In the output's row, select the sensor (activator), then enter the ON threshold and the OFF threshold:
The activator can be: thermometer, humidity sensor, consumption meter, energy meter (SDM) data, sunrise/sunset (the output switches tied to the time of day; an offset can also be set), as well as a universal sensor (activator ID = 300 + sensor ID, chapter 16) 4.x.
On the output pages, uSensors appear in a separate uSensor group of the sensor list; enter the ON/OFF thresholds in the sensor's own unit 4.3+. On the FET mixer and on ON-OFF outputs, uSensor works from version 4.4+ onwards.
Mixer mode – for outputs and PWM outputs: the output does not switch on/off; instead, depending on the sensor value, it settles continuously between the MIN and MAX levels (e.g. mixing valve, stepless fan speed control).
Protection makes two outputs mutually exclusive: the two members of the pair can never be on at the same time. Typical uses are shutters (up/down motor direction) and pump/valve pairs.
The joiner links one output to another: when element “A” switches on, “B” switches on too; when “A” switches off, so does “B”. Typical example: zone valve → boiler start, or extractor fan → damper.
Follow-ON only (Hold): “B” follows only the switch-on, not the switch-off – for example, the pump's after-run is ended by its own timer.
On the Outputs page you set up the dimmable (PCA9685) channels. A channel can work in two ways:
| Setting | How it works |
|---|---|
| MIN / MAX | The level of the “off” and “on” states in %. Any value above MIN counts as on. |
| Fade + Speed | Gradual ramp up/down: with the fade button (or the 4001 ID range) the channel glides up or down at the set speed; pressing again reverses the direction. For mood lighting and wake-up light. |
| Invert / Alarm / Hold / Timer / Delay | They work the same as for relays (chapter 7). The timer is also started by a value set with the slider (or the % field) – the brightness you set is kept, and when the timer expires the channel switches to MIN; moving the slider again restarts the time. Dragging the slider to MIN clears both the timer and the delay. |
| Sensor + thresholds / Mixer | Thermostat and mixer operation (chapter 8). |
A group lets you control several outputs (relays, dimmers, even another group) together. On the Groups page, name the group and select its members.
Watch buttons 3.12.7+: with the small switch at the left edge of the “Input Settings” header, the page polls the inputs every second: the row of a held input is highlighted live, and the last pressed one stays highlighted, without refreshing – so you can check the wiring by pressing the wall buttons (it catches short presses too). The switch position is kept in the browser; if the controller does not provide the states (old firmware or no permission), the page indicates this and stops.
On the Input page you assign the wall push buttons/switches to the outputs. An input can distinguish three kinds of events:
| Event | When? | Typical use |
|---|---|---|
| Short (Hit) | Short press and release | Toggling a light |
| Long (Press) | Holding the button down | Starting a dimmer fade, group, moving a shutter |
| Release | Releasing a long press | Stopping a shutter, stopping a fade |
A separate target can be assigned to each of the three events: relay, dimmer, fade, group or scene (slot load). There is no double-click.
The humidity sensor connectors on pins D26–D33 can be reconfigured as digital LED strip (WS2812) outputs – a pin is EITHER a DHT sensor OR an LED strip. The pin mode is stored in EEPROM and takes effect after a restart; all other settings apply live. The maximum number of strips and LEDs depends on the device (DLEDQ / DLEDMAX factory setting). The driver is DMA-based: a running effect does not slow down the controller or the response time of the buttons.
Connector numbering: both the UI and the event ID follow the order of the board's
connectors (IO1..IO8); the processor pin mapping:
IO1=D32, IO2=D33, IO3=D30, IO4=D31, IO5=D28, IO6=D29, IO7=D26, IO8=D27.
The ID formula: 3100 + (IO−1)×100 + program – so 3101 = program 1 of the IO1
strip, 3800 = full OFF of the IO8 strip.
3100 + (IO−1)×100
+ program. The same ID alternates ON/OFF (toggle). It can be called from a timer the same way.ledstrip.jsn file
(floppy menu – not into a slot); the pin mode is saved by the separate Apply button (EEPROM + restart).There are five built-in game effects that turn the strip into a game played with wall push buttons: 1D Pong (2 players: left/right paddle button), Reaction duel (2 players: after the flash in the middle the faster one wins, pressing early is a fault), Cyclone (1 player: stop the bouncing light dot inside the shrinking target zone), Twang dungeon (1 player: a 1D dungeon played with forward/backward/fire buttons – enemies, lava fields, levels) and Color snake (1 player: with the red/green/blue buttons, shoot the head of the approaching snake with a projectile matching its color – a wrong color sticks).
3100 + (IO−1)×100 + program) or from a timer. Several games can run at once on several strips/segments
(e.g. in two rooms), each with its own taught buttons.On the Timer page you create calendar schedules. A schedule consists of:
Below the list you can see when the next scheduled event is due. The time is synchronized via NTP (an internet time server); its status is shown on the General page.
On the General page, below the Time Zone row, there is the Automatic daylight saving time (EU)
checkbox – on by default. When on, the time zone is the standard (winter) offset
(Central Europe: 1), and the controller adds the summer hour by the EU rule:
on the last Sunday of March at 02:00 the clock jumps to 03:00, on the last Sunday of October
at 03:00 it goes back to 02:00 (both moments are 01:00 UTC). The change takes effect at once: the clock,
sunrise/sunset and the next timer are recalculated, and a System,DST,<0|1>,tz=<offset>
line is written to the event log. Next to the checkbox you can see whether summer or standard time is in
force and the effective offset (e.g. UTC+2).
At the top of the page you can switch views with the List / Week view tabs. The week view shows all timers on the week's 7×24-hour grid: a green block border marks an ON event, a red one an OFF event; a dashed border marks a date-bound row; the ⚠ sign marks a conflict (ON and OFF on the same output within 15 minutes). Today's column is highlighted, and the horizontal line is the current time. Sunrise/sunset timers appear in a separate ☀ lane (the exact minute is calculated daily by the controller), and rows without hour/minute (“all” / “–”) in the strip below the grid. Click any block to edit it in a side panel – saving works the same as in the list. The browser remembers the selected view. On a narrow display, day-selector buttons show the grid one day at a time.
The week view always shows the next 7 days: each column is a specific date (shown when you hover over the header), so timers that are due only later – for example, bound to a given month – appear only in the List view until then (a line below the grid shows how many there are). A row without weekday ticks and with “–” month and day runs every day – the controller and the week view interpret this identically. If several events fall at nearly the same time, they are merged into an “N×” stack block (its caption scrolls like a ticker) – click the stack to choose from a list which one you want to edit. Between an ON event and its matching OFF event, a faint colored band shows from when to when the output will be on – even if the switch-off falls on the next day (the band flows over into the next column at midnight). Right-click an empty part of the grid – or use the New Event button – to create a new timer with that day and time; here you can also choose a duration: then the matching switch-off is created automatically (on the next day if it ends after midnight). The editor dialog opens modally, in the middle of the page. The purple, “F”-marked blocks are the time and sunrise/sunset triggers of the Flow rule engine 4.4+ – they are read-only here; clicking one opens the Flow page.
Flow is automation built from cards: WHEN (trigger) / AND
(conditions) / THEN (actions). Example: “When it is morning AND it is cold AND the sun is shining,
raise the living-room shutter group.” The rules are run by the controller itself (without a browser or
the cloud); the configuration is stored in the root flows.jsn file – it can be saved/copied with the
file manager. Division of labor: plain weekly scheduling is the job of the Timer;
Flow is for conditional logic. At most 64 flows, with up to 8 AND conditions and 16 actions per flow.
$dev,
$flow, $val, $msg placeholders;
the result (HTTP code or error) is visible in the trace.psim.bin (written at most once a day).flows.jsn, and the
engine reloads it immediately – no restart needed. For testing, the manual Fire button starts the chain,
skipping the trigger and the conditions.A Program is Flow's “big brother”: short text programs that the controller itself
runs cyclically – like a PLC. Each program has its own cycle time (e.g. 500 ms or 5 s),
and in every cycle it runs from start to end; the state is kept in variables between cycles.
What Flow cards cannot do: timed sampling, ring buffers and averages, calculating from two
sensors, conditional branches (if … elif … else), edge and timer functions
(rising, ton), loading a slot on a condition, publishing a virtual sensor.
Programs switch through the same protection layer as a button press or Flow – protection
(interlock), the joiner and output timers always apply; a program cannot do more than
a button. Division of labor: weekly scheduling → Timer; simple “if–then”
cards → Flow; calculation, state machine, sampling → Program. If Flow and a
program switch the same output, the last command wins (within one round, the Program runs after Flow).
prog/1.txt … prog/16.txt in the root of the controller's
internal (QSPI) storage (Opta: 1–8). The number is the program's permanent identifier – the card's
#N mark shows it, and it also appears in the log. The name comes from the file header.# at the start of a line):
| Directive | Meaning |
|---|---|
#name Text | The program name (at most 23 characters) – shown on the card and in the log. |
#every 500ms | Cycle time: 100 ms … 24 h. Without a suffix it is milliseconds; s, m, h can also be used (#every 5s, #every 10m, #every 1h). Default: 1 s. |
#enabled 0 | Disabled program (not run by the scheduler). Default: 1. This is the source of truth – the Enable/Disable switch of the Script page rewrites this line in the file, and it stays in effect after a restart. |
#onboot | After loading (startup, reload) the program runs once immediately, without waiting for the first cycle time; in this run boot() = 1. |
// up to the end of the line; comments and empty lines do not end the header.
The same directive twice: the first one is valid, the others count as comments (the editor also rewrites the first one). A directive after the first statement is a compile error (E02).prog/ folder to the SD card, the ZIP backup on the Save page also contains it (prog/1.txt…), and on a blank QSPI it is also restored from the SD card. The source can be downloaded as a whole: /~q/prog/N.txt.prog/N.txt file – after the
latter, the card's Reload button (or !progreload*N. over serial) reads it in again.
The firmware never writes the program file by itself; the only “writer” is your save.http://<controller>/~q/prog/N.txt – it is worth keeping a copy on your own computer too.temp(12) = 21.37 (°C),
out(5) = 0 or 1, hum(0) = 54.3 (%). True = non-zero, false = 0
(true/false can also be written). An invalid value (faulty sensor, no
time sync, wrong ID) has its own marker: NaN. It propagates through calculations, every comparison with it
is false (!= is true), and it can be filtered with the valid(x) function. Division by zero → NaN
(not an error, the program keeps running).var: var a, b = 5, buf[10].
The initial value is set once, at load time; after that the variables keep their values between cycles
(this is the program's “memory”) – reload and restart reset them. buf[10] is a 10-element
ring buffer (at most 64 elements). Undeclared name: error (E10).x = temp(7) - 0.5; special targets: gvar(3) = x
(Flow variable), sens(40) = x (publishing a virtual sensor), pca(2001) = 128
(dimmer value).if condition then … elif condition then … else … end. It can also be written
on one line, with several statements separated by spaces: if not valid(t) then off(3) return end. Nesting
is limited to 8 levels. return = the cycle ends here (the rest is skipped); there are no loops
(for/while) – so the duration of a run is guaranteed to be bounded.or < and < not
< comparison (== != < <= > >=) < + - < * / %
< sign; parentheses can be used freely. Comparisons cannot be chained (instead of a < b < c write
a < b and b < c). and/or always evaluate both
sides – so edges (rising) are updated in every cycle even inside a condition.rising(x) / falling(x) / changed(x)
signal a transition relative to the previous cycle (true for one cycle; there is no edge in the first cycle).
ton(x, s) is true if x has been continuously true for at least s seconds
(on-delay, “quiet watcher”); tof(x, s) stays true for s seconds
after x is released (after-run). Each such call in the source is a separate “memory cell”, so
make sure return comes after the edges – otherwise they are not updated in that cycle.push(a, x) adds a new sample (if the buffer is full, the oldest one drops out);
avg(a) sum(a) min(a) max(a) calculate from the stored values (skipping invalid samples);
count(a), full(a), clear(a); a[0] is the latest sample,
a[1] the one before it (a sample that does not exist yet: NaN). An element cannot be overwritten directly –
only with push.out("ping"),
loadslot("Safemode") – see names) and in the log message of
log("text", x) (at most 47 characters).IDs are the unified identifiers: relay (logic output) 1–64, group 501–, output (dimmer)
2001–; input 1–, 1Wire thermometer 1–100 (EEPROM ID), universal sensor 0–100. Where the description says so, you can
write the name set in the controller in quotes instead of the ID (the controller resolves the name when the program
is saved; if there is no such name, it reports an E12 error). Every value is a number; the invalid value is
NaN: it propagates in calculations, is false in conditions, and can be checked with valid(x).
In the editor's reference panel, hovering over any item shows the same short description.
| Function | Description |
|---|---|
out(id|"name") | The current state of the output: 1 = on, 0 = off. Works for relays, groups and dimmers (for a dimmer: 1 if it is above the minimum). Unknown ID → NaN.if out("Boiler") then … |
pca(id|"name") | The current value of the dimmer (PCA output), 0–255 (255 = full brightness). Non-dimmer ID → NaN.if pca(2003) > 128 then … |
remain(id|"name") | How many seconds are left of the output's running timer (the Timer field in the output settings). 0 if no timer is running; NaN for a group.if remain(5) < 10 then log("expiring soon") end |
in(n|"name") | The state of the input (switch, push button, sensor contact): 1 = pressed/closed, 0 = open. After NC inversion, i.e. as the controller sees it.if rising(in("Motion")) then on(12) end |
| Function | Description |
|---|---|
temp(id|"name") | 1Wire thermometer value in °C, with decimals (e.g. 21.37). The identifier is the thermometer's EEPROM index (shown on the Sensors page) or its name. Missing or faulty sensor → NaN – so it is worth guarding it with valid().t = temp("Living room") |
hum(ch) | Humidity in % from the DHT sensor channel (0–7).if hum(0) > 70 then on("Fan") end |
sens(id|"name") | Universal sensor value (a Shelly, MQTT, KNX or web-injected value, on its own scale). If the sensor is stale (not updated within the set time, STALE), it returns NaN – so the program detects the dropout.if not valid(sens(30)) then log("sensor lost") end |
sdm(201…221) | The fields of the SDM energy meter (201 = power 1, 202 = voltage 1, 203 = current 1, then phases 2 and 3, apparent/reactive power, power factor, angle) on the Sensors page scale. Other ID → NaN.push(w, sdm(201)) |
gvar(n) | The n-th global variable of the Flow rule engine (1–32) – an integer. This is how Flow and Program “talk”: what Flow sets, the program sees, and vice versa.if gvar(1) == 1 then … end |
| Function | Description |
|---|---|
hour() minute() second() | The hour (0–23), the minute and the second. Without time sync (no NTP) all of them are NaN – a condition built on them is then false. |
dow() | Day of the week: 1 = Monday … 7 = Sunday.if dow() >= 6 then … end (weekend) |
tod() | Minute of the day: hour·60 + minute (e.g. 22:30 → 1350). The most convenient for time windows.este = tod() >= 20*60 or tod() < 6*60 |
sunrise() sunset() | Today's sunrise / sunset, also as minute of the day (according to the controller's geographic settings).if tod() >= sunset() then on("Garden light") end |
timesync() | 1 if the controller's clock is valid (NTP sync has happened), otherwise 0. |
slot() | The number of the currently loaded configuration slot. |
uptime() | Seconds since the controller started. |
dt() | Time elapsed since the previous run, in seconds (in the first run: the cycle time). For integration and rate calculations: osszeg = osszeg + fogyasztas * dt(). |
runs() | How many times the program has run since it was loaded. |
boot() | 1 in the first run after startup (if the program has #onboot), otherwise 0. |
| Function | Description |
|---|---|
abs(x) | Absolute value. |
min(a, b) max(a, b) | The smaller / larger of two numbers. (Called with an array name, it returns the smallest / largest element of the array – see below.) |
clamp(x, lo, hi) | x limited to the [lo, hi] range – e.g. when calculating a dimmer value: set(2001, clamp(p * 255, 0, 255)). |
round(x) trunc(x) | Rounding to the nearest integer / truncation to an integer (−2.7 → −2). |
valid(x) | 1 if x is a valid number, 0 if it is NaN. Use it to check every sensor reading before you rely on it. |
nan() | The invalid value itself – e.g. as the “no data” result of a calculation. |
| Command | Description |
|---|---|
on(id|"name") off(id|"name") toggle(id|"name") | Output on / off / toggle. It takes the same path as a button or the web page: the protection and joiner rules and the output's own timer and delay apply. On a thermostat-controlled (activator) output the command is refused – there the thermostat is in charge. A repeated on on an output that is already on does nothing (it does not restart the timer either).if rising(hideg) then on("Boiler") end |
set(id, v) / pca(id) = v | Dimmer value 0–255 (values outside the limits are clipped). For a relay/group: true (non-zero) → on, false → off.set("Pipe heating", round(p * 255)) |
gvar(n) = v | Writes the n-th global variable of Flow (rounded to an integer). Flow triggers based on the variable are evaluated immediately – so a program can start a Flow chain.gvar(7) = n |
sens(id) = v | Virtual sensor: publishes the value (rounded to one decimal) as a universal sensor – the Sensors page, Flow, MQTT and logging see it as a real sensor. The sensor must be enabled on the Sensors page. NaN → no update (the sensor's failsafe sets it to STALE after a while).sens(41) = avg(w) |
loadslot(n|"name") | Loads a configuration slot (scene) by its number or by the name given on the Save page. At most once every 10 seconds per program – and call it only on an edge (rising), otherwise it would reload in every cycle.if rising(lost) then loadslot("Safemode") end |
log("text"[, x]) | Writes a line to the event log and to the program's trace (text and/or a value). At most one every 1 second per program; the rest are silently skipped.log("outside", kint) |
msg() | Notification in the menu bar (M67 message) – the details are in the program's trace. |
hook(slot[, v]) | Fires the webhook (1–8) configured on the Flow page; $val is replaced with v. Asynchronous: the program does not wait for the request.hook(2, t) |
stop() | The program stops itself (it goes into the disabled state). To restart it: Enable or Reload on the Script page. |
A var a[10] declaration creates a 10-element ring buffer (at most 64 elements, 256 in total
per program). Each push discards the oldest element when the ring is full. Use it for moving averages, minimum/maximum
monitoring and storing history.
| Operation | Description |
|---|---|
push(a, x) | Adds a new value to the ring. NaN goes in too (so a faulty sample “ages out” naturally). |
avg(a) sum(a) | Average / sum of the valid elements – NaNs are skipped. If there is no valid element → NaN. |
min(a) max(a) | The smallest / largest valid element. |
count(a) | How many elements are in the ring (from 0 up to the capacity, NaNs included). |
full(a) | 1 if the ring is full (it holds as many samples as its capacity), otherwise 0. That is why the two-average example waits for 10 samples before deciding: if full(a) and full(b) and avg(a) > avg(b) then … |
clear(a) | Empties the ring (count = 0). |
a[i] | One element: a[0] is the most recently added one, a[1] the one before it… Non-existent index → NaN. An element cannot be overwritten directly, only with push. |
These calls have their own hidden state (separate for each call site), which persists between cycles –
that is why they work like the rising-edge detectors and TON/TOF timers of PLCs. In the first cycle no edge is signaled. Important:
edge detectors after return are not updated in that cycle, and both sides of and/or are always
evaluated (no “short-circuit”), so that no edge is lost.
| Function | Description |
|---|---|
rising(x) | Rising edge: 1 in the one cycle when x changes from false to true, otherwise 0. Use it to issue a command “once” for a lasting state.if rising(in(5)) then n = n + 1 end |
falling(x) | Falling edge: 1 when x changes from true to false. |
changed(x) | 1 when the truth value of x has changed in either direction. |
ton(x, s) | On-delay (PLC TON): 1 if x has been true for s seconds continuously; if it turned false in the meantime, the timing starts over. For debouncing and “sustained” conditions.lost = ton(not out("ping"), 12) |
tof(x, s) | Off-delay (PLC TOF): true while x is true, and for another s seconds after the release is detected. For after-run (e.g. let the fan run for 3 more minutes).if tof(in("Bathroom light"), 180) then on("Fan") else off("Fan") end |
At most 16 commands can be issued in one cycle (the rest are dropped – the card's “dropped commands” counter increases). On a thermostat-controlled (activator) output the command is refused: there the thermostat is in charge (a “refused” entry appears in the trace). A wrong ID returns NaN when read, and is refused as a command.
Outputs, inputs, thermometers and slots can also be referenced by the name set in the UI:
out("Ping"), on("Living room lamp"), in("Motion hallway"),
temp("Supply"), loadslot("Safemode"). The controller looks up the name in the descriptor files when the program
is loaded (output: in relay → group → dimmer order; input;
thermometer; slot) – upper/lower case does not matter, nor do spaces at either end of the name. If the name is not found
(or there is no such slot), the program stops with an E12 compile error until you fix the name or the source
and reload it. So after renaming something, you must reload the program too. Specify the universal
sensor (sens) by its number.
1. Ping guard – a remote server switches on the logic output named “ping” every 5 seconds;
this output has a 5 s output timer. Important fact: switching on an already-on relay again does NOT restart
its timer – so despite the pings, the relay can flip off and on
(5 s ON, then possibly ~5 s OFF until the next ping). That is why we do not look at the raw state, but at
12 s of continuous silence: ton flips only if the output has been OFF for 12 seconds
without interruption. rising makes sure the slot is loaded once,
and falling signals re-arming.
#name Ping guard
#every 500ms
// The server sends on("ping") every 5 s; the "ping" relay has a 5 s timer.
// FACT: switching on an already-ON relay again does NOT restart its timer
// -> despite the pings the relay can flip off and on (5 s ON / possibly ~5 s OFF).
// So we watch for "12 s of continuous silence", not the raw state.
var lost
lost = ton(not out("ping"), 12) // true if there has been no ping for 12 s continuously
if rising(lost) then // ONLY at the moment of the transition -> once
loadslot("Safemode")
log("ping lost - Safemode")
end
if falling(lost) then log("ping is back") end // re-armed
2. Average comparison – every 5 seconds it takes a sample from two thermometers and
compares the averages of the last 10 samples: if average A is higher, output 5 is ON, otherwise OFF.
full() waits until both buffers are full; avg skips faulty samples (NaN).
#name Average compare #every 5s var a[10], b[10] push(a, temp(12)) // 1Wire thermometer 12 (°C; NaN goes in on error) push(b, temp(13)) if full(a) and full(b) and avg(a) > avg(b) then // avg skips NaNs on(5) else off(5) end
3. Heating with hysteresis and fail-safe – the setpoint is the initial value of the var
(visible on the Variables panel); on a sensor fault the heating switches off and the cycle ends,
otherwise it switches within a ±0.3 °C band.
#name Heating living room #every 2s var want = 21.5 // setpoint, initialized at load time var t t = temp(7) if not valid(t) then off(3) return end // sensor fault: heating off if t < want - 0.3 then on(3) elif t > want + 0.3 then off(3) end
4. Supply–return difference – publishes the difference of the two thermometers as a virtual sensor (universal sensor 40: visible on the main page, in the chart, in Flow and over MQTT), and switches the pump based on the difference. On a faulty measurement (NaN) it does not switch and does not update.
#name Supply-return delta #every 10s var d d = temp(21) - temp(22) sens(40) = d // usens 40: seen by UI/Flow/MQTT; NaN = no update if valid(d) and d > 15 then on(9) elif valid(d) and d < 8 then off(9) end
5. Night motion light – between 22:00 and 6:00 the motion sensor (input 17) switches the light on with its
turn-on edge; after 3 minutes of quiet, ton switches it off.
Without time sync tod() is NaN → “night” is false, and the light does not switch on by itself.
#name Night motion light #every 200ms var night night = tod() >= 22*60 or tod() < 6*60 // no time sync -> NaN -> false if night and rising(in(17)) then on(12) end // motion edge: ON if ton(not in(17), 180) then off(12) end // 3 minutes of quiet: OFF (idempotent)
6. Gate counter with a Flow bridge – counts the turn-on edges of input 5, writes the value into Flow global variable 7 (Flow var triggers react immediately), resets at midnight, and sends a notification to the menu bar at 100 openings.
#name Gate counter #every 100ms var n if rising(in(5)) then n = n + 1 end if rising(hour() == 0) then n = 0 end // resets once at midnight gvar(7) = n // Flow var trigger sees it immediately if n >= 100 then msg() n = 0 end
#N, status badge (running /
disabled / ERROR / empty), cycle time, number of runs, last run, the longest run
(max µs), dropped commands, lag. On an error the card shows the line:column and the error code
(e.g. 12:5 E10 ismeretlen nev (var kell): foo – “unknown name (var needed)”).#enabled line and saves), Run now
(one cycle immediately – even on a disabled program), Reload, Delete.vars can be monitored here.
Global variables: the 32 Flow variables, which can also be edited by hand.?prog..On an error only the program concerned stops (ERROR state); the others keep running. The
M66 message appears once in the menu bar, and a Prog,N,err,… line is written to the event
log. After you save the fixed source (or use Reload), the program runs again.
line:column Exx text): E01 unknown character · E02 directive
in the wrong place / unknown directive · E03 name or number too long · E04 missing/expected element (e.g.
then, end, parenthesis) · E05 #every out of range · E06 string
in the wrong place · E10 unknown name (var missing) · E11 unknown function · E12 the name
cannot be resolved (no such output/input/thermometer/slot) · E13 ID out of range · E14 wrong
number of arguments · E15 command in an expression · E16 not assignable · E17 reserved name · E18 array/scalar
mix-up · E19 var not at the beginning of the program · E30–E39 limits: expression too complex, program too
long, expression nested too deep, too many variables, array size, too many edges/timers, too many constants,
too many strings, if nested too deep, source too long.Rxx): R01 instruction limit (2000 steps/run) · R02
time limit exceeded (one run over 2 ms, 3× in a row) · R03–R05 internal error (stack, instruction, jump).
Not errors, only counters: R06 refused command (wrong ID, activator output, missing slot, slot loads
too frequent), R07 command flood (more than 16 in one cycle). NaN is never an error – the program keeps running.rising,
ton… in the source), 8 levels of if; on Opta the limits are halved.log
once per second, loadslot once per 10 s, per program.hook goes through the webhook targets of
Flow). Every command, without exception, acts through the protection / joiner / timer layer.MIA (Settings → Control → MIA) is the caretaker of your house, a self-learning, observing layer: every minute the controller looks at every heating-type thermostat binding (thermometer + logic output, ON threshold < OFF threshold) and learns how the house behaves, zone by zone. As an observer it switches nothing – the thermostat, the timer, Flow and Program work exactly as before; you can switch MIA on and off at any time, and while it is off the learned values are kept. No cloud, no subscription: all data stays on the device. From 4.12 two intervening functions can also be enabled (disabled by default): self-learning pre-heating and pause heating on open window.
What it learns (per zone):
Open window detection: if the zone temperature drops within a short time (default 0.5 °C / 5 min) much
faster than the learned cool-down, the zone is flagged “window open?”: a log line (System,AI,WIN,R<n>,open)
and a message in the menu bar (M68, at most once per hour). The flag clears when the temperature recovers (+0.2 °C) or after the set
time. The flag on its own switches nothing; to pause the heating, enable the heating pause
function (4.12+).
Setup: switch MIA on (Enabled), leave the two observing functions on, and wait 1–2 days: the “Learned” column shows how reliable the numbers already are. The “Forget” button deletes the learned values (worth doing after replacing the boiler/radiators or after insulation work). The grey functions marked “coming soon” (overshoot-free control, consumption anomaly) arrive in upcoming releases.
If the comfort/eco switch is done by the timer (loading a slot's relays.xml:
a “Load All” or “Load Logic outputs” event), and the new slot gives a zone a higher ON threshold, MIA uses the learned
data to calculate how much earlier it has to start heating: dead time + (target temperature − now) × min / 1 °C, multiplied by a
learned correction (k). That much before the switch – at most the set number of minutes (default 120) – it
switches on the zone's heating, so the room is already at comfort temperature at the moment of the switch (the equivalent of Loxone's “intelligent
heating control” / tado “Early Start”).
Log lines: System,AI,PRE,R<n>,start,<min>,<T>,<target>,<hh:mm>,
…,done,<deviation min>,<T at target>,<k>, …,abort,<reason>; menu-bar message: M69.
Enabled together with open window detection: while a zone is flagged “window open?”, MIA keeps the zone's relay
switched off (otherwise the thermostat would start heating immediately because of the sudden cold), and when the flag clears it
hands it back to the thermostat. Safety: below 8 °C it releases immediately for frost protection; manual switching cancels it.
Log lines: System,AI,WACT,R<n>,hold / …,release,<reason>; menu-bar message: M70.
ai.jsn file (File manager) and are kept across restarts.A conventional thermostat switches off at the OFF threshold – but the radiator, and floor heating in particular, keeps heating afterwards (a floor by as much as 0.5–1 °C), and after the ON threshold the room keeps cooling. MIA learns this post-rise and dead time (it measures them during conventional operation too), and when you enable Overshoot-free control it switches OFF or ON earlier, inside the band, so that the peak stays right at the threshold. It never crosses the thermostat's band, needs no hold, and where the estimated gain is below 0.1 °C (a well-sized radiator) it does not intervene.
What you gain: with floor heating 0.6 → ~0.0 °C overshoot (time spent in the band 28 → ~90 %), with radiators 0.3 → ~0.05 °C – the MIA page shows the “before → after” overshoot figure per zone. The number of switchings increases (on a floor ~6 → 12/day); the per-zone min. ON/OFF time (0 = automatic: 10 min, 30 on floor heating) protects the boiler, the pump and the relay. MIA excludes relays with a delay or an auto-OFF timer automatically; any zone can also be excluded manually. It works on (universal) sensors with 0.1 °C resolution, but with more scatter – for a floor-heating zone a 1Wire thermometer is recommended.
System,AI,TPI,R<n>,off|on,<T>,<estimated>,<lead time>.The controller's own sensors:
Every sensor is logged and can be viewed as a graph on the Chart page, and can also be used as a thermostat activator.
A universal (network) sensor is a “receiving slot” into which a value arrives from outside: from a Shelly device (chapter 19), from another myIO (chapter 20) or from any device of your own (via the API). You set it up on the Sensors page:
In a thermostat, refer to the sensor by the activator number 300 + ID (e.g. the activator of universal sensor 3 is 303).
The myIO DewPoint card on the Sensors page handles a dew point sensor connected to the serial port: the sensor loads the dew point value into universal sensor 0, and the controller sends the % value of a selected dimmer channel back to the sensor (converted into a 0–10 V output). You can set the output channel sent back and the send interval. It is meant for condensation protection of cooled surfaces (e.g. wall cooling): the dew point value can be used as a thermostat activator (ID 300).
3.12.3+
The DUE (V3) controller has no universal sensors: here you enable it in the “myIO DewPoint protection”
block of the Setup page. The incoming dew point value is written into a selected thermo sensor
EEPROM-ID (on a ×10 scale: 4095 → 409.50), so it can be used as a thermostat activator with the usual
thermo sensor reference. Here you can also choose the source output of the PWM output (only
PWM-marked channels) and the send interval (max. 300 s). The settings are saved into the slot's
globals.xml (global save).
The eManager page handles the SDM three-phase energy meter connected to the serial port: power, voltage, current, power factor etc. can be read and logged per phase.
Consumption levels (load limiting): per phase you can set an UP and a DOWN threshold. If the measured consumption rises above the UP threshold, the controller steps up a level and switches off the output assigned to that level; if it drops below the DOWN threshold, it switches it back on. This way large consumers (water heater, air conditioner, EV charger) can be switched off automatically so that you do not exceed the supply limit.
The SDM values can also be used as thermostat activators (e.g. “if the power of phase 1 is above 2000 W → signal lamp on”).
SG-Ready is a common interface standard of heat pump manufacturers: two potential-free (dry) contacts encode four operating modes for the unit. Two designated myIO outputs (K1, K2) provide the two contacts — each can be either a relay (R1..R64) or an output (P1..) (for an output, ON = the set ON level, OFF = the OFF level):
| Operating mode | K1 | K2 | Meaning |
|---|---|---|---|
| 1. Blocked (EVU lock) | ON | OFF | The heat pump's compressor is blocked (frost protection stays active internally). Only with a time limit! |
| 2. Normal | OFF | OFF | Normal, energy-efficient automatic operation. |
| 3. Recommended ON | OFF | ON | Switch-on recommendation (e.g. during cheap electricity or PV surplus) – typically a raised target temperature. |
| 4. Forced ON | ON | ON | Definite start command (within the unit's control limits). |
Setup: on the Smart Grid page (Settings → Link → Smart Grid) select the K1 and K2 outputs (two different outputs; typing in the search field narrows the list — R = relay, P = output), enable the function, and set the EVU lock time limit (default 120 min – the standard's 2-hour ceiling). The mode is changed by a timer (Timer page, “SG-Ready mode” event, target = mode 1..4) or with the manual buttons on the page – so you can split the week into time bands for which mode applies when (cheap night-time electricity → Recommended ON; peak time → Blocked; etc.).
Interlock: the controller re-asserts every minute the correct state of the contact pair, so manual switching or switching from another source lasts at most ~1 minute. The designated outputs are shown with an SG badge on the Logic outputs and Outputs pages.
myIO handles Shelly smart devices (relays, dimmers, sensors) on the local network as if they were wired into the controller. On the Shelly page:
Polling only affects detecting changes coming from outside: pressing the wall switch, switching from the Shelly app and sensor value updates. A command issued from myIO (button, timer, group, protection) goes out immediately regardless.
So it is worth polling important devices – lamp relay, switch – frequently (0.5–1 s), and non-urgent ones – solenoid valve, irrigation, rarely changing sensor – rarely (10–60 s). The controller runs one outgoing request at a time, shared by the Shelly devices, myIO Link and the cloud, so polling the non-urgent devices less often directly improves the response time of the important ones.
On the Link page two (or more) myIO controllers can be connected over the network:
Devices are added by discovery or by entering an IP address; you also have to enter the remote controller's username/password. The connection state (online, bad credentials) is visible in the list.
Discovery also finds the older, Due-based (3.x) controllers – these are marked with a DUE badge in the results list and in the server header. The Due does not report a device name, so Test fetches its serial number as the name; a custom name typed into the Name field is kept, Test does not overwrite it. In the link rows, when you type the ID manually, the type and name drop-downs follow the typed ID immediately.
Changes take effect immediately, but until you press the Save Immediately button of the navigation bar they are only
runtime changes – without saving they are lost on restart. The link configuration is stored not in a slot but
in the root of the SD card (link.jsn), so here the button saves directly, without a slot
selector; a ✔ flashing on the button confirms a successful save.
(Identifying and deleting a server saves automatically.)
On the MQTT page (Link menu) the controller publishes its events and states to an MQTT broker – for example to Home Assistant, Node-RED or any MQTT-based system. By default the client only sends (publish-only); from 4.5 it can also receive, as described in the Subscribe section (control + external sensors).
DUE (3.12.31+): the same topic scheme, Home Assistant Discovery and control as on
4.x – in HA the DUE and the GIGA/Opta controllers appear the same way. Not available on the DUE:
TLS, economy mode, universal and external MQTT sensors, RS485 expander; the broker can be entered by
IP address only (not by hostname). The settings are saved into the controller's EEPROM
(not into mqtt.jsn), so the broker password never ends up on the SD card. The
persistent MQTT connection occupies one network (W5500) socket: with MQTT enabled the
web server accepts 2 parallel connections instead of 3. On an old W5100 (4-socket) board
MQTT only fits if the app port (843) and mDNS are disabled – the page warns you in that case.
myio-<name> or myIO/<name>)..../button/<id> = H/P/R:
short press = H; long press = P, then R on release – the gesture is sent even if
no event is assigned to it), output change (.../relay|pca|fet/<id>
= 1/0), sensor activator (.../sensact/<id>). For a Shelly-bound
switch the Shelly's mode decides: "button (H/P)" mode does not send R, "toggle (P/R)" mode does.1, 0, 23.50.homeassistant/... topics – they appear in HA
automatically. A relay/dimmer/PWM only gets an HA entity if it is named (the
raw topic of unnamed outputs still works); sensors are created with the unit matching their
type (°C, %, W, ...); 4.16.2+ also with state_class (measurement, total_increasing for the kWh sensor), so HA collects long-term statistics, and the kWh sensor can also be added to the Energy dashboard. Availability is
indicated by <base-topic>/status (online/offline, LWT).Changing a field writes the running config immediately – the enable checkbox
connects/stops right away. Saving (Save & Apply) writes to the root mqtt.jsn
file (on the DUE to the controller's EEPROM) (without it the setting is lost on restart), and reconnects the client with the
fresh settings. The connection state and the dropped-events counter are shown in the right
column of the page; serial diagnostics: the ?mqtt. command.
TLS encryption 4.4+.
With the TLS encryption checkbox the connection to the broker is encrypted (BearSSL, TLS 1.2).
The broker is authenticated not by a certificate chain but by a pinned public key: enter the
broker's EC P-256 public key into the Broker pinned key field
in base64url form (myIO Cloud provides it at pairing; for your own broker it can be
derived from the server key). With TLS enabled the port is typically 8883. If the controller
cannot obtain a reliable hardware random number, the connection is not established (fail-closed) –
better no TLS than sham encryption with weak keys. Encryption can be used both with your own broker
(e.g. Home Assistant) and with myIO Cloud.
Enabling the Subscribe section lets the client receive as well: after CONNACK it subscribes to the configured topics, and the connection stays open permanently (economy mode has no effect then – the page indicates this). Every saved setting takes effect without a reboot, with an immediate reconnect.
<base-topic>/relay/<id>/set and
<base-topic>/pca/<id>/set topics switches.
Relay payload: 1/ON, 0/OFF,
T (toggle). Output payload: number 0..255 = brightness (0 = off), or the text
ON/OFF/T. The command runs with the permissions of the selected
user slot (0 = admin) – with a narrower slot, the controllable set can be limited by the
per-element permissions of user management. The result of the switching is reported back immediately on the usual
state topic (.../relay/<id> = 1/0)..../pca/<id>/bri topic); an output
without PWM 3.12.34 / 4.18.8+
as an on/off-only light (no brightness slider – it can only be 0 or 255 anyway); after switching
the output's PWM mode the entity follows automatically;
with control disabled they revert to binary_sensor. The switch-over cleans up the old
entity configs automatically.21.5) or JSON – for the latter enter the JSON key (e.g.
temperature for the zigbee2mqtt message {"temperature":21.5});
ON/true = 1, OFF/false = 0 (as a threshold: 10/0 on the x10 scale).
Warning: binding the device's own publish topic creates a self-loop.Diagnostics: the right column of the page (received commands/values, denied
subscriptions), and the serial ?mqtt. command (subph,
cmdrx, senrx, last fields).
On the myIO Cloud page (Link menu) the controller connects to the myIO cloud broker, so the myIO app can reach it over the internet as well – without port forwarding, DDNS or VPN. The connection is always TLS-encrypted (with a pinned broker key), and the cloud client runs alongside the own-broker MQTT client, independently of it (both at once only on an Ethernet connection; on Wi-Fi the own-broker client has priority because of the few available sockets).
The essence of the security model: commands are signed not by the cloud but by the user on their phone (ECDSA signature; the private key never leaves the phone). The controller verifies the signature with the public key stored at pairing, and executes the command with the permissions of the user slot selected at pairing – with the same permission rules as the local interface. So even a compromised cloud server cannot forge commands; replay is ruled out by a counter check.
Changing a field writes the running config immediately; saving
(Save & Apply) writes to the root cloud.jsn file and reconnects.
Key operations (pairing, revoke, delete) save themselves. Serial diagnostics:
the ?cloud. command.
What does the QR contain? The broker address, the device ID, the device's public key, the broker's pinned key, the one-time pairing token and, 4.5+ the app's broker credentials. It does not contain a secret user key – that is created on the phone. The token is valid until the window closes, so you still must not publish the QR.
The app's broker credentials 4.5+ –
the two new fields of the Cloud page (App broker username and App broker password). myIO provides these
for the controller at installation; the controller only passes them on in the pairing QR so that the phone
can connect to the cloud broker at all. You do not need to type them in, and the phone's
owner does not need to know about them. Important: this only opens the connection – what the
phone may switch is still decided by its own key received at pairing and by the permissions of the user level
assigned to it. If the fields are left empty, the phone tries to connect anonymously;
the myIO production broker rejects this (the error message: notAuthorized).
Broker update (broker.new) 4.4+ –
if the address of the myIO cloud broker changes (relocation, provider change), no new
firmware is needed: myIO issues a vendor-signed broker.new file, which you can load on the
Cloud page with the Upload button. The controller verifies the signature with the built-in vendor
key, and based on the file's sequence number (seq) it rejects an old or
replayed file – so it cannot be redirected to a fake address even if the file
falls into the wrong hands. After a successful upload the new address takes effect immediately (save +
reconnect are automatic), and the page shows the sequence number of the accepted update. If the
setting is already exactly what the file contains, the controller indicates this
(already up-to-date) – it does not write or reconnect needlessly.
It also recovers by itself 4.4+ –
uploading the file manually is not mandatory. If the cloud connection does not recover for a while
(a few minutes), the controller first makes sure that there is internet access at all
(with a neutral, quick check), and only then fetches the fresh
broker.new from the vendor's address, then switches to the new broker by itself. As long as the cloud
works properly – or as long as there is no internet – the controller does not contact the vendor's
address at all. As a last resort the file can also be obtained by e-mail, and loaded at any time with the
Upload button above.
Mirror brokers 4.4+ –
if broker.new also contains backup (mirror) brokers, they are listed on the Cloud page.
On a connection failure the controller tries the list in order, then
connects to the one that worked last, even after a restart. On a new outage
it always starts again with the primary broker – in case it has come back in the meantime. The list
comes from broker.new and cannot be edited manually.
Vendor notice 4.5+ –
while the cloud connection is alive, the controller also listens to a vendor message channel. Through it
myIO can address the devices: maintenance, important information, and the
broker.new above can also arrive this way (so an address update takes effect immediately, without
waiting for the fallback query). The message is signed by the vendor, so not even
a compromised cloud server can send a fake message – at most it can suppress one, which is why the
automatic query remains as the last lifeline. The notice appears at the top of the Cloud page
(the Got it button hides it), and is also written to the Event log.
Messages (history) 4.7+ – a vendor message is not lost when you press Got it: the controller keeps the latest messages (up to 6) on the hidden system volume, and the Messages section of the Cloud page shows them even after a restart. “Got it” only marks a message as read – an unread message is shown in bold with an “unread” label. The user cannot delete a message (and the cloud server cannot revoke it without a signature): whatever the vendor has sent once remains retrievable here until newer messages push it out.
Hiding broker data on production firmware 4.5+ – on production (prod) firmware the Cloud page does not display the broker's address, port, credentials, pinned key and the resolved broker IP: a “hidden” marker is shown in their place, and they cannot be edited either. There is no need to: the address comes from the factory-burned list and the signed broker.new file. So the data does not leak even if someone takes a screenshot of the Cloud page or forwards the page. On development firmware everything is visible and editable. (The pairing QR works regardless – it necessarily contains the address, since the app has to connect using it.)
What does “pinned key” mean? For the TLS connection the controller does not trust certificate authorities (CA), but exclusively this single key: the handshake only succeeds if the other side signs with it. So a man-in-the-middle server or an expired or fake certificate is ruled out from the start, and no certificate store has to be kept on the device. The key is public (not a secret) – it is just operational data, which is why we hide it on prod firmware.
What this channel can NEVER do – this is the most important design decision: a vendor message cannot control anything (relay, output, scene), cannot run a command, cannot query data or logs, and cannot install firmware on its own. Outputs are moved exclusively by your own signed command, authenticated by your phone's private key – the vendor cannot interfere with that. A message of unknown type is dropped by the controller.
License 4.6+ – the License section of the Cloud page shows the purchased features, the expiry of the annual entitlement (with a countdown) and the app-pairing quota (used / allowed). A new or renewed license can arrive in two ways: automatically via the cloud (while the cloud connection is alive, the controller receives and automatically acknowledges it), or as a code received by e-mail, which you paste here into the input field and load with the Apply button – this route works even when the cloud is not reachable. The license is vendor-signed and bound to this single device: the controller rejects a forged code, a code issued for another device, or an older (replayed) one; the latest issue is always the valid one, regardless of which route it arrived by. Important: the actual gate of the annual Cloud entitlement is on the cloud server – local (LAN) operation is not affected by the expiry in any way, and an expired device can receive the renewal the same way.
On the BroadLink page you can add BroadLink-based Wi-Fi devices (smart plugs, Computherm Wi-Fi thermostats): after discovery and pairing a plug can be bound to a myIO output (bidirectional sync, with a child-lock switch), while a thermostat is managed on the Computherm subpage.
The Enable BroadLink checkbox and the Discovery button are at the top of the BroadLink page
(following the pattern of the Shelly and myIO Link subpages – previously enabling was on the Setup
page and Discovery in the save menu of the navigation bar). Enabling takes effect immediately, without a
restart; with the integration disabled the Discovery button is inactive. The list of paired
devices is not stored in a slot but in the root of the SD card (brlink.jsn) –
you save it with the Save Immediately button of the navigation bar; without saving, the
pairings are lost on restart.
The state of the enable checkbox is written immediately to the controller's non-volatile memory and survives a restart – no slot needs to be saved for it, and switching slots does not overwrite it either. When enabled, the controller automatically re-initializes the pairing keys and starts a discovery, so previously paired devices (e.g. a Computherm thermostat) re-authenticate without any intervention; when disabled, the allocated UDP socket is released immediately.
Reachability indicator and offline protection 3.12.4+ / 4.3.5+ –
a green/red dot in the header of every paired device shows whether the device is reachable on the
network. If a device does not respond (e.g. it is powered off or has dropped off Wi-Fi), the
controller marks it offline, and from then on retries it only once per minute –
previously the timeouts of the once-per-second retries noticeably slowed down the whole controller.
As soon as the device responds again (or a Discovery finds it), the indicator
turns green by itself and normal polling resumes; a switching command issued in the meantime
is not lost, it is sent on the retry. The transitions are also written to the event log
(BL-OFFLINE / BL-ONLINE lines). In addition, Discovery no longer
blocks the controller while it collects the responses (it runs asynchronously): during discovery the button
changes to “Discovery in progress…”, and at the end the page reloads by itself –
no manual refresh needed. The page also checks the device list in the background every 10 seconds
(brstat.json): on a new or vanished device or an authentication change it
reloads automatically, otherwise it only updates the reachability indicators in place.
On the Users page you create accounts with admin / editor / user level (chapter 2). For a “user” level account, permissions can additionally be granted per element:
An element without permission does not even appear on the user's interface or in their app. A group is only visible if the user has permission for every member of the group. With the buttons at the top of the table all permissions can be cleared/granted/inverted at once.
The Expanders page (System menu) shows the panels attached to the RS485 expander bus in a card list sorted by address, with live online state (how the list works is described in the expander list section). There are two panel types: the Ext16 (16 inputs / 16 outputs) and the Ext-KNX – the latter is a KNX TP1 gateway (the third one, the Ext-DALI gateway, is described below) that interoperates with KNX systems: from the controller's point of view it is “16 inputs + 16 outputs + 16 sensors”, only the channels are mapped not to terminals but to KNX group addresses. So all existing myIO logic (timers, Flow, protection, thermostat, app) works with KNX devices too, unchanged.
The group address table (48 rows) lives on the panel: “Save to panel” writes it over the bus and commits it, so the panel must be online. Copy the addresses from the ETS project (main/middle/sub, e.g. 1/2/3) – to the KNX bus the panel is a simple bus participant, ETS does not see it as a device, and it does not need to be programmed. Rows marked Init-read are queried by the panel after power-up, so the actual KNX state is applied immediately. The status block shows the KNX bus state and the traffic counters live.
Limitations: the panel can neither read nor write group addresses configured for KNX Data Secure – set the addresses to be integrated to unencrypted in ETS. 16+16+16 channels per panel is the ceiling; several panels with more addresses add up. The same group address can be mapped as an output on only ONE panel (to prevent echo loops).
From 4.15 the KNX part has its own page: Link → KNX. The group address table, the KNXnet/IP ETS interface switch and the KNX device registry moved here; the Ext-KNX card on the Expanders page only keeps the status summary and the link (as with DALI/DMX). Every row of the table is a KNX entity: name (a label only, stored on the controller), group address, direction, DPT and the myIO element, which you pick by name from a drop-down:
The source of truth for the table is still the panel (“Save to panel”). The group address is bound to the device
object by ETS or by the ETS-free downloader (tools/knx_dl.py, System B).
A controller fitted with an Ext-KNX panel also works as a KNX IP interface: ETS6 programs the KNX devices over the local network, via the panel's KNX bus (individual address, application download, group and bus monitor) – without a separate USB interface. To enable it: Expanders page → KNX group addresses section → KNXnet/IP interface (ETS). The switch is OFF by default: the KNXnet/IP tunnel has no authentication (this is a property of the standard), so enable it only for the duration of the installation.
The Ext-DALI panel is a gateway that interoperates with DALI systems: the lamps (drivers) on its bus can be added on the Link → DALI page, and each can be bound to an output channel (P-ID) – to the Outputs page, the app, the timer, Flow and the thermostat a bound lamp is an ordinary dimmer. Per panel the possible targets are 64 short addresses + 16 groups + broadcast; the registry lives on the controller (dali.jsn). The panel also provides a built-in bus power supply that can be disabled with a DIP switch (15 V, ~200 mA); there must ALWAYS be ONLY ONE power supply on a bus.
Commissioning: 1. Scan the bus (the panel queries short addresses 0..63, ~3 s), 2. the found addresses become endpoints with the Add button (the controller reads the driver's parameters immediately), 3. Assign channels: endpoints not yet bound get a free channel from the pool (from–to) – or you choose one manually in the Channel column. Only non-raster channels can be bound: the DALI panel's own 16-channel block and the virtual channels above SQ (raising PCAQ above SQ in genconfig gives more); the pool defaults to the end of the range. On binding, the channel's PWM is switched on. New drivers without an address get short addresses from Addressing (“only unaddressed devices”); the “ALL devices” mode re-addresses everything (the old addresses are lost!).
Group and broadcast = command. Switching the channel of a group or broadcast endpoint always goes out on the bus (OFF even if the channel was already OFF), and the controller writes the expected value into the member lamps' channels, so the Outputs page shows the real state. You set the membership in the Groups row of the short-address endpoints (a difference from the membership read from the driver is shown in orange), and “Write to driver” writes it out. A short-address endpoint sends its channel value on change.
Parameters (they live on the controller; “Write to driver” writes them into the lamp, “Read” loads the lamp's values): fade time (code 0..15 = none…90 s), fade rate, power-on level (brightness after the power returns; the factory default is often 254 = everything switches on – 0 is recommended), system failure level (when the bus fails; 255 = keep is recommended), min/max, dimming curve (log/lin for LED drivers), group membership; read from the driver: device type, physical minimum. Short address change, driver reset and Identify (1.5 s full light) via buttons. Into all lamps at once: the parameters set on the broadcast (or group) row are written by “Write to driver” into all member lamps with a single broadcast command; the member rows take them over and are re-read (a value rejected by the driver, e.g. a MIN below the physical minimum, is shown with an orange marker).
Health poll (switchable per panel): the panel polls the known lamps in turn. In “status” mode it reports lamp failures, and after a power loss (power-cycle bit) it automatically restores the lamp's expected brightness; in “status + level” mode it also reads the actual brightness (in case there is another DALI controller on the bus). The status block shows live the bus voltage, the internal supply, the hiccup, the counters, and the addresses with lamp failure or not answering (“not answering” is flagged after two consecutive missed replies).
Taking over from a foreign controller 4.8.7+: with “status + level” polling the controller requests the lamps' fresh brightness from the panel in the background (every 5 s) – fresh means what the panel has read since the last level command – and if it differs from the channel value (the driver's clamping to MIN/MAX does not count as a difference), it was set by another DALI controller (push-button module, another master): the bound channel value follows it, and no command is sent back to the bus. For 3 s after its own switching, during a fade and on a non-answering lamp there is no takeover. In the endpoint row, “⇐ %” next to the Value shows the driver's fresh level (orange if it differs). The parameters read from the driver (orange “≠” marker) are kept even after a restart.
Limitations: outputs only (DALI-2 push buttons and sensors in a later round); the software PWM fade of a group channel does not go out on the bus (the DALI fade time belongs to the driver); the panel is the only controller on the bus (multi-master only with a DALI-2-capable installation). “DALI” is a trademark of DiiA: the panel interoperates with DALI systems, without certification.
The Ext-DMX panel is a DMX512 gateway: it sits on the RS485 expander bus, and on its own DMX output it continuously (~40 times per second) transmits the 512-slot universe to the attached devices (LED decoders, dimmers, color changers, moving heads). On the Link → DMX page you add the fixtures with their DMX start address (1..512, set on the device with a DIP switch or in its menu) and their type (dimmer 1 channel, RGB 3, RGBW 4, generic 1..16), and bind each of their channels to an output channel (P-ID) – to the Outputs page, the app, the timer, Flow and the thermostat a bound channel is an ordinary dimmer, and its value goes to the slot (start address + channel index). The controller only sends the changes to the panel; the panel holds the image and, on master loss (5 s of silence), either holds it or blacks out, depending on the setting.
What is DMX512? A stage and architectural lighting control bus: an RS-485 signal pair (D+/D−, often a shielded twisted pair), daisy-chained (from device to device, not in a star), with a 120 Ω terminator at the end of the chain; connector: 5- or 3-pin XLR, RJ45 or a terminal block (on the panel a D+ · D− · GND terminal block; for XLR: 1 = GND, 2 = D−, 3 = D+). The bus is unidirectional: the controller transmits, the devices listen – there is no discovery and no feedback; the start address must be set on the device and entered here. A device uses as many consecutive slots from its start address as it has channels (RGBW decoder: 4). The slots of two devices must not overlap.
Commissioning: (1) genconfig: expanders[].type: "dmx" at the panel's address →
paired M4+M7 flash (proto v11); (2) the panel is online on the Expanders page; (3) Link → DMX: add a fixture
(start address + type) → “Assign channels” or manual binding → the slider moves the lamp; the Test button
drives every channel of the fixture to full light for 1.5 s (so you can identify which device is where);
Blackout = all 512 slots to 0. The panel settings (slots/frame, BREAK/MAB, failsafe, DE/CON
polarity, frame period) are saved on the panel.
Limitations: output only (RDM discovery and read-back in a later round); a DMX channel is 8-bit (0..255 = 0..100 %), 16-bit fine channels are not paired; the DMX panel's own 16-channel raster block and the virtual channels above SQ can be bound (channels of DALI endpoints cannot). The panel prototype can also run on an Ext16 panel (J5 Nextion header + 5 V MAX485 module).
The Ext-RS485 panel (the same hardware as the Ext-DMX, only with a different firmware role) sits as a Modbus RTU master on a Modbus bus with its own galvanically isolated RS-485 port, and polls the attached devices: energy meters (Eastron SDM120/230/630), inverters, heat pumps, industrial sensors. The measured values go into universal sensors – there the thermostat, the log and chart, Flow, the alarm, MQTT and the app see them, exactly like any other sensor – and the controller's outputs can write Modbus registers (coil/holding: setpoint, operating mode). The bus runs at its own pace, independently of the controller: the panel polls, the controller only receives the finished values over the RS485 expander bus.
What is Modbus RTU? An industrial polling protocol on an RS-485 signal pair (A/B, twisted pair, daisy chain, 120 Ω at the ends): one master asks, the addressed slave (1..247) answers. The device's data are held in 16-bit registers and bits (holding/input register, coil, discrete input); the protocol is fully standard, but which register holds what (voltage, power, SOC…) and in what form (16/32-bit integer, floating point, word order, decimal scale) is device-specific – this is described by the register map. myIO treats this map as data: the panel is a generic interpreter, so a new device needs no firmware, only the table rows (from a preset or entered manually).
The table: blocks (one Modbus request each: slave, function, 0-based start address, count, period – several adjacent registers in one request), fields (one value from the block: offset, type, word order, scale ×10^n, optional SunSpec scale-factor register → target universal sensor, with a name), write slots (channel k of the panel's own 16-channel output block → coil/holding: for a relay bit, off/on → off/on value; for PCA, 0..255 → low..high linearly; optional periodic rewrite) and the bus settings (baud, frame, timeout, retries, gap between requests). The table lives in the panel's flash (automatic save 8 s after a change, or immediately with the button); the names live on the controller (modbus.jsn). The preset library (SDM120/230/630, XY-MD02 verified; Deye, Growatt, Huawei, SunSpec as starting points) fills in the rows with one click using the given slave address – then you bind the fields to sensors.
Commissioning: (1) genconfig: expanders[].type: "modbus" at the panel's address → paired M4+M7 flash;
(2) the panel is online on the Expanders page (Ext-RS485/Modbus); (3) Link → Modbus: bus settings (according to the
baud/address/parity shown on the meter's display), a preset or a manual block + fields → the live value appears at the end of the row → choose the target sensor;
(4) the sensor's name/type on the Sensors page. Address scan and Raw request help with troubleshooting. If there is no answer, check:
A/B polarity (the A/B marking of Eastron meters is reversed compared to the usual one!), baud/parity, termination.
A/B polarity panel fw 0.2+: selectable in the bus settings – normal (J12.1 = D+ / A, J12.2 = D− / B), swapped (the panel swaps the two wires in software, no rewiring) or automatic: if no device answers, the panel switches polarity and locks on the first valid reply (an exception reply counts too). Once locked it only switches again when every block has gone silent (3 missed rounds) – one missing device on a mixed bus does not trigger it. Needs at least one read block. The live state is shown next to the selector: normal/swapped, searching/detected, number of switches. A factory-fresh panel defaults to automatic; an existing setting is kept on update. Eastron meters (Eastron A = −), fixed wiring: J12.1 → Eastron B, J12.2 → Eastron A.
SDM meter on the Modbus panel 4.18.13+ · panel fw 0.3+: besides a universal sensor, a field's target can be an SDM meter variable (SDM P/U/I/S/Q/PF/φ, L1–L3) – the same values the serial SDM reader used to fill: the energy manager, consumption-controlled outputs (activator 201–221), the PLC engine and MQTT read them. With the SDM120/SDM630 presets the “into the SDM variables” tick sets this up. Automatic detection: when the panel's table is completely empty (no block, field or write slot) and “Detect SDM meter automatically” is ticked (default on), after 5 s the panel looks for an Eastron meter at address 1 – with the current settings, then the usual baud/frame pairs (9600, 2400, 19200, 4800, 38400, 1200 · 8N1/8E1/8N2), with automatic A/B on both polarities. Identification: L1 voltage 50–500 V and frequency 40–70 Hz. When found, the panel writes and saves the table itself: one 42-register block with P/U/I/S/Q/PF/angle of the 3 phases (the phase count comes from the meter's type code, not the momentary voltages: an SDM630 is added with 3 phases even if only L1 is live at start – e.g. a tripped breaker – and L2/L3 fill in when they return; a single-phase type such as the SDM120 gets L1 only), plus frequency and import/export kWh without target; the bus settings switch to the detected baud and polarity. Nothing found: retry after 30 s (a harmless read at address 1). Untick it if you fill the empty table by hand or another device sits at address 1.
Device cards 4.19.1+: the Modbus page groups the panel's table by slave address. At the top the bus card (bus settings, A/B polarity, automatic SDM detection, status, save/re-read/clear, export/import of the panel table) and the capacity bar (blocks 2/16 · fields 24/64 · write slots 0/32 – against the panel's frame, 8/32/16 with older panel firmware; yellow above 80 %, red when full). Below, one collapsible card per device: the header shows the name, the template, the row counts, the block ticks (✓ = last request answered) and 2–3 main live values; inside are the device's blocks, fields and write slots (the field's block selector offers only this device's blocks, the new row is pre-filled with the slave address). The device name (max 24 characters) and template live on the controller (modbus.jsn); an automatically detected meter appears as “Eastron SDM (auto)” until you name it. Actions on the card: Load template onto this address, Save as template, Change address (every block and write slot of the device moves to the new address) and Delete device (with its blocks, the blocks' fields, its write slots and names). The page remembers which cards are open within the browser tab.
Template library 4.19.1+: a template is one device's register map as data (blocks, fields with name/unit/SDM target, write slots; JSON). Three sources in one list: built-in (in the web UI: SDM120/230/630, XY-MD02 verified; Deye, Growatt, Huawei, SunSpec as starting points), CDN (okoslak.hu/ext2/modbus/index.json + one JSON per device – a new device needs no firmware or UI update) and own (the controller's /modbus/*.json files). Add device (on the template card): template + slave address (+ “apply bus settings too”, “into the SDM variables”) → the rows go to the next free places, the device's name and template are set. Save as template (on the device card): the device's rows into a JSON → the controller's /modbus/ folder (own template, listed under “own”) and as a download; the id is the file name (a-z, 0-9, _ -, max 16). An own template can be deleted from the list; the downloaded file can be moved to another controller with the file manager (/modbus/) or submitted to the myIO CDN library. Panel table export/import (on the bus card): the whole table in one JSON (backup, panel replacement) – import clears the current table and loads the imported one, rows beyond the frame are skipped.
Write-slot source – element selector 4.19.1+: the source of a write slot is either the panel's own channel (k < 16: relay bit k on/off, PCA k 0..255 – carried by the controller's output raster) OR any controller element chosen by name: relay / logic output, Output (PCA), FET (on a FET controller). Then the controller pushes the level to the panel (on change and every 10 s) and the mapping is selectable: on/off → off/on value (coil or register), or level 0..255 → lo..hi linearly. A push slot without a source does not write. On older controller firmware the element ID is entered as a number.
Fast block (×0.1 s) and event-driven refresh 4.22+ · panel fw 0.7+: the controller's power regulator (energy manager, consumption-controlled outputs, 0.1 s check cycle) gets the freshest possible meter data. The “×0.1 s” tick in the block row makes the period unit 0.1 s (1..255 → 0.1..25.5 s), and the panel flags every fresh value to the controller immediately: instead of waiting for the 1 s polling cycle it fetches it right away – the regulator has the value ~0.1–0.15 s after the meter updated its registers (it used to be 1.5–3 s). The display (live values on the page) is not sped up, nor does it need to be. A request cannot be shorter than the bus transfer time: at 9600 baud the 42-register SDM block takes ~0.16 s, then the block simply runs back-to-back – either set 38400 baud on the meter, or use a separate small fast block (P L1–L3: FC4 12..17, 6 registers, ~30 ms) at 0.1 s and keep the big one at 1 s. The “fast P” templates (SDM630 / SDM120) are built this way: the P block at 0.1 s, voltage/current at 1–2 s, energy counters at 2–5 s. A fast block does not starve the other blocks (round-robin); the full chain needs panel firmware 0.7+ AND controller 4.22+ (paired M4+M7 flash: with an old M4 the fast block runs but the controller fetches once a second). On older panel firmware the tick is not shown and a template's fast block runs in seconds.
Limitations: 16 blocks, 64 fields and 32 write slots per panel (panel fw 0.4+ and controller 4.19.0+; 8/32/16 with older panel firmware – the capacity bar shows it), max 16 named devices; one bus setting per panel (devices with different baud rates need two panels); the sensor path rounds to tenths (for an energy counter 4.5 significant digits – the exact value is shown on the Modbus page); devices that require a sequence (login, read-modify-write) can be handled with the raw request. The panel prototype can also run on an Ext16 panel (J5 header + M5 Unit RS485 module).
The Expanders page speaks the card language of the settings pages: one card per expander, sorted by address (the address is set on the panel's DIP switches). The card header shows the address, the type, the panel's firmware version and the online/OFFLINE badge; clicking it expands the panel's parameters (identity, failsafe/standalone mode, AUX pins, program). The type is announced by the panel itself at login (Ext16, Ext-KNX, later Ext-DALI and Ext-WiSUN), not taken from the controller's config – if the two differ, an orange “config: …” badge on the card warns you (wrong address DIP or outdated config).
The controller remembers what it has once seen online at an address (type, firmware, the panel's unique
identifier – UID –, the time last seen, the number of logins), so a dropped-out panel stays in the
list even when offline, with the time it was last seen. An address where nothing has ever logged in
does not appear – so the list always shows what really is or was on the bus. The card of a removed panel
can be removed with the Forget button (only for an offline panel; the address's failsafe/AUX settings
are kept, and if the panel logs in again, it reappears). The controller recognizes a panel swap from the UID:
a System,Expander,NEW/IDENT/FORGET line is written to the event log.
The Ext-RS485 panel carries both of its roles in a single firmware image: DMX512 output (fixtures, Link → DMX) or Modbus RTU master (meters, inverters, sensors, Link → Modbus). The role lives in the panel's flash together with the other role's settings (DMX config, Modbus table) – they survive a switch; a factory-fresh panel defaults to Modbus + automatic SDM detection. On the Expanders page the panel's card has a Role section showing the current one; the selector + Switch (save + restart) button switches it: the panel saves the new role, restarts and logs in with the new type – the controller decides from that at run time (DMX universe / Modbus table, the /dmx and /modbus pages, output badges); the genconfig type (dmx / modbus / the new rs485) is only the starting point until the panel has logged in. With the old single-role panel firmware (DMX 0.1, Modbus ≤ 0.4) the switch is not shown – update the panel to the universal image (the old DMX settings and the Modbus table carry over).
On the Opta the expanders sit on the RS-485 expander bus. The GIGA V4 has no RS-485 bus port, so there the Ext-RS485 panel's J13 (AUX) terminal is the entry: J13 is the panel's second expbus port (3.3 V TTL, 115200 8N1) and connects point-to-point to the GIGA's X9 terminal (D0/D1 = Serial1): X9.9 TX → J13.3 RX, J13.2 TX → X9.8 RX, GND → GND (J13.1 3V3 and J13.5 P1+5V stay unconnected).
expanders[] list can be filled on a GIGA device too – gateway types only (knx / dali / dmx / modbus /
rs485), addresses contiguous from 1 (set the panel's DIP address accordingly). The 16/16 IO panel is not supported on the GIGA (native raster).!pwm output go silent (SDM values come from the panel's Modbus table,
see Modbus). If nobody answers → the old serial mode stays and the controller re-probes every 60 s while the line is quiet
(no SDM/dps frames) and no DewPoint PWM output is configured.?m4. → explink: mode=… probes=… found addr=…; evlog System,Expander,LINK.On the /expander page, on the panel's card: auto (default) = J13 is the second master port + bridge; off = the port does not run (e.g. a P1 smart meter or another serial device on the terminal – those modes come in a later round). Switching saves into the panel's flash and the panel restarts after ~0.3 s. The "J13 state" line: enabled / master link active (a frame arrived on J13 within 3 s) / bridge traffic / master on the RS-485 port too (two masters at once = wiring error). If this controller talks to the panel through J13, switching it off is refused. The "J13" badge on the /modbus bus card shows the same state.
DS18B20-family thermometers can be connected to the Ext16 panel's AUX1 terminal (5V_AUX – AUX1 – GND) (the 4k7 pull-up is on the panel; parasitic power also works). The panel itself searches for and reads the sensors every second, and the values come to the controller over the bus. The controller gives every sensor a permanent ID based on its ROM code – exactly like on the GIGA/Due's own 1Wire bus: the ID is kept after a panel swap and after moving the sensor to another panel, so “shuffling” sensors because of bus lengths does not upset the config. The ID list can be saved to the sensors.bak file with the “Backup IDs” button and restored when the controller is replaced.
The thermometer goes into the usual thermometer slot, exactly like on the GIGA's own 1Wire bus: you name it on the Inputs page (by ID), it is shown on the dashboard and in the app, it is logged (t_log_ID chart), and it can be used in the thermostat activator (1..100), in Flow, over MQTT and in joiners. A dropped-out (unplugged) sensor is shown in red as “absent” in the list, and its temperature toggles the usual way (±127 °C) after 255 s; the registration can be removed with the “Delete” button (the ID stays reserved, so the others do not shift).
The two AUX pins of the Ext16 work as 0–10 V analog outputs (mixing valve, 0–10 V LED driver, frequency inverter; min. 10 kΩ load). In 0–10 V mode AUX2 mirrors the panel's Out 15 output and AUX3 its Out 16 output: you configure that output channel on the Outputs page like any dimmer (name, PWM flag, timer, Flow, joiner, mixer thermostat, fade), and the pin outputs the duty cycle as 0–9.9 V (255 = 9.9 V). The Out 15/Out 16 terminal switches along with it, so do not connect any other load there while the AUX is in use. On the Outputs page an orange “AUX2/AUX3” badge is shown next to the affected channels. The mode (0–10 V / later LED strip) can be switched on the Expanders page, and it is also saved in the panel's own memory.
On the Expanders page, clicking the card of any Ext16 panel expands the panel's settings section. The panel is in failsafe until a master frame arrives after boot, or when the bus goes silent (Bus timeout, default 5 s). Four Failsafe modes are available: hold the outputs (HOLD), all OFF, apply the PRESET image, or standalone mode, in which the panel's own rule table takes over control. Setting Master to “none” turns the panel into a standalone controller (it runs in standalone mode from boot, but still answers on the bus).
The standalone program is derived from the Opta's settings, so you do not need to write it separately: it contains those input bindings (short/long/release → output or group, with timer, delay, dimmer), output thermostats (on the panel's own 1Wire thermometers), protection pairs, joiners and groups whose source AND target are on the same panel (max. 48 rows). After every save the Opta re-derives it and compares it by CRC with the one running on the panel; if they differ, it downloads it. LOCKED (program auto-update disabled) prevents writing: the panel runs its last saved program – a real safe mode, independent of the Opta's settings. You can check the table in readable form with the “Show rule table” button.
On return (relink) the Truth on relink setting decides the outputs: by default the Opta takes over the panel's image (flicker-free); switched over, the Opta forces its own image onto the panel. With the Local engine while linked setting the panel switches immediately on wall buttons even alongside the Opta (faster, but the Opta's decision may override it). A stale sensor in standalone mode results in 50% toggling by default (frost and overheat protection), or this can be switched off. The pin mode of AUX2/AUX3 (0–10 V or digital LED strip) can also be selected here. The panel keeps the program and the settings in its own flash memory (restored after a power cut), and protects itself with an independent watchdog (IWDG, ~1 s).
Availability: if an expander drops off the bus, the outputs, inputs, thermometers and groups on it get an OFFLINE marker on the Outputs/Inputs/Sensors pages and on the main page and an "off":1 field in the JSON, while in MQTT the exp/N/status topic and the Home Assistant discovery availability list indicate it (unavailable). The event log records System,Expander,LOST/BACK/PROG lines; the panel's 1Wire thermometers immediately go into error toggling, so the Opta's thermostat failsafe also kicks in.
An existing (or new) alarm panel can be connected to the myIO in two ways. The A) hardware route works with any panel and every myIO controller, without a protocol; the B) SIA DC-09 receiver is a feature of the 4.5.8+ (GIGA/Opta) firmware: the panel reports to the myIO as it would to a monitoring station, so per-zone and per-partition information arrives as well. The two routes can be used together (state via SIA, arming via a keyswitch zone).
Every alarm panel has programmable PGM outputs (usually open-collector transistor outputs switching to GND, 50–100 mA) and zone inputs, one of which can be programmed as a “keyswitch” type. These two elements make up the protocol-independent integration:
Alarm panels report to the monitoring station (ARC/CMS) over IP according to the SIA DC-09 standard: simple text frames over TCP or UDP, which the receiver acknowledges. The myIO takes on this receiver role: in the panel's “Remote monitoring / Monitoring station / ARC” settings, enter the myIO's IP address and the configured port (default 7080), as protocol select SIA DC-09 (SIA-DCS or ADM-CID / Contact ID format – it understands both), without encryption. This way every event (arming, disarming, alarm with zone, tamper, AC/battery fault, communication fault, bypass, periodic test) comes in.
System,Alarm,<account>,<code>,ri=,z=) — so
webhook slots marked err:1 fire as well —, arming/disarming/
bypass/trouble at I level; the periodic test and the heartbeat are only counted. On an alarm a
notification appears in the menu bar; likewise on loss of supervision.?alarm., simulation: !siasim*Nri1/BA01.Vendor setup examples (menu names depend on model and version; the essence is the same everywhere: receiver IP + port, SIA DC-09, encryption off):
On the Network page (on 4.x controllers) you can configure the network connection – changes can be applied without a restart:
myio-nappali →
http://myio-nappali.local.myIO-<name> network with your phone (default password myio12345 – change it!)
and the controller answers on 192.168.3.1, for commissioning without a cable. On by default;
a 5 s press of the USER button (network + users to defaults) also turns it back on.!ethernet init. serial command or a restart may help). The Outgoing HTTP rows show the
shared outgoing request slot of the Shelly and Link subsystems: normally “–”; a persistent
HDRS/BODY state indicates a stuck request. Refresh with the ↻ button.myio.local name resolution. OFF by default; not needed with a static IP
or if you know the IP address.net.jsn cannot do harm).The controller's settings can be saved into slots – a slot can be a full backup or a scene (e.g. “Movie mode”, “Holiday”). On the Save page (or with the menu bar's quick save) you choose what to save: everything, or just one category (outputs, inputs, groups, general settings…).
The Evlog page shows the controller's event log broken down by day: switching events (who switched: button, web, timer, thermostat…), network events, errors. The log is written to the SD card; without a card, a reduced logging mode runs that spares the internal storage. The log files can also be downloaded from the file manager.
4.x In the 4.x firmware log, every line starts with a level marker: E (Error), I (Info), D (Debug). With the Error / Info / Debug checkboxes below the date picker you can show or hide the lines of each level. The filter only affects the display (the file stays unchanged), and only applies to new, level-tagged logs – old logs starting with a date are not filtered, and the filter bar does not even appear there.
Search and row selection: the search field below the date picker filters the table to the rows containing the typed text as you type (combined with the level filter); the ✕ button clears the search. Click a row to select it: the selected row stays visible even after the filters are cleared or changed, and the page scrolls to it, so the surrounding log lines are easy to check. Click the row again to deselect it.
On the /fm page (admin) you can manage the controller's two storages: the internal flash (Q) and the SD card (S). Upload/download files, delete, rename, copy between the two storages. On the 3.12 (DUE) controller only the SD card panel is active (it has no internal QSPI storage). The Opta controller4.5.9 has no SD slot: there both panels show the internal storage, and no SD volume can be selected (not even in the recovery file manager). This is where the interface files, the settings backups and the logs live. Only change things here if you know what you are doing – after deleting the interface files the web page will not load.
Recovery file manager (/forcefm): the /forcefm address always opens the simple file manager built into the firmware – independent of the interface files on the storage. This is the lifeline if the web interface does not load (empty, corrupted or deleted storage): you can upload the interface files from it. The same page also appears at the plain /fm address if the controller cannot find the file manager's interface files.
Selection: clicking a row selects only that item (the previous selection is cleared). By clicking the checkbox column at the start of the row – or with Ctrl/⌘+click anywhere on the row – you can add or remove an item while keeping the existing selection. Shift+click selects a range from the last clicked item (Ctrl+Shift adds the range to the existing selection). The header checkbox toggles everything. Double-click enters a folder, or opens a file for viewing. In a subfolder the first row of the list is the usual .. row: one click takes you to the parent folder (the same as the ⇧ button in the panel header).
Operation window: longer operations (copy, move, upload, delete, zip) run in a pop-up window that opens in the center, with a progress bar and a Cancel button. The window's border shows the state: blue border = the operation is working, orange border = the operation is waiting for you (a confirmation or an overwrite question). If a file with the same name already exists at the destination, the operation asks file by file: Overwrite / Overwrite all / Skip / Cancel. While a folder's list is loading, a ⏳ indicator is shown in the panel header and the panel is not clickable – wait for the list to arrive.
Download as zip: a single selected file is downloaded directly; if you select several files or folders, the 📥 zip button downloads them packed into a single zip file. Selected folders go into the zip with their full content (including subfolders). The packing happens in the browser; the controller is only loaded by downloading the files one by one.
Delete: the selected items are deleted after confirmation. Folders can be deleted together with their full content (the confirmation dialog warns about this separately). Protected QSPI slots (slot_0, the boot slot, or a slot referenced by a timer/button) are still rejected by the controller in this case.
Drag-and-drop upload: you can upload files by dragging them from your computer onto the panel. If you drag the files onto a folder row, they go into that folder. After you drop them, a pop-up window shows how many files go where – the upload starts only after OK.
Date column: the files' modification time (after the size column, at the end of the row). On the SD card it is always shown; on the QSPI flash earlier firmware versions did not store it, so for files written there earlier the date stays empty until the file is rewritten once. In a narrower (tablet) view the date and the time wrap into two lines to leave more room for the file name; in a small (phone) view the column is hidden.
CSV chart: if exactly one .csv file is selected, the 📈 chart button appears in the toolbar. Clicking it opens the file in a pop-up chart view (zoom with the mouse/by dragging, time-range slider at the bottom). CSVs containing numeric time-series data can be plotted – such as the sensor and consumption logs (t_log, h_log, c_log, sdm_log); the event log (ev_log) is text, it is best viewed on the Log page. For a CSV with a header the column names come from the header; without a header the curves appear as C1, C2….
Sync menu (🔄 sync)4.4.3: it combines two functions. (1) QSPI → SD mirror: the controller copies the configuration (slot folders + root xml/csv/jsn/bak) – and, with the checkbox ticked, the htm/js web files too – to the SD card, so that in a possible restore (blank QSPI, self-healing reformat) the current state comes back, not an old backup. The mirror also runs automatically (every 6 hours, config only) and can be started immediately from the menu. Without a card (e.g. Opta, which has no SD slot) the mirror section and the “mirror now?” question after a download do not appear4.5. A foreign card (one with config on it but no mirror.tag) is left untouched by the mirror for protection – in that case the menu offers to adopt it. Secrets (cloud/license files) never go onto the card. (2) Update from HTTP: downloads the web files (js/htm) from a given CDN tree (e.g. https://okoslak.hu/ext2/) and uploads them to the device – done by the browser. Entering the plain tree URL is enough: the downloader automatically tries direct access, then the cors.php helper proxy (if the host does not send a CORS header for static files), and for http also https. If the tree has a files.jsn manifest (created by tools/gen_web_manifest.py), new files are installed as well; without a manifest only the existing files are updated, based on a size comparison. The size listed in the manifest must match the downloaded file: if a file on the tree was replaced without regenerating files.jsn (stale manifest), the downloader reports it as an error and does not write it to the device; the download everything checkbox skips the size check and accepts it. After the download the menu offers SD mirroring, so that the card is up to date as well.
The /sysdiag page (on the 3.12 DUE and on every 4.x/GIGA controller) shows the controller's internal state live, with graphs refreshed every second – a clear web version of the serial socket monitor. Panels: W5500 socket monitor (state of all 8 hardware sockets + a 60-second history graph per socket; the colors indicate the state: green = ESTABLISHED, blue = LISTEN, turquoise = UDP, yellow = stuck CLOSE_WAIT/TIME_WAIT, gray = free), memory (free SRAM ring + min. watermark + trend), storage (QSPI flash and SD card usage), M4 co-core (heartbeat + button-scanner error counters), network (link, RSSI, outgoing request queues) and boot & runtime (reset cause, crash phase, uptime, loops/s). On the 3.12 (DUE) controller a smaller set of panels is available (socket monitor, memory, boot & runtime, I2C diagnostics); the M4, QSPI and WiFi panels are specific to 4.x controllers.
Saving resources: the page loads the controller only while it is open and visible. The graph history is built by the browser (there is no background sampling in the firmware), and if the page goes to the background (another tab) or you turn off Auto refresh, polling stops – the controller is “released” a few seconds later. If you leave it open in several browsers, a warning appears in the header, because each of them loads the controller: close the unneeded ones.
Wire the motion sensor's contact to an input, and assign the lamp's relay to its short event. On the relay: Just-ON + timer (e.g. 90 s). Every motion restarts the time.
UP relay and DOWN relay, each with a timer equal to the travel time (e.g. 25 s), and a protection pair for the two. UP button short event: toggle UP relay; DOWN button: toggle DOWN relay. While moving, pressing the other direction safely reverses the direction (the protection first stops the opposite direction).
Link the remote (Shelly H&T or another myIO) thermometer into a universal sensor. On the heating relay: activator = 300+ID, ON=18.0, OFF=19.0. Set a ±2.0 error delta: if the sensor drops out, the heating keeps running at a 50% duty cycle, in frost-protection mode.
Wire the gate motor's start input to a relay: set the relay's timer to 1 s, in Just-ON mode. Triggered from any source (button, app, timer), the relay gives a 1-second pulse.
Two schedules: every day at sunset the “Outdoor lights” group ON; every day at 00:00 the same group OFF. Or: set the output's activator to “Sun” (255) and its action to SunSet – then no schedule is needed at all.
On the eManager page, for phase 1: UP threshold 5000 W, DOWN threshold 3500 W, and the output assigned to the level is the water heater's relay. While cooking, the water heater switches off automatically, then switches back on when the power becomes available again.
I cannot find the device on the network. Try the http://<name>.local address,
or check your router's device list. As a last resort, the device is also directly reachable in Wi-Fi AP mode
(myIO-… network).
An element does not appear on the main page. Either it has no name (turn on SuperVisor and give it a name), or the logged-in user has no permission for it (chapter 22).
The timers run at the wrong time. On the General page, check the time zone setting and the NTP sync status; for sunrise/sunset, also the geographic coordinates. If the difference is exactly one hour, check the automatic daylight saving time checkbox and the standard (winter) offset (Central Europe: 1).
My sensor is in “STALE” state and the relay keeps switching. This is the failsafe: the sensor did not send data in time. Check the sending device; the switching is intentional (frost protection), and it stops by itself when the sensor comes back.
I changed something and now it is broken. Load back the last good backup on the Load page (this is why it is worth saving before every major change).
The controller's full functionality is also accessible from programs over HTTP – you can write your own app, smart-home hub or script for it. The detailed, machine-readable description is in a separate document: Download the myIO API reference (API_REFERENCE.md, currently in Hungarian).