A simple Prometheus export that collect A/C target temperature & load power from Xiaomi Air Conditioning Companion.
It serves as a simple demo, you may expand it to other Mi devices & metrics. See python-miio's document.
A simple Prometheus export that collect A/C target temperature & load power from Xiaomi Air Conditioning Companion.
It serves as a simple demo, you may expand it to other Mi devices & metrics. See python-miio's document.
| #!/usr/bin/env python3 | |
| import os | |
| from io import TextIOWrapper | |
| from typing import Dict | |
| from pathlib import Path | |
| from http.server import HTTPServer, BaseHTTPRequestHandler | |
| from miio.airconditioningcompanion import AirConditioningCompanion | |
| HTTP_BIND = ('127.0.0.1', 8000) | |
| AC_ADDR = '192.0.2.12' | |
| def load_tokens() -> Dict[str, str]: | |
| cred_dir = os.environ.get('CREDENTIALS_DIRECTORY', '.') | |
| token_path = Path(cred_dir) / Path('tokens') | |
| addrs_token = dict() | |
| with token_path.open() as f: | |
| for line in f: | |
| line = line.strip() | |
| if line.startswith('#') or '/' not in line: | |
| continue | |
| addr, token = line.rsplit('/', 1) | |
| addrs_token[addr] = token | |
| return addrs_token | |
| class HTTPHandler(BaseHTTPRequestHandler): | |
| def do_GET(self): | |
| if self.path != '/metrics': | |
| self.send_response(404) | |
| self.send_header("Content-Type", "text/plain") | |
| self.end_headers() | |
| self.wfile.write(b"404 not found\n") | |
| return | |
| self.send_response(200) | |
| self.send_header("Content-Type", "text/plain; charset=utf-8") | |
| self.end_headers() | |
| body = TextIOWrapper(self.wfile) | |
| def metric(key, value, typ, help): | |
| print(f"# HELP miio_{key} {help}", file=body) | |
| print(f"# TYPE miio_{key} {typ}", file=body) | |
| print(f"miio_{key} {value}", file=body) | |
| print("", file=body) | |
| status = self.server.ac.status() | |
| if status.power: | |
| metric("ac_targettemp", status.target_temperature, "gauge", "Target temperature") | |
| metric("ac_loadpower", status.load_power, "gauge", "Current power load of the air conditioner") | |
| body.flush() | |
| body.detach() | |
| def main(): | |
| addr_tokens = load_tokens() | |
| ac = AirConditioningCompanion(AC_ADDR, addr_tokens[AC_ADDR]) | |
| with HTTPServer(HTTP_BIND, HTTPHandler) as httpd: | |
| httpd.ac = ac | |
| httpd.serve_forever() | |
| if __name__ == '__main__': | |
| main() |
| [Unit] | |
| Description=Xiaomi A/C Companion Prometheus exporter | |
| [Service] | |
| Type=simple | |
| DynamicUser=yes | |
| LoadCredential=tokens:/opt/mi_ac_exporter/tokens | |
| WorkingDirectory=/opt/mi_ac_exporter | |
| ExecStart=/opt/mi_ac_exporter/mi_ac_exporter.py | |
| Environment=PYTHONUNBUFFERED=1 | |
| NoNewPrivileges=true | |
| PrivateTmp=true | |
| PrivateDevices=true | |
| ProtectSystem=strict | |
| ProtectHome=true | |
| ProtectKernelModules=true | |
| ProtectControlGroups=true | |
| [Install] | |
| WantedBy=multi-user.target |
| # <IP-Address>/<Token-in-HEX> | |
| 192.0.2.12/abcdefabcdef12345678901234567890 | |
| 192.0.2.15/12345678901234567890abcdefabcdef |