class pf_openhab(Singleton): def __init__(self): try: if self.loadingdone: pass except: self.loadingdone = True self.logging = Logging() settings_c = Settings() self.openhab_server = settings_c.get_setting("main", "openhab_ip") self.openhab_port = settings_c.get_setting("main", "openhab_port") self.host = settings_c.get_setting("main", "hostname") self.port = settings_c.get_setting("main", "port") self.sitemap_name = settings_c.get_setting("main", "sitemap") self.sitemap_name = "main" if self.sitemap_name == None else self.sitemap_name self.resize_icons = settings_c.get_setting("main", "resize_icons") self.iconh = 64 self.iconw = 64 self.name = "pf_openhab" self.http = urllib3.PoolManager() def get_pages(self, filter): if type(filter) != list: filter = [filter] self.load_sitemap() data = [] n = 0 for i in range(len(self.sitemap["homepage"]["widgets"][0]["widgets"])): key = self.sitemap["homepage"]["widgets"][0]["widgets"][i] found = False for filt in filter: if key["label"][0:len(filt)] == filt and not found: icon_url = "/item/icon/" + key["icon"] + ".png" data.append({ "label": key["label"], "icon": icon_url, "id": n }) found = True n += 1 return data def get_page(self, name): for i in range(len(self.sitemap["homepage"]["widgets"][0]["widgets"])): key = self.sitemap["homepage"]["widgets"][0]["widgets"][i] if key["label"] == name: return key def get_items(self, pagename, subpage="none", subsubpage=0): self.load_sitemap() page = self.get_page(pagename) data = [] n_subp = 1 n = 0 page_type = [page["label"], page["icon"]] for key in page["linkedPage"]["widgets"]: n += 1 item = self.convert_item_data(key) item["id"] = item["name"] + "__" + pagename + "__" + str( subpage) + "__" + str(n) if subpage == "none" or str(subpage) == "0": if item["type"] == "Frame": item["page"] = pagename item["subpage"] = item["label"] data.append(item) elif item["type"] == "Frame" and item["label"] == subpage: page_type[0] = page_type[0] + " / " + key["label"] sub_n = 0 for k in key["widgets"]: sub_n += 1 item = self.convert_item_data(k) item["id"] = item["name"] + "__" + pagename + "__" + str( subpage) + "__" + str(sub_n) if item["type"] == "Frame": item["type"] = "Text" data.append(item) return [data, page_type] def load_sitemap(self): if self.host == None or self.openhab_server == None: #if self.openhab_server == None: data = self.load_error_sitemap(False) else: try: url = "http://" + self.openhab_server + ":" + self.openhab_port + "/rest/sitemaps/" + self.sitemap_name #print(url) content = self.http.request('GET', url) data = content.data.decode("utf-8") except: self.logging.error( "Error connecting to openHAB server, please check settings", location=self.name) data = self.load_error_sitemap(True) data = data.replace("true", "True").replace("false", "False") data = eval(data) self.sitemap = data def load_error_sitemap(self, exception): PATH = os.path.dirname(os.path.abspath(__file__)) errorfile = PATH + "/files/error_sitemap.txt" with open(errorfile, 'r') as f: data = f.read() if exception: data = data.replace("{{HOST_CONFIG_ERROR}}", "No error detected") data = data.replace("{{OPENHAB_CONFIG_ERROR}}", "Error connecting to server, check settings") else: data = data.replace("{{HOST_CONFIG_ERROR}}", "Check settings") data = data.replace("{{OPENHAB_CONFIG_ERROR}}", "Check settings") data = data.replace("{{IP}}", str(self.openhab_server)) data = data.replace("{{PORT}}", str(self.openhab_port)) data = data.replace("{{SITEMAP}}", str(self.sitemap_name)) data = data.replace("{{RESIZE}}", str(self.resize_icons)) data = data.replace("{{HOST_IP}}", str(self.host)) data = data.replace("{{HOST_PORT}}", str(self.port)) return data def get_item(self, item_name): name, pagename, subpage, n = self.split_item_name(item_name) #print(name, pagename, subpage, n) item = self.get_item_data(name, pagename, subpage, n) self.logging.debug("Item from get_item: " + str(item), location=self.name) return item def get_item_data(self, item_name, pagename, subpage, n): #search for the item self.load_sitemap() #print("Name: " + item_name) o = 1 subpage_o = 1 for i in range(len(self.sitemap["homepage"]["widgets"][0]["widgets"])): if self.sitemap["homepage"]["widgets"][0]["widgets"][i][ "label"] == pagename: for key in self.sitemap["homepage"]["widgets"][0]["widgets"][ i]["linkedPage"]["widgets"]: if key["label"].find("[") != -1: comp_label = key["label"][0:key["label"].find("[")] while comp_label[-1:] == " ": comp_label = comp_label[0:-1] else: comp_label = key["label"] try: if key["type"] == "Frame" and ( str(subpage_o) == subpage or comp_label == subpage): #print(subpage) sub_o = 0 for k in key["widgets"]: sub_o += 1 try: if k["item"][ "name"] == item_name and sub_o == n: item = self.convert_item_data(k) item["id"] = item[ "name"] + "__" + pagename + "__" + str( subpage) + "__" + str(n) item["page"] = pagename item["subpage"] = str(subpage) self.logging.debug("Item found, id: " + item["id"], location=self.name) return item except Exception as e: self.logging.warn( "Invalid item found in sitemap 2 %s" % (str(e)), location=self.name) elif "item" in key and key["item"][ "name"] == item_name and o == n: item = self.convert_item_data(key) item["id"] = item[ "name"] + "__" + pagename + "__" + str( subpage) + "__" + str(n) item["page"] = pagename item["subpage"] = str(subpage) self.logging.debug("Item found, id: " + item["id"], location=self.name) return item elif str(subpage) == "0" or subpage == "none": o += 1 elif key["type"] == "Frame": subpage_o += 1 o += 1 except Exception as e: self.logging.warn( "Invalid item found in sitemap 1: %s, %s" % (str(key), str(e)), location=self.name) def convert_item_data(self, key): #print(key) item_data = { "icon": key["icon"], "type": key["type"], "name": key["label"], "state": "" } if "item" in key: item_data.update({ "state": key["item"]["state"], "link": key["item"]["link"], "name": key["item"]["name"] }) if key["item"]["state"] != "" and key["item"]["state"].find( " ") == -1: item_data["icon"] = key["icon"] + "-" + key["item"]["state"] if "transformedState" in key["item"]: transformedState = key["item"]["transformedState"] if len(str(transformedState)) < 15: item_data.update( {"icon": key["icon"] + "-" + transformedState}) if "mappings" in key: item_data.update({"mappings": key["mappings"]}) if "label" in key: item_data.update({"label": key["label"]}) #### correct icon for state try: state = float(item_data["state"]) if state < 0.0: item_data["icon"] = key["icon"] + "-" + str(-1 * state + 100) except: if item_data["state"] == "NULL": item_data["state"] = "OFF" item_data["icon"] = key["icon"] + "-off" elif len(str(item_data["state"])) > 15: item_data["icon"] = key["icon"] #### transform state name if item_data["label"].find("[") != -1: label = item_data["label"][0:item_data["label"].find("[")] while label[-1:] == " ": label = label[0:-1] state = item_data["label"][item_data["label"].find("[") + 1:item_data["label"].find("]")] item_data.update({"state": state, "label": label}) if key["type"] == "Colorpicker": if item_data["state"].lower() != "off": item_data["state"] = self.convert_color_to_rgb( item_data["state"]) item_data["icon"] = key["icon"] + "-on" if len(item_data["state"].split(",")) > 2: if item_data["state"].split(",")[2] == "0" or item_data[ "state"].split(",")[2] == "0.0": item_data.update({ "state": "off", "icon": key["icon"] + "-off" }) elif key["type"] == "Selection": item_state = "" if len(key["mappings"]) == 1: item_data["type"] = "Switch_single" item_state = key["mappings"][0]["label"] for mapp in key["mappings"]: if mapp["command"].lower() == item_data["state"].lower(): ## mappings are like [ displayed state, actual state ] item_state = mapp["label"] elif mapp["label"].lower() == item_data["state"].lower(): item_state = mapp[ 'label'] ##sometimes a state is 0 ==> OFF, which is not mapped correctly with Off or 0 try: ##why is this????? #print(item_state) int(item_state) item_data.update({"item_state": item_data["label"]}) item_data.update({"label": "_"}) except: item_data.update({"state": item_state}) elif key["type"] == "Setpoint": try: step = round(float(key["step"]), 1) if int(step) == step: step = int(step) item_data.update({ "setpoint": [int(key["minValue"]), int(key["maxValue"]), step] }) except: pass elif key["type"] == "Slider": item_data.update({"setpoint": [0, 100, 10]}) ##update the icon to the right url: if self.resize_icons == "1": item_data["icon"] = "/item/icon/" + item_data["icon"] + ".png" else: item_data[ "icon"] = "http://" + self.openhab_server + ":" + self.openhab_port + "/icon/" + item_data[ "icon"] + ".png" return item_data def set_state(self, item_name, state): item, pagename, subpage, n = self.split_item_name(item_name) if state[0:5] == "Color": state = self.convert_color_to_hsv(state[5:]) #cmd = "curl --header \"Content-Type: text/plain\" --request POST --data \"" + state+ "\" " + self.openhab_server + ":" + self.openhab_port + "/rest/items/" + item url = "http://" + self.openhab_server + ":" + self.openhab_port + "/rest/items/" + item headers = {'Content-Type': 'text/plain'} requests.post(url, data=state, headers=headers) self.logging.info("Put state openhab: " + url + " " + str(state), location="openhab") #os.system(cmd) #print(cmd) self.load_sitemap() return self.get_item(item_name) def get_mappings(self, item_name, occurrence=1): ##self.load_sitemap() item = self.get_item(item_name) mappings = [] for mapping in item["mappings"]: mappings.append([mapping["label"], mapping["command"]]) return mappings def convert_color_to_rgb(self, color): if color == "": color = "0.0,0.0,0" color = color.split(",") color = colorsys.hsv_to_rgb( float(color[0]) / 360, float(color[1]) / 100, float(color[2]) / 100) red = hex(int(color[0] * 255))[2:] if len(red) < 2: red = "0" + red blue = hex(int(color[1] * 255))[2:] if len(blue) < 2: blue = "0" + blue green = hex(int(color[2] * 255))[2:] if len(green) < 2: green = "0" + green return "#" + red + blue + green def convert_color_to_hsv(self, color): color = colorsys.rgb_to_hsv( float(int("0x" + color[0:2], 0)) / 255.0, float(int("0x" + color[2:4], 0)) / 255.0, float(int("0x" + color[4:6], 0)) / 255.0) return str(color[0] * 360) + "," + str(color[1] * 100) + "," + str( color[2] * 100) def get_icon(self, name): URL = "http://" + self.openhab_server + ":" + self.openhab_port + "/icon/" + name response = requests.get(URL) with Image.open(BytesIO(response.content)) as image: imgByteArr = BytesIO() cover = Image.Image.resize(image, [self.iconh, self.iconw]) cover.save(imgByteArr, image.format) return imgByteArr.getvalue() def get_chart_data(self, item, period): try: p = int(period[0:len(period) - 1]) except: p = 1 if period[-1:] == "D": dt = datetime.datetime.now() - datetime.timedelta(days=p) elif period[-1:] == "H": p += 1 dt = datetime.datetime.now() - datetime.timedelta(hours=p) elif period[-1:] == "W": dt = datetime.datetime.now() - datetime.timedelta(weeks=p) elif period[-1:] == "M": dt = datetime.datetime.now() - relativedelta(months=p) start = dt.strftime("%Y-%m-%dT%H:%M:%S.000+01:00") self.logging.info("Starting date for data: " + start, location="openhab") start = start.replace("+", "%2B").replace(":", "%3A") name, pagename, subpage, n = self.split_item_name(item) URL = "http://" + self.openhab_server + ":" + self.openhab_port + "/rest/persistence/items/" + name + "?serviceId=rrd4j&starttime=" + start response = requests.get(URL) ## get item info i = self.get_item(item) if i == None: return None else: if i["icon"].lower().find("temp") != -1: typ = "temp" elif i["icon"].lower().find("humi") != -1: typ = "humi" elif i["icon"].lower().find("press") != -1: typ = "pres" elif i["icon"].lower().find("energy") != -1: typ = "watt" else: typ = "value" return {"data": response.content, "type": typ} def split_item_name(self, item_name): a = item_name.split("__") if len(a) > 1: name = a[0] pagename = a[1] subpage = a[2] n = int(a[3]) else: return item_name return name, pagename, subpage, n
class setting_handler(Singleton): def __init__(self): try: if self.loadingdone: pass except: self.loadingdone = True self.name = "setting_handler" self.database = main_database() self.logging = Logging() self.http = urllib3.PoolManager() self.settings = {} #self.settings["main_page"] = ["Front page", ["OpenHAB", "Weather"]] self.settings["screen_timeout"] = [ "Screen timeout (min)", [1, 3, 5, 10, 15, 30, "off"] ] self.settings["album_timeout"] = [ "Album timeout (hr)", [1, 3, 5, 10, 15, 30] ] self.settings["message_timeout"] = [ "Message timeout (s)", [10, 30, 60, 120, 300] ] self.settings["toast_timeout"] = [ "Info timeout (s)", [7, 10, 15, 20, 30] ] self.settings["mouse"] = ["Mouse button", ["on", "off"]] self.settings["items_per_page"] = [ "Number of items per page", [6, 8, 9, 12] ] self.settings["sensors_per_page"] = [ "Number of sensor items per page", [6, 8, 9, 12] ] self.settings["screen"] = ["Screen", ["on", "off"]] self.settings["frame"] = ["Photo / Clock", ["photoframe", "clock"]] self.settings["frame_info"] = [ "Frame Info", ["none", "load", "album", "both"] ] self.settings["frame_td"] = [ "Frame Time/Date", ["none", "clock", "date", "both"] ] self.settings["frame_display_time"] = [ "Photo display time", ["short", "medium", "long", "extra long"] ] self.settings["clock_type"] = ["Clock type", ["digital", "analog"]] self.settings["chart_period"] = [ "Default chart period", ["auto", "4 hours", "12 hours", "1 day", "3 days", "1 week"] ] for key, item in self.settings.items(): value = self.database.data["settings"][key] self.settings[key].append(int(value)) settings_c = Settings() self.enable_screen_control = settings_c.get_setting( "main", "enable_screen_control") try: topics = settings_c.get_setting( "main", "mqtt_control_topic").split(",") self.mqtt = mqtt() for topic in topics: self.mqtt.add_listener(topic, self, "received_mqtt_message") except: self.logging.warn("Mqtt not configured for handling settings", location="settings_handler") if self.enable_screen_control in ["pi", "black"]: pass elif self.enable_screen_control == "url": try: self.screen_control_on_url = settings_c.get_setting( "main", "screen_on_url") self.screen_control_off_url = settings_c.get_setting( "main", "screen_off_url") self.settings["screen_control_on_url"] = [ 0, 0, self.screen_control_on_url ] self.settings["screen_control_off_url"] = [ 0, 0, self.screen_control_off_url ] except: self.logging.error( "Add settings 'screen_on_url' and 'screen_off_url' for external url screen control", location="settings_handler") self.enable_screen_control = "black" elif self.enable_screen_control == "cmd": try: self.screen_control_on_cmd = settings_c.get_setting( "main", "screen_on_cmd") self.screen_control_off_cmd = settings_c.get_setting( "main", "screen_off_cmd") self.settings["screen_control_on_cmd"] = [ 0, 0, self.screen_control_on_cmd ] self.settings["screen_control_off_cmd"] = [ 0, 0, self.screen_control_off_cmd ] except: self.logging.error( "Add settings 'screen_on_cmd' and 'screen_off_cmd' for external command screen control", location="settings_handler") self.enable_screen_control = "black" elif self.enable_screen_control != "off": self.logging.error( "Incorrect screen control enable settings, screen control is off", location="settings_handler") self.enable_screen_control = "off" self.settings["main_enable_clock"] = [ 0, 0, settings_c.get_setting("main", "enable_clock") ] self.settings["main_enable_album"] = [ 0, 0, settings_c.get_setting("main", "enable_album") ] if settings_c.get_setting("main", "enable_album") == "0": self.logging.warn("Album not enabled, setting frame to clock", location="settings_handler") self.__set_setting("frame", "clock") elif settings_c.get_setting("main", "enable_clock") == "0": self.logging.warn( "clock not enabled, setting frame to photoframe", location="settings_handler") self.__set_setting("frame", "photoframe") if settings_c.get_setting( "main", "enable_clock") == "0" and settings_c.get_setting( "main", "enable_album") == "0": self.logging.warn( "Album and clock not enabled, turning off screen setting", location="settings_handler") self.__set_setting( "screen", "off" ) ##in this case only the screensaver determines if the screen is turned on or off self.settings["main_screen_control"] = [ 0, 0, self.enable_screen_control ] def get_name(self): return self.name def setting_request(self, request): if request[0] == "setsetting": setting = request[1] if setting == "screen" and self.get_setting( "main_enable_album") == "0" and self.get_setting( "main_enable_clock") == "0": return [ "Enable album or clock to be able to control this setting" ] if setting == "frame" and ( self.get_setting("main_enable_album") == "0" or self.get_setting("main_enable_clock") == "0"): return [ "Enable album and clock to be able to control this setting" ] cur_value = self.settings[setting][2] if cur_value + 1 < len(self.settings[setting][1]): self.settings[setting][2] = cur_value + 1 else: self.settings[setting][2] = 0 self.save_settings() self.logging.write( self.settings[setting][0] + ": " + str(self.settings[setting][1][self.settings[setting][2]]), level=2) return [ self.settings[setting][0] + ": " + str(self.settings[setting][1][self.settings[setting][2]]) ] elif request[0] == "getsetting": return [str(self.get_setting(request[1]))] def set_setting(self, setting, value): if setting == "screen" and self.get_setting( "main_enable_album") == "0" and self.get_setting( "main_enable_clock") == "0": return ["Enable album or clock to be able to control this setting"] if setting == "frame" and (self.get_setting("main_enable_album") == "0" or self.get_setting("main_enable_clock") == "0"): return [ "Enable album and clock to be able to control this setting" ] self.__set_setting(setting, value) def __set_setting(self, setting, value): values = self.settings[setting][1] self.screen_timeout_start = time.time() for i in range(len(values)): if values[i] == value: self.settings[setting][2] = i self.save_settings() def get_settings(self): settings = [] for key, item in self.settings.items(): if (key == "clock_type" ) and self.get_setting("main_enable_clock") == "0": pass elif (key == "album_timeout" or key == "frame_info" or key == "frame_td" or key == "frame_display_time" ) and self.get_setting("main_enable_album") == "0": pass elif key == "frame" and ( self.get_setting("main_enable_album") == "0" or self.get_setting("main_enable_clock") == "0"): pass elif key == "screen" and self.get_setting( "main_enable_album") == "0" and self.get_setting( "main_enable_clock") == "0": pass elif key == "screen" and self.enable_screen_control == "off": pass elif item[0] != 0: settings.append([key, item[0], item[1][item[2]]]) return settings def save_settings(self): for key, item in self.settings.items(): self.database.data["settings"][key] = item[2] self.database.save_datafile() def get_setting(self, setting): value = self.settings[setting][2] if self.settings[setting][0] != 0: return self.translate_setting(setting, self.settings[setting][1][value]) else: return value def translate_setting(self, setting, value): if setting == "frame_display_time": timing = { "short": 5000, "medium": 13000, "long": 20000, "extra long": 60000 } if value == 0: return "short" return timing[value] elif setting == "chart_period": period = { "auto": "auto", "4 hours": "4H", "12 hours": "12H", "1 day": "1D", "3 days": "3D", "1 week": "1W" } if value == 0: return "auto" return period[value] return value def received_mqtt_message(self, topic, payload): try: payload_setting = payload['setting'] for setting in self.settings: if setting == payload_setting: if "cmd" in topic.lower(): self.set_setting(setting, payload[setting]) self.logging.info( "Set setting via mqtt message: %s, %s" % (setting, payload[setting]), location="settings_handler") elif "state" in topic.lower(): val = self.get_setting(setting) topic = topic[0:topic.rfind("/") + 1] + "status" self.mqtt.publish( topic, str({ 'setting': setting, 'value': val })) if payload_setting == "all": topic = topic[0:topic.rfind("/") + 1] + "status" self.mqtt.publish(topic, str(list(self.settings.keys()))) except Exception as e: raise Exception(e)
class item_handler(): def __init__(self): self.openhab = pf_openhab() self.name = "item_handler" self.database = main_database() self.setting_handler = setting_handler() self.page_handler = page_handler() self.message_handler = message_handler() self.logging = Logging() settings_c = Settings() self.host = settings_c.get_setting("main", "hostname") self.port = settings_c.get_setting("main", "port") self.enable_clock = settings_c.get_setting("main", "enable_clock") self.enable_album = settings_c.get_setting("main", "enable_album") self.openhab_server = settings_c.get_setting("main", "openhab_ip") self.openhab_port = settings_c.get_setting("main", "openhab_port") self.http = urllib3.PoolManager() self.timeout_message_popup = 0 self.saved_chart_periods = {} self.screentrigger = 0 def item_request(self, request): action = request[0] if len(request) > 1: item = request[1] if action != "photoframe" and action != "popup_chart" and action != "icon": item_info = self.openhab.get_item(item) self.logging.debug("Item info for request: " + str(item_info), location=self.name) ##handle a button press per type of item: #print(item) if action == "set": if item_info == None: return "none_none" elif item_info["type"] == "Switch_single": item_info = self.openhab.set_state( item, item_info["mappings"][0]["command"]) elif item_info["type"] == "Switch": if item_info["state"] == "ON": item_info = self.openhab.set_state(item, "OFF") else: item_info = self.openhab.set_state(item, "ON") return self.page_handler.create_item_button(item_info, header=False) elif action == "cmd": command = request[2] item_info = self.openhab.set_state(item, command) if item_info == None: return "reload_widget_popup" elif item_info["subpage"][0:2] in ["a_", "c_", "d_", "s_"]: return "reload_widget_popup" else: return self.page_handler.create_item_button(item_info, header=False) elif action == "popup": if item_info == None: return "none_none" #return self.page_handler.create_popup("error", data = { "error": "Invalid Item" }) self.setting_handler.screen_timeout_start = time.time() if item_info["state"][-1:] == "%": s = item_info["state"][0:-1] elif item_info["state"][-1:] == "C": s = item_info["state"][0:-2] else: s = item_info["state"] if item_info["type"] == "Selection": mappings = self.openhab.get_mappings(item) return self.page_handler.create_popup("selection", data={ "id": item, "mappings": mappings, "n": len(mappings) }) elif item_info["type"] == "Colorpicker": return self.page_handler.create_popup("colorpicker", data=item_info) elif item_info["type"] == "Setpoint": item_info["state"] = s return self.page_handler.create_popup("setpoint", data=item_info) elif item_info["type"] == "Slider": item_info["state"] = s return self.page_handler.create_popup("slider", data=item_info) elif item_info["type"] == "Chart": ##item_info.update( { "chart_period": request[2] } ) return self.page_handler.create_popup("chart", data=item_info) else: return "none_none" elif action == "popup_chart": data = { "id": item, "periods": [["4 Hours", "4h"], ["Day", "D"], ["3 Days", "3D"], ["Week", "W"], ["2 Weeks", "2W"]] } return self.page_handler.create_popup("chart_period", data) elif action == "chart_data": data = self.get_chart_data(item, request[2].upper()) return data elif action == "icon": icon = self.openhab.get_icon(item) return ["jpg", icon] def get_chart_data(self, item, period): default_period = self.setting_handler.get_setting("chart_period") #print(default_period, period) if default_period == "auto" and period.lower() == "default": if item in self.saved_chart_periods: period = self.saved_chart_periods[item] else: period = "D" elif period.lower() == "default": period = default_period self.saved_chart_periods[item] = period data = self.openhab.get_chart_data(item, period) if data == None: self.logging.warn("No Chart data returned for %s" % item, location=self.name) return "None" d = eval(data["data"]) try: p = int(period[0:len(period) - 1]) e = "s" except: p = 1 e = "" formats = { "H": ["%H:%M", "Hour" + e], "D": ["%a %Hh", "Day" + e], "W": ["%d-%m", "Week" + e], "M": ["%d-%m", "Month" + e] } f = formats[period[-1:]] db = { "name": d["name"], "data": [], "labels": [], "fill": False, "grid": True, "color": "red", "xlabel": str(p) + " " + f[1], "ylabel": "Value", "points": 4 } if data["type"] == "temp": db.update({"ylabel": "Temperature (°C)"}) elif data["type"] == "humi": db.update({"ylabel": "Humidity (%)", "color": "blue"}) elif data["type"] == "pres": db.update({"ylabel": "Pressure (mb)", "color": "green"}) elif data["type"] == "watt": db.update({"ylabel": "Power (W)", "fill": True, "color": "yellow"}) for i in range(len(d["data"])): t = str(d["data"][i]["time"]) t = datetime.datetime.fromtimestamp(float(t[0:-3])).strftime(f[0]) val = "%.4f" % (float(d["data"][i]["state"])) if i == 0 or val != db["data"][-1]: db["data"].append(val) db["labels"].append(t) ##do not show point when there are a lot of datapoints if len(db["data"]) > 25: db["points"] = 0 return json.dumps(db)
class widgets_handler: def __init__(self): self.openhab = pf_openhab() self.logging = Logging() self.name = "widget_handler" PATH = os.path.dirname(os.path.abspath(__file__)) self.template_dir = PATH + "/../templates/" self.imported_widget_classes = {} def render_widget(self, page, lowerpage): w_info = self.get_widget_info(lowerpage, main_page=page) data = self.openhab.get_items(page, lowerpage) item_data = self.render_item_data_for_widget(data[0]) try: item_data = self.get_widget_data(w_info, item_data) if "error" in item_data: return render_template("error.html", data=item_data) except Exception as e: self.logging.error("Error creating widget %s" % str(e), location=self.name) er = self.render_widget_error(e, lowerpage) return render_template("error.html", data={"error": str(er)}) item_data["gen_name"] = w_info["name"] item_data["pagename"] = page try: self.logging.debug("Rendering widget: %s" % w_info["template"], location=self.name) return render_template("widgets/%s.html" % w_info["template"].lower(), data=item_data) except Exception as e: self.logging.error("Error creating widget %s" % str(e), location=self.name) er = self.render_widget_error(e, lowerpage) return render_template("error.html", data={"error": str(er)}) def create_mainpage_popup(self, page, subpage, menuwidget=False): if page == "none": ##in case the widget is on the main page data = self.openhab.get_items(subpage, page) else: data = self.openhab.get_items(page, subpage) item_data = self.render_item_data_for_widget(data[0]) if len(item_data) == 0: #return "widget not in sitemap" self.logging.error("Widget is not in sitemap", location=self.name) er = self.render_widget_error("widget not in sitemap", subpage) return render_template("popups/error.html", data={"error": str(er)}) info = self.get_widget_info(subpage, main_page=page) try: item_data = self.get_widget_data(info, item_data) if "error" in item_data: return render_template("popups/error.html", data=item_data) except Exception as e: self.logging.error("Error creating widget %s" % str(e), location=self.name) er = self.render_widget_error(e, subpage) if menuwidget: return render_template("popups/error.html", data={"error": str(er)}), "Error" else: return render_template("popups/error.html", data={"error": str(er)}) item_data["page_name"] = page item_data["widget_name"] = subpage item_data["menuwidget"] = menuwidget if info == None: return render_template( "popups/error.html", data={ "error": "Popup widget template for %s does not exist" % subpage.lower() }) elif info["template"] == "generic_button_page": return info["template"] else: try: self.logging.debug("Rendering popup widget: %s" % info["template"], location=self.name) data = render_template("popup_widgets/" + info["template"] + ".html", data=item_data) if menuwidget: title = self.find_var(data, "Title") if title == None: title = "" return data, title else: return data except Exception as e: self.logging.error("Error creating popup widget %s" % str(e), location=self.name) er = self.render_widget_error(e, subpage) if menuwidget: return render_template("popups/error.html", data={"error": str(er)}), "Error" else: return render_template("popups/error.html", data={"error": str(er)}) def get_widget_data(self, w_info, item_data): if w_info["name"] not in self.imported_widget_classes: try: a = __import__(w_info["name"] + "_widget") self.imported_widget_classes[w_info["name"]] = getattr( a, w_info["name"] + "_widget")() except Exception as e: self.logging.warn("Could not create widget %s" % str(e), location=self.name) self.logging.warn("Could not create widget %s" % str(w_info["name"]), location=self.name) self.logging.warn("Importing normal widget", location=self.name) a = __import__("widget") self.imported_widget_classes[w_info["name"]] = a.widget() cl = self.imported_widget_classes[w_info["name"]] item_data = cl.get_data(item_data) return item_data def check_widget_type(self, name): if name[0:2] == "m_": return "menu_button" if name[0:2] in ["a_"]: return "menu_popup" if name[0:2] in ["b_"]: return "widget_popup" if name[0:2] in ["c_"]: return "widget_subpage" if name[0:2] == "s_": info = self.get_widget_info(name) return "widget_" + str(info["rows"]) else: return name def get_widget_label(self, name): for n in ["b_"]: if name.find(n) != -1: return name[name.find(n) + 2:] if name.find("[") != -1 and name.find("]") != -1: name = name[name.find("[") + 1:name.find("]")] return name for n in ["m_", "a_", "b_", "c_", "d_"]: if name.find("/ " + n) != -1: return name[name.find("/ " + n) + 4:] if name.find(n) != -1: return name[name.find(n) + 2:] i = self.get_widget_info(name) if i == None: return name else: return i["name"] def get_widget_info(self, name, main_page="none"): ## s_ frontpage widget with possible popup widget ## a_ generic button page (with large button on frontpage ## b_ bottom page ## c_ popup widget with button ## d_ popup widget with button ## m_ generic button linking to a page instead of popup widget_data = {} widget_data["a_"] = { "type": "popup_widget", "name": name[2:], "template": "generic_button_page" } if name[0:2] in widget_data: return widget_data[name[0:2]] else: ##try to find the info from the widget template data = "_" name_lower = name[2:].lower() if name[0:2] == "s_": ##widget f_name = self.template_dir + "widgets/" + name_lower + ".html" if Path(f_name).is_file(): with open(f_name, 'r') as myfile: data = myfile.read().replace('\n', '') n = self.find_var(data, "Name") rows = self.find_var(data, "Rows") title = self.find_var(data, "Title") if rows != None and n != None: return { "title": title, "name": n, "rows": int(rows), "template": name_lower, "type": "frontpage_widget", "pagename": main_page } else: ##popup f_name = self.template_dir + "popup_widgets/" + name_lower + ".html" if Path(f_name).is_file(): with open(f_name, 'r') as myfile: data = myfile.read().replace('\n', '') n = self.find_var(data, "Name") if n != None: return { "name": n, "template": name_lower, "type": "popup_widget", "pagename": main_page } return None def find_var(self, data, var): if data.find(var + "=") != -1: pos = data.find(var + "=") pos2 = data[pos + len(var) + 1:].find("/") return data[pos + len(var) + 1:pos + len(var) + 1 + pos2] else: return None def render_item_data_for_widget(self, data): d = {} for dat in data: dat["state_length"] = len(dat["state"]) d[dat["label"].lower()] = dat return d def render_widget_error(self, e, w): e = str(e) if e.find("attribute") != -1: pos = e.find("attribute") n = e[pos + 10:] return "Please add an item labeled %s to the widget named %s" % ( n, str(w)) else: return "Widget: " + str(w) + " " + str(e)
class state_handler(Singleton): def __init__(self): try: if self.loadingdone: pass except: self.loadingdone = True self.openhab = pf_openhab() self.name = "state_handler" self.database = main_database() self.setting_handler = setting_handler() self.message_handler = message_handler() self.logging = Logging() settings_c = Settings() self.host = settings_c.get_setting("main", "hostname") self.port = settings_c.get_setting("main", "port") self.http = urllib3.PoolManager() self.timeouts = {} self.update_timeout("screensaver", 60) self.update_timeout("message", -1) self.screentrigger = 0 self.screen_state = True try: self.im_size = settings_c.get_setting("album", "image_size") self.album_url = settings_c.get_setting("album", "random_picture_url") except Exception as e: self.logging.error("Invalid album settings: " + str(e), location=self.name) self.album_url = "none" try: self.album_info_url = settings_c.get_setting("album", "picture_info_url") if self.album_info_url == None: raise Exception("No data") except: self.logging.warn("No album info url", location=self.name) self.album_info_url = "none" def state_request(self, request): action = request[0] if len(request) > 1: item = request[1] ##handle a button press per type of item: #print(item) if action == "photoframe": if item == "frame": ans = self.setting_handler.set_setting("frame", request[2]) if ans != None: return ans[0] return "Setting saved" elif item == "screen": ans = self.setting_handler.set_setting("screen", request[2]) if ans != None: return ans[0] return "Setting saved" elif item == "trigger": self.screentrigger = time.time() self.refresh_screensaver() self.logging.info("Triggered frame on externally", location=self.name) return "Screen triggered" else: return "none_none" elif action == "icon": icon = self.openhab.get_icon(item) return ["jpg", icon] elif action == "picture": try: if item == "new" and self.album_url != "none": response = requests.get(self.album_url) with Image.open(BytesIO(response.content)) as image: imgByteArr = BytesIO() cover = Image.Image.resize(image, [int(self.im_size.split("x")[0]), int(self.im_size.split("x")[1])]) cover.save(imgByteArr, image.format) return ["jpg", imgByteArr.getvalue()] elif item == "info" and self.album_info_url != "none" and self.album_info_url != "": content = self.http.request('GET', self.album_info_url) return content.data.decode("utf-8") elif item == "info": return "no album info" except Exception as e: self.logging.error("Error occured in processing picture for album %s" %e, location=self.name) return "error" def state_check(self, request): page = [] main_page = request[0] subpage = "none" if len(request) > 1: subpage = request[1] ## get used settings setting_album_timeout = self.setting_handler.get_setting("album_timeout") ##not used setting_popup_timeout = self.setting_handler.get_setting("message_timeout") setting_screen_user = self.setting_handler.get_setting("screen") ##screen on or off main_setting_screen_control = self.setting_handler.get_setting("main_screen_control") current_screen_state = self.get_screen_state() screensaver_page = self.get_screensaver_page() desired_screen_state = "off" desired_page = "unchanged" ##reset old screen trigger if self.screentrigger != 0 and time.time() > self.screentrigger + 5*60: self.screentrigger = 0 ##calculate desired screen state (on / off) ##switching is done depending on configuration if main_page in ["main", "settings", "messages"]: if self.check_timeout("screensaver") and setting_screen_user == "off": desired_screen_state = "off" else: desired_screen_state = "on" elif main_page in ["photoframe", "clock", "black"]: desired_screen_state = setting_screen_user ## check desired screen state for messages self.logging.debug("Unread messages: %s" %(str(self.message_handler.check_unread())), location=self.name) self.logging.debug("Popup flag: %s" %(str(self.message_handler.get_popup_flag())), location=self.name) if self.message_handler.check_unread() and not self.message_handler.get_popup_flag(): self.message_handler.set_popup_active(True) self.message_handler.set_popup_flag() self.logging.info("responding new message", location=self.name) desired_screen_state = "on" new_message = True ###turn on screen and make popup elif not self.check_timeout("message"): desired_screen_state = "on" ##keep screen on for message new_message = False else: new_message = False ##calculate desired page (main, black, album or clock) ##switching is done depending on configuration if main_page in ["main", "settings", "messages"]: if self.check_timeout("screensaver") and setting_screen_user == "on" and screensaver_page != "main": desired_page = screensaver_page elif main_page == "photoframe" and screensaver_page != "album": desired_page = screensaver_page elif main_page == "clock" and screensaver_page != "clock": desired_page = screensaver_page elif main_page == "black" and setting_screen_user == "on": desired_page = screensaver_page self.logging.debug("Show new messages: %s" %(str(new_message)), location=self.name) self.logging.debug("Screensaver page: %s" %(screensaver_page), location=self.name) self.logging.debug("Desired page: %s, desired_screen_state: %s" %(desired_page, desired_screen_state), location=self.name) self.logging.debug("Message timeout: %s, Subpage timeout: %s" %(self.check_timeout("message"), self.check_timeout("screensaver_subpage")), location=self.name) ## switch screen off is desired state is off if desired_screen_state == "off" and main_setting_screen_control != "off" and current_screen_state: ##=="on" return self.switch_off_screen(main_setting_screen_control) elif desired_screen_state == "off" and current_screen_state: ##=="on" link = self.get_desired_page_link("clock") return [link] elif self.screentrigger != 0: self.toggle_screen(state = True) self.screentrigger = 0 self.refresh_screensaver() self.logging.info("turn on screen for trigger", location=self.name) return ["close_return_main"] ##actions when we want the screen on elif new_message: if not current_screen_state: self.toggle_screen(state = "on") ##no need for a page change self.update_timeout("message", self.setting_handler.get_setting("message_timeout") + 2) ##in seconds return ["new_message"] elif self.message_handler.check_toast(): ##even if the screen is off self.logging.write("responding new toast message", level=2, location="item_handle") return ["new_toast"] elif desired_screen_state == "on" and desired_page != "unchanged" and self.check_timeout("message"): ##except when there is a message popup self.toggle_screen(state = setting_screen_user) link = self.get_desired_page_link(desired_page) return [link] elif self.check_timeout("screensaver_subpage") and self.check_timeout("message") and main_page not in ["photoframe", "clock"]: ##except when there is a message popup self.delete_timeout("screensaver_subpage") return ["close_return_main"] elif self.check_timeout("screensaver_subpage") and self.check_timeout("message"): self.delete_timeout("screensaver_subpage") ##if for some reason it end here; return ["no_actionHERE"] def switch_off_screen(self, main_setting_screen_control): self.toggle_screen(state = "off") if main_setting_screen_control == "black": link = self.get_desired_page_link("black") return [link] else: link = self.get_desired_page_link("main") return [link] def get_screensaver_page(self): enable_clock = self.setting_handler.get_setting("main_enable_clock") enable_album = self.setting_handler.get_setting("main_enable_album") setting_album_clock = self.setting_handler.get_setting("frame") ##photoframe or clock when screen is on if enable_album == "1" and setting_album_clock == "photoframe": return "album" elif enable_clock == "1" and setting_album_clock == "clock": return "clock" else: return "main" def get_desired_page_link(self, page): if page == "album": self.logging.info("http://"+self.host+":"+self.port+"/page/photoframe", location=self.name) return "http://"+self.host+":"+self.port+"/page/photoframe" elif page == "clock": type_clock = self.setting_handler.get_setting("clock_type") self.logging.info("http://"+self.host+":"+self.port+"/page/clock/"+type_clock, location=self.name) return "http://"+self.host+":"+self.port+"/page/clock/"+type_clock elif page == "main": main_setting_screen_control = self.setting_handler.enable_screen_control self.logging.info("External screen control: %s, no action, stay on main page" %main_setting_screen_control, location=self.name) return "http://"+self.host+":"+self.port+"/page/maindiv/screensaver" elif page == "black": self.logging.write("http://"+self.host+":"+self.port+"/black", level=2) return "http://"+self.host+":"+self.port+"/page/black" def update_timeout(self, name, delta): ##delta in seconds self.timeouts[name] = time.time() + delta def check_timeout(self, name): if name in self.timeouts: self.logging.debug(("Timeout %s: "%name).ljust(30) + str(time.ctime(int(self.timeouts[name]))), location=self.name) if self.timeouts[name] < time.time(): return True else: return False else: return False def delete_timeout(self, name): if name in self.timeouts: del self.timeouts[name] else: self.logging.debug("Timeout %s not deleted, not existing" %name, location=self.name) def refresh_screensaver(self): to = self.setting_handler.get_setting("screen_timeout") if to == "off": to = 24*60*300 self.logging.info("refreshing screensaver", location=self.name) self.update_timeout("screensaver", int(to)*60) self.delete_timeout("screensaver_subpage") def refresh_screensaver_subpage(self): self.refresh_screensaver() self.logging.info("refreshing screensaver for popup", location=self.name) self.update_timeout("screensaver_subpage", 25) def toggle_screen(self, state = True): screen_control = self.setting_handler.get_setting("main_screen_control") switch = False if str(self.screen_state) != str(state): self.logging.info("Switching screen state: %s, %s" %(str(self.screen_state), str(state)), location=self.name) else: self.logging.debug("Switching screen state: %s, %s" %(str(self.screen_state), str(state)), location=self.name) if (state == "off" or state == "0" or state == False) and self.get_screen_state() == True: state = False self.screen_state = False switch = True elif (state == "on" or state == "1" or state == True) and self.get_screen_state() == False: state = True self.screen_state = True switch = True if screen_control == "pi": if not state: self.logging.write("Turn off screen", level=2) cmd = "echo 1 > /sys/class/backlight/rpi_backlight/bl_power" os.system(cmd) else: self.logging.write("Turn on screen", level=2) cmd = "echo 0 > /sys/class/backlight/rpi_backlight/bl_power" os.system(cmd) elif screen_control == "url" and switch: if not state: self.logging.info("Turn off screen via url", location=self.name) url = self.setting_handler.get_setting("screen_control_off_url") else: self.logging.info("Turn on screen via url", location=self.name) url = self.setting_handler.get_setting("screen_control_on_url") self.logging.debug("Screensaver url: %s" %url, location=self.name) self.http.request('GET', url) elif screen_control == "cmd" and switch: if not state: self.logging.info("Turn off screen via cmd", location=self.name) cmd = self.setting_handler.get_setting("screen_control_off_cmd") else: self.logging.info("Turn on screen via cmd", location=self.name) cmd = self.setting_handler.get_setting("screen_control_on_cmd") self.logging.debug("Screensaver cmd: %s" %cmd, location=self.name) os.system(cmd) def get_screen_state(self): return self.screen_state