From ca8d4b15109e93b5cb21e201ef358e782aab4f75 Mon Sep 17 00:00:00 2001 From: Rich Date: Fri, 15 Apr 2016 10:35:33 -0400 Subject: [PATCH 01/14] uncommented read-in stuff --- hue_off.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hue_off.py b/hue_off.py index 239c4db..af9576a 100755 --- a/hue_off.py +++ b/hue_off.py @@ -77,7 +77,7 @@ def main(argv): raw_input("Press enter to continue") """ - """ + # Read in status from last time app ran try: with open("off.lights") as f: @@ -87,8 +87,7 @@ def main(argv): if prev_status[1] == bridge: - """ - + try: bridge = Bridge(bridgeIP) -- 2.39.5 From 43590d02e0773247b051069d5c27b5ffed591237 Mon Sep 17 00:00:00 2001 From: Rich Date: Sun, 17 Apr 2016 17:00:01 -0400 Subject: [PATCH 02/14] Added code to turn off lights listed in off.lights file --- hue_off.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hue_off.py b/hue_off.py index af9576a..681ebb0 100755 --- a/hue_off.py +++ b/hue_off.py @@ -86,7 +86,17 @@ def main(argv): print "Error reading file." if prev_status[1] == bridge: + # Same bridge as before, apply the light status! + lights_list = bridge.get_light_objects('list') + # Since the only names in the file are lights that should be off + # let's turn any light name in the file off. + print "Searching off.lights for lights that were previously off..." + for light in lights_list: + for line in prev_status: + if light.name == line: + light.on = "off" + print "Turning off: %s" % line try: -- 2.39.5 From 5b53db293db673fee732241f363b43ffb6b1289e Mon Sep 17 00:00:00 2001 From: Rich Date: Sun, 17 Apr 2016 20:08:56 -0400 Subject: [PATCH 03/14] After read in of off.lights file, added code to turn off the lights listed in file and debugged incorrect variable spellings --- hue_off.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/hue_off.py b/hue_off.py index 681ebb0..7904197 100755 --- a/hue_off.py +++ b/hue_off.py @@ -45,6 +45,7 @@ def main(argv): createnew = False off_lights = [] powerfailure = False + prev_status = [] # How often (in seconds) to check the pwrstatd log for events check_interval = 60 @@ -82,21 +83,24 @@ def main(argv): try: with open("off.lights") as f: prev_status = f.readlines() + f.close() except: print "Error reading file." - - if prev_status[1] == bridge: - # Same bridge as before, apply the light status! - lights_list = bridge.get_light_objects('list') + + # Check if prev_status contains items + if prev_status: + if prev_status[1] == bridgeIP: + # Same bridge as before, apply the light status! + lights_list = bridge.get_light_objects('list') - # Since the only names in the file are lights that should be off - # let's turn any light name in the file off. - print "Searching off.lights for lights that were previously off..." - for light in lights_list: - for line in prev_status: - if light.name == line: - light.on = "off" - print "Turning off: %s" % line + # Since the only names in the file are lights that should be off + # let's turn any light name in the file off. + print "Searching off.lights for lights that were previously off..." + for light in lights_list: + for line in prev_status: + if light.name == line: + light.on = "off" + print "Turning off: %s" % line try: @@ -118,11 +122,17 @@ def main(argv): try: lights_list = bridge.get_light_objects('list') + #print "Got list of lights" lights_file = open("off.lights", 'w') + #print "Opened off.lights" lights_file.truncate() + #print "Truncated off.lights" lights_file.write(datestamp()) + #print "Wrote datestamp to file" lights_file.write("\n") - lights_file.write(bridge) + #print "Wrote newline" + lights_file.write(bridgeIP) + #print "Wrote BridgeIP" lights_file.write("\n") # Store list of lights that are currently off off_lights = [] -- 2.39.5 From 0f6e68c1e1b5e3e4cb9b35ee4294542d1f5b845b Mon Sep 17 00:00:00 2001 From: Rich Date: Wed, 15 Feb 2017 17:35:14 -0500 Subject: [PATCH 04/14] Converted hue_off.py code to python 3, saved old python 2 as hue_off.py.bak --- hue_off.py | 48 ++++++------ hue_off.py.bak | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+), 24 deletions(-) create mode 100755 hue_off.py.bak diff --git a/hue_off.py b/hue_off.py index 7904197..5978ff3 100755 --- a/hue_off.py +++ b/hue_off.py @@ -35,7 +35,7 @@ Copyright (c) 2016, Richard K. Oberholtzer import sys import getopt from phue import Bridge -import commands +import subprocess import time def main(argv): @@ -57,7 +57,7 @@ def main(argv): optUsage() sys.exit(2) except ValueError: - print "Incorrect option usage!" + print("Incorrect option usage!") opUsage() sys.exit(2) for opt, arg in opts: @@ -85,7 +85,7 @@ def main(argv): prev_status = f.readlines() f.close() except: - print "Error reading file." + print("Error reading file.") # Check if prev_status contains items if prev_status: @@ -95,22 +95,22 @@ def main(argv): # Since the only names in the file are lights that should be off # let's turn any light name in the file off. - print "Searching off.lights for lights that were previously off..." + print("Searching off.lights for lights that were previously off...") for light in lights_list: for line in prev_status: if light.name == line: light.on = "off" - print "Turning off: %s" % line + print("Turning off: %s" % line) try: bridge = Bridge(bridgeIP) except: - print "Error, App not registered. Press Bridge button and try again." + print("Error, App not registered. Press Bridge button and try again.") if createnew: - print "You've requested to connect to a new Bridge. Connecting..." - print "Press the button on your Bridge, then press Enter." + print("You've requested to connect to a new Bridge. Connecting...") + print("Press the button on your Bridge, then press Enter.") bridge.connect() # Main guts of the program @@ -144,7 +144,7 @@ def main(argv): lights_file.close() except: # Unknown exception! - print "%s: An unknown exception has appeared..." % datestamp() + print("%s: An unknown exception has appeared..." % datestamp()) #Pause time.sleep(check_interval) @@ -156,30 +156,30 @@ def main(argv): print(light.name) """ - raw_input("Press Enter") + input("Press Enter") def optUsage(): # This function displays options usage information - print "Options Usage:" - print " -b, --bridge | IP address of your Hue Bridge" - print " -c, --createnew | Connect to a new Hue Bridge" - print " -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60" + print("Options Usage:") + print(" -b, --bridge | IP address of your Hue Bridge") + print(" -c, --createnew | Connect to a new Hue Bridge") + print(" -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60") return 0 def checkPowerOutage(): # This function checks the current pwrstatd process to see if we're on utility power or not. - pwrstatus = commands.getstatusoutput("pwrstat -status | grep 'Power Supply'") - if "Utility" not in pwrstatus[1]: - # We have a power failure! - print "%s:Mains power FAILURE" % datestamp() - return True - else: - # No power failure... some other problem. Don't touch the lights. - print "%s:Mains power OK" % datestamp() + pwrstatus = subprocess.getstatusoutput("pwrstat -status | grep 'Power Supply'") + if "Utility" not in pwrstatus[1]: + # We have a power failure! + print("%s:Mains power FAILURE" % datestamp()) + return True + else: + # No power failure... some other problem. Don't touch the lights. + print("%s:Mains power OK" % datestamp()) return False def waitSetlights(off_lights,check_interval): @@ -189,9 +189,9 @@ def waitSetlights(off_lights,check_interval): outage = checkPowerOutage() if not outage: # Turn off the lights that were off prior to the power outage - print "%s: ... Mains power has returned, turning off lights that were not on prior to outage:" % datestamp() + print("%s: ... Mains power has returned, turning off lights that were not on prior to outage:" % datestamp()) for light in off_lights: - print "%s: Turning off %s" % (datestamp(), light.name) + print("%s: Turning off %s" % (datestamp(), light.name)) light.on = False return 0 time.sleep(check_interval) diff --git a/hue_off.py.bak b/hue_off.py.bak new file mode 100755 index 0000000..7904197 --- /dev/null +++ b/hue_off.py.bak @@ -0,0 +1,206 @@ +#!/bin/python + +""" + This program uses CyberPower's pwrstatd daemon and the Phue Python library to turn + off your Hue lights after a brief power outage. + + The program routinely query's your Hue bridge to a list of lights and stores the ones + that are currently off. It also queries pwrstat to determine if we are running on + MAINS or BATTERY power. When it detects that we are running on BATTERY power, it + waits for MAINS power to return, then turns off the Hue lights that were off prior + to the outage. + + Created By: + Rich Oberholtzer + rich@rickelobe.com + https://gitlab.rickelobe.com + +Copyright (c) 2016, Richard K. Oberholtzer + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +""" + +import sys +import getopt +from phue import Bridge +import commands +import time + +def main(argv): + + # IP address of your Hue Bridge + bridgeIP = "0" + createnew = False + off_lights = [] + powerfailure = False + prev_status = [] + + # How often (in seconds) to check the pwrstatd log for events + check_interval = 60 + + # Parse the options + try: + opts, args = getopt.getopt(argv,"hb:ci:",["bridge=","createnew=","interval="]) + except getopt.GetoptError: + optUsage() + sys.exit(2) + except ValueError: + print "Incorrect option usage!" + opUsage() + sys.exit(2) + for opt, arg in opts: + if opt == '-h': + optUsage() + sys.exit() + elif opt in ("-b", "--bridge"): + bridgeIP = arg + elif opt in ("-c", "--createnew"): + createnew = True + elif opt in ("-i", "--interval"): + check_interval = float(arg) + """ + # Debugging + print "Bridge IP: %s" % bridgeIP + print "Createnew: %s" % createnew + print "Username: %s" % username + raw_input("Press enter to continue") + """ + + + # Read in status from last time app ran + try: + with open("off.lights") as f: + prev_status = f.readlines() + f.close() + except: + print "Error reading file." + + # Check if prev_status contains items + if prev_status: + if prev_status[1] == bridgeIP: + # Same bridge as before, apply the light status! + lights_list = bridge.get_light_objects('list') + + # Since the only names in the file are lights that should be off + # let's turn any light name in the file off. + print "Searching off.lights for lights that were previously off..." + for light in lights_list: + for line in prev_status: + if light.name == line: + light.on = "off" + print "Turning off: %s" % line + + + try: + bridge = Bridge(bridgeIP) + except: + print "Error, App not registered. Press Bridge button and try again." + + if createnew: + print "You've requested to connect to a new Bridge. Connecting..." + print "Press the button on your Bridge, then press Enter." + bridge.connect() + + # Main guts of the program + # Start an endless loop + while True: + + if checkPowerOutage(): + waitSetlights(off_lights,check_interval) + + try: + lights_list = bridge.get_light_objects('list') + #print "Got list of lights" + lights_file = open("off.lights", 'w') + #print "Opened off.lights" + lights_file.truncate() + #print "Truncated off.lights" + lights_file.write(datestamp()) + #print "Wrote datestamp to file" + lights_file.write("\n") + #print "Wrote newline" + lights_file.write(bridgeIP) + #print "Wrote BridgeIP" + lights_file.write("\n") + # Store list of lights that are currently off + off_lights = [] + for light in lights_list: + if not light.on: + off_lights.append(light) + lights_file.write(light.name) + lights_file.write("\n") + lights_file.close() + except: + # Unknown exception! + print "%s: An unknown exception has appeared..." % datestamp() + + #Pause + time.sleep(check_interval) + + # End of endless loop... hmm + """ + # Print the name of the lights that are Off + for light in off_lights: + print(light.name) + """ + + raw_input("Press Enter") + + + +def optUsage(): + # This function displays options usage information + + print "Options Usage:" + print " -b, --bridge | IP address of your Hue Bridge" + print " -c, --createnew | Connect to a new Hue Bridge" + print " -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60" + return 0 + +def checkPowerOutage(): + # This function checks the current pwrstatd process to see if we're on utility power or not. + + pwrstatus = commands.getstatusoutput("pwrstat -status | grep 'Power Supply'") + if "Utility" not in pwrstatus[1]: + # We have a power failure! + print "%s:Mains power FAILURE" % datestamp() + return True + else: + # No power failure... some other problem. Don't touch the lights. + print "%s:Mains power OK" % datestamp() + return False + +def waitSetlights(off_lights,check_interval): + # Periodically check for return to Utility power, and then turn off lights that were off prior to event. + while True: + # Loop in here until mains power returns + outage = checkPowerOutage() + if not outage: + # Turn off the lights that were off prior to the power outage + print "%s: ... Mains power has returned, turning off lights that were not on prior to outage:" % datestamp() + for light in off_lights: + print "%s: Turning off %s" % (datestamp(), light.name) + light.on = False + return 0 + time.sleep(check_interval) + return 0 + +def datestamp(): + # this function returns a date string formatted as YYYYMMDD-HHMMSS + return time.strftime("%Y%m%d-%H%M%S") + +if __name__ == "__main__": + + main(sys.argv[1:]) -- 2.39.5 From b28f88257f6a450828c7b1fb91ec69a774fdb0ef Mon Sep 17 00:00:00 2001 From: Rich Date: Wed, 15 Feb 2017 17:52:49 -0500 Subject: [PATCH 05/14] Added mode option argument to be used for adding NUT server functionality --- hue_off.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/hue_off.py b/hue_off.py index 5978ff3..d0c6c9c 100755 --- a/hue_off.py +++ b/hue_off.py @@ -15,7 +15,7 @@ rich@rickelobe.com https://gitlab.rickelobe.com -Copyright (c) 2016, Richard K. Oberholtzer +Copyright (c) 2017, Richard K. Oberholtzer This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,13 +46,15 @@ def main(argv): off_lights = [] powerfailure = False prev_status = [] + username = "" + mode = "" # Mode Options: cyberpower, nut # How often (in seconds) to check the pwrstatd log for events check_interval = 60 # Parse the options try: - opts, args = getopt.getopt(argv,"hb:ci:",["bridge=","createnew=","interval="]) + opts, args = getopt.getopt(argv,"hb:ci:m:",["bridge=","createnew=","interval=","mode="]) except getopt.GetoptError: optUsage() sys.exit(2) @@ -70,13 +72,16 @@ def main(argv): createnew = True elif opt in ("-i", "--interval"): check_interval = float(arg) - """ + elif opt in ("-m", "--mode"): + mode = arg.lower() + # Debugging - print "Bridge IP: %s" % bridgeIP - print "Createnew: %s" % createnew - print "Username: %s" % username - raw_input("Press enter to continue") - """ + print ("Bridge IP: %s" % bridgeIP) + print ("Createnew: %s" % createnew) + print ("Username: %s" % username) + print ("Mode: %s" % mode) + input("Press enter to continue") + # Read in status from last time app ran -- 2.39.5 From e2da7d3770cfc5771744e0c98ce7b80fc4021463 Mon Sep 17 00:00:00 2001 From: Rich Date: Wed, 15 Feb 2017 18:45:50 -0500 Subject: [PATCH 06/14] Changed checkPowerOutage to checkCyberPowerOutage --- hue_off.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hue_off.py b/hue_off.py index d0c6c9c..f533f45 100755 --- a/hue_off.py +++ b/hue_off.py @@ -74,15 +74,15 @@ def main(argv): check_interval = float(arg) elif opt in ("-m", "--mode"): mode = arg.lower() - + + ''' # Debugging print ("Bridge IP: %s" % bridgeIP) print ("Createnew: %s" % createnew) print ("Username: %s" % username) print ("Mode: %s" % mode) input("Press enter to continue") - - + ''' # Read in status from last time app ran try: @@ -122,7 +122,7 @@ def main(argv): # Start an endless loop while True: - if checkPowerOutage(): + if checkCyberPowerOutage(): waitSetlights(off_lights,check_interval) try: @@ -174,7 +174,7 @@ def optUsage(): print(" -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60") return 0 -def checkPowerOutage(): +def checkCyberPowerOutage(): # This function checks the current pwrstatd process to see if we're on utility power or not. pwrstatus = subprocess.getstatusoutput("pwrstat -status | grep 'Power Supply'") -- 2.39.5 From 79a7c177b797a79d6c51e3d2fe1d52fac2e533b5 Mon Sep 17 00:00:00 2001 From: Rich Date: Sun, 19 Feb 2017 18:08:52 -0500 Subject: [PATCH 07/14] Created Stub function for NutOutage check --- hue_off.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/hue_off.py b/hue_off.py index f533f45..acd9075 100755 --- a/hue_off.py +++ b/hue_off.py @@ -83,7 +83,6 @@ def main(argv): print ("Mode: %s" % mode) input("Press enter to continue") ''' - # Read in status from last time app ran try: with open("off.lights") as f: @@ -123,7 +122,7 @@ def main(argv): while True: if checkCyberPowerOutage(): - waitSetlights(off_lights,check_interval) + waitSetlights(off_lights,check_interval,mode) try: lights_list = bridge.get_light_objects('list') @@ -172,6 +171,8 @@ def optUsage(): print(" -b, --bridge | IP address of your Hue Bridge") print(" -c, --createnew | Connect to a new Hue Bridge") print(" -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60") + print(" -m, --mode | Set the mode for checking if there's a power outage") + print(" Options: 'cyberpower' or 'nut'") return 0 def checkCyberPowerOutage(): @@ -186,12 +187,23 @@ def checkCyberPowerOutage(): # No power failure... some other problem. Don't touch the lights. print("%s:Mains power OK" % datestamp()) return False +def checkNutOutage(): + # This function returns True if the Nut Server sends an outage alert. -def waitSetlights(off_lights,check_interval): + #placeholder return + return True + +def waitSetlights(off_lights,check_interval,mode): # Periodically check for return to Utility power, and then turn off lights that were off prior to event. while True: # Loop in here until mains power returns - outage = checkPowerOutage() + + # Determine which mode we're operating in and check for power outage accordingly + if mode == "cyberpower": + outage = checkCyberPowerOutage() + elif mode == "nut": + outage = checkNutOutage() + # if not outage: # Turn off the lights that were off prior to the power outage print("%s: ... Mains power has returned, turning off lights that were not on prior to outage:" % datestamp()) -- 2.39.5 From e580baf8bdadf4d7bb3b861dcfefa9e469b0b642 Mon Sep 17 00:00:00 2001 From: Rich Date: Wed, 1 Mar 2017 21:42:54 -0500 Subject: [PATCH 08/14] more work on NUT check --- hue_off.py | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/hue_off.py b/hue_off.py index acd9075..eb4759c 100755 --- a/hue_off.py +++ b/hue_off.py @@ -115,13 +115,17 @@ def main(argv): if createnew: print("You've requested to connect to a new Bridge. Connecting...") print("Press the button on your Bridge, then press Enter.") - bridge.connect() + try: + bridge.connect() + except: + print("Bridge connect failed") + bridge.get_api() # Main guts of the program # Start an endless loop while True: - if checkCyberPowerOutage(): + if checkForPowerOutage(mode): waitSetlights(off_lights,check_interval,mode) try: @@ -172,9 +176,15 @@ def optUsage(): print(" -c, --createnew | Connect to a new Hue Bridge") print(" -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60") print(" -m, --mode | Set the mode for checking if there's a power outage") - print(" Options: 'cyberpower' or 'nut'") + print(" Options: 'cyberpower' or 'nut'") return 0 +def checkForPowerOutage(mode): + if mode == "cyberpower": + return checkCyberPowerOutage() + else: + return checkNutOutage() + def checkCyberPowerOutage(): # This function checks the current pwrstatd process to see if we're on utility power or not. @@ -187,11 +197,32 @@ def checkCyberPowerOutage(): # No power failure... some other problem. Don't touch the lights. print("%s:Mains power OK" % datestamp()) return False + + def checkNutOutage(): # This function returns True if the Nut Server sends an outage alert. - #placeholder return - return True + #Search for "on battery" + if watchLog("on battery","/var/log/syslog"): + return False + elif watchLog("on line power","/var/log/syslog"): + return True + + +def watchLog(watchfor,watchfile): + # This function will watch watchfile (path to file in filesystem) for watchfor (string) + # then return the line it found + + f = open(watchfile, "r") + + f.seek(0,2) # Go to the end of the file + while True: + line = f.readline() + if not line: + time.sleep(0.1) # Sleep briefly + continue + f.close() + return True def waitSetlights(off_lights,check_interval,mode): # Periodically check for return to Utility power, and then turn off lights that were off prior to event. -- 2.39.5 From 9b89b2ef231eab09f9718e4e45c4c6b0ac445ad5 Mon Sep 17 00:00:00 2001 From: Rich Date: Fri, 17 Mar 2017 19:05:35 -0400 Subject: [PATCH 09/14] Woah, there's a lot of changes here... for too many to list, but we can now monitor NUT! --- hue_off.py | 135 ++++++++++++++++++++++++++++--------------- off.lights | 9 +++ updateLightStatus.py | 69 ++++++++++++++++++++++ 3 files changed, 168 insertions(+), 45 deletions(-) create mode 100644 off.lights create mode 100755 updateLightStatus.py diff --git a/hue_off.py b/hue_off.py index eb4759c..eb1eddf 100755 --- a/hue_off.py +++ b/hue_off.py @@ -37,24 +37,27 @@ import getopt from phue import Bridge import subprocess import time +import gevent def main(argv): # IP address of your Hue Bridge bridgeIP = "0" createnew = False + global off_lights off_lights = [] powerfailure = False prev_status = [] username = "" mode = "" # Mode Options: cyberpower, nut + logfile = "" # How often (in seconds) to check the pwrstatd log for events check_interval = 60 # Parse the options try: - opts, args = getopt.getopt(argv,"hb:ci:m:",["bridge=","createnew=","interval=","mode="]) + opts, args = getopt.getopt(argv,"hb:ci:m:l",["bridge=","createnew=","interval=","mode=","logfile="]) except getopt.GetoptError: optUsage() sys.exit(2) @@ -73,19 +76,22 @@ def main(argv): elif opt in ("-i", "--interval"): check_interval = float(arg) elif opt in ("-m", "--mode"): - mode = arg.lower() + mode = arg.lower() + elif opt in ("-l", "--logfile"): + logfile = arg - ''' + ''' # Debugging print ("Bridge IP: %s" % bridgeIP) print ("Createnew: %s" % createnew) print ("Username: %s" % username) print ("Mode: %s" % mode) + print ("Logfile: %s" % logfile) input("Press enter to continue") ''' # Read in status from last time app ran try: - with open("off.lights") as f: + with open("off.lights", 'r') as f: prev_status = f.readlines() f.close() except: @@ -102,7 +108,7 @@ def main(argv): print("Searching off.lights for lights that were previously off...") for light in lights_list: for line in prev_status: - if light.name == line: + if light.name == line[:-1]: light.on = "off" print("Turning off: %s" % line) @@ -122,12 +128,41 @@ def main(argv): bridge.get_api() # Main guts of the program + cmd = ("python3 updateLightStatus.py -b " + str(bridgeIP)) + print(cmd) + p = subprocess.Popen(str(cmd), shell=True) + #p = subprocess.Popen('python3 updateLightStatus.py -b 10.3.0.120', shell=True) + print("Spun off updateLightStatus process") + # Start an endless loop while True: + print("Waiting for power failure") + state = checkForPowerOutage(mode,logfile) + if state: + waitSetlights(bridge,check_interval,mode,logfile) - if checkForPowerOutage(mode): - waitSetlights(off_lights,check_interval,mode) - + #Pause + time.sleep(check_interval) + # End of endless loop... hmm + +def optUsage(): + # This function displays options usage information + + print("Options Usage:") + print(" -b, --bridge | IP address of your Hue Bridge") + print(" -c, --createnew | Connect to a new Hue Bridge") + print(" -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60") + print(" -m, --mode | Set the mode for checking if there's a power outage") + print(" Options: 'cyberpower' or 'nut'") + print(" --logfile | Specify the logfile to monitor if choosing 'nut' mode" + return 0 + + +''' +No Longer Needed: broke out into subprocess +def updateLightStatus(bridgeIP): + bridge = Bridge(bridgeIP) + while True: try: lights_list = bridge.get_light_objects('list') #print "Got list of lights" @@ -153,37 +188,40 @@ def main(argv): except: # Unknown exception! print("%s: An unknown exception has appeared..." % datestamp()) + bridge.get_api() + time.sleep(5) +''' + +def turnOffLights(bridge): - #Pause - time.sleep(check_interval) + try: + with open("off.lights") as f: + prev_status = f.readlines() + f.close() + except: + print("Error reading file.") + + # Check if prev_status contains items + if prev_status: + # Same bridge as before, apply the light status! + lights_list = bridge.get_light_objects('list') + + # Since the only names in the file are lights that should be off + # let's turn any light name in the file off. + print("Searching off.lights for lights that were previously off...") + for light in lights_list: + for line in prev_status: + if light.name == line[:-1]: + light.on = False + print("Turning off: %s" % line) - # End of endless loop... hmm - """ - # Print the name of the lights that are Off - for light in off_lights: - print(light.name) - """ - - input("Press Enter") - - - -def optUsage(): - # This function displays options usage information - - print("Options Usage:") - print(" -b, --bridge | IP address of your Hue Bridge") - print(" -c, --createnew | Connect to a new Hue Bridge") - print(" -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60") - print(" -m, --mode | Set the mode for checking if there's a power outage") - print(" Options: 'cyberpower' or 'nut'") return 0 -def checkForPowerOutage(mode): +def checkForPowerOutage(mode,logfile): if mode == "cyberpower": return checkCyberPowerOutage() else: - return checkNutOutage() + return checkNutOutage(logfile) def checkCyberPowerOutage(): # This function checks the current pwrstatd process to see if we're on utility power or not. @@ -199,17 +237,19 @@ def checkCyberPowerOutage(): return False -def checkNutOutage(): +def checkNutOutage(logfile): # This function returns True if the Nut Server sends an outage alert. #Search for "on battery" - if watchLog("on battery","/var/log/syslog"): - return False - elif watchLog("on line power","/var/log/syslog"): + if watchLog("on battery","on line power",str(logfile)): + print("%s:Mains power FAILURE" % datestamp()) return True + else: + print("%s:Mains power OK" % datestamp()) + return False -def watchLog(watchfor,watchfile): +def watchLog(watchfortrue,watchforfalse,watchfile): # This function will watch watchfile (path to file in filesystem) for watchfor (string) # then return the line it found @@ -221,26 +261,31 @@ def watchLog(watchfor,watchfile): if not line: time.sleep(0.1) # Sleep briefly continue - f.close() - return True + if line.find(watchfortrue) >= 0: + f.close() + return True + elif line.find(watchforfalse) >= 0: + f.close() + return False -def waitSetlights(off_lights,check_interval,mode): +def waitSetlights(bridge,check_interval,mode,logfile): # Periodically check for return to Utility power, and then turn off lights that were off prior to event. + print("Waiting for power to return") while True: # Loop in here until mains power returns - # Determine which mode we're operating in and check for power outage accordingly - if mode == "cyberpower": - outage = checkCyberPowerOutage() - elif mode == "nut": - outage = checkNutOutage() + outage = checkForPowerOutage(mode,logfile) + # if not outage: # Turn off the lights that were off prior to the power outage print("%s: ... Mains power has returned, turning off lights that were not on prior to outage:" % datestamp()) + turnOffLights(bridge) + ''' for light in off_lights: print("%s: Turning off %s" % (datestamp(), light.name)) light.on = False + ''' return 0 time.sleep(check_interval) return 0 diff --git a/off.lights b/off.lights new file mode 100644 index 0000000..2e6d30a --- /dev/null +++ b/off.lights @@ -0,0 +1,9 @@ +20170317-190435 +10.3.0.120 +Living Room 2 +Living Room 1 +Dining room lamp +Living Room Pharmacy +Bedroom +Porch +Middle Bedroom Ceiling diff --git a/updateLightStatus.py b/updateLightStatus.py new file mode 100755 index 0000000..e0f5230 --- /dev/null +++ b/updateLightStatus.py @@ -0,0 +1,69 @@ +#!/bin/python + +import sys +from phue import Bridge +import getopt +import time + +def main(argv): + + bridgeIP = "0" + check_interval = 60 + + try: + opts, args = getopt.getopt(argv,"b:",["bridge="]) + except getopt.GetoptError: + optUsage() + sys.exit(2) + except ValueError: + print("ULS: Incorrect option usage!") + sys.exit(2) + for opt, arg in opts: + if opt in ("-b", "--bridge"): + bridgeIP = arg + #elif opt in ("-i", "--interval"): + #check_interval = float(arg) + + #print("ULS: Bridge IP Address: %s" % bridgeIP) + + try: + bridge = Bridge(bridgeIP) + except: + print("ULS: Woah there, and error appeared!") + + while True: + try: + lights_list = bridge.get_light_objects('list') + #print "Got list of lights" + lights_file = open("off.lights", 'w') + #print "Opened off.lights" + lights_file.truncate() + #print "Truncated off.lights" + lights_file.write(datestamp()) + #print "Wrote datestamp to file" + lights_file.write("\n") + #print "Wrote newline" + lights_file.write(bridgeIP) + #print "Wrote BridgeIP" + lights_file.write("\n") + # Store list of lights that are currently off + #off_lights = [i] + for light in lights_list: + if not light.on: + #off_lights.append(light) + lights_file.write(light.name) + lights_file.write("\n") + lights_file.close() + except Exception as e: + # Exception! + print("%s: ULS: An exception has appeared... %s" % (datestamp(), str(e))) + bridge.get_api() + time.sleep(check_interval) + +def datestamp(): + # this function returns a date string formatted as YYYYMMDD-HHMMSS + return time.strftime("%Y%m%d-%H%M%S") + + +if __name__ == "__main__": + main(sys.argv[1:]) -- 2.39.5 From 6d2851695096102dce93938258441174d98c2145 Mon Sep 17 00:00:00 2001 From: Rich Date: Fri, 17 Mar 2017 19:09:58 -0400 Subject: [PATCH 10/14] Fixed syntax error on line 158 --- hue_off.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hue_off.py b/hue_off.py index eb1eddf..f105bf5 100755 --- a/hue_off.py +++ b/hue_off.py @@ -154,7 +154,7 @@ def optUsage(): print(" -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60") print(" -m, --mode | Set the mode for checking if there's a power outage") print(" Options: 'cyberpower' or 'nut'") - print(" --logfile | Specify the logfile to monitor if choosing 'nut' mode" + print(" --logfile | Specify the logfile to monitor if choosing 'nut' mode") return 0 -- 2.39.5 From d69bb543dd24a93a954a6972f0ad3b7e3cc0515b Mon Sep 17 00:00:00 2001 From: Rich Date: Fri, 17 Mar 2017 19:29:40 -0400 Subject: [PATCH 11/14] removed hue_off.py.bak --- off.lights | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/off.lights b/off.lights index 2e6d30a..0c773c0 100644 --- a/off.lights +++ b/off.lights @@ -1,4 +1,4 @@ -20170317-190435 +20170317-192830 10.3.0.120 Living Room 2 Living Room 1 -- 2.39.5 From 4287c06c26cc28dfc436eeefa92fd88561a884cb Mon Sep 17 00:00:00 2001 From: Rich Date: Fri, 17 Mar 2017 19:29:46 -0400 Subject: [PATCH 12/14] Delete hue_off.py.bak --- hue_off.py.bak | 206 ------------------------------------------------- 1 file changed, 206 deletions(-) delete mode 100755 hue_off.py.bak diff --git a/hue_off.py.bak b/hue_off.py.bak deleted file mode 100755 index 7904197..0000000 --- a/hue_off.py.bak +++ /dev/null @@ -1,206 +0,0 @@ -#!/bin/python - -""" - This program uses CyberPower's pwrstatd daemon and the Phue Python library to turn - off your Hue lights after a brief power outage. - - The program routinely query's your Hue bridge to a list of lights and stores the ones - that are currently off. It also queries pwrstat to determine if we are running on - MAINS or BATTERY power. When it detects that we are running on BATTERY power, it - waits for MAINS power to return, then turns off the Hue lights that were off prior - to the outage. - - Created By: - Rich Oberholtzer - rich@rickelobe.com - https://gitlab.rickelobe.com - -Copyright (c) 2016, Richard K. Oberholtzer - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -""" - -import sys -import getopt -from phue import Bridge -import commands -import time - -def main(argv): - - # IP address of your Hue Bridge - bridgeIP = "0" - createnew = False - off_lights = [] - powerfailure = False - prev_status = [] - - # How often (in seconds) to check the pwrstatd log for events - check_interval = 60 - - # Parse the options - try: - opts, args = getopt.getopt(argv,"hb:ci:",["bridge=","createnew=","interval="]) - except getopt.GetoptError: - optUsage() - sys.exit(2) - except ValueError: - print "Incorrect option usage!" - opUsage() - sys.exit(2) - for opt, arg in opts: - if opt == '-h': - optUsage() - sys.exit() - elif opt in ("-b", "--bridge"): - bridgeIP = arg - elif opt in ("-c", "--createnew"): - createnew = True - elif opt in ("-i", "--interval"): - check_interval = float(arg) - """ - # Debugging - print "Bridge IP: %s" % bridgeIP - print "Createnew: %s" % createnew - print "Username: %s" % username - raw_input("Press enter to continue") - """ - - - # Read in status from last time app ran - try: - with open("off.lights") as f: - prev_status = f.readlines() - f.close() - except: - print "Error reading file." - - # Check if prev_status contains items - if prev_status: - if prev_status[1] == bridgeIP: - # Same bridge as before, apply the light status! - lights_list = bridge.get_light_objects('list') - - # Since the only names in the file are lights that should be off - # let's turn any light name in the file off. - print "Searching off.lights for lights that were previously off..." - for light in lights_list: - for line in prev_status: - if light.name == line: - light.on = "off" - print "Turning off: %s" % line - - - try: - bridge = Bridge(bridgeIP) - except: - print "Error, App not registered. Press Bridge button and try again." - - if createnew: - print "You've requested to connect to a new Bridge. Connecting..." - print "Press the button on your Bridge, then press Enter." - bridge.connect() - - # Main guts of the program - # Start an endless loop - while True: - - if checkPowerOutage(): - waitSetlights(off_lights,check_interval) - - try: - lights_list = bridge.get_light_objects('list') - #print "Got list of lights" - lights_file = open("off.lights", 'w') - #print "Opened off.lights" - lights_file.truncate() - #print "Truncated off.lights" - lights_file.write(datestamp()) - #print "Wrote datestamp to file" - lights_file.write("\n") - #print "Wrote newline" - lights_file.write(bridgeIP) - #print "Wrote BridgeIP" - lights_file.write("\n") - # Store list of lights that are currently off - off_lights = [] - for light in lights_list: - if not light.on: - off_lights.append(light) - lights_file.write(light.name) - lights_file.write("\n") - lights_file.close() - except: - # Unknown exception! - print "%s: An unknown exception has appeared..." % datestamp() - - #Pause - time.sleep(check_interval) - - # End of endless loop... hmm - """ - # Print the name of the lights that are Off - for light in off_lights: - print(light.name) - """ - - raw_input("Press Enter") - - - -def optUsage(): - # This function displays options usage information - - print "Options Usage:" - print " -b, --bridge | IP address of your Hue Bridge" - print " -c, --createnew | Connect to a new Hue Bridge" - print " -i, --interval | Set how often (in seconds) to check the pwrstatd log. Defaults to 60" - return 0 - -def checkPowerOutage(): - # This function checks the current pwrstatd process to see if we're on utility power or not. - - pwrstatus = commands.getstatusoutput("pwrstat -status | grep 'Power Supply'") - if "Utility" not in pwrstatus[1]: - # We have a power failure! - print "%s:Mains power FAILURE" % datestamp() - return True - else: - # No power failure... some other problem. Don't touch the lights. - print "%s:Mains power OK" % datestamp() - return False - -def waitSetlights(off_lights,check_interval): - # Periodically check for return to Utility power, and then turn off lights that were off prior to event. - while True: - # Loop in here until mains power returns - outage = checkPowerOutage() - if not outage: - # Turn off the lights that were off prior to the power outage - print "%s: ... Mains power has returned, turning off lights that were not on prior to outage:" % datestamp() - for light in off_lights: - print "%s: Turning off %s" % (datestamp(), light.name) - light.on = False - return 0 - time.sleep(check_interval) - return 0 - -def datestamp(): - # this function returns a date string formatted as YYYYMMDD-HHMMSS - return time.strftime("%Y%m%d-%H%M%S") - -if __name__ == "__main__": - - main(sys.argv[1:]) -- 2.39.5 From a1b380ba211ceb98dfd55d1fc7fb25488d2a90dc Mon Sep 17 00:00:00 2001 From: Rich Date: Sat, 18 Mar 2017 17:27:26 -0400 Subject: [PATCH 13/14] Removing duplicate code for turning off lights upon run, also moved that action to after checking if bridge is reachable --- hue_off.py | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/hue_off.py b/hue_off.py index f105bf5..1289e0a 100755 --- a/hue_off.py +++ b/hue_off.py @@ -88,30 +88,7 @@ def main(argv): print ("Mode: %s" % mode) print ("Logfile: %s" % logfile) input("Press enter to continue") - ''' - # Read in status from last time app ran - try: - with open("off.lights", 'r') as f: - prev_status = f.readlines() - f.close() - except: - print("Error reading file.") - - # Check if prev_status contains items - if prev_status: - if prev_status[1] == bridgeIP: - # Same bridge as before, apply the light status! - lights_list = bridge.get_light_objects('list') - - # Since the only names in the file are lights that should be off - # let's turn any light name in the file off. - print("Searching off.lights for lights that were previously off...") - for light in lights_list: - for line in prev_status: - if light.name == line[:-1]: - light.on = "off" - print("Turning off: %s" % line) - + ''' try: bridge = Bridge(bridgeIP) @@ -127,6 +104,9 @@ def main(argv): print("Bridge connect failed") bridge.get_api() + # Turn off any lights listed in the off.lights file from last run + turnOffLights(bridge) + # Main guts of the program cmd = ("python3 updateLightStatus.py -b " + str(bridgeIP)) print(cmd) -- 2.39.5 From 39b81c2caef82f0ac9a66d57b6a4d39a014acc2c Mon Sep 17 00:00:00 2001 From: Rich Date: Sat, 18 Mar 2017 20:06:13 -0400 Subject: [PATCH 14/14] Revert "Merge branch 'master' into 'Dev'" This reverts commit bd3cdfd65496d23399c3abd203fa7a811080b342 --- hue_off.py | 2 +- off.lights | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hue_off.py b/hue_off.py index 8f7de42..1289e0a 100755 --- a/hue_off.py +++ b/hue_off.py @@ -276,4 +276,4 @@ def datestamp(): if __name__ == "__main__": - main(sys.argv[1:]) \ No newline at end of file + main(sys.argv[1:]) diff --git a/off.lights b/off.lights index 18d1b0c..0c773c0 100644 --- a/off.lights +++ b/off.lights @@ -6,4 +6,4 @@ Dining room lamp Living Room Pharmacy Bedroom Porch -Middle Bedroom Ceiling \ No newline at end of file +Middle Bedroom Ceiling -- 2.39.5