apcget is a Python script that scrapes UPS status information from the APC PowerChute Serial Shutdown for Business Web UI, providing command-line output, JSON output, MQTT publishing, and Zabbix sender integration.
APC PowerChute Serial Shutdown for Business does not provide an SNMP interface, making it difficult to retrieve UPS information through standard monitoring protocols. This tool bypasses that limitation by parsing the Web UI HTML, enabling integration with monitoring systems such as Home Assistant and Zabbix.
| Item | Value |
|---|---|
| Target Software | APC PowerChute Serial Shutdown for Business v1.4.0 |
| Web UI Port | TCP 6547 (HTTPS) |
| Language | Python 3.6+ |
| Dependencies | Standard library only (MQTT/Zabbix send requires additional tools) |
| License | MIT License |
┌──────────────────┐
│ Parse arguments │
│ Resolve credentials │
└────────┬─────────┘
│
▼
┌──────────────────┐ ┌─────────────────────────┐
│ Login │────▶│ Detect existing session │
│ │◀────│ → Log off → Retry │
└────────┬─────────┘ └─────────────────────────┘
│
▼
┌──────────────────┐
│ Get status page │ ← Parse /status page HTML
│ Detect locale │ ← Check <html lang="...">
│ Switch to English │ ← POST /setLocale if non-English
│ Extract values │ ← Extract by HTML element ID
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Output │ ← stdout / JSON / MQTT / Zabbix
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Restore locale │ ← Restore original locale
│ Log off │
└──────────────────┘
| Mode | Trigger | Destination | Description |
|---|---|---|---|
| Normal | No option or --load etc. |
stdout | Print selected items separated by spaces |
| JSON | --json |
stdout | Print all items in JSON format |
| MQTT | --mqtt-send |
MQTT broker | Publish all items as a JSON payload |
| Zabbix | --zabbix-send |
Zabbix server | Send all items as trapper items |
JSON / MQTT / Zabbix can be used simultaneously (processed by independent if statements).
apcget.py ← Single-file design (no external module dependencies)
| Name | Type | Description |
|---|---|---|
ITEMS |
dict | Item name → HTML element ID mapping |
ZABBIX_KEYS |
dict | Item name → Zabbix trapper item key mapping |
DEFAULT_CONFIG_PATH |
str | Default config file path (~/.apcget.conf) |
HTTP_TIMEOUT |
int | HTTP request timeout (30 seconds) |
| Item Name | HTML Element ID | Zabbix Key | Unit |
|---|---|---|---|
status |
value_DeviceStatus |
apc.status |
- (string) |
load |
value_RealPowerPct |
apc.load |
% |
runtime |
value_RuntimeRemaining |
apc.runtime |
min |
voltage |
value_InputVoltage |
apc.voltage |
VAC |
battery |
value_BatteryCharge |
apc.battery |
% |
batteryvoltage |
value_VoltageDC |
apc.batteryvoltage |
VDC |
load_config(config_path) → dictip, username, password from the [powerchute] section of an INI-format config fileresolve_credential(args_value, env_name, config_value, label) → strAPCGET_IP, APCGET_USERNAME, APCGET_PASSWORD)create_openers() → (opener, opener_noredir)opener: Follows redirects (for normal page retrieval)opener_noredir: Does not follow redirects (for login redirect detection)CookieJar (session persistence)Connection: close to force per-request connection teardown, preventing connection accumulation on the PowerChute Tomcat sideDesign decision: SSL verification is disabled because PowerChute uses self-signed certificates.
_is_status_page(html) → boolvalue_DeviceStatus_is_already_logged_on(html) → boolalreadyLoggedOn HTML element ID_detect_locale(html) → str | None<html lang="..."> attribute (e.g., "en", "ja")_set_locale(opener, base_url, locale) → str/setLocale endpointnewLocale (locale code), targetURL (redirect destination)/status page (in the newly set locale)restore_locale(opener, base_url, original_locale)Design decision: Status values (especially status) are language-dependent (e.g., “On Line” vs “オンライン”). To ensure consistent output, data is always retrieved in English, and the original language is restored after processing.
login(opener, opener_noredir, base_url, username, password)Processing flow:
1. Access /status → Redirected to login page
2. If "already logged on" → Log off via /logoff and retry
3. If status page is already displayed → No login needed (return)
4. Extract formtoken / formtokenid from HTML (CSRF protection tokens)
5. Dynamically read the login button's value attribute (language-aware)
6. POST to /j_security_check (Java EE form-based auth)
7. Determine success/failure by redirect destination:
- Redirect to /status → Success
- Redirect to /logon → Authentication failure
Design decisions:
opener_noredir (no redirect following) to determine login result from the Location headervalue attribute is dynamically read from the page, enabling language-independent loginlogoff(opener, base_url)/logoff to terminate the sessionget_status_page(opener, base_url) → (html: str, original_locale: str | None)/status page HTML_set_locale to switch to English and uses the returned HTML directly (avoids an extra round-trip)original_locale (the pre-switch locale code, or None if already English) for later restorationextract_value(html, element_id) → str | Noneid="{element_id}"[^>]*>([^<]+)<<span id="value_DeviceStatus" ...>On Line</span> etc.zabbix_send(zabbix_server, zabbix_host, all_values, zabbix_port=10051)zabbix_sender command with -i - (stdin mode)zabbix_sender invocation_sanitize_zabbix_host)_sanitize_zabbix_value)Prerequisite: zabbix_sender command must be installed
mqtt_publish(mqtt_broker, mqtt_topic, all_values, mqtt_port, mqtt_user, mqtt_password)mosquitto_pub is found in PATH → Use mosquitto_pubpaho-mqtt Python package_mqtt_publish_paho(...)paho.mqtt.publish.single()paho-mqtt is not installed)Prerequisite: Either mosquitto_pub or paho-mqtt must be installed
High priority
│ 1. Command-line arguments apcget.py <IP> <USER> <PASS>
│ 2. Environment variables APCGET_IP, APCGET_USERNAME, APCGET_PASSWORD
▼ 3. Config file ~/.apcget.conf (changeable with --config)
Low priority
[powerchute]
ip = 192.168.1.100
username = your_username
password = your_password
| Argument | Required | Description |
|---|---|---|
ip |
Conditional | PowerChute IP address |
username |
Conditional | Login username |
password |
Conditional | Login password |
*Can be omitted if specified via config file or environment variables.
| Option | Description |
|---|---|
--status |
Device status |
--load |
UPS load (%) |
--runtime |
Runtime remaining (min) |
--voltage |
Input voltage (VAC) |
--battery |
Battery charge (%) |
--batteryvoltage |
Battery voltage (VDC) |
Defaults to --load if no item is specified.
| Option | Argument | Default | Description |
|---|---|---|---|
--json |
None | - | Print all items in JSON to stdout |
--mqtt-send |
MQTT_BROKER | - | MQTT broker address |
--mqtt-topic |
TOPIC | apcget/ups |
MQTT topic name |
--mqtt-port |
PORT | 1883 |
MQTT broker port |
--mqtt-user |
USERNAME | - | MQTT auth username |
--mqtt-password |
PASSWORD | - | MQTT auth password |
--zabbix-send |
ZABBIX_SERVER | - | Zabbix server address |
--zabbix-host |
HOSTNAME | IP address | Zabbix host name |
--zabbix-port |
PORT | 10051 |
Zabbix server port |
| Option | Description |
|---|---|
--config |
Config file path (default: ~/.apcget.conf) |
--help |
Display help |
| Situation | Action | Exit Code |
|---|---|---|
| Credentials not specified | Error output to stderr | 1 |
| Invalid IP address / hostname format | Error output to stderr | 1 |
| Network connection failure (timeout, etc.) | Error output to stderr | 1 |
| Login failure | Error output to stderr | 1 |
| Invalid session | Error output to stderr | 1 |
| Value retrieval failed (normal mode) | Error output to stderr | 1 |
| Value retrieval failed (all-items mode) | Warning to stderr, skip | - |
| No items retrieved | Error output to stderr | 1 |
| zabbix_sender failure | Error output to stderr | 1 |
| MQTT publish failure | Error output to stderr | 1 |
| MQTT tools not installed | Error output to stderr | 1 |
The finally block guarantees:
restore_locale)logoff)Both silently ignore exceptions, ensuring execution even during error conditions.
ip, --zabbix-send): Validated with _validate_host() using ^[\w.\-\[\]:]+$ (prevents URL injection / SSRF)_sanitize_zabbix_host() using ^[\w.\-]+$ (prevents command injection)"On Line") are double-quoted in the stdin formatsubprocess.run with list-form arguments (prevents shell injection)--mqtt-password with mosquitto_pub, the password is visible in the process list (ps aux). Using paho-mqtt avoids this exposureThe PowerChute Web UI supports multiple display languages (English, Japanese, etc.). Since UPS status values are language-dependent (e.g., “On Line” vs “オンライン”), the following approach was adopted to ensure data consistency.
Login
│
▼
Detect current display language ← <html lang="..."> attribute
│
├── English → Continue as is
│
└── Non-English
│
▼
POST /setLocale (newLocale=en) → Switch to English
│
▼
Retrieve data (in English for consistency)
│
▼
POST /setLocale (newLocale=original) → Restore original language
│
▼
Log off
The following detection logic uses HTML element IDs and is not dependent on display language:
| Detection | Method | Rationale |
|---|---|---|
| Status page detection | Presence of value_DeviceStatus |
HTML element IDs are language-invariant |
| Existing session detection | Presence of alreadyLoggedOn (element ID only) |
HTML element IDs are language-invariant; no display text matching |
| Value extraction | Regex on id="value_..." |
HTML element IDs are language-invariant |
| Login button | Dynamic read of name="login" value attribute |
Display value is language-dependent but dynamically read |
apcget.py ──▶ mosquitto_pub (preferred) ──▶ MQTT Broker ──▶ Home Assistant
Subscriber
└──▶ paho-mqtt (fallback) ───────┘
apcget/ups)apcget.py ──▶ zabbix_sender -i - ──▶ Zabbix Server
When not using MQTT, integration is also possible by combining the --json option with a Home Assistant command_line sensor.
* * * * * /usr/bin/python3 /path/to/apcget.py \
--config /path/to/.apcget.conf \
--zabbix-send 127.0.0.1 --zabbix-host ups-living \
--mqtt-send 192.168.1.9 --mqtt-topic apcget/ups-living \
--mqtt-user mqtt --mqtt-password mqttpass \
>/dev/null 2>&1
| Item | Description |
|---|---|
| Single-session restriction | PowerChute allows only one session at a time. The Web UI is inaccessible while apcget is running |
| HTML structure dependency | If HTML element IDs change due to a PowerChute version upgrade, value extraction may fail |
| Language switch side effect | If another user is using the Web UI simultaneously, the display language may temporarily change |
| Session timeout | Processing must complete within the PowerChute session timeout (900 seconds) |
| HTTPS verification disabled | SSL verification is disabled to accommodate self-signed certificates, posing MITM risk (LAN-only usage assumed) |