HotLoop core 4.19.0 release notes
A value you write to a scaled tag now goes to the device the way you meant it. That is the one thing in 4.19.0 you need to read before you touch a running plant, because it changes what lands in a register, and a plant that worked around the old behavior has to stop. A Kafka event stream and OPC UA PubSub over UADP come with it, both new, neither changing anything that runs today.
MCP ships on, with writes
Section titled “MCP ships on, with writes”- Agents can write now, where an operator could. MCP is on in every HotLoop product and reads and writes.
allowMcpWritesdefaults to true in the code and in all three charts. A deployment that hasallowWriteson and never setallowMcpWritesgets agent writes after this upgrade, through the same gate as the screen: the user’s permissions, the tag’s writable mark and its range all still apply. allowWritesdid not change. It stays off by default and still decides whether anything can write, the UI and agents alike.- A deployment that saved
allowMcpWrites: falsestays off, including one installed from the App Store, which stores every answer. Change it if you want agents to write. - The MCP endpoint is never open. A HotLoop started with no
HOTLOOP_MCP_TOKEN(a Quadlet unit, a bare binary) makes a token at its first start and keeps it in its database. A call without it is a401.hotloop mcp-tokenprints it. The charts already set one.
Writes to a scaled tag
Section titled “Writes to a scaled tag”- What was wrong. A tag with a scale, say 0.1 on a temperature register that holds tenths of a degree, shows you the engineering value: the register says 555 and the screen says 55.5. Writing went the other way without the same care. Type 55.5 for the setpoint and HotLoop put 55 in the register, which then read back as 5.5. It was the same on every screen, through the API, from an automation, a script, a recipe, a loop and over MCP, because they all go through the same write gate. The audit row said 55.5 the whole time, so nothing looked wrong in the log.
- What changes. The number you write is in the tag’s own units, the same as the number you read. HotLoop takes the scale and the offset back off before it sends: 55.5 on a tag scaled by 0.1 puts 555 in the register, and it reads back as 55.5. A tag with no scale and no offset is not touched at all. Bits, coils, strings and Sparkplug and MQTT writes of a flag are not scaled either.
- If you worked around it, stop. If anyone has been typing raw counts into a scaled tag to get the right number, typing 555 to get 55.5, that now sends 5550 and reads back as 555. Type the engineering value. Look at your scaled tags before you upgrade: any tag with a scale other than 1, or an offset, that somebody writes to by hand, from an automation or from a recipe is worth a look. Recipes and automations that wrote counts need their numbers changed to engineering values in the same step as the upgrade.
- A value the register cannot hold is refused. After the scale is taken off, a count that does not fit the register, 7000 on a
uint16scaled by 0.1, is refused with a plain message that names the count and the register, and nothing is sent. It used to wrap quietly: 70000 on a 16-bit register went out as 4464. That goes for Modbus, S7, EtherNet/IP and OPC UA integer types. - A recipe with a value that does not fit is refused whole. Before its first write, every target is checked, so a recipe never half-applies and then refuses. For Modbus, S7 and EtherNet/IP the check knows each tag’s register type from its address. For OPC UA, which is the path to a CODESYS runtime, it uses the type the tag’s last reading arrived as. A tag that has never been read since HotLoop started has no type to check against yet, so a recipe that targets it is refused at that tag’s own write, after the targets before it were written, and the application records which. Reading it once, which the poll does within a second of connecting, closes that.
- A fraction of a count is cut off. A register holds whole counts, so 12.34 on a tenth-scaled register holds 12.3, as it always did.
- What did not change. The range check on a tag and the audit row use the number you typed. The write gate, the permissions and the refusal rules are the same.
A Kafka event stream
Section titled “A Kafka event stream”- What it is. HotLoop can send its readings and its system log to any broker that speaks the Kafka API: Apache Kafka, Redpanda, and the rest of that family. It is a new kind of historian target, under Settings, Historian targets, so it has the queue, the catch-up from HotLoop’s own history and the status those have. Readings go to one topic and the system log (alarms, devices going up and down, changes) to another, as JSON.
- TLS and SASL. SCRAM-SHA-256, SCRAM-SHA-512 and PLAIN (PLAIN only over TLS), with your own CA if the broker’s certificate isn’t from a public one. Client certificates, Kerberos and OAuth logins are not supported yet.
- It is at least once. A reading can arrive twice after an outage, because the catch-up overlaps what already went out on purpose.
deviceId,tagandtsname a duplicate. Events are the live feed and have no history to catch up from, so one produced while the broker is away is lost, and the target’s status counts it. - A broker that vanishes mid-write. The target goes down within 25 seconds and the readings in that write are owed, instead of staying “up” with its queue overflowing.
- A topic made a moment ago. The first look at a topic that was just made, by you or by the target’s own last try, waits for it to exist and have a leader instead of reporting the target down.
- What it costs. About 5 MiB on the binary of every product with a database. The Edge Relay has no historian targets and doesn’t carry it. The client is franz-go, credited in
NOTICE. - Upgrading. Migration 0026 adds an
optionscolumn to the historian targets, empty for every target that exists. Nothing changes for them. HISTORIAN-TARGETS.md has the record formats and what is and isn’t proven.
OPC UA PubSub over UADP
Section titled “OPC UA PubSub over UADP”- Subscribing. A device with the protocol
opcua-pubsublistens for a publisher’s DataSets sent as UADP over UDP, unicast or multicast. A tag’s address is<DataSetWriterId>/<field index>. Variant, DataValue and RawData fields, key, delta and keep alive frames. A lost message marks that writer’s tags uncertain until its next key frame, and a silent writer reads bad. - Publishing. Edge and Gateway can publish the tags a layout names as UADP, off until
HOTLOOP_UADP_PUBLISH_ENABLEDis set. Key frames when values change and keep alives when they don’t. A bad tag is published as its status, never as its last value. - Not yet. Message security (signed or encrypted messages) is refused by name, as are chunked messages, discovery, events and array fields. Delta frames are read but not sent.
- What it was proven against. open62541 1.5.10, an independent OPC UA stack, in both directions in the lab. It has not been run against a CODESYS, Siemens or Beckhoff publisher or across a router. PROTOCOLS.md has the setup and the limits.