Venus OS service that discovers and manages Hughes Power Watchdog surge protectors via Bluetooth Low Energy.
A single-process Venus OS service that scans BLE for Power Watchdog devices and presents them as dimmable switches in the Venus OS switches pane. When a device is enabled, the service connects to it via BLE and publishes L1/L2 AC voltage, current, power, energy, and frequency to the Venus OS D-Bus.
Only one Power Watchdog device may be active at a time. Enabling a device automatically disables any previously active one. Each device's switch includes a slider to control the polling interval (100ms-10000ms).
Supports role assignment as a grid meter, generator meter, or PV inverter
meter. Settings (role, custom name, position, polling interval) are
persisted via com.victronenergy.settings and survive reboots.
Supports both 30A (single-line) and 50A (dual-line L1+L2) Power Watchdog models, including gen1 (BT-only) and gen2 (WiFi+BT) hardware.
Overview dashboard — Power Watchdog reporting as the grid meter (178W across L1+L2), with AC Loads, Essential Loads, DC Loads, battery, and inverter status all visible:
Power Watchdog device detail — per-phase AC voltage, current, power, and cumulative energy totals:
Switch panel — Discovery toggle, system setting overrides, and the per-device polling interval slider (shown at 500ms):
dbus-power-watchdog.py (single process — discovery + BLE + grid service)
├─ Registers as com.victronenergy.switch.power_watchdog
├─ Discovery toggle in Venus OS switches pane
├─ Per-device dimmable switches (on/off + polling interval slider)
├─ "Report AC Input Loads" system toggle (HasAcInLoads)
├─ "Use Inverter Metering" system toggle (RunWithoutGridMeter)
└─ When a device is enabled:
├─ Resolves the device (BlueZ cache first, then a scan)
├─ Connects via bleak-retry-connector, routed underneath by
│ bleak-connection-manager v2 (the "bleak catcher")
│ ├─ bleak.BleakClient rebound process wide at startup
│ ├─ Claim-aware adapter selection (/run/bt-claims, bt-claims
│ │ convention — other BLE services on the GX see our use)
│ ├─ Per-adapter link slots (ble_link_caps) and failure-driven
│ │ rotation for pinned devices
│ ├─ Post-connect validation: a link whose GATT is not a Power
│ │ Watchdog is torn down and retried on the next radio
│ └─ habluetooth connection-parameter tuning over the mgmt socket
├─ NotificationWatchdog tears the session down on a silent link
└─ Registers com.victronenergy.grid.power_watchdog_{mac_id}
Only one device may be active at a time. Enabling a second device
automatically disables the first. power_watchdog_device.py is retained
as a standalone fallback for manual use.
- Line 1: Voltage (V), Current (A), Power (W), Energy (kWh), Frequency (Hz)
- Line 2: Voltage (V), Current (A), Power (W), Energy (kWh), Frequency (Hz)
- Combined: Total Power (W), Total Current (A), Average Voltage (V), Total Energy (kWh)
- Error Code: 0-9 (see Hughes documentation)
Each enabled device registers as com.victronenergy.grid.power_watchdog_{mac}
(or genset/pvinverter depending on role).
| Path | Description |
|---|---|
/Role |
Current role (writable: grid, pvinverter, genset) |
/AllowedRoles |
Available roles |
/Position |
PV inverter position (writable, only used when role=pvinverter) |
/CustomName |
User-defined name (writable, persisted) |
/NrOfPhases |
Number of phases (1 or 2, auto-detected) |
/RefreshTime |
Measurement interval in milliseconds |
/Ac/L1/* |
Line 1 measurements |
/Ac/L2/* |
Line 2 measurements (50A models) |
/Ac/Power |
Total AC power (W) |
/ErrorCode |
Current error code |
The service scans BLE for two naming patterns:
| Generation | BLE Name Pattern | Example |
|---|---|---|
| Gen2 (WiFi+BT) | WD_{type}_{serial} |
WD_E7_aabbccddeeff |
| Gen1 (BT-only) | PM{S|D}... (19 chars) |
PMD... (50A), PMS... (30A) |
Discovery scans use plain BleakScanner on the configured adapter. Set
ble_wrap_scanner = true to route scans through the connection manager's
adapter-bound, claiming scanner instead.
- Venus OS >= 3.x (Cerbo GX or similar)
- Hughes Power Watchdog with Bluetooth (gen1 or gen2)
- BLE adapter available on the GX device (one or two HCI adapters)
- Python 3 (included with Venus OS)
- Git (for cloning; the installer will install it via
opkgif needed)
The entire BLE stack comes from the shared install at /data/bcm — one
bleak-connection-manager checkout serving every BLE service on the GX.
install.sh converges that checkout (clone, or fetch + --ff-only) and runs
its installer; the service then puts that checkout on its own sys.path at
start (ble_stack.py, a verbatim copy of the fleet's reference consumer
contract) and imports the connection manager before bleak. Nothing about
how it was launched decides the stack: service/run is plain python3.
This is what keeps every BLE service on the box speaking one version of the
claims convention. Two config keys govern it, ble_connection_manager_dir
(where the install is; empty means never look) and ble_force_start_notify
(the fleet's BlueZ StartNotify policy, stated per consumer).
This repo vendors no part of that stack, and pins no version of it. A private pin is how one service drifts onto a different convention than the rest of the fleet — and it also let this repo's tests run bleak 2.1.1 while production ran 3.0.2. The shared install failing is therefore fatal, not a degradation: nothing else provides bleak.
| Submodule | Purpose |
|---|---|
velib_python |
Victron D-Bus service helper library (not served by the shared install) |
The suite needs a bleak of its own, pinned to what the shared checkout serves so it cannot drift from production:
python3 -m venv .venv && .venv/bin/python -m pip install -r requirements-dev.txt.venv/bin/python -m pytest -qbleak-connection-manager is deliberately absent there — the tests stub it, and its absence exercises the documented "connect uncoordinated" path.
ssh root@<cerbo-ip> "curl -fsSL https://raw.githubusercontent.com/TechBlueprints/dbus-power-watchdog/main/install.sh | bash"ssh root@<cerbo-ip>
cd /data/apps
git clone --recurse-submodules https://github.com/TechBlueprints/dbus-power-watchdog.git
cd dbus-power-watchdog
bash enable.shIf you cloned without --recurse-submodules, initialize them manually:
git submodule update --init --recursive- Install the service (see above)
- Open the Venus OS Remote Console or VRM
- Navigate to Settings > I/O > Switches (or the device list)
- Find "Power Watchdog Manager" and enable Device Discovery
- Discovered Power Watchdog devices will appear as dimmable switches
- Enable a device to start reading AC data (only one may be active at a time)
- Use the slider to adjust the polling interval (100ms-10000ms)
- The device will appear as a grid meter (or genset/pvinverter after role change)
The Power Watchdog Manager pane exposes additional controls beyond the discovery toggle. Each discovered device has its own polling interval slider (see below), and two system-level toggles affect how Venus OS interprets your system topology.
Persistent setting: /Settings/Devices/power_watchdog/Device_{mac_id}/PollIntervalMs
Default: 5000ms (slider position 50)
Each discovered Power Watchdog device appears as a dimmable switch (Type 2) in the Venus OS switches pane. The switch has two controls:
- State (on/off): Enables or disables the BLE connection for this device
- Dimming (slider 1-100): Controls the polling interval
Each slider step is 100ms:
| Slider | Interval | Notes |
|---|---|---|
| 1 | 100ms | Fastest; highest BLE traffic |
| 50 | 5000ms | Default; good balance of responsiveness and efficiency |
| 100 | 10000ms | Slowest; lowest BLE overhead |
The switch name label updates in real-time to show the current interval,
e.g. WD_E7_aabbccddeeff (5000ms).
Slider changes are debounced by 5 seconds before taking effect. This prevents rapid GUI dragging from causing excessive BLE rescheduling. The interval is persisted per device and restored on reboot.
D-Bus setting: /Settings/SystemSetup/HasAcInLoads
Default: ON (1)
Tells Venus OS that there are loads wired between the grid meter and the
inverter's AC input. When enabled, dbus-systemcalc-py calculates
/Ac/ConsumptionOnInput as the difference between the grid meter reading
and the inverter AC-in reading, and publishes it separately from
/Ac/ConsumptionOnOutput.
| Setting | Cerbo GUI | VRM Portal |
|---|---|---|
| ON | Shows separate "AC Loads" (input-side) and "Essential Loads" (output-side) tiles | "AC Loads" tile shows input-side consumption instead of "-" |
| OFF | Shows a single "AC Loads" tile using total /Ac/Consumption |
"AC Loads" tile shows "-" (blank) |
Recommendation: Leave ON if you have any loads between the Power Watchdog and the inverter (e.g., shore power outlets, HVAC). Turn OFF for the simplest display if the Power Watchdog is wired directly to the inverter with nothing in between.
D-Bus setting: /Settings/CGwacs/RunWithoutGridMeter
Default: OFF (0)
Controls whether Venus OS uses the external grid meter (Power Watchdog) or the inverter's internal metering for system calculations.
| Setting | Behavior | VRM Portal |
|---|---|---|
| OFF (default) | Power Watchdog is the authoritative grid meter; system calculations use its readings | Shows "Essential Loads" / "Non-Essential Loads" layout; DC Loads tile is hidden (known VRM limitation) |
| ON | Inverter internal metering is authoritative; Power Watchdog readings are display-only | Shows standard layout with "DC Loads" tile visible; Power Watchdog data still appears but is not used for system calculations |
Recommendation: Leave OFF for accurate grid metering. Only turn ON if you specifically need DC Loads visible on the VRM portal and accept that the Power Watchdog's readings become informational rather than authoritative.
The VRM portal (vrm.victronenergy.com) is a closed-source application with
rendering logic that differs from the local Cerbo GUI and the
venus-html5-app.
When an external grid meter is active (RunWithoutGridMeter = 0), VRM uses
an ESS-style layout that:
- Hides the DC Loads tile entirely (even though
/Dc/System/Poweris correctly published on D-Bus) - Splits consumption into Essential Loads (AC output) and Non-Essential Loads (AC input) instead of showing a single AC Loads value
This is a known, long-standing VRM limitation
based on an old design assumption that systems with external grid meters
do not have DC loads. It affects all external grid meter drivers, including
Victron-maintained ones like dbus-cgwacs and dbus-shelly. We filed
venus#1590 to track
this with Victron.
The local Cerbo GUI and venus-html5-app (used on MFDs) render correctly
in all configurations -- they show DC Loads whenever /Dc/System/Power has
a value, and use /Ac/Consumption for AC Loads without the ESS split.
No D-Bus workaround exists; the fix requires a VRM portal UI change by Victron Energy.
The Power Watchdog publishes BLE error codes (0-9) to /ErrorCode on its
grid service. However, the Venus OS GUI (ListAcInError.qml) only
displays the error code row for two hard-coded product IDs:
- Fronius PV Inverter (
0xA142) — shows the raw error code number - Carlo Gavazzi Energy Meter (
0xB002) — shows a translated message
For any other ProductId, the error row is hidden entirely.
As a workaround, this service sets its /ProductId to 0xA142 (Fronius).
The Fronius code path simply displays the raw error number, which is the
correct behavior for our use case. This only affects the error display
logic in ListAcInError.qml — no other GUI components use the Fronius
product ID for conditional behavior.
We have submitted a pull request to victronenergy/gui-v2 to make error
code display generic for all energy meters:
gui-v2#2816.
Once that PR is accepted, we will switch to a dedicated product ID and
remove this workaround.
Optional: copy config.default.ini to config.ini to customize:
[DEFAULT]
scan_interval = 60
bluetooth_adapters = hci0,hci1
reconnect_delay = 10
reconnect_max_delay = 120
ble_connection_manager = true
ble_link_caps = hci0:5
ble_wrap_scanner = falseThe polling interval is configured per-device via the GUI slider (see above) and is not in the config file. All other configuration is optional. By default the service considers every adapter the kernel exposes and uses sensible defaults.
bluetooth_adapters entries are passed to bleak-connection-manager verbatim
and also decide which adapter our own scans run on:
| Entry | Meaning |
|---|---|
hci1 |
joins the shared pool of adapters links may be placed on |
AA:BB:CC:DD:EE:FF@hci1 |
pins that device to hci1; repeat the MAC for an ordered fallback list |
ble_connection_manager = false disables the catcher and connects with plain
bleak + bleak-retry-connector, uncoordinated with other BLE services.
ble_link_caps (hciX:N) bounds established links per adapter — dongle
limits are undocumented, so this is deployment config, not discovery (~5 is a
workable start for CSR8510-based adapters, ~7 for Broadcom). A full adapter
is passed over instead of failing. An adapter with no cap is never gated.
svc -u /service/dbus-power-watchdog # Start
svc -d /service/dbus-power-watchdog # Stop
svc -t /service/dbus-power-watchdog # Restart
svstat /service/dbus-power-watchdog # Status
tail -f /var/log/dbus-power-watchdog/current | tai64nlocal # Logs- BLE protocol based on prior open-source work by spbrogan and tango2590
- Venus OS D-Bus integration patterns from dbus-ble-advertisements
This project includes velib_python
by Victron Energy BV, located in ext/velib_python/. It is licensed under the
MIT License:
Copyright (c) 2014 Victron Energy BV
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
The full MIT license text is available at ext/velib_python/LICENSE.
Apache License 2.0 - see LICENSE


