A Home Assistant custom integration for monitoring Waveshare UPS hardware connected to a Raspberry Pi over I2C. Supported models include the Waveshare UPS HAT A, B, and D, along with the Waveshare UPS Module 3S.
This project was originally inspired by rpi_waveshare_ups by uvjim. The current implementation was independently written from the INA219 datasheet, Waveshare hardware documentation, and Home Assistant developer documentation.
This project was created to make UPS information available in Home Assistant and to provide useful states for automations. One example is safely shutting down a Raspberry Pi when an outage continues long enough for the battery to reach a critical level.
Battery percentage and runtime are estimates derived from the measurements reported by the UPS board. They should not be treated as laboratory-grade measurements or as a replacement for the protections built into the UPS. Always test shutdown automations before relying on them.
- A supported Waveshare UPS connected to the Raspberry Pi
- I2C enabled on the Raspberry Pi
- Home Assistant with access to the Raspberry Pi I2C bus
- HACS for the recommended installation method
Instructions for enabling I2C depend on the Home Assistant installation:
This integration is not currently included in the default HACS repository list. Add it as a custom repository:
- Open HACS in Home Assistant.
- Open the menu and select Custom repositories.
- Enter
https://github.com/ComputerWhisperers/waveshare_ups. - Select Integration as the category.
- Download the integration and restart Home Assistant.
- Go to Settings > Devices & services > Add Integration.
- Search for Waveshare 3S UPS for Raspberry Pi.
You can also open the repository in HACS with this button:
During setup, the integration scans the available I2C buses for the UPS. The settings are divided into four pages so related options stay together:
- UPS and runtime: Name, I2C address, board version, battery capacity, battery-power current threshold, full-charge voltage, estimated runtime load, internal runtime calibration, and update interval.
- Shutdown thresholds: Warning battery level and Shutdown battery level.
- Battery health: Replacement date, age and cycle limits, deep-discharge limits, high-voltage exposure limits, and runtime-degradation threshold.
- Calibration, self-test, and GPIO: Automatic testing toggle, self-test limits, relay GPIO and polarity, and safety timeouts.
The defaults are intended for the Waveshare 3S board with 3200 mAh batteries. The default battery-power threshold is -100 mA.
The current threshold is the single reference used for Source, Battery State, runtime behavior, utility-mode percentage stabilization, and Output status.
Entity IDs use the name entered during setup. For example, a setup name of
MyUPS creates entities beginning with sensor.myups_ and
binary_sensor.myups_.
| Name | Example entity ID | Description |
|---|---|---|
| Status | binary_sensor.myups_status |
Online when the integration can communicate with the UPS board. |
| Battery State | binary_sensor.myups_battery_state |
Charging when current is at or above the configured battery-power threshold; otherwise discharging. |
| Initiate Shutdown | binary_sensor.myups_initiate_shutdown |
On when Output is Critical, Output Source is Battery, and neither calibration nor self-test is active. |
| Battery Maintenance | binary_sensor.myups_battery_maintenance |
On when a configured gradual-wear limit is reached. It turns off while Battery Fault is on. |
| Battery Fault | binary_sensor.myups_battery_fault |
Latched on when a self-test detects a severe percentage or voltage drop. |
| Name | Example entity ID | Description |
|---|---|---|
| Battery | sensor.myups_battery |
Estimated remaining battery percentage. |
| Runtime | sensor.myups_runtime |
Estimated remaining runtime in hours. |
| Calibration Status | sensor.myups_calibration_status |
Idle, Waiting for Battery, or Running. It returns to Idle when a test ends. |
| Calibration Elapsed | sensor.myups_calibration_elapsed |
Battery runtime measured during the active or most recent test. |
| Last Calibration Date | sensor.myups_last_runtime_calibration |
Date when the most recent successful calibration completed. |
| Last Calibration Status | sensor.myups_last_calibration_status |
Completed or Cancelled result from the latest calibration. |
| Last Battery Change | sensor.myups_last_battery_change |
Persisted date when the batteries were last replaced. |
| Battery Age | sensor.myups_battery_age |
Number of days since the stored battery replacement date. |
| Self-Test Status | sensor.myups_self_test_status |
Idle, Waiting for Battery, or Running. Disabled when automatic testing is off. |
| Last Self-Test Status | sensor.myups_last_self_test_status |
Passed, Failed, Cancelled, or Interrupted. Disabled when automatic testing is off. |
| Last Self-Test Date | sensor.myups_last_self_test_date |
Date of the latest self-test result. Disabled when automatic testing is off. |
| Output Source | sensor.myups_source |
Reports Utility or Battery. The existing entity-ID suffix remains source. |
| Output | sensor.myups_output |
Reports Normal, Warning, Critical, or High Voltage for automations. |
| Current | sensor.myups_current |
Current reported by the UPS in mA. |
| Battery Voltage | sensor.myups_battery_voltage |
Voltage measured on the load side of the current-sense circuit. |
| Supply Voltage | sensor.myups_supply_voltage |
Input supply voltage while utility power is present; reports 0 V while running on battery. |
| Current Sense Voltage | sensor.myups_current_sense_voltage |
Small voltage drop used by the UPS to determine current. |
| Power | sensor.myups_power |
Power reported by the UPS in watts. |
Equivalent battery cycles, deep-discharge count, and high-voltage time are tracked internally for Battery Maintenance but are not exposed as entities. They remain available in the integration diagnostics for troubleshooting.
| Name | Example entity ID | Description |
|---|---|---|
| Battery Replaced | button.myups_battery_replaced |
Stores the current date as the last battery replacement date. |
| Start Runtime Calibration | button.myups_start_runtime_calibration |
Arms a full-charge battery runtime test. |
| Cancel Runtime Calibration | button.myups_cancel_runtime_calibration |
Cancels an active or waiting calibration test. |
| Start Self-Test | button.myups_start_self_test |
Starts a short relay-controlled battery test. Disabled when automatic testing is off. |
| Cancel Self-Test | button.myups_cancel_self_test |
Cancels a self-test and restores utility. Disabled when automatic testing is off. |
Open Configure, select Reset Configuration to Defaults, and confirm the reset. The board address, battery replacement date, learned runtime calibration, and persistent battery history are preserved.
Battery percentage is estimated from the voltage characteristics for the selected UPS model. The 3S module uses a multi-point discharge curve and a configurable Full Charge Voltage, which defaults to 12.49 V. The upper curve scales smoothly to the configured value instead of merely moving the 100% cutoff, preserving gradual readings through 94%, 96%, 98%, and 99%.
While utility power is present, the displayed percentage may increase as the batteries charge, but temporary voltage sag from CPU activity will not lower it. When the UPS switches to battery power, the percentage follows the measured voltage curve again. Downward changes are rate-limited to no more than one percentage point every two minutes so the immediate voltage sag under load is not mistaken for an equally immediate loss of capacity.
Runtime is calculated from the configured battery capacity, estimated battery percentage, and expected discharge current. The configured runtime load is used as a minimum because some UPS boards may report less current than the Raspberry Pi is actually consuming. Runtime is smoothed and displayed in six-minute increments to reduce rapid fluctuations. Runtime calibration is applied after this calculation. For example, if an estimate of three hours consistently lasts only 2.4 hours, set calibration to 80%.
The integration can measure actual battery runtime and update the calibration percentage automatically:
- Leave utility power connected and charge the Battery sensor to 100%.
- Press Start Runtime Calibration. Calibration Status changes to Waiting for Battery.
- Disconnect utility power from the UPS. When Output Source changes to Battery, Calibration Status changes to Running and the timer starts.
- Keep Home Assistant and the Raspberry Pi under their normal operating load. Leave the UPS on battery until Output reaches Critical. The test completes automatically and Calibration Status returns to Idle.
- Reconnect utility power.
At completion, the integration compares the measured battery runtime with the uncalibrated expected runtime from 100% to the configured Critical level. It then saves the resulting Runtime Calibration percentage and applies it to future runtime estimates.
The active test, start time, and latest result are stored in Home Assistant's persistent storage. A manual calibration therefore resumes after a Home Assistant restart. If utility power returns before Output reaches Critical, the test is marked Cancelled and no new calibration percentage is saved. The Cancel Runtime Calibration button can also stop a test manually.
The Raspberry Pi may shut down before Home Assistant records Critical if a shutdown automation reacts immediately to Output. During the first calibration test, allow enough time for the integration to record the Critical state before shutdown.
When Enable Automatic Calibration and Self-Test is on, steps 3 and 5 are performed by the configured GPIO relay. When it is off, calibration remains manual and the relay is not controlled.
The beta GPIO option can make runtime calibration self-contained on Raspberry
Pi 4 and Raspberry Pi 5 systems running Home Assistant OS. It uses the
official gpiod Python bindings and discovers the Raspberry Pi GPIO
controller by its pinctrl label, so it does not depend on a fixed
/dev/gpiochip number.
Automatic testing defaults to off. Configure:
- Enable Automatic Calibration and Self-Test: Enables GPIO relay control and the self-test entities.
- Relay GPIO Number (BCM): GPIO number, not physical header pin number. GPIO17, physical pin 11, is the default.
- Relay activates on LOW: Off by default, meaning a HIGH GPIO signal activates the relay. Turn it on only for a relay that activates from LOW.
- Switch-to-battery timeout: Restores utility if Source does not become Battery after the relay opens.
- Maximum calibration duration: Restores utility if Critical is not reached within the configured number of hours.
Use a relay module that accepts the Raspberry Pi's 3.3 V GPIO signal. The relay contacts must be rated for the UPS input voltage and current. Wire the UPS's positive DC supply through the relay's normally closed contacts so utility remains connected when the relay is not energized. Never connect 13 V, relay-coil current, or mains voltage directly to a Raspberry Pi GPIO.
GPIO2 and GPIO3 cannot be selected because the UPS uses them for I2C. Confirm that the selected GPIO is not used by a fan, HAT, or another integration.
With automatic testing off, Start and Cancel Runtime Calibration never operate GPIO; utility must be disconnected and restored by hand. With it on, Start energizes the relay and verifies that Source changes to Battery, while Cancel immediately restores utility. At Critical, timeout, UPS communication failure, integration unload, or a normal Home Assistant restart, the integration also commands the relay back to utility-connected. An automatic test interrupted by a restart is cancelled once utility is detected. Normally closed contacts provide the additional hardware fallback when GPIO control is lost.
The Start Self-Test button briefly removes utility power. After the configured settling time, the integration fails the test if battery percentage falls to the configured failure level or voltage reaches the emergency level. A failure restores utility and latches Battery Fault. A healthy test restores utility after the configured duration and records Passed. A restart or loss of UPS communication records Interrupted rather than assuming a battery fault.
This is beta functionality because GPIO availability can vary with Home Assistant OS and Raspberry Pi hardware revisions. Test relay polarity and utility restoration before relying on unattended testing.
The replacement date is stored in the Home Assistant config entry. Usage counters, battery fault, self-test history, and calibration state are stored in Home Assistant persistent storage. These values survive restarts and integration updates.
Battery Maintenance evaluates battery age, equivalent cycles, deep discharges, high-voltage exposure, and degradation from the first learned runtime. Battery Fault represents a severe self-test failure and suppresses Battery Maintenance so only the more urgent condition is shown.
Pressing Battery Replaced records the current date and resets both warnings, all battery-use counters, self-test history, and learned runtime calibration.
Last Battery Change and Battery Age remain available even if communication with the UPS board is temporarily lost.
Percentage smoothing affects only the displayed Battery and Runtime estimates. Output continues to use the immediate measured percentage and model-specific low-voltage limit so Warning and Critical automations are not delayed.
While utility power is present, Output changes to High Voltage only after Battery Voltage remains strictly above the configured High Voltage Threshold for 15 seconds. A reading exactly equal to the threshold does not activate the state. A small recovery margin prevents repeated state changes near the limit. High Voltage does not turn on Initiate Shutdown.
After installation, open the integration and select Configure. Options use the same four grouped pages as initial setup: UPS and runtime, shutdown thresholds, battery health, and calibration/self-test with GPIO.
The shutdown battery level must be lower than the warning battery level.
This project is licensed under the MIT License.