def newfunc(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) elapsed_time = time.time() - start_time ha.log( conf.dash, "INFO", 'function [{}] finished in {} ms'.format(func.__name__, int(elapsed_time * 1000))) return result
def func_wrapper(*args, **kwargs): if not reading_messages: ha.log( conf.logger, "WARNING", "Attempt to call Home Assistant while disconnected: {}".format( func)) return (lambda *args: None) else: return (func(*args, **kwargs))
def cancel_listen_state(self, handle): name = self.name ha.log(conf.logger, "DEBUG", "Canceling listen_state for {}".format(name)) with conf.callbacks_lock: if name in conf.callbacks and handle in conf.callbacks[name]: del conf.callbacks[name][handle] if name in conf.callbacks and conf.callbacks[name] == {}: del conf.callbacks[name]
def run_at_sunset(self, callback, **kwargs): name = self.name ha.log( conf.logger, "DEBUG", "Registering run_at_sunset with kwargs = {} for {}".format( kwargs, name ) ) handle = self._schedule_sun(name, "next_setting", callback, **kwargs) return handle
def process_sun(action): ha.log(conf.logger, "DEBUG", "Process sun: {}, next sunrise: {}, next sunset: {}".format(action, conf.sun["next_rising"], conf.sun["next_setting"])) for name in conf.schedule.keys(): for entry in sorted(conf.schedule[name].keys(), key=lambda uuid: conf.schedule[name][uuid]["timestamp"]): schedule = conf.schedule[name][entry] if schedule["type"] == action and "inactive" in schedule: del schedule["inactive"] c_offset = ha.get_offset(schedule) schedule["timestamp"] = ha.calc_sun(action) + c_offset schedule["offset"] = c_offset
def info_listen_event(self, handle): name = self.name ha.log(conf.logger, "DEBUG", "Calling info_listen_event for {}".format(name)) with conf.callbacks_lock: if name in conf.callbacks and handle in conf.callbacks[name]: callback = conf.callbacks[name][handle] return callback["event"], callback["kwargs"].copy() else: raise ValueError("Invalid handle: {}".format(handle))
def info_timer(self, handle): name = self.name ha.log(conf.logger, "DEBUG", "Calling info_timer for {}".format(name)) if name in conf.schedule and handle in conf.schedule[name]: callback = conf.schedule[name][handle] return (datetime.datetime.fromtimestamp(callback["timestamp"]), callback["interval"], ha.sanitize_timer_kwargs(callback["kwargs"])) else: raise ValueError("Invalid handle: {}".format(handle))
def run_in(self, callback, seconds, **kwargs): name = self.name ha.log(conf.logger, "DEBUG", "Registering run_in in {} seconds for {}".format(seconds, name)) # convert seconds to an int if possible since a common pattern is to # pass this through from the config file which is a string exec_time = ha.get_now_ts() + int(seconds) handle = ha.insert_schedule(name, exec_time, callback, False, None, **kwargs) return handle
def _check_entity(self, entity): if "." not in entity: raise ValueError("{}: Invalid entity ID: {}".format( self.name, entity)) with conf.ha_state_lock: if entity not in conf.ha_state: ha.log( conf.logger, "WARNING", "{}: Entity {} not found in Home Assistant".format( self.name, entity))
def set_app_state(self, entity_id, state): ha.log(conf.logger, "DEBUG", "set_app_state: {}".format(entity_id)) if entity_id is not None and "." in entity_id: with conf.ha_state_lock: if entity_id in conf.ha_state: old_state = conf.ha_state[entity_id] else: old_state = None data = {"entity_id": entity_id, "new_state": state, "old_state": old_state} args = {"event_type": "state_changed", "data": data} conf.appq.put_nowait(args)
def fire_event(self, event, **kwargs): ha.log(conf.logger, "DEBUG", "fire_event: {}, {}".format(event, kwargs)) if conf.ha_key != "": headers = {'x-ha-access': conf.ha_key} else: headers = {} apiurl = "{}/api/events/{}".format(conf.ha_url, event) r = requests.post(apiurl, headers=headers, json=kwargs) r.raise_for_status() return r.json()
def info_listen_state(self, handle): name = self.name ha.log(conf.logger, "DEBUG", "Calling info_listen_state for {}".format(name)) if name in conf.callbacks and handle in conf.callbacks[name]: callback = conf.callbacks[name][handle] return (callback["entity"], callback["kwargs"].get("attribute", None), ha.sanitize_state_kwargs(callback["kwargs"])) else: raise ValueError("Invalid handle: {}".format(handle))
def dump_objects(): ha.log(conf.logger, "INFO", "--------------------------------------------------") ha.log(conf.logger, "INFO", "Objects") ha.log(conf.logger, "INFO", "--------------------------------------------------") for object in conf.objects.keys(): ha.log(conf.logger, "INFO", "{}: {}".format(object, conf.objects[object])) ha.log(conf.logger, "INFO", "--------------------------------------------------")
def set_state(self, entity_id, **kwargs): self._check_entity(entity_id) ha.log(conf.logger, "DEBUG", "set_state: {}, {}".format(entity_id, kwargs)) if conf.ha_key != "": headers = {'x-ha-access': conf.ha_key} else: headers = {} apiurl = "{}/api/states/{}".format(conf.ha_url, entity_id) r = requests.post(apiurl, headers=headers, json=kwargs) r.raise_for_status() return r.json()
def process_state_change(data): entity_id = data['data']['entity_id'] ha.log(conf.logger, "DEBUG", "Entity ID:{}:".format(entity_id)) device, entity = entity_id.split(".") # First update our global state with conf.ha_state_lock: conf.ha_state[entity_id] = data['data']['new_state'] # Process state callbacks with conf.callbacks_lock: for name in conf.callbacks.keys(): for uuid in conf.callbacks[name]: callback = conf.callbacks[name][uuid] if callback["type"] == "state": cdevice = None centity = None if callback["entity"] != None: if "." not in callback["entity"]: cdevice = callback["entity"] centity = None else: cdevice, centity = callback["entity"].split(".") if callback["kwargs"].get("attribute") == None: cattribute = "state" else: cattribute = callback["kwargs"].get("attribute") cold = callback["kwargs"].get("old") cnew = callback["kwargs"].get("new") if cdevice == None: check_and_disapatch(name, callback["function"], entity_id, cattribute, data['data']['new_state'], data['data']['old_state'], cold, cnew, callback["kwargs"]) elif centity == None: if device == cdevice: check_and_disapatch(name, callback["function"], entity_id, cattribute, data['data']['new_state'], data['data']['old_state'], cold, cnew, callback["kwargs"]) elif device == cdevice and entity == centity: check_and_disapatch(name, callback["function"], entity_id, cattribute, data['data']['new_state'], data['data']['old_state'], cold, cnew, callback["kwargs"])
def call_service(self, service, **kwargs): self._check_service(service) d, s = service.split("/") ha.log(conf.logger, "DEBUG", "call_service: {}/{}, {}".format(d, s, kwargs)) if conf.ha_key != "": headers = {'x-ha-access': conf.ha_key} else: headers = {} apiurl = "{}/api/services/{}/{}".format(conf.ha_url, d, s) r = requests.post(apiurl, headers=headers, json=kwargs) r.raise_for_status() return r.json()
def ws_update(jdata): ha.log(conf.dash, "DEBUG", "Sending data to {} dashes: {}".format(len(app['websockets']), jdata)) data = json.dumps(jdata) for ws in app['websockets']: if "dashboard" in app['websockets'][ws]: ha.log(conf.dash, "DEBUG", "Found dashboard type {}".format(app['websockets'][ws]["dashboard"])) ws.send_str(data)
def run_every(self, callback, start, interval, **kwargs): name = self.name now = ha.get_now() if start < now: raise ValueError("start cannot be in the past") ha.log( conf.logger, "DEBUG", "Registering run_every starting {} in {}s intervals for {}".format( start, interval, name ) ) exec_time = start.timestamp() handle = ha.insert_schedule(name, exec_time, callback, True, None, interval=interval, **kwargs) return handle
def get_state(self, entity_id=None, attribute=None): ha.log(conf.logger, "DEBUG", "get_state: {}.{}".format(entity_id, attribute)) device = None entity = None if entity_id is not None and "." in entity_id: if not self.entity_exists(entity_id): return None if entity_id is not None: if "." not in entity_id: if attribute is not None: raise ValueError( "{}: Invalid entity ID: {}".format(self.name, entity)) device = entity_id entity = None else: device, entity = entity_id.split(".") with conf.ha_state_lock: if device is None: return conf.ha_state elif entity is None: devices = {} for entity_id in conf.ha_state.keys(): thisdevice, thisentity = entity_id.split(".") if device == thisdevice: devices[entity_id] = conf.ha_state[entity_id] return devices elif attribute is None: entity_id = "{}.{}".format(device, entity) if entity_id in conf.ha_state: return conf.ha_state[entity_id]["state"] else: return None else: entity_id = "{}.{}".format(device, entity) if attribute == "all": if entity_id in conf.ha_state: return conf.ha_state[entity_id] else: return None else: if attribute in conf.ha_state[entity_id]: return conf.ha_state[entity_id][attribute] elif attribute in conf.ha_state[entity_id]["attributes"]: return conf.ha_state[entity_id]["attributes"][ attribute] else: return None
def run_dash(loop): # noinspection PyBroadException try: set_paths() setup_routes() handler = app.make_handler() f = loop.create_server(handler, "0.0.0.0", int(conf.dash_port)) conf.srv = loop.run_until_complete(f) conf.rss = loop.run_until_complete(update_rss(loop)) except: ha.log(conf.dash, "WARNING", '-' * 60) ha.log(conf.dash, "WARNING", "Unexpected error in dashboard thread") ha.log(conf.dash, "WARNING", '-' * 60) ha.log(conf.dash, "WARNING", traceback.format_exc()) ha.log(conf.dash, "WARNING", '-' * 60)
def get_callback_entries(self): callbacks = {} for name in conf.callbacks.keys(): callbacks[name] = {} ha.log(conf.logger, "INFO", "{}:".format(name)) for uuid_ in conf.callbacks[name]: callbacks[name][uuid_] = {} if "entity" in callbacks[name][uuid_]: callbacks[name][uuid_]["entity"] = conf.callbacks[name][uuid_]["entity"] else: callbacks[name][uuid_]["entity"] = None callbacks[name][uuid_]["type"] = conf.callbacks[name][uuid_]["type"] callbacks[name][uuid_]["kwargs"] = conf.callbacks[name][uuid_]["kwargs"] callbacks[name][uuid_]["function"] = conf.callbacks[name][uuid_]["function"] callbacks[name][uuid_]["name"] = conf.callbacks[name][uuid_]["name"] return(callbacks)
def add_layout(value, layout, occupied, dash, page, includes, css_vars, global_parameters): if value is None: return widgetdimensions = re.compile("^(.+)\\((\d+)x(\d+)\\)$") value = ''.join(value.split()) widgets = value.split(",") column = 1 for wid in widgets: size = widgetdimensions.search(wid) if size: name = size.group(1) xsize = size.group(2) ysize = size.group(3) elif "widget_size" in dash: name = wid xsize = dash["widget_size"][0] ysize = dash["widget_size"][1] else: name = wid xsize = 1 ysize = 1 while "{}x{}".format(column, layout) in occupied: column += 1 if name != "spacer": sanitized_name = name.replace(".", "-").replace("_", "-").lower() widget = {} widget["id"] = "{}-{}".format(page, sanitized_name) if widget_exists(dash["widgets"], widget["id"]): ha.log(conf.dash, "WARNING", "Duplicate widget name '{}' - ignored".format(name)) else: widget["position"] = [column, layout] widget["size"] = [xsize, ysize] widget["parameters"] = load_widget(dash, includes, name, css_vars, global_parameters) dash["widgets"].append(widget) for x in range(column, column + int(xsize)): for y in range(layout, layout + int(ysize)): occupied["{}x{}".format(x, y)] = 1 column += int(xsize)
def get_styles(style_str, name, field): # # Parse styles in order from a string and allow later entries to override earlier ones # result = {} styles = style_str.split(";") for style in styles: if style != "" and style is not None: pieces = style.split(":") if len(pieces) == 2: result[pieces[0].strip()] = pieces[1] else: ha.log( conf.dash, "WARNING", "malformed CSS: {} in widget '{}', field '{}' (could be a problem in the skin) - ignoring" .format(style, name, field)) return result
def set_state(self, entity_id, **kwargs): with conf.ha_state_lock: self._check_entity(entity_id) ha.log( conf.logger, "DEBUG", "set_state: {}, {}".format(entity_id, kwargs) ) if conf.ha_key != "": headers = {'x-ha-access': conf.ha_key} else: headers = {} apiurl = "{}/api/states/{}".format(conf.ha_url, entity_id) if entity_id not in conf.ha_state: # Its a new state entry conf.ha_state[entity_id] = {} conf.ha_state[entity_id]["attributes"] = {} args = {} if "state" in kwargs: args["state"] = kwargs["state"] else: if "state" in conf.ha_state[entity_id]: args["state"] = conf.ha_state[entity_id]["state"] if "attributes" in conf.ha_state[entity_id]: args["attributes"] = conf.ha_state[entity_id]["attributes"] if "attributes" in kwargs: args["attributes"].update(kwargs["attributes"]) else: if "attributes" in kwargs: args["attributes"] = kwargs["attributes"] r = requests.post(apiurl, headers=headers, json=args, verify=conf.certpath) r.raise_for_status() # Update our local copy of state state = r.json() conf.ha_state[entity_id] = state return state
def run_api(loop, tasks): # noinspection PyBroadException try: setup_api() if conf.api_ssl_certificate is not None and conf.api_ssl_key is not None: context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context.load_cert_chain(conf.api_ssl_certificate, conf.api_ssl_key) else: context = None handler = app.make_handler() f = loop.create_server(handler, "0.0.0.0", int(conf.api_port), ssl = context) tasks.append(asyncio.async(f)) except: ha.log(conf.dash, "WARNING", '-' * 60) ha.log(conf.dash, "WARNING", "Unexpected error in api thread") ha.log(conf.dash, "WARNING", '-' * 60) ha.log(conf.dash, "WARNING", traceback.format_exc()) ha.log(conf.dash, "WARNING", '-' * 60)
def logon(request): data = yield from request.post() success = False password = data["password"] if password == conf.dash_password: ha.log(conf.dash, "INFO", "Succesful logon from {}".format(request.host)) hashed = bcrypt.hashpw(str.encode(conf.dash_password), bcrypt.gensalt()) #ha.log(conf.dash, "INFO", hashed) response = yield from list_dash_no_secure(request) response.set_cookie("adcreds", hashed.decode("utf-8")) else: ha.log(conf.dash, "WARNING", "Unsuccesful logon from {}".format(request.host)) response = yield from list_dash(request) return response
def init_object(name, class_name, module_name, args): ha.log( conf.logger, "INFO", "Loading Object {} using class {} from module {}".format( name, class_name, module_name)) module = __import__(module_name) APPclass = getattr(module, class_name) conf.objects[name] = { "object": APPclass(name, conf.logger, conf.error, args, conf.global_vars), "id": uuid.uuid4() } # Call it's initialize function with conf.threads_busy_lock: conf.threads_busy += 1 q.put_nowait({ "type": "initialize", "name": name, "id": conf.objects[name]["id"], "function": conf.objects[name]["object"].initialize })
def wshandler(request): ws = web.WebSocketResponse() yield from ws.prepare(request) request.app['websockets'][ws] = {} # noinspection PyBroadException try: while True: msg = yield from ws.receive() if msg.type == aiohttp.WSMsgType.TEXT: ha.log(conf.dash, "INFO", "New dashboard connected: {}".format(msg.data)) request.app['websockets'][ws]["dashboard"] = msg.data elif msg.type == aiohttp.WSMsgType.ERROR: ha.log(conf.dash, "INFO", "ws connection closed with exception {}".format(ws.exception())) except: ha.log(conf.dash, "INFO", "Dashboard disconnected") finally: request.app['websockets'].pop(ws, None) return ws
def error(self, msg, level="WARNING"): msg = self._sub_stack(msg) ha.log(self._error, level, msg, self.name)
def log(self, msg, level="INFO"): msg = self._sub_stack(msg) ha.log(self._logger, level, msg, self.name)