diff --git a/config.xml b/config.xml index 3941994..a937855 100644 --- a/config.xml +++ b/config.xml @@ -1,7 +1,7 @@ - + Get Status Key diff --git a/libs/hue_lib/hue_bridge.py b/libs/hue_lib/hue_bridge.py index 1f99835..5d22046 100644 --- a/libs/hue_lib/hue_bridge.py +++ b/libs/hue_lib/hue_bridge.py @@ -225,27 +225,26 @@ def get_html_device_list(self): return info_data def __register_device_type(self, data): - with supp_fct.TraceLog(self.logger): - global devices - for item in data: - item_id = supp_fct.get_val(item, "id") + global devices + for item in data: + item_id = supp_fct.get_val(item, "id") - services = supp_fct.get_val(item, "services") - device = hue_item.HueDevice(self.logger) - device.device_id = item_id - metadata = supp_fct.get_val(item, "metadata") - device.name = supp_fct.get_val(metadata, "name") + services = supp_fct.get_val(item, "services") + device = hue_item.HueDevice(self.logger) + device.device_id = item_id + metadata = supp_fct.get_val(item, "metadata") + device.name = supp_fct.get_val(metadata, "name") - for service in services: - rid = supp_fct.get_val(service, "rid") + for service in services: + rid = supp_fct.get_val(service, "rid") - rtype = supp_fct.get_val(service, "rtype") - if rtype == "light": - device.light_id = rid - elif rtype == "zigbee_connectivity": - device.zigbee_connectivity_id = rid + rtype = supp_fct.get_val(service, "rtype") + if rtype == "light": + device.light_id = rid + elif rtype == "zigbee_connectivity": + device.zigbee_connectivity_id = rid - devices[device.device_id] = device + devices[device.device_id] = device def __register_room_type(self, data): with supp_fct.TraceLog(self.logger): @@ -320,23 +319,22 @@ def __register_scene_type(self, data): devices[device.device_id] = device def __register_grouped_light_type(self, data): - with supp_fct.TraceLog(self.logger): - global devices - for item in data: - item_id = supp_fct.get_val(item, "id") - owner = supp_fct.get_val(item, "owner") - rid = supp_fct.get_val(owner, "rid") - rtype = supp_fct.get_val(owner, "rtype") + global devices + for item in data: + item_id = supp_fct.get_val(item, "id") + owner = supp_fct.get_val(item, "owner") + rid = supp_fct.get_val(owner, "rid") + rtype = supp_fct.get_val(owner, "rtype") - for device in devices.values(): - if rtype == "room": - if device.room == rid: - device.grouped_lights.append(item_id) - devices[device.device_id] = device - elif rtype == "zone": - if device.zone == rid: - device.grouped_lights.append(item_id) - devices[device.device_id] = device + for device in devices.values(): + if rtype == "room": + if device.room == rid: + device.grouped_lights.append(item_id) + devices[device.device_id] = device + elif rtype == "zone": + if device.zone == rid: + device.grouped_lights.append(item_id) + devices[device.device_id] = device def register_devices(self, key, rid, host_ip): """ @@ -370,7 +368,8 @@ def register_devices(self, key, rid, host_ip): try: data = json.loads(data_raw["data"]) # type: dict except Exception as e: - self.logger.error("In register_devices #377, " + str(e)) + self.logger.error("hue_bridge.py | register_devices({key}, {rid}, {host}) | " + "Caught exception '{msg}'".format(key=key, rid=rid, host=host_ip, msg=e)) continue data = supp_fct.get_val(data, "data") @@ -397,29 +396,32 @@ def get_own_device(self, rid): :rtype: hue_item.HueDevice :return: Device """ - with supp_fct.TraceLog(self.logger): - global devices + global devices + + # check if identified before + if rid in self.device.get_device_ids(True): + if rid != self.device.id: + self.logger.debug("hue_bridge.py | get_own_device({}) | Restorig device".format(rid)) + self.device.id = rid + self.device.get_type_of_device() + return self.device - # check if identified before - if rid in self.device.get_device_ids(): - if rid != self.device.id: + # search and store own device if not identified before + else: + for device in devices.values(): + if rid in device.get_device_ids(True): + self.logger.debug("hue_bridge.py | get_own_device({}) | Registering device".format(rid)) + self.device = device self.device.id = rid self.device.get_type_of_device() - return self.device - - # search and store own device if not identified before - else: - for device in devices.values(): - if rid in device.get_device_ids(): - self.device = device - self.device.id = rid - self.device.get_type_of_device() - break + break - if self.device is None: - self.logger.warning("Requested device not found") + if not self.device.id: + error_msg = ("hue_bridge.py | get_own_device('{rid}') | Requested device not found!".format(rid=rid)) + self.logger.error(error_msg) + assert error_msg - return self.device + return self.device def connect_to_eventstream(self, conn, host_ip, key): with supp_fct.TraceLog(self.logger): diff --git a/libs/hue_lib/hue_item.py b/libs/hue_lib/hue_item.py index da9aa02..79047a1 100644 --- a/libs/hue_lib/hue_item.py +++ b/libs/hue_lib/hue_item.py @@ -60,12 +60,18 @@ def __str__(self): return res - def get_device_ids(self): + def get_device_ids(self, full_search=False): with supp_fct.TraceLog(self.logger): - ret = [self.device_id, self.light_id, self.zigbee_connectivity_id, self.room, self.zone] # type: [str] + ret = [self.device_id, self.light_id, self.zigbee_connectivity_id] # type: [str] for scene in self.scenes: ret.append(scene["id"]) # tod: check if extend vs. append - # ret.extend(self.grouped_lights) # Fix for https://github.com/En3rGy/14100_Hue/issues/29 + + if self.rtype == "room" or full_search: # Fix for https://github.com/En3rGy/14100_Hue/issues/29 + ret.extend(self.room) + if self.rtype == "zone" or full_search: + ret.extend(self.zone) + if self.rtype == "grouped_light" or full_search: + ret.extend(self.grouped_lights) ret = filter(lambda x: x != '', ret) # remove empty elements return ret @@ -296,4 +302,6 @@ def get_type_of_device(self): elif self.id in self.light_id: self.rtype = "light" + self.logger.debug("hue_item.py | get_type_of_device | Deviye type is {}".format(self.rtype)) + return self.rtype diff --git a/libs/hue_lib/supp_fct.py b/libs/hue_lib/supp_fct.py index 4a9196e..66053fb 100644 --- a/libs/hue_lib/supp_fct.py +++ b/libs/hue_lib/supp_fct.py @@ -136,35 +136,35 @@ def get_data(ip, key, api_cmd, logger): :return: {'data': Data received, 'status': html return code} :rtype: {str, str} """ - with TraceLog(logger): - logger.debug("#### {} // {}".format(ip,key)) - api_path = 'https://' + ip + '/clip/v2/resource/' + api_cmd - url_parsed = urlparse.urlparse(api_path) - headers = {'Host': url_parsed.hostname, "hue-application-key": key} - - # Build a SSL Context to disable certificate verification. - ctx = ssl._create_unverified_context() - - try: - # Build a http request and overwrite host header with the original hostname. - request = urllib2.Request(api_path, headers=headers) - # Open the URL and read the response. - response = urllib2.urlopen(request, data=None, timeout=5, context=ctx) - data = {'data': response.read(), 'status': str(response.getcode())} - logger.debug("Received {} byte with return code {}".format(len(data.get("data")), data.get("status"))) - - if int(data["status"]) != 200: - logger.warning( - "In supp_dct.get_data #99, Hue bridge response code for '{cmd}' is {status}".format(cmd=api_cmd, - status=data[ - "status"])) - - except Exception as e: - data = {'data': str(e), 'status': str(0)} - logger.error("In get_data #291, for request '{cmd}' received '{error}', data: {data}".format(cmd=api_cmd, - error=e, - data=data)) - return data + logger.debug("supp_fct.py | get_data | ip= '{}', key= '{}', api_cmd= '{}'".format(ip, key, api_cmd)) + api_path = 'https://{}/clip/v2/resource/{}'.format(ip, api_cmd) + url_parsed = urlparse.urlparse(api_path) + headers = {'Host': url_parsed.hostname, "hue-application-key": key} + + # Build a SSL Context to disable certificate verification. + ctx = ssl._create_unverified_context() + + try: + # Build a http request and overwrite host header with the original hostname. + request = urllib2.Request(api_path, headers=headers) + # Open the URL and read the response. + response = urllib2.urlopen(request, data=None, timeout=5, context=ctx) + data = {'data': response.read(), 'status': str(response.getcode())} + logger.debug("supp_dct.py | get_data | Received {} byte with return code {}".format(len(data.get("data")), + data.get("status"))) + + if int(data["status"]) != 200: + logger.warning( + "supp_dct.py | get_data | Hue bridge response code for '{cmd}' is {status}".format(cmd=api_cmd, + status=data[ + "status"])) + + except Exception as e: + data = {'data': str(e), 'status': str(0)} + logger.error("supp_dct.py | get_data | For request '{cmd}' received '{error}', data: {data}".format(cmd=api_cmd, + error=e, + data=data)) + return data def http_put(ip, key, device_rid, api_path, payload, logger): diff --git a/src/14100_Hue Group (14100).py b/src/14100_Hue Group (14100).py index 96f76cb..feca886 100644 --- a/src/14100_Hue Group (14100).py +++ b/src/14100_Hue Group (14100).py @@ -144,9 +144,7 @@ def process_json(self, msg): device_id = supp_fct.get_val(data, "id") corresponding_device_ids = hue_device.get_device_ids() - print("###########{} in {}? {}".format(device_id, - corresponding_device_ids, - device_id in corresponding_device_ids)) + if device_id not in corresponding_device_ids: pass @@ -265,14 +263,16 @@ def process_eventstream_msgs(self, msgs): try: module_instance.process_json(msg) except Exception as e: - self.logger.warning("In eventstream #336, calling remote modules, '" + str(e) + "'.") + self.logger.warning("14100_Hue Group (14100).py | process_eventstream_msgs(...) | " + "Calling remote modules, '{}'".format(e)) self.process_json(msg) except Exception as e: - self.logger.error("Eventstream #342, error with '" + e.message[:len(e.message)] + "'.") - self.log_data("Eventstream #342 error msg", str(e)) - self.log_data("Eventstream #342 erroneous str", msg) + self.logger.error("14100_Hue Group (14100).py | " + "process_eventstream_msgs(...) | {}".format(e.message)) + self.log_data("14100_Hue Group (14100).py | process_eventstream_msgs(...) | Exception", + "{}:\n---Start---\n{}\n---End---".format(e, msg)) def eventstream(self, running, key): """ @@ -367,18 +367,25 @@ def do_init(self): # get own lamp data if already registered device = self.bridge.get_own_device(device_id) - data = supp_fct.get_data(ip, key, "light/" + device.light_id, self.logger) + self.logger.debug("14100_Hue Group (14100).py | do_init | Printing Device:" + "---Start of device---\n{}\n---End of device---".format(device)) + + # if e.g. grouped_light, there is no light_id available + if device.light_id: + data = supp_fct.get_data(ip, key, "light/{}".format(device.light_id), self.logger) - if int(data["status"]) is 200: - self.process_json(data) - else: - self.logger.warning("Could not retrieve data for master light id in on_init") + if int(data["status"]) is 200: + self.process_json(data) + else: + self.logger.warning("Could not retrieve data for master light id in on_init") - data = supp_fct.get_data(ip, key, "zigbee_connectivity/" + device.zigbee_connectivity_id, self.logger) - if int(data["status"]) is 200: - self.process_json(data) - else: - self.logger.warning("Could not retrieve zigbee connectivity data for master light") + # if e.g. grouped_light, there is no light_id available + if device.zigbee_connectivity_id: + data = supp_fct.get_data(ip, key, "zigbee_connectivity/" + device.zigbee_connectivity_id, self.logger) + if int(data["status"]) is 200: + self.process_json(data) + else: + self.logger.warning("Could not retrieve zigbee connectivity data for master light") if self.singleton.is_master(): # eventstream init & start @@ -471,14 +478,15 @@ def on_input_value(self, index, value): self.logger.debug("Received Item Index input.") self.bridge.register_devices(key, value, self.FRAMEWORK.get_homeserver_private_ip()) device = self.bridge.get_own_device(value) + device.get_type_of_device() # get own lamp data if registered - data = supp_fct.get_data(ip, key, "light/" + device.light_id, self.logger) + data = supp_fct.get_data(ip, key, "light/{}".format(device.light_id), self.logger) if data["status"] == 200: self.process_json(data) else: - self.logger.warning("Could not retrieve data for master light id in on_init") + self.logger.warning("X | on_input_value | Could not retrieve data for master light id") elif ((self.PIN_I_R == index) or (self.PIN_I_G == index) or diff --git a/tests/tst_Hue_Group.py b/tests/tst_Hue_Group.py index eba6ae8..6b5a6c0 100644 --- a/tests/tst_Hue_Group.py +++ b/tests/tst_Hue_Group.py @@ -46,7 +46,7 @@ def load_data(self, module, itm_id=None): module.debug = False # hue_bridge.set_bridge_ip(self.cred["PIN_I_SHUEIP"]) - module.FRAMEWORK.my_ip = self.cred["my_ip"] + module.FRAMEWORK.my_ip = self.cred["my_ip_wlan"] global EVENTSTREAM_TIMEOUT EVENTSTREAM_TIMEOUT = 1 @@ -89,6 +89,28 @@ def tearDown(self): del self.dummy print("\n\nFinished.\n\n") + def helper_light_test_device(self, itm_idx, rtype): + self.dummy.debug_input_value[self.dummy.PIN_I_ITM_IDX] = itm_idx + self.device = hue_item.HueDevice(self.dummy.logger) + self.device.id = itm_idx + self.device.rtype = rtype + ret = self.device.set_on(self.ip, self.key, False) + self.assertTrue(ret) + time.sleep(3) + ret = self.device.set_on(self.ip, self.key, True) + self.assertTrue(ret) + time.sleep(3) + + def helper_light_test_full(self, itm_idx): + self.dummy.debug_input_value[self.dummy.PIN_I_ITM_IDX] = itm_idx + self.dummy.on_init() + self.dummy.on_input_value(self.dummy.PIN_I_ON_OFF, False) + time.sleep(3) + # self.assertFalse(self.dummy.debug_output_value[self.dummy.PIN_O_STATUS_ON_OFF]) + self.dummy.on_input_value(self.dummy.PIN_I_ON_OFF, True) + time.sleep(3) + # self.assertTrue(self.dummy.debug_output_value[self.dummy.PIN_O_STATUS_ON_OFF]) + def test_00_init(self): # 2022-11-16 OK print("\n### ### test_00_init") self.dummy.on_init() @@ -172,34 +194,28 @@ def test_10_eventstream_reconnect(self): # 2022-11-17 OK print("Continuing") self.assertFalse(get_eventstream_is_connected()) - def test_on_off_grouped_light(self): - print("\n### test_on_off_grouped_light") - - rid = self.cred["hue_grouped_light"] - - self.device = hue_item.HueDevice(self.dummy.logger) - self.device.id = rid - self.device.rtype = "grouped_light" - - self.dummy.debug_input_value[self.dummy.PIN_I_ITM_IDX] = rid + def test_on_off_light_w_device(self): + print("\n### test_on_off_light_w_device") + rid = self.cred["hue_light_id"] + self.helper_light_test_device(rid, "light") - ret = self.device.set_on(self.ip, self.key, False) - self.assertTrue(ret) - time.sleep(3) + def test_on_off_light_full(self): + print("\n### test_on_off_light_full") + rid = self.cred["hue_light_id"] + self.helper_light_test_full(rid) - ret = self.device.set_on(self.ip, self.key, True) - self.assertTrue(ret) - time.sleep(3) - - ret = self.device.set_on(self.ip, "0", False) - self.assertFalse(ret) - time.sleep(3) + def test_on_off_grouped_light_w_device(self): + print("\n### test_on_off_gouped_light_w_device") + rid = self.cred["hue_grouped_light"] + self.helper_light_test_device(rid, "grouped_light") - ret = self.device.set_on(self.ip, self.key, True) - self.assertTrue(ret) + def test_on_off_grouped_light_full(self): + print("\n### test_on_off_gouped_light_full") + rid = self.cred["hue_grouped_light"] + self.helper_light_test_full(rid) def test_14_on_off_zone(self): - # on / off not available for zones, so just chek that nothing happens + # on / off not available for zones, so just check that nothing happens print("\n### test_14_on_off_zone") rid = self.cred["hue_zone_id"]