diff --git a/plugins/hue/hue b/plugins/hue/hue new file mode 100755 index 00000000..a0f8be32 --- /dev/null +++ b/plugins/hue/hue @@ -0,0 +1,139 @@ +#!/usr/bin/python3 -tt +# -*- coding: utf-8 -*- + +"""Munin plugin to read various data from Philips Hue. + +Copyright 2019, Kim B. Heino, b@bbbs.net, Foobar Oy +License GPLv2+ + +Munin configuration: + +[hue] +env.host hue-bridge-ip-or-hostname +env.auth auth-key +""" + +import os +import sys +import unicodedata +import requests + + +def cleanup(value): + """Convert value to Munin key.""" + ret = [] + for char in value: + if 'a' <= char <= 'z' or '0' <= char <= '9': + ret.append(char) + elif 'A' <= char <= 'Z': + ret.append(char.lower()) + else: + ret.append('_') + return ''.join(ret) + + +def sensor_name(data, sensor): + """Find "parent" sensor for temperatue/light.""" + uniqueid = sensor['uniqueid'][:27] + name = sensor['name'] + for item in data['sensors'].values(): + if item.get('uniqueid', '').startswith(uniqueid): + name = item['name'] + break + + # Convert รค to a and return it as utf8 string, py2/3 compatible way + name = unicodedata.normalize('NFKD', name) + return name.encode('ASCII', 'ignore').decode('utf-8') + + +def print_temperatures(data, config, both): + """Temperature sensors.""" + if not any([sensor['type'] == 'ZLLTemperature' + for sensor in data['sensors'].values()]): + return + + print('multigraph hue_temp') + if config: + print('graph_title Hue temperatures') + print('graph_vlabel Celsius') + print('graph_category sensors') + print('graph_args --base 1000') + print('graph_info Temperature sensor values.') + for sensor in data['sensors'].values(): + if sensor['type'] != 'ZLLTemperature': + continue + label = cleanup(sensor['name']) + if config: + print('{}.label {}'.format(label, sensor_name(data, sensor))) + if not config or both: + value = sensor['state']['temperature'] / 100 + print('{}.value {}'.format(label, value)) + + +def print_light_levels(data, config, both): + """Light level sensors.""" + if not any([sensor['type'] == 'ZLLLightLevel' + for sensor in data['sensors'].values()]): + return + + print('multigraph hue_light_level') + if config: + print('graph_title Hue light levels') + print('graph_vlabel Lux') + print('graph_category sensors') + print('graph_args --base 1000') + print('graph_scale no') + print('graph_info Light level sensor values.') + for sensor in data['sensors'].values(): + if sensor['type'] != 'ZLLLightLevel': + continue + label = cleanup(sensor['name']) + if config: + print('{}.label {}'.format(label, sensor_name(data, sensor))) + if not config or both: + value = 10 ** ((sensor['state']['lightlevel'] - 1) / 10000) + print('{}.value {}'.format(label, value)) + + +def print_lights(data, config, both): + """Lights on/off.""" + if not data['lights']: + return + + print('multigraph hue_lights') + if config: + print('graph_title Hue lights on') + print('graph_vlabel Count') + print('graph_category sensors') + print('graph_args --base 1000 --lower-limit 0') + print('graph_info Number of turned on lights.') + count = 0 + for light in data['lights'].values(): + if light['state']['on']: + count += 1 + if config: + print('lights.label Number of lights on') + if not config or both: + print('lights.value {}'.format(count)) + + +def print_data(config): + """Print config or values.""" + # Get values + data = requests.get('http://{}/api/{}/'.format( + os.getenv('host'), os.getenv('auth')), timeout=50).json() + both = os.getenv('MUNIN_CAP_DIRTYCONFIG') == '1' + + # Print config/values + print_temperatures(data, config, both) + print_light_levels(data, config, both) + print_lights(data, config, both) + + +if __name__ == '__main__': + if len(sys.argv) > 1 and sys.argv[1] == 'autoconf': + print('no') + elif len(sys.argv) > 1 and sys.argv[1] == 'config': + print_data(True) + else: + print_data(False)