153 lines
4.3 KiB
Python
Executable File
153 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""Munin plugin to read various data from Philips Hue devices.
|
|
|
|
=head1 NAME
|
|
|
|
hue - monitor various data from Philips Hue devices
|
|
|
|
=head1 APPLICABLE SYSTEMS
|
|
|
|
Homes with Philips Hue light bulbs and motion sensors.
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
Following config is needed:
|
|
|
|
[hue]
|
|
env.host hue-bridge-ip-or-hostname
|
|
env.auth auth-key
|
|
|
|
=head1 AUTHOR
|
|
|
|
Kim B. Heino <b@bbbs.net>
|
|
|
|
=head1 LICENSE
|
|
|
|
GPLv2
|
|
=cut
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import unicodedata
|
|
import urllib.request
|
|
|
|
|
|
def safename(name):
|
|
"""Return safe variable name."""
|
|
# Convert ä->a as isalpha('ä') is true
|
|
value = unicodedata.normalize('NFKD', name)
|
|
value = value.encode('ASCII', 'ignore').decode('utf-8')
|
|
|
|
# Remove non-alphanumeric chars
|
|
return ''.join(char.lower() if char.isalnum() else '_' for char in value)
|
|
|
|
|
|
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
|
|
return name
|
|
|
|
|
|
def print_temperatures(data, config, both):
|
|
"""Motion sensor has temperature sensor - Read it's value."""
|
|
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 = safename(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):
|
|
"""Motion sensor has light level sensor - Read it's value."""
|
|
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 = safename(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):
|
|
"""Light bulbs 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
|
|
url = 'http://{}/api/{}/'.format(os.getenv('host'), os.getenv('auth'))
|
|
try:
|
|
data = json.loads(urllib.request.urlopen(url, timeout=50).read())
|
|
except (OSError, ValueError, TypeError):
|
|
return
|
|
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 (this is not autoconf plugin)')
|
|
elif len(sys.argv) > 1 and sys.argv[1] == 'config':
|
|
print_data(True)
|
|
else:
|
|
print_data(False)
|