Prometheus exporters for IoT and smart-home devices, including TP-Link Tapo plugs.
Project description
Python Prometheus Exporters for IoT Devices
Python Prometheus Exporters (pyprom-exporters) is a small Python package that exposes Prometheus
metrics for IoT / smart-home
devices.
The current concrete exporter targets TP-Link Tapo smart plugs via python-kasa.
What It Does
- Discovers Tapo devices on your LAN (UDP broadcast) and/or monitors an explicit list of device IPs.
- Updates device state on a background asyncio loop (or on scrape), with retries and backoff.
- Exposes metrics via a Prometheus HTTP endpoint.
How It Works
prom-exporterstarts an asyncio event loop on a background thread.- The Tapo exporter runs discovery, then chooses update mode from
exporters.tapo.prometheus_options.refresh_interval:- Integer value: performs an initial update pass and periodic background updates, and scrapes read cached metrics.
null: disables background updates and refreshes metrics when Prometheus callscollect().
To reduce device/network traffic, set refresh_interval to match your Prometheus scrape interval
(or higher). Set it to null when you want scrape-time freshness.
Project Layout
src/pyprom_exporters/prom_exporter.py: CLI entry point and runtime wiring.src/pyprom_exporters/config.py: OmegaConf-compatible dataclass configuration models.src/pyprom_exporters/exporters/tapo.py: Tapo smart plug exporter implementation.src/pyprom_exporters/task_collector/async_task_collector.py: async retry runner used for device updates.tests/: unit tests for config/exporter behavior.
Requirements
- Python 3.11+ (uses
asyncio.TaskGroup). - Network reachability from the exporter host to the devices.
- Tapo credentials (provided via env vars or CLI; never written to
config.yaml).
Installation
This repo is set up to use uv and a checked-in uv.lock.
# Development (includes the dev dependency group by default)
uv sync --frozen
# Minimal runtime environment
uv sync --frozen --no-dev
Running
- Provide credentials (default env keys):
export TP_LINK_USERNAME="you@example.com"
export TP_LINK_PASSWORD="your-password"
- Run the exporter:
uv run prom-exporter \
--prometheus-port 8090 \
--tapo-plug-devices 10.10.2.100,10.10.2.101
You can also reduce log verbosity:
uv run prom-exporter --log-level WARNING
- Scrape metrics:
http://localhost:8090/metrics
Environment Variable Overrides
The runtime supports a few convenience overrides:
- Precedence: CLI flags override env vars; env vars override
config.yaml. PYPROM_EXPORTERS_LOG_LEVEL(orLOG_LEVEL): overrideslog_level.PROMETHEUS_PORT: overridesprometheus_port.TAPO_PLUG_DEVICES: overridesexporters.tapo.devices(space or comma-separated).TAPO_USERNAME/TAPO_PASSWORD: overrides credentials directly.
Configuration (config.yaml)
prom-exporter reads config.yaml from the working directory.
- If
config.yamldoes not exist, it writes one with defaults. - If
config.yamlexists, it writes back the merged configuration on startup so defaults are explicit. - Credentials are always scrubbed from the written YAML; provide them via env vars or CLI.
write_non_default_config: truewrites only values that differ from defaults.
Important fields:
log_level: root logging level for the process.prometheus_port: exporter listen port.exporters.tapo.devices: list of device IPs to monitor (used in addition to discovery).exporters.tapo.prometheus_options.refresh_interval: background update interval (seconds) and per-device minimum update interval. Set tonullto disable background updates and refresh on scrape.exporters.tapo.discovery_options.*: discovery parameters passed topython-kasa.exporters.tapo.discovery_options.tapo_username_env_key/tapo_password_env_key: env var names used to populatepython-kasaCredentialsby default.exporters.tapo.supported_device_families: currently onlyPLUG.exporters.tapo.per_device_family_metrics.plug: plug metrics to export.
Polling Behavior Examples
Set the option internally (Python dataclass value):
# Background polling every 15 seconds.
app_config.exporters.tapo.prometheus_options.refresh_interval = 15
# Disable background polling; refresh on every Prometheus scrape.
app_config.exporters.tapo.prometheus_options.refresh_interval = None
When config.yaml is written back, those values appear as:
exporters:
tapo:
prometheus_options:
refresh_interval: 15
exporters:
tapo:
prometheus_options:
refresh_interval: null
Discovery note: broadcast discovery generally does not work across VLAN boundaries. If your devices
are on a separate IoT VLAN, set exporters.tapo.devices (or use --tapo-plug-devices) to the
device IPs.
Metrics
The Tapo plug exporter currently emits:
tapo_discovered_devices: number of discovered devices (gauge).current_consumption{host,alias}: watts (gauge).current_voltage{host,alias}: volts (gauge).current_current{host,alias}: amps (gauge).current_consumption_today{host,alias}: watt-hours (gauge).current_month_consumption{host,alias}: watt-hours (gauge).current_rssi{host,alias}: RSSI value reported by the device (gauge).
Only devices that report the current_consumption feature are exported.
Prometheus Scrape Config
Example prometheus.yml:
scrape_configs:
- job_name: pyprom-exporters
static_configs:
- targets: ["<exporter-host>:8090"]
Docker
Build:
docker build -t pyprom-exporters:latest .
Run:
docker run --rm \
-e TP_LINK_USERNAME="you@example.com" \
-e TP_LINK_PASSWORD="your-password" \
-e PROMETHEUS_PORT=8090 \
-e TAPO_PLUG_DEVICES="10.10.2.100,10.10.2.101" \
-p 8090:8090 \
pyprom-exporters:latest
The container entrypoint runs uv run prom-exporter and forwards PROMETHEUS_PORT and
TAPO_PLUG_DEVICES into CLI flags.
Troubleshooting
- No devices discovered:
- Set
exporters.tapo.devices(or use--tapo-plug-devices) instead of relying on broadcast discovery. - Check firewall rules and IoT VLAN routing.
- Set
- Metrics are missing for a device:
- The exporter skips devices that do not report a
current_consumptionfeature.
- The exporter skips devices that do not report a
- Authentication failures:
- Ensure
TP_LINK_USERNAME/TP_LINK_PASSWORD(orTAPO_USERNAME/TAPO_PASSWORD) are set.
- Ensure
Development
# pytest - for tests
uv run pytest
# ruff - for linting and formatting
uv run ruff check .
# format with ruff (or your editor integration)
uv run ruff format .
# pre-commit - for running all configured pre-commit hooks (ruff, isort, etc.)
uv run pre-commit run --all-files
Documentation
Documentation is generated with Sphinx using Markdown (MyST) sources in docs/.
# Build HTML docs locally with uv
uv run docs
# Build HTML docs locally with make
make docs
License
Apache-2.0 (see LICENSE).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyprom_exporters-0.1.0.tar.gz.
File metadata
- Download URL: pyprom_exporters-0.1.0.tar.gz
- Upload date:
- Size: 123.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8eb084c2a69a8355380419dbb3988251e266d17682f210c35d7a16dd31692cbe
|
|
| MD5 |
db42e5420572c4daf457b95d41fd8dde
|
|
| BLAKE2b-256 |
7e1f118e89b9168660a31fc2a1be6a56186e6738bf8fbb9b0566643dcee37ee9
|
Provenance
The following attestation bundles were made for pyprom_exporters-0.1.0.tar.gz:
Publisher:
release.yml on andylamp/pyprom-exporters
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyprom_exporters-0.1.0.tar.gz -
Subject digest:
8eb084c2a69a8355380419dbb3988251e266d17682f210c35d7a16dd31692cbe - Sigstore transparency entry: 976804812
- Sigstore integration time:
-
Permalink:
andylamp/pyprom-exporters@61819e589dcf1ab2030902d3c52b60eb646dba97 -
Branch / Tag:
refs/heads/release - Owner: https://github.com/andylamp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@61819e589dcf1ab2030902d3c52b60eb646dba97 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyprom_exporters-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyprom_exporters-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6bb39e5cf690e9bccbe5612713732d6655f636037a8ef770999ce1986f6a966
|
|
| MD5 |
a296c35b067b63e9c735945734fd3493
|
|
| BLAKE2b-256 |
3017c0b1cd92a10d8670e8f571c7d175eafb575d01b5b4b3d2689d5a22f31c87
|
Provenance
The following attestation bundles were made for pyprom_exporters-0.1.0-py3-none-any.whl:
Publisher:
release.yml on andylamp/pyprom-exporters
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyprom_exporters-0.1.0-py3-none-any.whl -
Subject digest:
f6bb39e5cf690e9bccbe5612713732d6655f636037a8ef770999ce1986f6a966 - Sigstore transparency entry: 976804813
- Sigstore integration time:
-
Permalink:
andylamp/pyprom-exporters@61819e589dcf1ab2030902d3c52b60eb646dba97 -
Branch / Tag:
refs/heads/release - Owner: https://github.com/andylamp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@61819e589dcf1ab2030902d3c52b60eb646dba97 -
Trigger Event:
push
-
Statement type: