Repository navigation
Breaking changes π¨
- none
New features β¨
- Adapters/Admin GUI: Adds a WEBHOOK adapter that turns an incoming HTTP call into a value on a DataPoint, for the many devices that can only fetch a URL when something happens β a door station when the bell is pressed, an IP push button, a camera's "HTTP action on motion" β with no headers, no request body and no MQTT. Previously such a device could not reach OBS at all:
POST /api/v1/datapoints/{id}/valueneeds a POST with a JSON body and anAuthorization/X-API-Keyheader, and the anonymous Visu write path needs POST plusX-Page-Id, so the only options left were a reverse proxy rewriting GET into POST or a separate translator service. A webhook binding is an ordinary SOURCE binding, so the same DataPoint's existing DEST binding (KNX, for example) puts the telegram on the bus for the chime, the Visu and the Logic engine to react to β the KNX side stays pure configuration. Each instance claims a path prefix (/hookby default) and a per-IP rate limit; each binding gets a slug, and can restrict callers by CIDR allowlist, the allowed methods (GET,POSTor both), either a fixed value (truefor a bell) or a value read from the request (?value=1, or the same field in a JSON body), an optional debounce window, and a server-generated token of its own. The Admin GUI shows the finished call URL ready to copy β with the token as a query parameter or as a path segment, for devices whose configuration field rejects query strings β next to that binding's call counters and last call, and can reissue the token, which revokes the old URL immediately without touching any other integration. The per-binding token is the point: an API key may write every DataPoint, which is the wrong thing to store in a device mounted outside the house whose configuration is often in clear text and whose URLs end up in device and proxy logs, whereas this token authorizes exactly one DataPoint with exactly one value mapping β a compromised device can only ring the bell. For the same reason a webhook binding cannot point at acentral_plantDataPoint (the boundary the anonymous Visu write path already draws, re-checked on every call in case the DataPoint is reclassified later), unknown slug, wrong token, disallowed method and blocked source address all answer an indistinguishable404, and creating or reissuing a binding stays an audited configuration change that needs a user login with write permission on the adapter instance βWEBHOOKdeliberately declares no adapter delegation capability, so an API key can never mint a device token. #1256 - Adapter/Zeitschaltuhr/Admin GUI/Visu: Schedule points can now write a value of any object type β
BOOLEAN,INTEGER,FLOAT,STRING,DATE,TIMEandDATETIMEβ instead of only booleans. The switching value was previously parsed without knowing the target object, so1/0/true/on/einalways becameTrue/False; the WriteRouter then silently discarded the event for a numeric object (a window position could be driven to50but never to0 %or1 %), anINTEGERobject received a boolean instead of a number, aSTRINGobject could never be sent the literalson,off,1orein, andDATE/TIME/DATETIMEobjects were never reachable at all. The value is now parsed against the bound object'sdata_type: booleans keep accepting1/0,true/false,on/offandein/aus, numbers stay numbers, strings are taken literally, and temporal objects accept ISO 8601 values such as08:00:00or2026-12-24; objects of unknown type keep the previous heuristic. Both the Admin GUI binding form and the Visu schedule-point editor now render a matching input control (toggle, number field with the object's unit, text field or date/time picker) with a type-specific hint and inline validation, and an incompatible value is rejected when the binding is saved (HTTP 422) instead of failing invisibly hours later at switching time. A value that still cannot be converted at runtime is logged and recorded as atype_mismatchdiagnostic on the object. The Visu Zeitschaltuhr widget no longer colours non-boolean outputs as an active/inactive badge, where a window position of50used to read as a green βactiveβ. #1008 - Logic Engine/Admin GUI: Added a Sensor Watchdog function block (Edomi-style "SensorWatchdog") that monitors up to 10 inputs for the absence of new values. Each input has its own configurable timeout, fault (replacement) value, optional label and optional repeat interval; once an input's timeout elapses without a fresh value, its output switches to the configured fault value and a shared fault-text/fault-trigger output pulses once β or repeatedly at the configured interval, while the input stays stale, if a repeat interval is set. The block runs its own autonomous scheduler independent of any other activity in the graph, so it reliably detects a timeout even when nothing else re-evaluates the sheet β a migrated sheet reproducing the same behaviour with the existing Delay/Impuls timer blocks needed 92 nodes and still could not self-wake once armed. Each monitored input requires both the source's value and its change-notification output wired in (a Read Object node's
WertandGeΓ€ndertoutputs) β only the change signal reliably distinguishes a genuinely new reading from the value a Read Object re-reports on every graph evaluation regardless of cause. #1218 - Logic Engine/Admin GUI: Logic sheets can now be organized using the same Hierarchy already used for DataPoints and KNX devices (Settings β Hierarchy, renamed "Hierarchies"/"Hierarchien" to reflect that it is no longer device-specific), instead of picking a graph from one flat, alphabetically-sorted dropdown. A sheet can be grouped into any number of hierarchies at once β e.g. a technical tree by function (shading/lighting/sockets) and a topological tree by room, both containing the same sheets β since the link is purely organizational and does not affect the sheet's own, independent access permissions. The Logic editor's sheet selector is now a button that opens a folder browser (breadcrumb navigation), including an "Unassigned logic sheets"/"Nicht zugeordnete Logiken" entry for sheets not yet sorted into any hierarchy and a "List of all logic sheets"/"Liste aller Logiken" entry listing every sheet flat and alphabetically regardless of assignment, so it's always possible to see what sheets exist without clicking through every tree; the currently open sheet is marked with a dot. All hierarchy assignment now happens directly in this picker rather than via drag&drop on the Settings page (which stays a pure tree/node manager): every row offers "Assign hierarchy" to link additively and "Delete" to remove the sheet entirely (after a confirmation prompt, including all of its hierarchy assignments); rows inside a hierarchy additionally offer "Remove from hierarchy" for just the position being browsed, while rows in "List of all logic sheets" instead offer "Links" β a popup listing every hierarchy position the sheet is assigned to, each individually removable without navigating to it. The "New sheet" dialog also gained an optional hierarchy-node picker (the same search field already used for hierarchy filtering in the Monitor's filter sets), so a sheet can be placed straight into one or more hierarchy positions right when it's created; left empty, it stays unassigned as before. #1217
- Logic Engine/Admin GUI: Function blocks now share one documented, opt-in set of
###NAME###variables, so a path, URL or text no longer has to be assembled by extra logic blocks. Date/time variables β###H###/###HH###(hour),###m###/###mm###(minute),###s###/###ss###,###d###/###dd###(day),###EE###/###EEE###/###EEEE###(weekday),###M###β¦###MMMM###(month),###yy###/###yyyy###, plus###DATE###,###TIME###(configured formats) and###TS###(ISO 8601) β are evaluated in the configured application timezone from one time snapshot per logic run, so several fields of a run never straddle a second or hour boundary; object variables###OBS1###,###OBS2###, β¦ read the current value of a configured object. They work in the JSON and XML extractor paths (e.g.[###H###].text.valuepicks the current hour from a 24-hour forecast), the API Client (URL path/query, headers, authentication; the existing###OBSn###behaviour is unchanged), the iCalendar URL, the Notification title and message, the message archive, the String concatenation static texts and the Search/Replace replacement text. Placeholders that are not supported stay literal for backward compatibility but are flagged in the block's configuration panel, substituted values are never expanded a second time, and a failing object variable skips the action instead of sending or storing a half-resolved text. The block configuration offers a "Variable einfΓΌgen" picker on every opted-in field, shows the resolved extractor path of the last run and translated variable problems. Host Check keeps its host static by design. #1301
Fixes π
- Adapter/1-Wire: The README told users to pass a plain USB busmaster (DS9490) into the Proxmox LXC or Docker container as the udev symlink
/dev/onewire-busmaster. That does not work (verified on Proxmox LXC):owserver(server: usb = all) finds busmasters through libusb only under/dev/bus/usb/<bus>/<device>, so it failed withLIBUSB_ERROR_NO_DEVICE/No valid 1-wire buses found. The setup steps (EN/DE) now bind-mount/dev/bus/usbtogether with the matching cgroup rule (c 189:* rwm) and use a udev rule that only grants the unprivileged container's root group access (GROUP="100000", MODE="0660") instead of creating a symlink; this also covers several busmasters and re-plugging. The ElabNET PBM keeps its stable serial path. #1287 - Adapter/Webhook: The uvicorn access log β the systemd journal on LXC,
docker logsin a container β recorded every webhook call URL in full, and with it the binding's token, whether it was sent as?token=β¦or as the path segment after the slug. A logging filter onuvicorn.accessnow replaces the token with[redacted]in both variants while keeping the caller address, method, slug and status code for troubleshooting; other request lines are unchanged. The filter needs no database lookup, so it also covers calls to a stopped or disabled instance:tokenis masked as a query parameter on every path, every segment after the slug is masked below a prefix a running instance claims, and below any other non-reserved path a trailing segment shaped like a generated token is masked. The webhook help (DE/EN) now warns that reverse proxies, the calling device and log shipping in front of OBS still see and may log the token in clear text, recommends restricting access to and retention of those logs, and points to reissuing an exposed token. #1305 - Admin GUI/KNX: A device's communication-object list sorted by number as text instead of numerically, so e.g. KO 10 was listed between KO 1 and KO 2 once a device had ten or more objects. Every place that orders communication objects by number β the device detail view, its datapoint-traceability panel, and the group-address device list β now sorts numerically. #1132
- Docker/LXC:
owserver(1-Wire) listened on all network interfaces although OBS only talks to it locally, and owserver has no authentication β once 1-Wire was enabled, the bus was readable and writable (e.g. DS2408 outputs) from the whole LAN. In the LXC template the Ubuntu package enablesowserver.socketon port 4304 for every installation, regardless of the 1-Wire opt-in; a socket drop-in now binds it to127.0.0.1:4304. The Docker Compose sidecar publishes its port on127.0.0.1only (OBS keeps reaching it asowserverover the Compose network). Existing LXC installations are not changed by an update β see the issue for the two-line drop-in. #1288 - Docker/LXC: A fresh installation is now set up in the browser instead of on a shell. Until an owner exists, OBS starts in setup mode: it serves only the setup page at
/setupand the two endpoints that page needs, and refuses every other page, the API, the Visu and websocket handshakes β so there is no effective login and no reachable API before an administrator account has been created. Entering a username and password there ends setup mode in the running process, without a restart. Previously the server refused to start at all, loggedNo OBS owner is configured, and expected the operator to runobs-admin auth first-ownerinside the container or LXC guest; that offline path still works and stays documented for installations that must never be claimable over the network. Two long-standing problems on that path are fixed as well: a failed startup now exits with code 3 instead of leaving a container running as unhealthy forever β uvicorn reports it withsys.exit(3)from insideserve(), and the non-daemon worker threads of the already opened SQLite connections then kept the interpreter alive β and the container generates a random per-instance JWT secret on its first start and persists it in the data volume (/data/secrets/jwt-secret), mirroringobs-first-boot.servicein the LXC template, instead of every Compose installation running on thechangemeplaceholder. A secret the operator set themselves, viaOBS_JWT_SECRET, the legacyOPENTWS_SECURITY__JWT_SECRETor a mountedconfig.yaml, is never replaced. #1229 - Logic Engine: The Trigger function block's cron schedule was evaluated against UTC instead of the configured application timezone, so a schedule such as "Daily at 07:00" fired at 07:00 UTC and drifted across daylight-saving transitions instead of firing at 07:00 local time. It now resolves the same configured timezone already used by the iCalendar and Host Check schedulers, falling back to
Europe/Zurichif that setting is unresolvable. #1201 - Logic Engine/Admin GUI: Function-block cards in the logic editor sized themselves to a fixed 130px regardless of content, clipping longer port labels unreadably β most noticeably the new Sensor Watchdog block's per-input labels. Cards now size themselves to their widest row and their title instead; blocks with short labels are unaffected. #1218
- Logic Engine/Admin GUI: The JSON Extractor's path picker ("Select path from data") disappeared after adding an output with + and only came back with the next real trigger: the auto-save that follows the edit reloads the sheet, and the next untriggered evaluation (an iCalendar refresh, any datapoint event) re-runs the block without a payload, which wiped the received-data preview. The config panel now keeps the last received payload until a new one arrives, regardless of whether it came in through a real trigger, a manual run or a debug override. Two further cases that hid the picker although data was visibly present are fixed as well: payloads larger than 20 KB were cut mid-document into invalid JSON (the preview now carries the full document up to 256 KB and is pruned structurally above that β first array items, shortened strings, all object keys kept β so it stays parseable; only a document whose object keys alone exceed the limit is still cut, and the panel then shows a hint), and a JSON document that had been serialised twice (a JSON string containing JSON) was treated as plain text β one such level is now unwrapped, both for extraction and for the picker. In debug mode the block's debug band and the Debug values tab now show the extractor's outputs under their configured names instead of
out_1,out_2, β¦. #1104 #1173 - Logic Engine/Admin GUI: The Wake on LAN and Host Check function blocks ignored a repeated
trueon their trigger input: after the first packet or ping, a secondtruetelegram on the driving data point did nothing until the trigger had been set tofalsein between. Both blocks only reacted to a rising edge, which protected against re-firing whenever an unrelated data point re-evaluated the sheet, but also swallowed every genuinely newtrueevent. Both blocks now have a trigger mode setting: On every event fires on every freshly deliveredtrueβ the data point wired to the trigger (directly or through logic blocks such as AND/OR/NOT) must actually have received a new value, so events on other data points of the same sheet still do not fire β while Rising edge only keeps the previous behaviour. Newly placed blocks default to On every event; blocks in existing sheets keep Rising edge only until changed, so devices that report a cyclictruestatus do not suddenly start sending packets on every telegram. Timer/Cron, Change Filter and Edge Detection pulses keep firing on every pulse in both modes, and a manual run of the sheet counts as a fresh event in On every event mode. #1274 - Adapter/KNX: Values for DPT 2.x and DPT 3.x (e.g. 3.007 dimming, 3.008 blinds) were sent as a separate data byte (
DPTArray) instead of in the 6 APCI bits (DPTBinary), so devices expecting the binary form discarded or misread the telegram. They are now sent asDPTBinaryfor writes and read responses, as xknx encodes them; other DPT types are unchanged. #1314
Known Issues π
- none
Contributors π€
Thank you to everyone who contributed to this release:
- Daniel (@abeggled, founder)
- Jochen HΓ€berle (@Micsi)
- @dscheater π (first contribution!)
- Niels (@ikkeflikkeri) π (first contribution!)
- Yves Schumann (@starwarsfan)
Docker Image
docker pull ghcr.io/abeggled/openbridgeserver:latestView on GitHub Container Registry
Checksums (SHA-256)
App Bundle
openbridgeserver-app-bundle_2026.10.0.tar.gz:
32e8253dab247e746b7704e080369038401ae9ab4933976f66f0fe00e7ff13ab
openbridgeserver-lxc_2026.10.0_amd64.tar.zst:
5d5258bb7914f043a727ed8216858925ab071e9af08864e13fef8c2cf47f1071
openbridgeserver-lxc_2026.10.0_arm64.tar.zst:
6cc53f3437acfbc2d7784b77a39d9f5cca4620cd9947c3fe914e4b069cc18086
Thank you!
Thank you for using the open bridge server!
Any support is highly appreciated - be it a star on Github or the recommendation of the project anywhere else.