Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
Initial commit with support for receiving alert status from your Datadog monitors and flash your Philips Hue lights to let you know.

Lights will flash red for critical alerts, or orange for warnings and then green when all monitors are resolved.

Has the option to only flash Philips Hue Light Bulbs during active hours so you don't get woken up :)
  • Loading branch information
Chris Board committed Jan 18, 2020
0 parents commit 86f1eb0
Show file tree
Hide file tree
Showing 3 changed files with 348 additions and 0 deletions.
241 changes: 241 additions & 0 deletions DDMonitorCheck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
from calendar import calendar
from datetime import datetime
import calendar
import time

import requests
from datadog import initialize, api
import json
from phue import Bridge
from enum import Enum


class BulbColour(Enum):
RED = 1
ORANGE = 2
GREEN = 3


def discover_ip():
response = requests.get('https://discovery.meethue.com')

# process response

if response and response.status_code == 200:
data = response.json()

if 'internalipaddress' in data[0]:
return data[0]['internalipaddress']
return None


def is_during_active_hours(options):

# Check if the flag for alerting only during active hours is set. If its set to 0, just return True
# as user wants alerted 24/7

if options['alert_active_hours_only'] == 0:
return True

# Get the current epoch time
current_epoch = time.time()
current_date = str(datetime.date(datetime.now()))

start_active_hours_epoch = calendar.timegm(time.strptime(current_date + ' ' + options['start_active_hours'],
'%Y-%m-%d %H:%M'))
end_active_hours_epoch = calendar.timegm(time.strptime(current_date + ' ' + options['end_active_hours'],
'%Y-%m-%d %H:%M'))

if current_epoch >= start_active_hours_epoch and current_epoch <= end_active_hours_epoch:
return True
else:
return False


def flash_lights(bulb_colour):
options = {
'alert_active_hours_only': 1,
'start_active_hours': '09:00',
'end_active_hours': '22:00'
}

if not is_during_active_hours(options):
print("Not during active hours so not flashing lights")
# Return False to tell the main function there was no alert done so the config file
# doesn't get updated. This allows that if the monitor is still active when it runs again
# during active hours the bulbs are flashed
return False

print("Flashing lights as during active hours or active hours only alerting is disabled")

lights = b.lights

if bulb_colour == BulbColour.RED:
hue = 64015
saturation = 254
elif bulb_colour == BulbColour.ORANGE:
hue = 8382
saturation = 252
elif bulb_colour == BulbColour.GREEN:
hue = 25652
saturation = 254

# Need to store the existing light settings so we can revert the lights back after flashing
original_light_states = []
# Get the colour light indexes so can update the hue and sat without receiving an error setting a non colour bulb
colour_light_ids = []

# Need a list of all light id so can control all lights on/off state together
all_light_ids = []

for light in lights:
light_state = {}
light_state['id'] = light.light_id
light_state['name'] = light.name
light_state['on'] = light.on
light_state['brightness'] = light.brightness
if light.type == 'Extended color light':
colour_light_ids.append(light.light_id)
light_state['hue'] = light.hue
light_state['sat'] = light.saturation
light_state['xy'] = light.xy

original_light_states.append(light_state)
all_light_ids.append(light.light_id)

# Flash each light on an off every 1 second for 3 seconds
bulb_on_state = 40
b.set_light(all_light_ids, 'on', True)
# Set the bulbs to the alert colour
b.set_light(colour_light_ids, 'hue', hue)
b.set_light(colour_light_ids, 'sat', saturation)
time.sleep(0.3)
for i in range(8):
b.set_light(all_light_ids, 'bri', bulb_on_state)
# Set bulb state to opposite of current state
if bulb_on_state == 40:
bulb_on_state = 254
else:
bulb_on_state = 40

time.sleep(0.3)

# Now restore the states of the bulbs
# Because each bulb might and most likely different settings each bulb has to be individually updated
for light in lights:
light_id = light.light_id

# Loop over the original states looking for this id and if found update the buld to the correct settings
for state in original_light_states:
if state['id'] == light_id:
light.on = state['on']
if state['on']:
light.brightness = state['brightness']
if light.type == 'Extended color light':
light.hue = state['hue']
light.saturation = state['sat']
light.xy = state['xy']
break
# Return true so the main method knows the lights were flashed so user was updated
# therefore update the config file so they don't get alerted again
return True


hue_bridge_ip = discover_ip()
print("Hue Bridge IP: " + hue_bridge_ip)
if hue_bridge_ip is None:
print("The IP address of Philips Hue Bridge could not be found. Please provide the IP address manually")
exit(0)

b = Bridge(hue_bridge_ip)
b.connect()

dd_options = {
'api_key': '',
'app_key': ''
}

# Check if the count file exists if not create a default one
try:
f = open("alert_count.json")
except IOError:
counts_json = '{"warn_count": 0, "alert_count": 0}'
f = open("alert_count.json", "w")
f.write(counts_json)
f.close()

initialize(**dd_options)

monitors = api.Monitor.get_all()

alert_count = 0
warn_count = 0
ok_count = 0

for monitor in monitors:
monitorName = monitor['name']
monitorStatus = monitor['overall_state']
if monitorStatus == 'OK':
ok_count += 1
elif monitorStatus == 'Alert':
alert_count += 1
elif monitorStatus == 'Warn':
warn_count += 1

print('Monitor Name: ' + monitorName + " Status: " + monitorStatus)

print("OK Count: " + str(ok_count))
print("Warn Count: " + str(warn_count))
print("Alert Count: " + str(alert_count))

with open('alert_count.json') as json_file:
counts_json = json.load(json_file)

file_warn_counts = counts_json['warn_count']
file_alert_counts = counts_json['alert_count']

were_lights_updated = False

if warn_count > 0 or alert_count > 0:
# Check the alert_count.json file, if the counts in the file are 0, not previously alerted so flash the lights

if file_warn_counts == 0 and file_alert_counts == 0:
# Need to flash the lights - check what the wost alert level is
if alert_count > 0:
# Need to flash the lights red
print("Flashing philips hue to be red")
were_lights_updated = flash_lights(BulbColour.RED)
elif warn_count > 0:
# Need to flash the lights orange
print("Flashing philips hue to be orange")
were_lights_updated = flash_lights(BulbColour.ORANGE)
elif file_warn_counts > 0 or file_alert_counts > 0:
if file_alert_counts > 0 and alert_count == 0 and warn_count > 0:
# If here, then previously alerted red but red alert recovered and now on warning
# so flash philips hue orange instead
print("alert recovered so flashing orange instead")
were_lights_updated = flash_lights(BulbColour.ORANGE)
elif (file_alert_counts > 0 or file_warn_counts) and (alert_count > 0 or warn_count > 0):
# The file has some alerts and warning counts and there are still current alert and warning
# counts so don't do anything with the lights
print("no alerts and warnings recovered so not flashing the lights")
else:
print("Previously alerted and alerts are still happening so don't do anything with lights")

else:
# There are no warnings and no errors check the file counts, if any are over 0, the monitors are therefore
# recovered so flash green
if file_alert_counts > 0 or file_warn_counts > 0:
# The file did have an alert count the last time it run, so flash the lights green
print("Everything recovered so flash lights green")
were_lights_updated = flash_lights(BulbColour.GREEN)

if were_lights_updated:
# Save a JSON file of the current counts. This will be used so that if the file already shows the counts for non OK
# are > 1 so don't inadvertently keep flashing the users lights
counts_json = '{"warn_count": ' + str(warn_count) + ', "alert_count":' + str(alert_count) + '}'
f = open("alert_count.json", "w")
f.write(counts_json)
f.close()

exit(0)
107 changes: 107 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# DDHueAlert
![image alt="logo" style="float: left"](logo.png)

This is a small python script that you can run as a scheduled task using either the
Windows Scheduler or as Linux Cron jobs and can flash your Philips Hue
(https://www2.meethue.com/en-gb) based on the status of the monitors you
have set up in your Datadog (https://datadoghq.com) account.

Below is described how to get started.

# Installing the Script
You need to have Python 3.x installed and need to run the following dependencies
```
pip install requests
pip install datadog
pip install phue
```

*Details about the library are below*

Once installed you need to press the button on your Philips Hue Bridge and then
run your script once within 30 seconds (`python DDMonitorCheck.py`) to authorise DDHueAlert script with your
Philips Hue Bridge - the script will automatically find your bridge IP - if it throws an
exception when you run the script, then likely the python script wasn't executed within
30 seconds of the button on top of the bridge being pressed.

# Setting up
Now that you have the script installed and authorised with your bridge now its
time to set up your Datadog API key and app key and the options for when you want to
be alerted.

1. In the main method towards the bottom of the script
you should see an object created called dd_options. This contains two keys
api_key and app_key. Fill these in with your key, they can be retrieved
from your account on the datadog website (https://app.datadoghq.com/account/settings#api).
Its recommended to create a new API key and App Key that is used for this script.

2. There's an option object in the method `flash_lights`. This contains
some options on when you should be alerted, for example, you can have it set up
to always flash your lights when a triggered Datadog monitor is found or only during
active monitoring hours so you don't get woken up. By default active hours alerting is
turned on and the active hours are between 09:00 and 22:00. You can amend these
to your needs (times have to be in 24 hour format) or you can set `alert_active_hours_only`
to '0' so that you always get an alert.

# How does it work?
The script runs once and finishes. It could be done as a loop and keeps going to
sleep but thought run once and finish was the best option as you don't need
to worry about creating start up scripts or worry about the script dying for some reason
and not restarting.

You can run it as often as you like, but bear in mind there may be some usage limits
with the Datadog API. I've been making it run every 5 minutes on a Linux server
in a cron job.

When the script runs, it connects to the bridge to ensure its authenticated.
It then checks all of the monitors under your datacount account and creates
a count of warning and critical alerts. If there's warnings and critical alerts
the lights will flash red and then revert to their original state.

The script updates a file in the path of the script called alert_count.json.
This provides a history of what was triggered the last time it ran.

If the script runs again and previously the critical count > 0 and the warning count
was greater than 0, but now the critical count = 0, then the lights flash orange
to show that state has now changed to warning.

When the script runs again, if one or more of the counts were greater than 0 on the
last run, but now both counts retrieved are now 0, then the lights flash green so you
know everythings been resolved.

The file stops the lights flashing every single time it runs so please
make sure that where the script runs from has read/write access to that directory
to avoid flashing your Philips Hue lights unnecessarily.

# What if I Don't Have Philips Hue Colour Bulbs?
Its no problem, the script will still work, obviously won't show the colours though?

The script goes through all bulbs, light strips ets and checks their type. If they are detected
as a colour bulb then they will have their colour updated to match the datadog monitor severity. If the
bulb is detected as a non colour bulb, e.g. Philips Hue Ambient White Light Bulb, then the bulb will just
flash.

# Thanks to the following libraries
**Requests - https://pypi.org/project/requests/**: Used for sending API
requests to the Datadog API.

**Datadog - https://docs.datadoghq.com/integrations/python/:** Python
library to access your Datadog account via Datadog's API

**phue - https://github.com/studioimaginaire/phue**: Python library
for interacting with the Philips Hue API.

*This project is in no way affiliated with Datadog (https://datadoghq.com) or
Philips Hue (https://meethue.com).*

*Boardies IT Solutions can take no responsibility for any unexpected or unintended
additional costs from your Datadog Account or any unexpected or unintended
damage to your Philips Hue devices or property. This is provided as is and there are
no guarantees or warranties associated with this project*

If you need any help, then please feel free to contact me either via here or by raising
a support ticket at https://support.boardiesitsolutions.com.

Boardies IT Solutions - Copyright &copy; 2020

<img src="https://boardiesitsolutions.com/images/logo.png">
Binary file added logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 86f1eb0

Please sign in to comment.