Skip to content

Add Device Manager for exisiting devices - #600

Merged
GermanBluefox merged 13 commits into
ioBroker:masterfrom
patricknitsch:master
Aug 26, 2026
Merged

Add Device Manager for exisiting devices#600
GermanBluefox merged 13 commits into
ioBroker:masterfrom
patricknitsch:master

Conversation

@patricknitsch

@patricknitsch patricknitsch commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Adds an ioBroker Device Manager for the Sonoff adapter and, over the course of the PR, fixes several real-world issues found while testing it against live Tasmota devices.

Device Manager

Devices connecting via MQTT (server or bridge mode) show up as auto-discovered cards in admin's Device Manager tab, mirroring the flexible, data-driven approach used by ioBroker.shelly's deviceManager - Sonoff/Tasmota devices have no fixed model registry, so everything is derived from whichever data points a device actually reports.

Devices are grouped (relays, lights/dimmers, covers, Zigbee bridges, energy meters, sensors) by scanning their data points.
The main tile shows connection/RSSI/battery status plus sensor readouts and power draw; the details panel adds network/firmware info (from INFO.), a power metering breakdown and digital inputs.
Controls are auto-generated from the device's writable states: relay/Zigbee/OpenBeken switches, dimmer/color-temperature/shutter sliders, RGB(W) color pickers, and a "Settings" group for any other writable data point.
Actions: rename, open the device's web interface, and "Delete and recreate all data points" - a manual, confirmation-gated reset that clears a device's data points (keeping its name) so they get recreated from scratch the next time it reports its state. Useful after a structural adapter setting (like "Create object tree") changed, or if a device's data points otherwise ended up in a stale/inconsistent shape.
Power metering
Power/voltage/current/energy readings are found by data point name rather than one fixed state ID, so they're picked up wherever they actually exist: the built-in ENERGY.
group, a bridged external meter under its own group name (e.g. SML_Total_in), or German-named aliases (Leistung, Spannung, Strom, ...). When the same reading exists more than once for a device, a deterministic priority resolves it to a single value instead of showing duplicates:

A copy nested under SENSOR/STATE/RESULT/WAKEUP (created by the "Create object tree" option) is preferred over a bare one.
Otherwise, the most recently updated copy wins.
The built-in energy group is labeled "Power"/"Leistung" (translated) instead of the raw "ENERGY" group name.

Bug fixes found along the way

Root cause of a duplicated ENERGY folder: the STATUS10 response handler (stat/.../STATUS10, e.g. requested by Home Assistant discovery) always used an empty prefix, regardless of the "Create object tree" setting - unlike every other handler in mqttBase.ts. With that option on, this produced a malformed state ID with a leading dot (..ENERGY.) instead of the correct .SENSOR.ENERGY., which looked like a second, independent "ENERGY" folder. Fixed, with a regression test.
Data points that can exist under more than one topic group (e.g. Uptime as a one-off echo in a RESULT response vs. the periodic STATE telemetry) now prefer the periodic telemetry group, so the shown value keeps updating instead of getting stuck on a stale echo.
Removed "Period" (Tasmota's internal energy-accounting counter) from the power metering section - it was mislabeled as a Watt reading and empty for most users.
The model shown on a device's tile no longer falls back to a "Tasmota device" placeholder when INFO.Module isn't known yet - just the identifier is shown.
Fixed two sizing issues from a misread of the dm-utils item schema: the sensor/power readouts on the tile were rendered at a fixed, too-small 12px font instead of the default size, and the "Power metering"/"Digital inputs" section headers used a fairly large heading level.

claude and others added 13 commits August 25, 2026 18:21
Devices connecting via MQTT (server or bridge mode) now show up as
auto-discovered cards in the admin's Device Manager tab, mirroring the
flexible, data-driven approach used by ioBroker.shelly's deviceManager.

- Devices are grouped (relays, lights/dimmers, covers, Zigbee bridges,
  energy meters, sensors) by scanning which data points a device
  actually created, since Tasmota/Sonoff devices have no fixed model
  registry.
- Main tile shows connection/RSSI/battery status plus sensor readouts
  and power draw; the details panel adds network/firmware info (from
  INFO.*), energy metering breakdown and digital inputs.
- Controls are auto-generated from the device's writable states:
  relay/Zigbee/OpenBeken switches, dimmer/color-temperature/shutter
  sliders, RGB(W) color pickers, and a "Settings" group for any other
  writable data point.
- Device rename and "open web interface" actions.
- Wired into the adapter via objectChange/stateChange forwarding and a
  new admin "Device manager" tab.
Extends the Device Manager's power metering, following the approach
from ioBroker.shelly PR #1562: instead of only looking at the
built-in ENERGY.* group, every state of a device is matched against
a table of known power/voltage/current/energy data points by name.

This also picks up power values from bridged external meters (SML
smart-meter heads, PZEM sensors, ...), which Tasmota publishes under
their own custom group name (e.g. "SML_Total_in") rather than under
ENERGY. When a device has more than one meter, the details panel now
groups the values per meter; the main tile bolds the "Power" (W)
reading(s) the same way as before.

Also fills in MARGINS.* (power thresholds) and the German-named
duplicate data points (Leistung, Spannung, Strom, Frequenz, ...) that
were previously not shown at all.
When a device's INFO.Module isn't known yet, the card showed the
literal fallback text "Tasmota device" next to the identifier (e.g.
"messdose-trockner Tasmota device"). Just omit the model in that case
so only the identifier is shown.
With the "Create object tree" adapter option enabled, Tasmota's
SENSOR/STATE payloads (which is where ENERGY, Wifi.RSSI and Uptime
usually live) get nested under an extra "SENSOR."/"STATE." group, e.g.
"SENSOR.ENERGY.Power" or "STATE.Wifi.RSSI" instead of the flat/
single-level state IDs the device manager assumed. That made power
values, RSSI, uptime and other sensor readouts silently disappear
from the tiles for anyone using that option.

State lookups for these data points now match by their last path
segment (the actual data point name) instead of a fixed suffix, so
they find the value no matter how many groups it is nested under.
When "Create object tree" (OBJ_TREE) is toggled, a data point's state
ID changes (e.g. ENERGY.Power vs. SENSOR.ENERGY.Power), but the old
state is never deleted. Both then showed up as separate "meters",
duplicating the whole power metering section. Power entries are now
grouped by their channel with any SENSOR./STATE./RESULT./WAKEUP.
prefix stripped, keeping only the one entry per meter/data point that
matches the adapter's current OBJ_TREE setting.

Also fixes two sizing issues from a misread of dm-utils' item types:
"size" on a state/staticInfo item is a font size, not a grid width -
the sensor readouts on the tile were rendered at a fixed 12px, making
them look smaller than everything else. And "size" on a header item
is a heading level (1 biggest), so the section headers in the
Information tab used a fairly large heading (4) - shrunk to 6.
The previous dedup grouped entries by their literal data point name
(e.g. "Power"), which doesn't merge a reading that also exists under
a different, German-named alias for the same value (e.g. "Leistung"
mapping to the same "Power" label). Grouping by the resolved label
instead - which is what's actually rendered - catches that case too,
so the same meter no longer shows up twice with the raw channel name
("ENERGY") as a stand-in label.

Also format the tile's power reading like the other sensor readouts
(dimmed, regular weight) instead of bold, and fix a stray embedded
NUL byte that had ended up in the grouping key.
Guessing which of two duplicate power states is "the current one"
from the adapter's OBJ_TREE setting didn't hold up: a leftover
ENERGY.* state from before OBJ_TREE was toggled kept showing up next
to the live one, since Tasmota can apparently keep the old topic
around independently of that setting. Deleting the leftover object
tree manually made the duplicate go away, confirming it really is a
stale, no-longer-updated state rather than a second live meter.

Now the entry with the most recently updated state (`ts`) wins per
meter and metric - the leftover simply stops receiving updates once
its topic is no longer published, so it naturally loses out to the
live one without needing to know why it's stale.

Also: the built-in ENERGY group's label is now translated to "Power"/
"Leistung" (via i18n) instead of showing the raw "ENERGY" group name,
both on the tile (when a device has more than one meter) and in the
Information tab's per-meter header. A bridged external meter's group
name (e.g. "SML") has no fixed translation and is still shown as-is.
Toggling "Create object tree" (OBJ_TREE) moves a data point to a
different state ID (e.g. RSSI: "Wifi_RSSI" -> "STATE.Wifi.RSSI"), but
the adapter never deleted the old one - it just kept existing next to
the live one, most visibly duplicating every power-metering value.

The device manager now runs a cleanup once at startup and every hour
after that: for every data point that exists more than once for the
same device and meter/value, it deletes the copy/copies that don't
match the *current* OBJ_TREE setting - but only once a copy that does
match already exists, so nothing is ever deleted before its live
replacement is confirmed to be there. Since this restarts on every
adapter start (which js-controller does whenever the config is
saved), the leftover from before a config change is cleaned up
automatically without any manual object-tree surgery.

Covers power metering, RSSI, uptime, battery and the sensor readouts
(temperature, humidity, ...) - the data points that move around under
OBJ_TREE the same way.
The automatic cleanup after an OBJ_TREE change (from the previous
commit) needed to special-case more and more data points that must
never be touched (the POWER switch itself, for one) to stay safe,
which made it fragile and hard to fully trust.

Simpler and safer: a "Delete and recreate all data points of this
device" action on each device card. It deletes every data point of
the device (keeping the channel object, so a custom name via "rename"
survives) - the MQTT handling recreates them from scratch, in
whatever shape the current adapter configuration produces, the next
time the device reports its state. Requires confirmation since it's
destructive.
Some Tasmota firmware reports energy readings both nested inside its
regular "SENSOR" telemetry (which "Create object tree" nests further
under "SENSOR.ENERGY") and via its own dedicated "ENERGY" topic (a
bare "ENERGY.*" state, handled separately in mqttBase.ts) - so both
can be live and continuously updated at the same time, not just one
stale and one current. The freshness-based dedup couldn't tell those
apart reliably.

A device that also sends its own dedicated topic still sends the
regular telemetry too, so whenever a SENSOR/STATE/RESULT/WAKEUP-nested
copy of a reading exists, it is now always preferred over a bare one,
independent of timestamps. Freshness is only used as a fallback when
no nested copy exists (or more than one nested copy exists, e.g. two
aliased data point names).
Found from the reported log line: "stat/Trockner/STATUS10 =
{"StatusSNS":{"ENERGY":{...}}}". Requesting Status 10 mirrors the
same data as the periodic tele/.../SENSOR telemetry, but its handler
always passed an empty prefix to checkData(), regardless of the
"Create object tree" (OBJ_TREE) option - unlike every other call site
in this file, which switches to a named prefix ("SENSOR"/"STATE"/...)
when OBJ_TREE is on.

With OBJ_TREE on, checkData() builds the prefix for a nested object
(here "ENERGY") as `${prefix}.${group}`; with an empty prefix that
produces the malformed id "<device>..ENERGY.<field>" (leading dot) -
a separate, bogus state from the real "<device>.SENSOR.ENERGY.<field>"
one, which is what the "duplicate ENERGY folder" the user kept seeing
actually was: not a device sending two independent topics, but this
one topic's handler misplacing its data every time a client (Home
Assistant discovery, a script, ...) requests Status 10.
"Period" (Tasmota's energy accounting period counter) was mapped as
if it were a power reading (unit "W"), but it isn't one and stayed
empty for most users - dropped from the power metering section.

findDataPointSuffix picked whichever matching state happened to come
first in object iteration order, which could be a one-off echo (e.g.
"RESULT.Uptime", only ever set once as part of a command response)
instead of the periodic telemetry ("STATE.Uptime") that actually keeps
updating - showing a frozen value. When a data point exists in more
than one group, the periodic telemetry groups (STATE, then SENSOR)
are now preferred, fixing this for Uptime and any other data point
read through this helper (RSSI, battery, sensor readouts).
…-hi62gu

Add ioBroker Device Manager support
@patricknitsch

Copy link
Copy Markdown
Contributor Author

@GermanBluefox

@GermanBluefox
GermanBluefox merged commit d90f8e8 into ioBroker:master Aug 26, 2026
1 check passed
@patricknitsch

Copy link
Copy Markdown
Contributor Author

@GermanBluefox Release 4.1.0 failed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants