diff --git a/BridgeEmulator/HueEmulator.py b/BridgeEmulator/HueEmulator.py
index d7f0526b..f97c26b1 100755
--- a/BridgeEmulator/HueEmulator.py
+++ b/BridgeEmulator/HueEmulator.py
@@ -15,24 +15,21 @@
run_service = True
bridge_config = defaultdict(lambda:defaultdict(str))
-lights_address = {}
new_lights = {}
-alarm_config = {}
sensors_state = {}
-capabilities = {"groups": {"available": 64},"lights": {"available": 63},"resourcelinks": {"available": 64},"rules": {"actions": {"available": 400},"available": 200,"conditions": {"available": 400}},"scenes": {"available": 200,"lightstates": {"available": 2048}},"schedules": {"available": 100},"sensors": {"available": 63,"clip": {"available": 63},"zgp": {"available": 63},"zll": {"available": 63}}}
-def send_email(triggered_sensor):
+def sendEmail(triggered_sensor):
import smtplib
TEXT = "Sensor " + triggered_sensor + " was triggered while the alarm is active"
# Prepare actual message
message = """From: %s\nTo: %s\nSubject: %s\n\n%s
- """ % (alarm_config["mail_from"], ", ".join(alarm_config["mail_recipients"]), alarm_config["mail_subject"], TEXT)
+ """ % (bridge_config["alarm_config"]["mail_from"], ", ".join(bridge_config["alarm_config"]["mail_recipients"]), bridge_config["alarm_config"]["mail_subject"], TEXT)
try:
- server_ssl = smtplib.SMTP_SSL(alarm_config["smtp_server"], alarm_config["smtp_port"])
+ server_ssl = smtplib.SMTP_SSL(bridge_config["alarm_config"]["smtp_server"], bridge_config["alarm_config"]["smtp_port"])
server_ssl.ehlo() # optional, called by login()
- server_ssl.login(alarm_config["mail_username"], alarm_config["mail_password"])
- server_ssl.sendmail(alarm_config["mail_from"], alarm_config["mail_recipients"], message)
+ server_ssl.login(bridge_config["alarm_config"]["mail_username"], bridge_config["alarm_config"]["mail_password"])
+ server_ssl.sendmail(bridge_config["alarm_config"]["mail_from"], bridge_config["alarm_config"]["mail_recipients"], message)
server_ssl.close()
print("successfully sent the mail")
return True
@@ -49,40 +46,22 @@ def send_email(triggered_sensor):
print("CRITICAL! Config file was not loaded")
sys.exit(1)
-try:
- with open('lights_address.json', 'r') as fp:
- lights_address = json.load(fp)
- print("Lights address loaded")
-except Exception:
- print("Lights adress file was not loaded")
#load and configure alarm virtual light
-try:
- with open('alarm_config.json', 'r') as fp:
- alarm_config = json.load(fp)
- print("Alarm config loaded")
- if alarm_config["mail_username"] != "":
- print("E-mail account configured")
- if "virtual_light" not in alarm_config:
- print("Send test email")
- if send_email("dummy test"):
- print("Mail succesfully sent\nCreate alarm virtual light")
- i = 1
- while (str(i)) in bridge_config["lights"]:
- i += 1
- bridge_config["lights"][str(i)] = {"state": {"on": False, "bri": 200, "hue": 0, "sat": 0, "xy": [0.690456, 0.295907], "ct": 461, "alert": "none", "effect": "none", "colormode": "xy", "reachable": True}, "type": "Extended color light", "name": "Alarm", "uniqueid": "1234567ffffff", "modelid": "LLC012", "swversion": "66009461"}
- alarm_config["virtual_light"] = str(i)
- with open('alarm_config.json', 'w') as fp:
- json.dump(alarm_config, fp, sort_keys=True, indent=4, separators=(',', ': '))
- else:
- print("Mail test failed")
+if bridge_config["alarm_config"]["mail_username"] != "":
+ print("E-mail account configured")
+ if "virtual_light" not in bridge_config["alarm_config"]:
+ print("Send test email")
+ if sendEmail("dummy test"):
+ print("Mail succesfully sent\nCreate alarm virtual light")
+ new_light_id = nextFreeId("lights")
+ bridge_config["lights"][new_light_id] = {"state": {"on": False, "bri": 200, "hue": 0, "sat": 0, "xy": [0.690456, 0.295907], "ct": 461, "alert": "none", "effect": "none", "colormode": "xy", "reachable": True}, "type": "Extended color light", "name": "Alarm", "uniqueid": "1234567ffffff", "modelid": "LLC012", "swversion": "66009461"}
+ bridge_config["alarm_config"]["virtual_light"] = new_light_id
else:
- print("E-mail account not configured")
+ print("Mail test failed")
-except Exception:
- print("Alarm config file was not loaded")
-def generate_sensors_state():
+def generateSensorsState():
for sensor in bridge_config["sensors"]:
if sensor not in sensors_state and "state" in bridge_config["sensors"][sensor]:
sensors_state[sensor] = {"state": {}}
@@ -90,33 +69,37 @@ def generate_sensors_state():
if key in ["lastupdated", "presence", "flag", "dark", "status"]:
sensors_state[sensor]["state"].update({key: "2017-01-01T00:00:00"})
-generate_sensors_state() #comment this line if you don't want to restore last known state to all lights on startup
+def nextFreeId(element):
+ i = 1
+ while (str(i)) in bridge_config[element]:
+ i += 1
+ return str(i)
+
+generateSensorsState() #comment this line if you don't want to restore last known state to all lights on startup
-def get_ip_address():
+def getIpAddress():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
-bridge_config["config"]["ipaddress"] = get_ip_address()
-bridge_config["config"]["gateway"] = get_ip_address()
+bridge_config["config"]["ipaddress"] = getIpAddress()
+bridge_config["config"]["gateway"] = getIpAddress()
bridge_config["config"]["mac"] = mac[0] + mac[1] + ":" + mac[2] + mac[3] + ":" + mac[4] + mac[5] + ":" + mac[6] + mac[7] + ":" + mac[8] + mac[9] + ":" + mac[10] + mac[11]
bridge_config["config"]["bridgeid"] = (mac[:6] + 'FFFE' + mac[6:]).upper()
-def save_config():
+def saveConfig():
with open('config.json', 'w') as fp:
json.dump(bridge_config, fp, sort_keys=True, indent=4, separators=(',', ': '))
- with open('lights_address.json', 'w') as fp:
- json.dump(lights_address, fp, sort_keys=True, indent=4, separators=(',', ': '))
-def ssdp_search():
+def ssdpSearch():
SSDP_ADDR = '239.255.255.250'
SSDP_PORT = 1900
MSEARCH_Interval = 2
multicast_group_c = SSDP_ADDR
multicast_group_s = (SSDP_ADDR, SSDP_PORT)
server_address = ('', SSDP_PORT)
- Response_message = 'HTTP/1.1 200 OK\r\nHOST: 239.255.255.250:1900\r\nEXT:\r\nCACHE-CONTROL: max-age=100\r\nLOCATION: http://' + get_ip_address() + ':80/description.xml\r\nSERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.20.0\r\nhue-bridgeid: ' + (mac[:6] + 'FFFE' + mac[6:]).upper() + '\r\n'
+ Response_message = 'HTTP/1.1 200 OK\r\nHOST: 239.255.255.250:1900\r\nEXT:\r\nCACHE-CONTROL: max-age=100\r\nLOCATION: http://' + getIpAddress() + ':80/description.xml\r\nSERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.20.0\r\nhue-bridgeid: ' + (mac[:6] + 'FFFE' + mac[6:]).upper() + '\r\n'
custom_response_message = {0: {"st": "upnp:rootdevice", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac + "::upnp:rootdevice"}, 1: {"st": "uuid:2f402f80-da50-11e1-9b23-" + mac, "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}, 2: {"st": "urn:schemas-upnp-org:device:basic:1", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}}
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(server_address)
@@ -138,13 +121,13 @@ def ssdp_search():
print(Response_message + "ST: " + custom_response_message[x]["st"] + "\r\nUSN: " + custom_response_message[x]["usn"] + "\r\n\r\n")
sleep(1)
-def ssdp_broadcast():
+def ssdpBroadcast():
print("start ssdp broadcast")
SSDP_ADDR = '239.255.255.250'
SSDP_PORT = 1900
MSEARCH_Interval = 2
multicast_group_s = (SSDP_ADDR, SSDP_PORT)
- message = 'NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=100\r\nLOCATION: http://' + get_ip_address() + ':80/description.xml\r\nSERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.20.0\r\nNTS: ssdp:alive\r\nhue-bridgeid: ' + (mac[:6] + 'FFFE' + mac[6:]).upper() + '\r\n'
+ message = 'NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=100\r\nLOCATION: http://' + getIpAddress() + ':80/description.xml\r\nSERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.20.0\r\nNTS: ssdp:alive\r\nhue-bridgeid: ' + (mac[:6] + 'FFFE' + mac[6:]).upper() + '\r\n'
custom_message = {0: {"nt": "upnp:rootdevice", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac + "::upnp:rootdevice"}, 1: {"nt": "uuid:2f402f80-da50-11e1-9b23-" + mac, "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}, 2: {"nt": "urn:schemas-upnp-org:device:basic:1", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}}
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(MSEARCH_Interval+0.5)
@@ -157,7 +140,7 @@ def ssdp_broadcast():
#print (message + "NT: " + custom_message[x]["nt"] + "\r\nUSN: " + custom_message[x]["usn"] + "\r\n\r\n")
sleep(60)
-def scheduler_processor():
+def schedulerProcessor():
while run_service:
for schedule in bridge_config["schedules"].iterkeys():
delay = 0
@@ -188,10 +171,28 @@ def scheduler_processor():
if bridge_config["schedules"][schedule]["autodelete"]:
del bridge_config["schedules"][schedule]
if (datetime.now().strftime("%M:%S") == "00:00"): #auto save configuration every hour
- save_config()
+ saveConfig()
sleep(1)
-def check_rule_conditions(rule, sensor, ignore_ddx=False):
+def addTradfriRemote(sensor_id, group_id):
+ rules = [{"actions": [{"address": "/groups/" + group_id + "/action","body": {"on": True},"method": "PUT"}],"conditions": [{"address": "/sensors/" + sensor_id + "/state/lastupdated","operator": "dx"},{"address": "/sensors/" + sensor_id + "/state/buttonevent","operator": "eq","value": "1002"},{"address": "/groups/" + group_id + "/action/on","operator": "eq","value": "false"}],"name": "Remote " + sensor_id + " button on"}, {"actions": [{"address": "/groups/" + group_id + "/action","body": {"on": False},"method": "PUT"}],"conditions": [{"address": "/sensors/" + sensor_id + "/state/lastupdated","operator": "dx"},{"address": "/sensors/" + sensor_id + "/state/buttonevent","operator": "eq","value": "1002"},{"address": "/groups/" + group_id + "/action/on","operator": "eq","value": "true"}],"name": "Remote " + sensor_id + " button off"},{ "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "bri_inc": 30, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "2002" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " up-press" }, { "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "bri_inc": 56, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "2001" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " up-long" }, { "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "bri_inc": -30, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "3002" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " dn-press" }, { "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "bri_inc": -56, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "3001" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " dn-long" }, { "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "ct_inc": 50, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "4002" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " ctl-press" }, { "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "ct_inc": 100, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "4001" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " ctl-long" }, { "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "ct_inc": -50, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "5002" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " ct-press" }, { "actions": [ { "address": "/groups/" + group_id + "/action", "body": { "ct_inc": -100, "transitiontime": 9 }, "method": "PUT" } ], "conditions": [ { "address": "/sensors/" + sensor_id + "/state/buttonevent", "operator": "eq", "value": "5001" }, { "address": "/sensors/" + sensor_id + "/state/lastupdated", "operator": "dx" } ], "name": "Dimmer Switch " + sensor_id + " ct-long" }]
+ resourcelinkId = nextFreeId("resourcelinks")
+ bridge_config["resourcelinks"][resourcelinkId] = {"classid": 15555,"description": "Rules for sensor " + sensor_id, "links": ["/sensors/3"],"name": "Emulator rules " + sensor_id,"owner": bridge_config["config"]["whitelist"].keys()[0]}
+ for rule in rules:
+ ruleId = nextFreeId("rules")
+ bridge_config["rules"][ruleId] = rule
+ bridge_config["rules"][ruleId].update({"creationtime": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S"), "lasttriggered": None, "owner": bridge_config["config"]["whitelist"].keys()[0], "recycle": True, "status": "enabled", "timestriggered": 0})
+ bridge_config["resourcelinks"][resourcelinkId]["links"].append("/rules/" + ruleId);
+
+def addHueMotionSensor():
+ new_sensor_id = nextFreeId("sensors")
+ bridge_config["sensors"][new_sensor_id] = {"name": "Hue temperature sensor 1", "uniqueid": new_sensor_id + "0f:12:23:34:45:56:d0:5b-02-0402", "type": "ZLLTemperature", "swversion": "6.1.0.18912", "state": {"temperature": None, "lastupdated": "none"}, "manufacturername": "Philips", "config": {"on": False, "battery": 100, "reachable": True, "alert":"none", "ledindication": False, "usertest": False, "pending": []}, "modelid": "SML001"}
+ bridge_config["sensors"][str(int(new_sensor_id) + 1)] = {"name": "Entrance Lights sensor", "uniqueid": new_sensor_id + "0f:12:23:34:45:56:d0:5b-02-0406", "type": "ZLLPresence", "swversion": "6.1.0.18912", "state": {"lastupdated": "none", "presence": None}, "manufacturername": "Philips", "config": {"on": False,"battery": 100,"reachable": True, "alert": "lselect", "ledindication": False, "usertest": False, "sensitivity": 2, "sensitivitymax": 2,"pending": []}, "modelid": "SML001"}
+ bridge_config["sensors"][str(int(new_sensor_id) + 2)] = {"name": "Hue ambient light sensor 1", "uniqueid": new_sensor_id + "0f:12:23:34:45:56:d0:5b-02-0400", "type": "ZLLLightLevel", "swversion": "6.1.0.18912", "state": {"dark": True, "daylight": False, "lightlevel": 6000, "lastupdated": "none"}, "manufacturername": "Philips", "config": {"on": False,"battery": 100, "reachable": True, "alert": "none", "tholddark": 21597, "tholdoffset": 7000, "ledindication": False, "usertest": False, "pending": []}, "modelid": "SML001"}
+ return(str(int(new_sensor_id) + 1))
+
+
+def checkRuleConditions(rule, sensor, ignore_ddx=False):
ddx = 0
sensor_found = False
for condition in bridge_config["rules"][rule]["conditions"]:
@@ -239,9 +240,9 @@ def check_rule_conditions(rule, sensor, ignore_ddx=False):
else:
return [False, ddx]
-def ddx_recheck(rule, sensor, ddx_delay):
+def ddxRecheck(rule, sensor, ddx_delay):
sleep(ddx_delay)
- rule_state = check_rule_conditions(rule, sensor, True)
+ rule_state = checkRuleConditions(rule, sensor, True)
if rule_state[0]: #if all conditions are meet again
print("delayed rule " + rule + " is triggered")
bridge_config["rules"][rule]["lasttriggered"] = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
@@ -249,11 +250,11 @@ def ddx_recheck(rule, sensor, ddx_delay):
for action in bridge_config["rules"][rule]["actions"]:
Thread(target=sendRequest, args=["/api/" + bridge_config["rules"][rule]["owner"] + action["address"], action["method"], json.dumps(action["body"])]).start()
-def rules_processor(sensor):
+def rulesProcessor(sensor):
bridge_config["config"]["localtime"] = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") #required for operator dx to address /config/localtime
for rule in bridge_config["rules"].iterkeys():
if bridge_config["rules"][rule]["status"] == "enabled":
- rule_result = check_rule_conditions(rule, sensor)
+ rule_result = checkRuleConditions(rule, sensor)
if rule_result[0] and bridge_config["rules"][rule]["lasttriggered"] != datetime.now().strftime("%Y-%m-%dT%H:%M:%S"): #if all condition are meet + anti loopback
if rule_result[1] == 0: #if not ddx rule
print("rule " + rule + " is triggered")
@@ -263,7 +264,7 @@ def rules_processor(sensor):
Thread(target=sendRequest, args=["/api/" + bridge_config["rules"][rule]["owner"] + action["address"], action["method"], json.dumps(action["body"])]).start()
else: #if ddx rule
print("ddx rule " + rule + " will be re validated after " + str(rule_result[1]) + " seconds")
- Thread(target=ddx_recheck, args=[rule, sensor, rule_result[1]]).start()
+ Thread(target=ddxRecheck, args=[rule, sensor, rule_result[1]]).start()
def sendRequest(url, method, data, time_out=3, delay=0):
if delay != 0:
@@ -339,21 +340,21 @@ def convert_xy(x, y, bri): #needed for milight hub that don't work with xy value
def sendLightRequest(light, data):
payload = {}
- if light in lights_address:
- if lights_address[light]["protocol"] == "native": #ESP8266 light or strip
- url = "http://" + lights_address[light]["ip"] + "/set?light=" + str(lights_address[light]["light_nr"]);
+ if light in bridge_config["lights_address"]:
+ if bridge_config["lights_address"][light]["protocol"] == "native": #ESP8266 light or strip
+ url = "http://" + bridge_config["lights_address"][light]["ip"] + "/set?light=" + str(bridge_config["lights_address"][light]["light_nr"]);
method = 'GET'
for key, value in data.iteritems():
if key == "xy":
url += "&x=" + str(value[0]) + "&y=" + str(value[1])
else:
url += "&" + key + "=" + str(value)
- elif lights_address[light]["protocol"] == "hue": #Original Hue light
- url = "http://" + lights_address[light]["ip"] + "/api/" + lights_address[light]["username"] + "/lights/" + lights_address[light]["light_id"] + "/state"
+ elif bridge_config["lights_address"][light]["protocol"] == "hue" or bridge_config["lights_address"][light]["protocol"] == "deconz": #Original Hue light or Deconz light
+ url = "http://" + bridge_config["lights_address"][light]["ip"] + "/api/" + bridge_config["lights_address"][light]["username"] + "/lights/" + bridge_config["lights_address"][light]["light_id"] + "/state"
method = 'PUT'
payload = data
- elif lights_address[light]["protocol"] == "milight": #MiLight bulb
- url = "http://" + lights_address[light]["ip"] + "/gateways/" + lights_address[light]["device_id"] + "/" + lights_address[light]["mode"] + "/" + str(lights_address[light]["group"]);
+ elif bridge_config["lights_address"][light]["protocol"] == "milight": #MiLight bulb
+ url = "http://" + bridge_config["lights_address"][light]["ip"] + "/gateways/" + bridge_config["lights_address"][light]["device_id"] + "/" + bridge_config["lights_address"][light]["mode"] + "/" + str(bridge_config["lights_address"][light]["group"]);
method = 'PUT'
for key, value in data.iteritems():
if key == "on":
@@ -370,8 +371,8 @@ def sendLightRequest(light, data):
payload["color"] = {}
(payload["color"]["r"], payload["color"]["g"], payload["color"]["b"]) = convert_xy(value[0], value[1], bridge_config["lights"][light]["state"]["bri"])
print(json.dumps(payload))
- elif lights_address[light]["protocol"] == "ikea_tradfri": #IKEA Tradfri bulb
- url = "coaps://" + lights_address[light]["ip"] + ":5684/15001/" + str(lights_address[light]["device_id"])
+ elif bridge_config["lights_address"][light]["protocol"] == "ikea_tradfri": #IKEA Tradfri bulb
+ url = "coaps://" + bridge_config["lights_address"][light]["ip"] + ":5684/15001/" + str(bridge_config["lights_address"][light]["device_id"])
for key, value in data.iteritems():
if key == "on":
payload["5850"] = int(value)
@@ -397,13 +398,13 @@ def sendLightRequest(light, data):
pprint(payload)
try:
- if lights_address[light]["protocol"] == "ikea_tradfri":
+ if bridge_config["lights_address"][light]["protocol"] == "ikea_tradfri":
if "transitiontime" in payload:
transitiontime = payload["transitiontime"]
else:
transitiontime = 4
for key, value in payload.iteritems(): #ikea bulbs don't accept all arguments at once
- print(check_output("./coap-client-linux -m put -u \"Client_identity\" -k \"" + lights_address[light]["security_code"] + "\" -e '{ \"3311\": [" + json.dumps({key : value, "5712": transitiontime}) + "] }' \"" + url + "\"", shell=True).split("\n")[3])
+ print(check_output("./coap-client-linux -m put -u \"Client_identity\" -k \"" + bridge_config["lights_address"][light]["security_code"] + "\" -e '{ \"3311\": [" + json.dumps({key : value, "5712": transitiontime}) + "] }' \"" + url + "\"", shell=True).split("\n")[3])
sleep(0.5)
else:
sendRequest(url, method, json.dumps(payload))
@@ -414,7 +415,7 @@ def sendLightRequest(light, data):
bridge_config["lights"][light]["state"]["reachable"] = True
print("LightRequest: " + url)
-def update_group_stats(light): #set group stats based on lights status in that group
+def updateGroupStats(light): #set group stats based on lights status in that group
for group in bridge_config["groups"]:
if light in bridge_config["groups"][group]["lights"]:
for key, value in bridge_config["lights"][light]["state"].iteritems():
@@ -433,13 +434,13 @@ def update_group_stats(light): #set group stats based on lights status in that g
bridge_config["groups"][group]["state"] = {"any_on": any_on, "all_on": all_on, "bri": avg_bri, "lastupdated": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")}
-def scan_for_lights(): #scan for ESP8266 lights and strips
+def scanForLights(): #scan for ESP8266 lights and strips
print(json.dumps([{"success": {"/lights": "Searching for new devices"}}], sort_keys=True, indent=4, separators=(',', ': ')))
#return all host that listen on port 80
- device_ips = check_output("nmap " + get_ip_address() + "/24 -p80 --open -n | grep report | cut -d ' ' -f5", shell=True).split("\n")
+ device_ips = check_output("nmap " + getIpAddress() + "/24 -p80 --open -n | grep report | cut -d ' ' -f5", shell=True).split("\n")
del device_ips[-1] #delete last empty element in list
for ip in device_ips:
- if ip != get_ip_address():
+ if ip != getIpAddress():
try:
f = urllib2.urlopen("http://" + ip + "/detect")
device_data = json.loads(f.read())
@@ -449,29 +450,28 @@ def scan_for_lights(): #scan for ESP8266 lights and strips
for light in bridge_config["lights"].iterkeys():
if bridge_config["lights"][light]["uniqueid"].startswith( device_data["mac"] ):
device_exist = True
- lights_address[light]["ip"] = ip
+ bridge_config["lights_address"][light]["ip"] = ip
if not device_exist:
print("is a new device")
for x in xrange(1, int(device_data["lights"]) + 1):
- i = 1
- while (str(i)) in bridge_config["lights"]:
- i += 1
+ new_light_id = nextFreeId("lights")
modelid = "LCT001"
if device_data["type"] == "strip":
modelid = "LST001"
elif device_data["type"] == "generic":
modelid = "LCT003"
- bridge_config["lights"][str(i)] = {"state": {"on": False, "bri": 200, "hue": 0, "sat": 0, "xy": [0.0, 0.0], "ct": 461, "alert": "none", "effect": "none", "colormode": "ct", "reachable": True}, "type": "Extended color light", "name": "Hue " + device_data["type"] + " " + device_data["hue"] + " " + str(x), "uniqueid": device_data["mac"] + "-" + str(x), "modelid": modelid, "swversion": "66009461"}
- new_lights.update({str(i): {"name": "Hue " + device_data["type"] + " " + device_data["hue"] + " " + str(x)}})
- lights_address[str(i)] = {"ip": ip, "light_nr": x, "protocol": "native"}
+ bridge_config["lights"][new_light_id] = {"state": {"on": False, "bri": 200, "hue": 0, "sat": 0, "xy": [0.0, 0.0], "ct": 461, "alert": "none", "effect": "none", "colormode": "ct", "reachable": True}, "type": "Extended color light", "name": "Hue " + device_data["type"] + " " + device_data["hue"] + " " + str(x), "uniqueid": device_data["mac"] + "-" + str(x), "modelid": modelid, "swversion": "66009461"}
+ new_lights.update({new_light_id: {"name": "Hue " + device_data["type"] + " " + device_data["hue"] + " " + str(x)}})
+ bridge_config["lights_address"][new_light_id] = {"ip": ip, "light_nr": x, "protocol": "native"}
except Exception as e:
print(ip + " is unknow device " + str(e))
+ scanDeconz()
def syncWithLights(): #update Hue Bridge lights states
- for light in lights_address:
- if lights_address[light]["protocol"] == "native":
+ for light in bridge_config["lights_address"]:
+ if bridge_config["lights_address"][light]["protocol"] == "native":
try:
- light_data = json.loads(sendRequest("http://" + lights_address[light]["ip"] + "/get?light=" + str(lights_address[light]["light_nr"]), "GET", "{}", 0.5))
+ light_data = json.loads(sendRequest("http://" + bridge_config["lights_address"][light]["ip"] + "/get?light=" + str(bridge_config["lights_address"][light]["light_nr"]), "GET", "{}", 0.5))
except:
bridge_config["lights"][light]["state"]["reachable"] = False
bridge_config["lights"][light]["state"]["on"] = False
@@ -479,11 +479,11 @@ def syncWithLights(): #update Hue Bridge lights states
else:
bridge_config["lights"][light]["state"]["reachable"] = True
bridge_config["lights"][light]["state"].update(light_data)
- elif lights_address[light]["protocol"] == "hue":
- light_data = json.loads(sendRequest("http://" + lights_address[light]["ip"] + "/api/" + lights_address[light]["username"] + "/lights/" + lights_address[light]["light_id"] + "/state"), "GET", "{}", 1)
+ elif bridge_config["lights_address"][light]["protocol"] == "hue":
+ light_data = json.loads(sendRequest("http://" + bridge_config["lights_address"][light]["ip"] + "/api/" + bridge_config["lights_address"][light]["username"] + "/lights/" + bridge_config["lights_address"][light]["light_id"] + "/state"), "GET", "{}", 1)
bridge_config["lights"][light]["state"].update(light_data)
- elif lights_address[light]["protocol"] == "ikea_tradfri":
- light_stats = json.loads(check_output("./coap-client-linux -m get -u \"Client_identity\" -k \"" + lights_address[light]["security_code"] + "\" \"coaps://" + lights_address[light]["ip"] + ":5684/15001/" + str(lights_address[light]["device_id"]) +"\"", shell=True).split("\n")[3])
+ elif bridge_config["lights_address"][light]["protocol"] == "ikea_tradfri":
+ light_stats = json.loads(check_output("./coap-client-linux -m get -u \"Client_identity\" -k \"" + bridge_config["lights_address"][light]["security_code"] + "\" \"coaps://" + bridge_config["lights_address"][light]["ip"] + ":5684/15001/" + str(bridge_config["lights_address"][light]["device_id"]) +"\"", shell=True).split("\n")[3])
bridge_config["lights"][light]["state"]["on"] = bool(light_stats["3311"][0]["5850"])
bridge_config["lights"][light]["state"]["bri"] = light_stats["3311"][0]["5851"]
if "5706" in light_stats["3311"][0]:
@@ -496,13 +496,105 @@ def syncWithLights(): #update Hue Bridge lights states
else:
bridge_config["lights"][light]["state"]["ct"] = 470
+def longPressButton(sensor, buttonevent):
+ print("long press detected")
+ sleep(1)
+ while bridge_config["sensors"][sensor]["state"]["buttonevent"] == buttonevent:
+ print("still pressed")
+ sensors_state[sensor]["state"]["lastupdated"] = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
+ rulesProcessor(sensor)
+ sleep(0.9)
+ return
+
+
+def websocketClient():
+ from ws4py.client.threadedclient import WebSocketClient
+ class EchoClient(WebSocketClient):
+ def opened(self):
+ self.send("hello")
+
+ def closed(self, code, reason=None):
+ print(("deconz websocket disconnected", code, reason))
+
+ def received_message(self, m):
+ print(m)
+ message = json.loads(str(m))
+ try:
+ if message["r"] == "sensors":
+ bridge_sensor_id = bridge_config["deconz"]["sensors"][message["id"]]["bridgeid"]
+ if "state" in message:
+ bridge_config["sensors"][bridge_sensor_id]["state"].update(message["state"])
+ for key in message["state"].iterkeys():
+ sensors_state[bridge_sensor_id]["state"][key] = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
+ rulesProcessor(bridge_sensor_id)
+ if "buttonevent" in message["state"]:
+ if message["state"]["buttonevent"] in [2001, 3001, 4001, 5001]:
+ Thread(target=longPressButton, args=[bridge_sensor_id, message["state"]["buttonevent"]]).start()
+ elif "config" in message:
+ bridge_config["sensors"][bridge_sensor_id]["config"].update(message["config"])
+
+ except:
+ print("unable to process the request" + m)
+
+ try:
+ ws = EchoClient('ws://127.0.0.1:' + str(bridge_config["deconz"]["websocketport"]))
+ ws.connect()
+ ws.run_forever()
+ except KeyboardInterrupt:
+ ws.close()
+
+def scanDeconz():
+ if bridge_config["deconz"]["enabled"]:
+ if "port" in bridge_config["deconz"]:
+ port = bridge_config["deconz"]["port"]
+ else:
+ port = 8080
+
+ if "username" not in bridge_config["deconz"]:
+ try:
+ registration = json.loads(sendRequest("http://127.0.0.1:" + str(port) + "/api", "POST", "{\"username\": \"283145a4e198cc6535\", \"devicetype\":\"Hue Emulator\"}"))
+ except:
+ print("registration fail, is the link button pressed?")
+ return
+ if "success" in registration[0]:
+ bridge_config["deconz"]["username"] = registration[0]["success"]["username"]
+ deconz_config = json.loads(sendRequest("http://127.0.0.1:" + str(port) + "/api/" + bridge_config["deconz"]["username"] + "/config", "GET", "{}"))
+ bridge_config["deconz"]["websocketport"] = deconz_config["websocketport"]
+ registered_deconz_lights = []
+ for light in bridge_config["lights_address"]:
+ if bridge_config["lights_address"][light]["protocol"] == "deconz":
+ registered_deconz_lights.append( bridge_config["lights_address"][light]["light_id"] )
+ deconz_lights = json.loads(sendRequest("http://127.0.0.1:" + str(port) + "/api/" + bridge_config["deconz"]["username"] + "/lights", "GET", "{}"))
+ for light in deconz_lights:
+ if light not in registered_deconz_lights:
+ new_light_id = nextFreeId("lights")
+ print("register new light " + new_light_id)
+ bridge_config["lights"][new_light_id] = deconz_lights[light]
+ bridge_config["lights_address"][new_light_id] = {"username": bridge_config["deconz"]["username"], "light_id": light, "ip": "127.0.0.1:" + str(port), "protocol": "deconz"}
+ deconz_sensors = json.loads(sendRequest("http://127.0.0.1:" + str(port) + "/api/" + bridge_config["deconz"]["username"] + "/sensors", "GET", "{}"))
+ for sensor in deconz_sensors:
+ if sensor not in bridge_config["deconz"]["sensors"]:
+ new_sensor_id = nextFreeId("sensors")
+ if deconz_sensors[sensor]["modelid"] == "TRADFRI remote control":
+ print("register TRADFRI remote control")
+ bridge_config["sensors"][new_sensor_id] = {"config": deconz_sensors[sensor]["config"], "manufacturername": deconz_sensors[sensor]["manufacturername"], "modelid": deconz_sensors[sensor]["modelid"], "name": deconz_sensors[sensor]["name"], "state": deconz_sensors[sensor]["state"], "swversion": deconz_sensors[sensor]["swversion"], "type": deconz_sensors[sensor]["type"], "uniqueid": deconz_sensors[sensor]["uniqueid"]}
+ bridge_config["deconz"]["sensors"][sensor] = {"bridgeid": new_sensor_id}
+ elif deconz_sensors[sensor]["modelid"] == "TRADFRI motion sensor":
+ print("register TRADFRI remote control as Philips Motion Sensor")
+ newMotionSensorId = addHueMotionSensor()
+ bridge_config["deconz"]["sensors"][sensor] = {"bridgeid": newMotionSensorId}
+
+
+
+
+
def description():
return """