home-assistant: fix zigbee sensor battery reporting
Some checks failed
Run nix flake check / flake-check (push) Has been cancelled
Some checks failed
Run nix flake check / flake-check (push) Has been cancelled
WSDCGQ12LM sensors report battery: 0 due to firmware quirk. Override battery calculation using voltage via homeassistant value_template. Also adds zigbee_sensor_stale alert for detecting dead sensors regardless of battery reporting accuracy (1 hour threshold). Device configuration moved from external devices.yaml to inline NixOS config for declarative management. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,11 +5,11 @@
|
|||||||
Three Aqara Zigbee temperature sensors report `battery: 0` in their MQTT payload, making the `hass_sensor_battery_percent` Prometheus metric useless for battery monitoring on these devices.
|
Three Aqara Zigbee temperature sensors report `battery: 0` in their MQTT payload, making the `hass_sensor_battery_percent` Prometheus metric useless for battery monitoring on these devices.
|
||||||
|
|
||||||
Affected sensors:
|
Affected sensors:
|
||||||
- **Temp Living Room** (`0x54ef441000a54d3c`) — area: living_room
|
- **Temp Living Room** (`0x54ef441000a54d3c`) — WSDCGQ12LM
|
||||||
- **Temp Office** (`0x54ef441000a547bd`) — area: office
|
- **Temp Office** (`0x54ef441000a547bd`) — WSDCGQ12LM
|
||||||
- **temp_server** — area: server_room
|
- **temp_server** (`0x54ef441000a564b6`) — WSDCGQ12LM
|
||||||
|
|
||||||
The **Temp Bedroom** sensor (`0x00124b0025495463`) is a different model and reports battery correctly (69% at time of investigation).
|
The **Temp Bedroom** sensor (`0x00124b0025495463`) is a SONOFF SNZB-02 and reports battery correctly.
|
||||||
|
|
||||||
## Findings
|
## Findings
|
||||||
|
|
||||||
@@ -17,15 +17,47 @@ The **Temp Bedroom** sensor (`0x00124b0025495463`) is a different model and repo
|
|||||||
- The Zigbee2MQTT payload includes a `voltage` field (e.g., `2707` = 2.707V), which indicates healthy battery levels (~40-60% for a CR2032 coin cell).
|
- The Zigbee2MQTT payload includes a `voltage` field (e.g., `2707` = 2.707V), which indicates healthy battery levels (~40-60% for a CR2032 coin cell).
|
||||||
- CR2032 voltage reference: ~3.0V fresh, ~2.7V mid-life, ~2.1V dead.
|
- CR2032 voltage reference: ~3.0V fresh, ~2.7V mid-life, ~2.1V dead.
|
||||||
- The `voltage` field is not exposed as a Prometheus metric — it exists only in the MQTT payload.
|
- The `voltage` field is not exposed as a Prometheus metric — it exists only in the MQTT payload.
|
||||||
- This is a known firmware quirk with some Aqara sensors that always report 0% battery.
|
- This is a known firmware quirk with some Aqara WSDCGQ12LM sensors that always report 0% battery.
|
||||||
|
|
||||||
## Possible Solutions
|
## Implementation
|
||||||
|
|
||||||
### 1. Expose voltage as a Prometheus metric
|
### Solution 1: Calculate battery from voltage in Zigbee2MQTT (Implemented)
|
||||||
Enable the voltage sensor entities in Home Assistant (they may exist but be disabled by default). The HA Prometheus integration would then export them automatically.
|
|
||||||
|
|
||||||
### 2. Calculate battery from voltage in Zigbee2MQTT
|
Override the Home Assistant battery entity's `value_template` in Zigbee2MQTT device configuration to calculate battery percentage from voltage.
|
||||||
Override the battery calculation using the voltage field. Approximate formula: `(voltage - 2100) / (3000 - 2100) * 100`.
|
|
||||||
|
|
||||||
### 3. Alert on sensor staleness instead
|
**Formula:** `(voltage - 2100) / 9` (maps 2100-3000mV to 0-100%)
|
||||||
Create a Prometheus alert based on `hass_last_updated_time_seconds` going stale (e.g., no temperature update in 1 hour). This detects dead sensors regardless of battery reporting accuracy.
|
|
||||||
|
**Changes in `services/home-assistant/default.nix`:**
|
||||||
|
- Device configuration moved from external `devices.yaml` to inline NixOS config
|
||||||
|
- Three affected sensors have `homeassistant.sensor_battery.value_template` override
|
||||||
|
|
||||||
|
**Expected battery values based on current voltages:**
|
||||||
|
| Sensor | Voltage | Expected Battery |
|
||||||
|
|--------|---------|------------------|
|
||||||
|
| Temp Living Room | 2710 mV | ~68% |
|
||||||
|
| Temp Office | 2658 mV | ~62% |
|
||||||
|
| temp_server | 2765 mV | ~74% |
|
||||||
|
|
||||||
|
### Solution 2: Alert on sensor staleness (Implemented)
|
||||||
|
|
||||||
|
Added Prometheus alert `zigbee_sensor_stale` in `services/monitoring/rules.yml` that fires when a Zigbee temperature sensor hasn't updated in over 1 hour. This provides defense-in-depth for detecting dead sensors regardless of battery reporting accuracy.
|
||||||
|
|
||||||
|
**Alert details:**
|
||||||
|
- Expression: `(time() - hass_last_updated_time_seconds{entity=~"sensor\\.(0x[0-9a-f]+|temp_server)_temperature"}) > 3600`
|
||||||
|
- Severity: warning
|
||||||
|
- For: 5m
|
||||||
|
|
||||||
|
## Post-Deployment Steps
|
||||||
|
|
||||||
|
After deploying to ha1:
|
||||||
|
|
||||||
|
1. Restart zigbee2mqtt service (automatic on NixOS rebuild)
|
||||||
|
2. In Home Assistant, the battery entities may need to be re-discovered:
|
||||||
|
- Go to Settings → Devices & Services → MQTT
|
||||||
|
- The new `value_template` should take effect after entity re-discovery
|
||||||
|
- If not, try disabling and re-enabling the battery entities
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Device configuration is now declarative in NixOS. Future device additions via Zigbee2MQTT frontend will need to be added to the NixOS config to persist.
|
||||||
|
- The `devices.yaml` file on ha1 will be overwritten on service start but can be removed after confirming the new config works.
|
||||||
|
|||||||
@@ -69,6 +69,44 @@
|
|||||||
frontend = true;
|
frontend = true;
|
||||||
permit_join = false;
|
permit_join = false;
|
||||||
serial.port = "/dev/ttyUSB0";
|
serial.port = "/dev/ttyUSB0";
|
||||||
|
|
||||||
|
# Inline device configuration (replaces devices.yaml)
|
||||||
|
# This allows declarative management and homeassistant overrides
|
||||||
|
devices = {
|
||||||
|
# Temperature sensors with battery fix
|
||||||
|
# WSDCGQ12LM sensors report battery: 0 due to firmware quirk
|
||||||
|
# Override battery calculation using voltage (mV): (voltage - 2100) / 9
|
||||||
|
"0x54ef441000a547bd" = {
|
||||||
|
friendly_name = "0x54ef441000a547bd";
|
||||||
|
homeassistant.sensor_battery.value_template = "{{ (((value_json.voltage | float) - 2100) / 9) | round(0) | int | min(100) | max(0) }}";
|
||||||
|
};
|
||||||
|
"0x54ef441000a54d3c" = {
|
||||||
|
friendly_name = "0x54ef441000a54d3c";
|
||||||
|
homeassistant.sensor_battery.value_template = "{{ (((value_json.voltage | float) - 2100) / 9) | round(0) | int | min(100) | max(0) }}";
|
||||||
|
};
|
||||||
|
"0x54ef441000a564b6" = {
|
||||||
|
friendly_name = "temp_server";
|
||||||
|
homeassistant.sensor_battery.value_template = "{{ (((value_json.voltage | float) - 2100) / 9) | round(0) | int | min(100) | max(0) }}";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Other sensors
|
||||||
|
"0x00124b0025495463".friendly_name = "0x00124b0025495463"; # SONOFF temp sensor (battery works)
|
||||||
|
"0x54ef4410009ac117".friendly_name = "0x54ef4410009ac117"; # Water leak sensor
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
"0x54ef441000a1f907".friendly_name = "btn_livingroom";
|
||||||
|
"0x54ef441000a1ee71".friendly_name = "btn_bedroom";
|
||||||
|
|
||||||
|
# Philips Hue lights
|
||||||
|
"0x001788010d1b599a" = {
|
||||||
|
friendly_name = "0x001788010d1b599a";
|
||||||
|
transition = 5;
|
||||||
|
};
|
||||||
|
"0x001788010d253b99".friendly_name = "0x001788010d253b99";
|
||||||
|
"0x001788010e371aa4".friendly_name = "0x001788010e371aa4";
|
||||||
|
"0x001788010dc5f003".friendly_name = "0x001788010dc5f003";
|
||||||
|
"0x001788010dc35d06".friendly_name = "0x001788010dc35d06";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,14 @@ groups:
|
|||||||
annotations:
|
annotations:
|
||||||
summary: "Mosquitto not running on {{ $labels.instance }}"
|
summary: "Mosquitto not running on {{ $labels.instance }}"
|
||||||
description: "Mosquitto has been down on {{ $labels.instance }} more than 5 minutes."
|
description: "Mosquitto has been down on {{ $labels.instance }} more than 5 minutes."
|
||||||
|
- alert: zigbee_sensor_stale
|
||||||
|
expr: (time() - hass_last_updated_time_seconds{entity=~"sensor\\.(0x[0-9a-f]+|temp_server)_temperature"}) > 3600
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: "Zigbee sensor {{ $labels.friendly_name }} is stale"
|
||||||
|
description: "Zigbee temperature sensor {{ $labels.entity }} has not reported data for over 1 hour. The sensor may have a dead battery or connectivity issues."
|
||||||
- name: smartctl_rules
|
- name: smartctl_rules
|
||||||
rules:
|
rules:
|
||||||
- alert: smart_critical_warning
|
- alert: smart_critical_warning
|
||||||
|
|||||||
Reference in New Issue
Block a user