Exemplo n.º 1
0
 async def check_overdue_and_dead_threads(self):
     if self.AD.sched.realtime is True and self.AD.thread_duration_warning_threshold != 0:
         for thread_id in self.threads:
             if self.threads[thread_id]["thread"].is_alive() is not True:
                 self.logger.critical("Thread %s has died", thread_id)
                 self.logger.critical("Pinned apps were: %s", await self.get_pinned_apps(thread_id))
                 self.logger.critical("Thread will be restarted")
                 id = thread_id.split("-")[1]
                 await self.add_thread(silent=False, pinthread=False, id=id)
             if await self.get_state("_threading", "admin", "thread.{}".format(thread_id)) != "idle":
                 start = utils.str_to_dt(
                     await self.get_state(
                         "_threading", "admin", "thread.{}".format(thread_id), attribute="time_called",
                     )
                 )
                 dur = (await self.AD.sched.get_now() - start).total_seconds()
                 if (
                     dur >= self.AD.thread_duration_warning_threshold
                     and dur % self.AD.thread_duration_warning_threshold == 0
                 ):
                     self.logger.warning(
                         "Excessive time spent in callback: %s - %s",
                         await self.get_state(
                             "_threading", "admin", "thread.{}".format(thread_id), attribute="callback",
                         ),
                         dur,
                     )
Exemplo n.º 2
0
 async def dump_threads(self):
     self.diag.info("--------------------------------------------------")
     self.diag.info("Threads")
     self.diag.info("--------------------------------------------------")
     current_busy = await self.get_state("_threading", "admin", "sensor.threads_current_busy")
     max_busy = await self.get_state("_threading", "admin", "sensor.threads_max_busy")
     max_busy_time = utils.str_to_dt(await self.get_state("_threading", "admin", "sensor.threads_max_busy_time"))
     last_action_time = await self.get_state("_threading", "admin", "sensor.threads_last_action_time")
     self.diag.info("Currently busy threads: %s", current_busy)
     self.diag.info("Most used threads: %s at %s", max_busy, max_busy_time)
     self.diag.info("Last activity: %s", last_action_time)
     self.diag.info("Total Q Entries: %s", self.total_q_size())
     self.diag.info("--------------------------------------------------")
     for thread in sorted(self.threads, key=self.natural_keys):
         t = await self.get_state("_threading", "admin", "thread.{}".format(thread), attribute="all")
         # print("thread.{}".format(thread), t)
         self.diag.info(
             "%s - qsize: %s | current callback: %s | since %s, | alive: %s, | pinned apps: %s",
             thread,
             t["attributes"]["q"],
             t["state"],
             t["attributes"]["time_called"],
             t["attributes"]["is_alive"],
             await self.get_pinned_apps(thread),
         )
     self.diag.info("--------------------------------------------------")
Exemplo n.º 3
0
 def as_datetime(args, key):
     if key in args:
         if isinstance(args[key], str):
             return utils.str_to_dt(args(key)).replace(microsecond=0)
         elif isinstance(args[key], datetime.datetime):
             return self.AD.tz.localize(
                 args[key]).replace(microsecond=0)
         else:
             raise ValueError(f"Invalid type for {key}")
Exemplo n.º 4
0
    async def update_thread_info(self, thread_id, callback, app, type, uuid):
        self.logger.debug("Update thread info: %s", thread_id)
        if self.AD.log_thread_actions:
            if callback == "idle":
                self.diag.info(
                         "%s done", thread_id)
            else:
                self.diag.info(
                         "%s calling %s callback %s", thread_id, type, callback)

        now = await self.AD.sched.get_now()
        if callback == "idle":
            start = utils.str_to_dt(await self.get_state("_threading", "admin", "thread.{}".format(thread_id), attribute="time_called"))
            if self.AD.sched.realtime is True and (now - start).total_seconds() >= self.AD.thread_duration_warning_threshold:
                self.logger.warning("callback %s has now completed", await self.get_state("_threading", "admin", "thread.{}".format(thread_id)))
            await self.add_to_state("_threading", "admin", "sensor.threads_current_busy", -1)
            await self.add_to_attr("_threading", "admin", "app.{}".format(app), "callbacks", 1)
            await self.add_to_attr("_threading", "admin", "{}_callback.{}".format(type, uuid), "executed", 1)
            await self.add_to_state("_threading", "admin", "sensor.callbacks_total_executed", 1)
            self.current_callbacks_executed += 1
        else:
            await self.add_to_state("_threading", "admin", "sensor.threads_current_busy", 1)
            self.current_callbacks_fired += 1

        current_busy = await self.get_state("_threading", "admin", "sensor.threads_current_busy")
        max_busy = await self.get_state("_threading", "admin", "sensor.threads_max_busy")
        if current_busy > max_busy:
            await self.set_state("_threading", "admin", "sensor.threads_max_busy" , state=current_busy)
            await self.set_state("_threading", "admin", "sensor.threads_max_busy_time", state=utils.dt_to_str((await self.AD.sched.get_now()).replace(microsecond=0), self.AD.tz))

            await self.set_state("_threading", "admin", "sensor.threads_last_action_time", state=utils.dt_to_str((await self.AD.sched.get_now()).replace(microsecond=0), self.AD.tz))

        # Update thread info

        await self.set_state("_threading", "admin", "thread.{}".format(thread_id),
                             q=self.threads[thread_id]["queue"].qsize(),
                             state=callback,
                             time_called=utils.dt_to_str(now.replace(microsecond=0), self.AD.tz),
                             is_alive = self.threads[thread_id]["thread"].is_alive(),
                             pinned_apps=await self.get_pinned_apps(thread_id)
                             )
        await self.set_state("_threading", "admin", "app.{}".format(app), state=callback)
Exemplo n.º 5
0
    async def update_thread_info(self, thread_id, callback, app, type, uuid, silent):
        self.logger.debug("Update thread info: %s", thread_id)
        if silent is True:
            return

        if self.AD.log_thread_actions:
            if callback == "idle":
                self.diag.info("%s done", thread_id)
            else:
                self.diag.info("%s calling %s callback %s", thread_id, type, callback)

        appinfo = self.AD.app_management.get_app_info(app)

        if appinfo is None:  # app possibly terminated
            return

        appentity = "{}.{}".format(appinfo["type"], app)

        now = await self.AD.sched.get_now()
        if callback == "idle":
            start = utils.str_to_dt(
                await self.get_state("_threading", "admin", "thread.{}".format(thread_id), attribute="time_called",)
            )
            if (
                self.AD.sched.realtime is True
                and (now - start).total_seconds() >= self.AD.thread_duration_warning_threshold
            ):
                self.logger.warning(
                    "callback %s has now completed",
                    await self.get_state("_threading", "admin", "thread.{}".format(thread_id)),
                )
            await self.add_to_state("_threading", "admin", "sensor.threads_current_busy", -1)

            await self.add_to_attr("_threading", "admin", appentity, "totalcallbacks", 1)
            await self.add_to_attr("_threading", "admin", appentity, "instancecallbacks", 1)

            await self.add_to_attr(
                "_threading", "admin", "{}_callback.{}".format(type, uuid), "executed", 1,
            )
            await self.add_to_state("_threading", "admin", "sensor.callbacks_total_executed", 1)
            self.current_callbacks_executed += 1
        else:
            await self.add_to_state("_threading", "admin", "sensor.threads_current_busy", 1)
            self.current_callbacks_fired += 1

        current_busy = await self.get_state("_threading", "admin", "sensor.threads_current_busy")
        max_busy = await self.get_state("_threading", "admin", "sensor.threads_max_busy")
        if current_busy > max_busy:
            await self.set_state("_threading", "admin", "sensor.threads_max_busy", state=current_busy)
            await self.set_state(
                "_threading",
                "admin",
                "sensor.threads_max_busy_time",
                state=utils.dt_to_str((await self.AD.sched.get_now()).replace(microsecond=0), self.AD.tz),
            )

            await self.set_state(
                "_threading",
                "admin",
                "sensor.threads_last_action_time",
                state=utils.dt_to_str((await self.AD.sched.get_now()).replace(microsecond=0), self.AD.tz),
            )

        # Update thread info

        if thread_id == "async":
            await self.set_state(
                "_threading",
                "admin",
                "thread.{}".format(thread_id),
                q=0,
                state=callback,
                time_called=utils.dt_to_str(now.replace(microsecond=0), self.AD.tz),
                is_alive=True,
                pinned_apps=[],
            )
        else:
            await self.set_state(
                "_threading",
                "admin",
                "thread.{}".format(thread_id),
                q=self.threads[thread_id]["queue"].qsize(),
                state=callback,
                time_called=utils.dt_to_str(now.replace(microsecond=0), self.AD.tz),
                is_alive=self.threads[thread_id]["thread"].is_alive(),
                pinned_apps=await self.get_pinned_apps(thread_id),
            )
        await self.set_state("_threading", "admin", appentity, state=callback)
Exemplo n.º 6
0
    async def get_history_api(self, **kwargs):

        if "entity_id" in kwargs and kwargs["entity_id"] != "":
            filter_entity_id = "?filter_entity_id={}".format(
                kwargs["entity_id"])
        else:
            filter_entity_id = ""
        start_time = ""
        end_time = ""
        if "days" in kwargs:
            days = kwargs["days"]
            if days - 1 < 0:
                days = 1
        else:
            days = 1
        if "start_time" in kwargs:
            if isinstance(kwargs["start_time"], str):
                start_time = utils.str_to_dt(
                    kwargs["start_time"]).replace(microsecond=0)
            elif isinstance(kwargs["start_time"], datetime.datetime):
                start_time = self.AD.tz.localize(
                    kwargs["start_time"]).replace(microsecond=0)
            else:
                raise ValueError("Invalid type for start time")

        if "end_time" in kwargs:
            if isinstance(kwargs["end_time"], str):
                end_time = utils.str_to_dt(
                    kwargs["end_time"]).replace(microsecond=0)
            elif isinstance(kwargs["end_time"], datetime.datetime):
                end_time = self.AD.tz.localize(
                    kwargs["end_time"]).replace(microsecond=0)
            else:
                raise ValueError("Invalid type for end time")

        # if both are declared, it can't process entity_id
        if start_time != "" and end_time != "":
            filter_entity_id = ""

        # if starttime is not declared and entity_id is declared, and days specified
        elif (filter_entity_id != ""
              and start_time == "") and "days" in kwargs:
            start_time = (await self.AD.sched.get_now()).replace(
                microsecond=0) - datetime.timedelta(days=days)

        # if starttime is declared and entity_id is not declared, and days specified
        elif filter_entity_id == "" and start_time != "" and end_time == "" and "days" in kwargs:
            end_time = start_time + datetime.timedelta(days=days)

        # if endtime is declared and entity_id is not declared, and days specified
        elif filter_entity_id == "" and end_time != "" and start_time == "" and "days" in kwargs:
            start_time = end_time - datetime.timedelta(days=days)

        if start_time != "":
            timestamp = "/{}".format(
                utils.dt_to_str(start_time.replace(microsecond=0), self.AD.tz))

            if filter_entity_id != "":  # if entity_id is specified, end_time cannot be used
                end_time = ""

            if end_time != "":
                end_time = "?end_time={}".format(
                    quote(
                        utils.dt_to_str(end_time.replace(microsecond=0),
                                        self.AD.tz)))

        # if no start_time is specified, other parameters are invalid
        else:
            timestamp = ""
            end_time = ""

        return "{}/api/history/period{}{}{}".format(self.config["ha_url"],
                                                    timestamp,
                                                    filter_entity_id, end_time)
Exemplo n.º 7
0
    async def call_plugin_service(self, namespace, domain, service, data):
        self.logger.debug(
            "call_plugin_service() namespace=%s domain=%s service=%s data=%s",
            namespace, domain, service, data)

        #
        # If data is a string just assume it's an entity_id
        #
        if isinstance(data, str):
            data = {"entity_id": data}

        config = (await self.AD.plugins.get_plugin_object(namespace)).config
        if "token" in config:
            headers = {'Authorization': "Bearer {}".format(config["token"])}
        elif "ha_key" in config:
            headers = {'x-ha-access': config["ha_key"]}
        else:
            headers = {}

        if domain == "database":
            if "entity_id" in data and data["entity_id"] != "":
                filter_entity_id = "?filter_entity_id={}".format(
                    data["entity_id"])
            else:
                filter_entity_id = ""
            start_time = ""
            end_time = ""
            if "days" in data:
                days = data["days"]
                if days - 1 < 0:
                    days = 1
            else:
                days = 1
            if "start_time" in data:
                if isinstance(data["start_time"], str):
                    start_time = utils.str_to_dt(
                        data["start_time"]).replace(microsecond=0)
                elif isinstance(data["start_time"], datetime.datetime):
                    start_time = self.AD.tz.localize(
                        data["start_time"]).replace(microsecond=0)
                else:
                    raise ValueError("Invalid type for start time")

            if "end_time" in data:
                if isinstance(data["end_time"], str):
                    end_time = utils.str_to_dt(
                        data["end_time"]).replace(microsecond=0)
                elif isinstance(data["end_time"], datetime.datetime):
                    end_time = self.AD.tz.localize(
                        data["end_time"]).replace(microsecond=0)
                else:
                    raise ValueError("Invalid type for end time")

            #if both are declared, it can't process entity_id
            if start_time != "" and end_time != "":
                filter_entity_id = ""

            #if starttime is not declared and entity_id is declared, and days specified
            elif (filter_entity_id != ""
                  and start_time == "") and "days" in data:
                start_time = (await self.AD.sched.get_now()).replace(
                    microsecond=0) - datetime.timedelta(days=days)

            #if starttime is declared and entity_id is not declared, and days specified
            elif filter_entity_id == "" and start_time != "" and end_time == "" and "days" in data:
                end_time = start_time + datetime.timedelta(days=days)

            #if endtime is declared and entity_id is not declared, and days specified
            elif filter_entity_id == "" and end_time != "" and start_time == "" and "days" in data:
                start_time = end_time - datetime.timedelta(days=days)

            if start_time != "":
                timestamp = "/{}".format(
                    utils.dt_to_str(start_time.replace(microsecond=0),
                                    self.AD.tz))

                if filter_entity_id != "":  #if entity_id is specified, end_time cannot be used
                    end_time = ""

                if end_time != "":
                    end_time = "?end_time={}".format(
                        quote(
                            utils.dt_to_str(end_time.replace(microsecond=0),
                                            self.AD.tz)))

            # if no start_time is specified, other parameters are invalid
            else:
                timestamp = ""
                end_time = ""

            api_url = "{}/api/history/period{}{}{}".format(
                config["ha_url"], timestamp, filter_entity_id, end_time)

        elif domain == "template":
            api_url = "{}/api/template".format(config["ha_url"])

        else:
            api_url = "{}/api/services/{}/{}".format(config["ha_url"], domain,
                                                     service)

        try:
            if domain == "database":
                r = await self.session.get(api_url,
                                           headers=headers,
                                           verify_ssl=self.cert_verify)
            else:
                r = await self.session.post(api_url,
                                            headers=headers,
                                            json=data,
                                            verify_ssl=self.cert_verify)

            if r.status == 200 or r.status == 201:
                if domain == "template":
                    result = await r.text()
                else:
                    result = await r.json()
            else:
                self.logger.warning(
                    "Error calling Home Assistant service %s/%s/%s", namespace,
                    domain, service)
                txt = await r.text()
                self.logger.warning("Code: %s, error: %s", r.status, txt)
                result = None

            return result
        except (asyncio.TimeoutError, asyncio.CancelledError):
            self.logger.warning("Timeout in call_service(%s/%s/%s, %s)",
                                namespace, domain, service, data)
        except aiohttp.client_exceptions.ServerDisconnectedError:
            self.logger.warning(
                "HASS Disconnected unexpectedly during call_service()")
        except:
            self.logger.warning('-' * 60)
            self.logger.warning(
                "Unexpected error during call_plugin_service()")
            self.logger.warning("Service: %s.%s.%s Arguments: %s", namespace,
                                domain, service, data)
            self.logger.warning('-' * 60)
            self.logger.warning(traceback.format_exc())
            self.logger.warning('-' * 60)
            return None