[APP][Pro] SNMP - Network Devices — your UPS, NAS and switch ports in Homey

SNMP - Network Devices — act on a power cut, don’t just survive it.


A UPS keeps Homey alive through a cut. It never tells Homey a cut is happening — so nothing gets shut down cleanly, and you find out afterwards.

This app reads your UPS over the network and turns the moment mains power is lost into a Flow trigger, while the battery still holds. The non-essentials go off, the NAS shuts down properly, and you get told.

:white_check_mark: Nothing to install on the equipment. Everything is read over SNMP, which UPS network cards, NAS boxes and managed switches already speak.


Test version

Source code :octopus:


The one that was missing: a UPS plugged into your NAS

Most home UPS units have no network card — that card costs about as much as the UPS itself. They sit on a USB cable behind a NAS, which is exactly where the existing Homey apps stop.

This app reads that UPS through the NAS. On a Synology: enable SNMP in DSM, have the UPS configured in Hardware & Power, and it appears. You install nothing, you configure no daemon, you run no NUT server.

:mag: If you have been following the APC thread stuck on “no new devices found” since 2018 — this is the case it never covered.


What it does

  • :battery: A UPS — charge, runtime in minutes, output load, voltages, temperature
  • :rotating_light: Alarms for mains lost, low battery, overload, and a battery needing replacement
  • :speech_balloon: An alarm that names what is wrong, in the UPS’s own words. Four booleans can say something is wrong; only this says which thing — a failed fan, a dead charger, or a UPS running on bypass and no longer protecting anything
  • :floppy_disk: A NAS or server — CPU load, memory, storage per volume, uptime. Standard HOST-RESOURCES-MIB, so a Synology, a QNAP, a Linux box with snmpd or an ESXi all work the same way
  • :electric_plug: Switch ports, one Homey device per port — link, negotiated speed, errors, PoE draw
  • :mag: It finds your equipment for you — a sweep of your network, and anything that answers is offered. You can still add one by address

Nothing is hard-coded per model

Everything read comes from the standard MIBs — RFC 1628 for a UPS, RFC 2790 for a host, RFC 2863 and RFC 3621 for a switch. One driver covers every brand: APC, Eaton, Riello, CyberPower, Socomec and the third-party network cards all answer the same questions.

Where a vendor speaks its own dialect, the app falls back to it rather than giving up — a Synology relaying NUT, an APC card answering PowerNet instead of the RFC.


Flow cards

  • :arrow_forward: Mains power is lost — the one the app exists for
  • :arrow_forward: Mains power returns
  • :arrow_forward: Contact is lost during an outage — see the honest limitation below
  • :arrow_forward: The battery gets low
  • :arrow_forward: Runtime drops below a number of minutes you choose
  • :arrow_forward: Overload, battery needing replacement, or any status change
  • :heavy_check_mark: The UPS is on battery (condition)
  • :heavy_check_mark: Runtime is above, or charge is above, a value (conditions)
  • :arrows_counterclockwise: Read the device now (action)

Every capability above also gets its own Flow card automatically.


Control, off by default :lock:

Switch ports can be switched off and PoE cycled — a stuck camera comes back without walking to the rack.

This is disabled until you enable it per device, and the setting says plainly why: the port you cut may be the one feeding your access point, your router or Homey itself, and getting it back then needs physical access.


The honest limitation :warning:

On the NAS path, the sensor is powered by the thing it watches. Your Synology runs on the UPS it reports on, and DSM shuts it down partway through a long outage.

So the app sees the beginning of a cut reliably — the part that matters, and it fires in about ten seconds — and loses contact before the end of a long one. It does not pretend the power came back: losing contact while the last known state was on battery is its own Flow trigger, and the state stays frozen rather than flipping to “unknown”.

A UPS with its own network card has none of this: the card is powered by the UPS and lives as long as it does.


Requirements

  • :house: Homey Pro — Homey Cloud and Homey Bridge cannot reach devices on your network
  • :key: SNMP enabled on the equipment — off by default on recent APC network cards, and off until enabled on a Synology. When the app finds nothing it tells you what to switch on rather than showing an empty list
  • :pushpin: A fixed address, or a device whose MAC prefix the app knows — a changed DHCP lease is the most common reason a working device goes quiet

Not supported, plainly

  • :x: QNAP UPS relaying — the OIDs exist but are unconfirmed on real hardware
  • :x: A Linux host running NUT — NUT has no SNMP agent, so a plain snmpd exposes nothing
  • :x: SNMPv3 — v1 and v2c only for now
  • :x: PDUs — no RFC covers outlet control, so it is a vendor dialect per brand

Languages :globe_with_meridians:

English, French, Dutch.


What has happened since :handshake:

A real UPS has now answered this code. @SunBeech has a CyberPower VP700ELCD on USB behind a Synology, and cross-checked it against the NUT-based app: status, remaining runtime, output load, battery voltage and the switch to battery all read correctly.

That was the one thing this app shipped without. On the Synology path, it is no longer open.

The NAS and switch paths still have not met real hardware. The shots below are a real Homey reading a local SNMP simulator that serves the real MIBs over real UDP — which proves the wire, not the OIDs.

Everything is checked against the RFCs and the vendor MIBs, with a test suite of 569 that pins the parts which fail silently — unit conversions, and the difference between “the device did not say” and “zero”. A UPS reporting nothing must never look like a UPS reporting an empty battery.

If you have a UPS, a NAS or a managed switch, I would very much like to hear what it does. Good or bad.


What it looks like :camera:


A cut in progress — on battery, runtime falling, the alarm raised.


A NAS. RAM and virtual memory are not offered as volumes — only real filesystems are. There is no temperature tile because this host does not publish ENTITY-SENSOR-MIB: the app declares only what actually answers.


Picking ports on a switch. The names are the switch’s own port descriptions, and each PoE socket is matched to its port. Ports with no link are left unchecked.


One port, as its own device — link, PoE, negotiated speed, throughput and error counters. A port without PoE simply has no PoE tile.

Thanks for this app :grinning_face:!

I have a Synology NAS to which a Cyberpower UPS is connected. I use one of the other apps to monitor the UPS: Network UPS Tools (NUT). That is also helpful now as I can compare the values reported by your app to the other app.

The reported values seem to be fine.

On grid power

On battery

Are there other metrics available that you are planning to add to the app? Like the input and output voltage and battery runtime?

Thank you — and this matters more than it looks. Yours is the first real UPS this code has ever read. Everything before it was a simulator I wrote from the MIB, and a simulator built from the same reading as the app can only prove the wire, never the reading. Cross-checking against the NUT app is exactly the check I could not do myself.

Runtime is already there — it is the 55 min tile. Synology reports it in seconds (upsBatteryRuntimeValue) and the app converts.

Input and output voltage: you are right, and they exist. I simply had not read that far into the MIB — I built the first version from the objects two independent sources had confirmed, and those stop at the battery group. They are there:

  • upsInputVoltageValue1.3.6.1.4.1.6574.4.4.1.1.0
  • upsOutputVoltageValue1.3.6.1.4.1.6574.4.5.1.1.0

The same branch also carries input and output frequency and current, apparent and real power, and one I like a lot: upsInputTransferReason (…4.4.2.1.0) — the reason it last switched to battery, in the UPS’s own words. That would make a good Flow token.

I will add them. Whether your UPS populates them is a separate question: a USB UPS only exposes what its HID profile carries, and DSM only relays what NUT managed to read.

Which brings me to something in your screenshots: there is no battery charge tile. The app declares a capability only when the value actually answers, so it looks like your VP700ELCD does not report battery.charge through DSM — while happily reporting runtime, load and battery voltage. Does the other app show you a charge percentage?

If you are willing, one command from any machine on your network would settle all of this at once:

snmpwalk -v2c -c public <nas-ip> 1.3.6.1.4.1.6574.4

(public, or whichever community you set in DSM.) That dumps everything your Synology actually exposes for that UPS. With it I can add exactly what works on real hardware, instead of what the MIB merely promises.

You are right; I missed that one. My bad.

Yes, I am aware of that. And that’s not a problem. The other app also has several capabilities for which my UPS does not provide information, like the ‘Current output’ and ‘Battery temperature’.

By the way: I did just note that the sensors tab of your app now includes an additional capability, with no value: Alarm. I think it was added after I cut the power from the UPS and I restarted my NAS.

No the other app does not have a battery charge capability. It only has a ‘Battery runtime’ capability (in seconds).

The snmpwalk command was not available on my Windows machine. I downloaded the SnmpWalk tool, which uses a slightly different syntax. But I think the following is what you are looking for:

SnmpWalk output
OID=.1.3.6.1.4.1.6574.4.1.1.0, Type=OctetString, Value=VP700ELCD
OID=.1.3.6.1.4.1.6574.4.1.2.0, Type=OctetString, Value=CPS
OID=.1.3.6.1.4.1.6574.4.1.3.0, Type=OctetString, Value=
OID=.1.3.6.1.4.1.6574.4.1.4.0, Type=OctetString, Value=ups
OID=.1.3.6.1.4.1.6574.4.2.1.0, Type=OctetString, Value=OL
OID=.1.3.6.1.4.1.6574.4.2.5.0, Type=OctetString, Value=VP700ELCD
OID=.1.3.6.1.4.1.6574.4.2.6.1.0, Type=OctetString, Value=CPS
OID=.1.3.6.1.4.1.6574.4.2.7.0, Type=OctetString, Value=
OID=.1.3.6.1.4.1.6574.4.2.8.0, Type=OctetString, Value=0764
OID=.1.3.6.1.4.1.6574.4.2.9.0, Type=OctetString, Value=0501
OID=.1.3.6.1.4.1.6574.4.2.12.1.0, Type=Opaque, Value=  9F 78 04 41 30 00 00
OID=.1.3.6.1.4.1.6574.4.2.14.1.0, Type=Integer, Value=30
OID=.1.3.6.1.4.1.6574.4.2.14.3.0, Type=Integer, Value=20
OID=.1.3.6.1.4.1.6574.4.2.15.1.0, Type=Integer, Value=-60
OID=.1.3.6.1.4.1.6574.4.2.15.3.0, Type=Integer, Value=-60
OID=.1.3.6.1.4.1.6574.4.2.16.2.0, Type=OctetString, Value=No test initiated
OID=.1.3.6.1.4.1.6574.4.2.21.2.0, Type=Opaque, Value=  9F 78 04 43 C3 00 00
OID=.1.3.6.1.4.1.6574.4.2.22.0, Type=OctetString, Value=enabled
OID=.1.3.6.1.4.1.6574.4.3.1.1.0, Type=Opaque, Value=  9F 78 04 42 C8 00 00
OID=.1.3.6.1.4.1.6574.4.3.1.2.0, Type=Opaque, Value=  9F 78 04 41 20 00 00
OID=.1.3.6.1.4.1.6574.4.3.1.4.0, Type=Opaque, Value=  9F 78 04 41 A0 00 00
OID=.1.3.6.1.4.1.6574.4.3.2.1.0, Type=Opaque, Value=  9F 78 04 41 5C CC CD
OID=.1.3.6.1.4.1.6574.4.3.2.2.0, Type=Opaque, Value=  9F 78 04 41 40 00 00
OID=.1.3.6.1.4.1.6574.4.3.6.1.0, Type=Integer, Value=3306
OID=.1.3.6.1.4.1.6574.4.3.6.2.0, Type=Integer, Value=300
OID=.1.3.6.1.4.1.6574.4.3.9.0, Type=OctetString, Value=CPS
OID=.1.3.6.1.4.1.6574.4.3.12.0, Type=OctetString, Value=PbAcid
OID=.1.3.6.1.4.1.6574.4.4.1.1.0, Type=Opaque, Value=  9F 78 04 43 67 00 00
OID=.1.3.6.1.4.1.6574.4.4.1.4.0, Type=Opaque, Value=  9F 78 04 43 66 00 00
OID=.1.3.6.1.4.1.6574.4.4.2.2.0, Type=Opaque, Value=  9F 78 04 43 2F 00 00
OID=.1.3.6.1.4.1.6574.4.4.2.3.0, Type=Opaque, Value=  9F 78 04 43 8F 80 00
OID=.1.3.6.1.4.1.6574.4.4.6.1.0, Type=Opaque, Value=  9F 78 04 43 F9 80 00
OID=.1.3.6.1.4.1.6574.4.5.1.1.0, Type=Opaque, Value=  9F 78 04 43 68 00 00
OID=.1.3.6.1.4.1.6574.4.7.1.0, Type=OctetString, Value=usbhid-ups
OID=.1.3.6.1.4.1.6574.4.7.2.0, Type=OctetString, Value=DSM7-4-90076-260709
OID=.1.3.6.1.4.1.6574.4.7.3.0, Type=OctetString, Value=CyberPower HID 0.6
OID=.1.3.6.1.4.1.6574.4.7.4.0, Type=OctetString, Value=0.47
OID=.1.3.6.1.4.1.6574.4.7.5.0, Type=Integer, Value=5
OID=.1.3.6.1.4.1.6574.4.7.6.0, Type=OctetString, Value=auto
OID=.1.3.6.1.4.1.6574.4.7.7.0, Type=Integer, Value=30

That walk is the most useful thing anyone has given this app. Thank you for going and finding a tool for it.

First, I was wrong about the battery charge, and your own data proves it. upsBatteryChargeValue is right there:

.4.3.1.1.0  Opaque  9F 78 04 42 C8 00 00  ->  100.0 %

Those Opaque values are IEEE-754 floats behind a Net-SNMP header (9F 78 04 then four bytes), so they are unreadable as printed — which is exactly why I asked for the raw walk. I had inferred the capability was missing from your screenshot. It is not missing: Homey renders measure_battery as a battery gauge rather than a sensor tile, so it simply is not where I was looking. My apologies for sending you off on that.

Decoded, your UPS says:

Battery charge 100.0 %
Input voltage 231.0 V
Output voltage 232.0 V
Battery voltage 13.8 V (DC — not the mains)
Load 11.0 %
Runtime 3306 s = 55.1 min
Nominal input 230.0 V · nominal battery 12.0 V · nominal real power 390 W

Input and output voltage now work. They are written and tested, against your bytes rather than against my own fixtures — every other test in this app is built from the same MIB reading as the app itself, so none of them could ever have contradicted it. Yours can. It already did.

Three things your walk decided that I would otherwise have got wrong:

  • Real power is not there. Only upsInfoRealPowerNominal (390 W) answers, not the measured value. I could multiply your 11 % load by 390 W and show 43 W — plausible, and invented. measure_power stays empty rather than lie.
  • upsInputTransferReason is absent on your unit. That was the one I said I liked; it stays unused until some device proves it answers.
  • Input frequency reads 499 on a 50 Hz grid — tenths of a Hz. Adding it blind would have printed “499 Hz”.

On the empty Alarm capability: that is a real bug, and a worse one than it looks. The capability is created the first time an alarm fires and is never removed — removing it would destroy its Insights history. But it was never rewritten either, so it stayed frozen on the alarm that created it, or blank when the alarm had already cleared. A stale alarm text is worse than none: it describes a past incident with the authority of the present. It now updates every cycle, and reads “No alarm” instead of sitting blank.

Both fixes are in the next build. Thank you — none of this was reachable from the MIB alone.

nice!! pinned for future implementation

You’re welcome and looking forward to the next build.

You are right. I also was not looking there. But now I see a battery tab in your app that includes both the Charge Level and the Battery Alarm.

v1.0.5 is up on the test channel now, with both of them in it:

It is in certification, so it will take a couple of weeks to reach the live channel — but the test channel updates on its own, so you may already have it.

Two things to look for on your VP700ELCD:

  • Input voltage and Output voltage as new tiles. Mine reads them from a simulator fed with your bytes — 231 V and 232 V. If yours shows something else, that is the interesting case and I would like to know.
  • The Alarm capability should now read No alarm instead of sitting blank, and should follow the UPS from now on rather than staying frozen.

Your bytes are now a test in the repository. Every other test in this app is built from the same reading of the MIB as the app itself, so none of them can ever contradict it — yours can, and it already did once. That makes it the only one worth having.

One thing I decided not to add, for the record: your walk shows upsInfoRealPowerNominal at 390 W but no measured real power. I could multiply your 11 % load by that and display 43 W. It would look like a measurement and it would be arithmetic, so the power tile stays empty instead.

@Patrick_Van_Deursen thanks — if you do get to it, a walk of 1.3.6.1.4.1.6574.4 from whatever you have is worth more to this app than anything I can reason out from a MIB file.

I can confirm both changes: the voltages are added and the Alarm capability reads No alarm (‘Geen alarm’ in Dutch).

For reference I also included the values reported by the Network UPS Tools (NUT) app (on the righthand side) that I am also using.

  • Input voltage = Ingangsspanning = Voltage input
  • Output voltage = Uitgangsspanning = Voltage output

I assume the slight difference in voltage values is due to a different polling point in time. Currently they are the same.

I also tried your app’s NAS device and switchport device, and it is able to obtain the following statistics of my Synology NAS:

And from my switch:

They do state that, but often the apps already get published the next working day

would love to help, and will note it down. but… what is: a walk of 1.3.6.1.4.1.6574.4 from whatever you have is worth more to this app than anything I can reason out from a MIB file.

Sorry — that was jargon. Plain version:

SNMP is a big numbered tree. 1.3.6.1.4.1.6574.4 is the branch Synology puts its UPS readings on. A walk just means “print me everything under this branch”. It reads, it never writes.

Why it is worth more than the MIB file: the MIB says what a device could report. The walk says what yours actually does. On @SunBeech’s CyberPower, half the branch was empty and one value came back in tenths of a Hz — neither of which the MIB mentions. I would have shipped “499 Hz” on a 50 Hz grid.

If you ever feel like it, from a Mac or Linux box on the same network:

snmpwalk -v2c -c public <nas-ip> 1.3.6.1.4.1.6574.4

On Windows there is no built-in snmpwalk; SunBeech used a free tool called SnmpWalk, whose syntax differs slightly. Paste whatever comes out — the Opaque lines look like gibberish (9F 78 04 42 C8 00 00) but that is a float in disguise and I can decode it.

And no rush at all. This app has exactly one real UPS behind it so far, so anything is a gain.

The syntax is as follows:

snmpwalk -v:2c -c:<snmp community> -r:<nas ip> -os:1.3.6.1.4.1.6574.4 -op:1.3.6.1.4.1.6574.5

Regrading the snmp community: default is public, but you might have configured a different one on your Synology NAS.

You also need to enable snmp on your NAS (it is disabled by default). If your NAS is accessible via the internet, make sure that you block the SNMP service in your NAS’s firewall and enable it only for local/LAN traffic.

Thank you — the Windows syntax and the firewall note are both worth more than the original post said, and I have nothing to add to either.

v1.0.7 is on the test channel, and two of its fixes came straight out of the screenshots you posted. You did not report them as bugs; I found them by looking.

Your NAS was offering /tmp and /tmp/SynologyAuthService as volumes. They are tmpfs — RAM, not storage. The filter that rejects “Physical memory” could not catch them, because DSM declares them with a disk type, exactly like /volume1. Only the mount-path filter could, and it did not know /tmp. Gone now.

Your switch port showed no throughput at all. The app was only asking for ifHCInOctets/ifHCOutOctets — the 64-bit counters of ifXTable. Your switch does not serve that table; its error counters worked because those come from ifTable, which it does serve. There is now a 32-bit fallback.

That one needed care rather than code. A 32-bit counter wraps every 34 seconds at a saturated gigabit, so a counter going backwards is probably a wrap — but “probably” is not enough to publish a number. The fallback only corrects a wrap when the link speed is known, when the corrected rate fits what the link can carry, and when two wraps were physically impossible in the elapsed window. If two wraps could have happened, one and two are indistinguishable, and picking “one” would halve the real throughput while looking perfectly plausible. In that case it reports nothing.

Also in 1.0.7, and this one is not from your hardware: a switch that renumbers its interfaces after a restart was only noticed on the slow poll, so for up to five minutes a Flow card could have switched off a neighbouring port. The command is now refused with an explanation instead.

If /tmp still shows on your NAS after the update, it is a leftover: the app never deletes a capability, because that would destroy its Insights history. Re-pairing the device is the only way to clear old ones.

I checked version 1.0.7 and re-created the devices (I removed them earlier).

These folders are now indeed not included:

The incoming and outgoing throughput capabilities are now displayed: