#!/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 " __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()