#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Plugin installation:
# wget "https://pasternok.org/sources/airpurifiermiot_stats.py?raw" -O /etc/munin/plugins/airpurifiermiot_stats
# chmod +x /etc/munin/plugins/airpurifiermiot_stats
# systemctl restart munin-node
### config
DEVICE = {
"name": "Mi Air Purifier",
"ip": "10.1.1.203",
"token": "00112233445566778899aabbccddeeff",
}
PARAMS = [
# [0] - label
# [1] - unit
# [2] - AirPurifierMiotStatus property name
# [3] - additional unit conversions (optional)
# [4] - warning threshold (for filter lifespan)
# [5] - critical threshold (for filter lifespan)
("AQI", "ug/m^3", "aqi"),
("average AQI", "ug/m^3", "average_aqi"),
("humidity", "%", "humidity"),
("temperature", "C", "temperature"),
("filter lifespan", "%", "filter_life_remaining", None, 5, 2),
("filter use time", "h", "filter_hours_used"),
("total use time", "h", "use_time", " / 3600"),
("purify volume", "m^3", "purify_volume"),
("motor speed", "rpm", "motor_speed"),
]
### code - do not change anything below here
__author__ = "Damian Pasternok <my_forename at pasternok.org>"
__version__ = "0.1.1"
__date__ = "2023/02/27"
__copyright__ = "Copyright (C) 2022 - 2023 Damian Pasternok"
__license__ = "GPL"
import argparse
import socket
from miio import AirPurifierMiot
from miio.exceptions import DeviceException
from time import sleep
# set default timeout for sockets
socket.setdefaulttimeout(10)
class AirPurifier:
def __init__(self):
tries = 10
for i in range(tries):
try:
self.data = AirPurifierMiot(
DEVICE.get("ip"), DEVICE.get("token")
).status()
except DeviceException as e:
if i < tries - 1:
sleep(2)
continue
else:
raise
else:
break
class Munin(AirPurifier):
def __init__(self):
pass
def config(self):
super(Munin, self).__init__()
out = f"""graph_title {DEVICE["name"]} stats
graph_args --logarithmic --units=si
graph_scale no
graph_category smarthome
graph_vlabel device parameters
"""
for param in PARAMS:
out += f"""{param[2]}.label {param[0]} [{param[1]}]\n"""
try:
if param[4]:
out += f"""{param[2]}.draw LINE2\n"""
out += f"""{param[2]}.warning {param[4]}:\n"""
out += f"""{param[2]}.critical {param[5]}:\n"""
except IndexError:
pass
return out.rstrip()
def data(self):
super(Munin, self).__init__()
out = ""
for param in PARAMS:
value = eval(f"self.data.{param[2]}")
try:
if param[3]:
value = int(eval(f"{value}{param[3]}"))
except IndexError:
pass
out += f"""{param[2]}.value {value}\n"""
return out.rstrip()
def main():
munin = Munin()
parser = argparse.ArgumentParser(description=f"""{DEVICE["name"]} Munin Plugin.""")
parser.add_argument("config", help="print graph configuration", nargs="?")
args = parser.parse_args()
if args.config == "config":
print(munin.config())
elif not args.config:
print(munin.data())
else:
parser.print_help()
if __name__ == "__main__":
main()