Esempio n. 1
0
def run_xset(content: str) -> None:
    """
    Processes a command
    """
    try:
        payload: t.Dict[str, t.Any] = json.loads(content)
    except ValueError:
        log.error("Received malformed JSON '%s'", content)
    else:
        command = payload.get("command", None)

        if command:
            display = payload.get("display", CONFIG[CONF_KEY_DEFAULT_DISPLAY])
            display = f"-display {display}" if display else ""
            # strip out commands that follow for security
            command = command.split(";")[0].split("&")[0].split("|")[0].strip()
            command = f"xset {display} {command}"
            log.debug("Running command '%s'", command)
            publish_queue.put_nowait(
                PublishMessage(path="xset/command", content=command))
            result = subprocess.run(command.split(),
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT)
            log.trace("Command '%s' result: %s", command, result.stdout)
            publish_queue.put_nowait(
                PublishMessage(path="xset/stdout", content=result.stdout))
            publish_queue.put_nowait(
                PublishMessage(path="xset/stderr", content=result.stderr))
        else:
            log.error("No command provided in message '%s'", content)
Esempio n. 2
0
 def begin(self) -> bool:
     """
     Setup the LED array
     **SUBCLASSES MUST SUPER() THIS METHOD**
     """
     self._anim_cancel = threading.Event()
     self._anim_soft_cancel = threading.Event()
     self._anim_queue = queue.Queue()
     self._anim_manager = threading.Thread(
         name=f"manager_{self._name}",
         target=self._anim_queue_manager,
         daemon=False,
     )
     self._anim_manager.start()
     common.publish_queue.put_nowait(
         PublishMessage(path=f"{self.id}/count",
                        content=self._count,
                        mqtt_retained=True))
     common.publish_queue.put_nowait(
         PublishMessage(
             path=f"{self.id}/leds-per-pixel",
             content=self._per_pixel,
             mqtt_retained=True,
         ))
     common.publish_queue.put_nowait(
         PublishMessage(path=f"{self.id}/fps",
                        content=self._fps,
                        mqtt_retained=True))
     return True
Esempio n. 3
0
    def _anim_loop(self, anim_args: t.Dict[str, t.Any]) -> None:
        """
        **DO NOT OVERRIDE THIS METHOD**
        Internal animation thread loop method
        """
        anim: str = anim_args[ANIM_KEY_NAME]
        repeat: int = anim_args[ANIM_KEY_REPEAT]
        remain: int = repeat or 1
        error_count: int = 0
        kwargs = {  # args to pass into anim function
            k: anim_args[k]
            for k in anim_args
            if k not in [ANIM_KEY_NAME, ANIM_KEY_PRIORITY, ANIM_KEY_REPEAT]
        }

        while remain > 0 and not self._anim_cancel.is_set():
            if self._anim_soft_cancel.is_set():
                break
            try:
                common.publish_queue.put_nowait(
                    PublishMessage(path=f"{self.id}/animation", content=anim))
                self.anims[anim].__globals__[
                    "FRAME_MS"] = self._frame_ms  # type: ignore
                self.anims[anim](self, self._anim_cancel, **kwargs)
            except:
                self._log.debug(
                    "An error occurred while running animation '%s' for array '%s'",
                    anim,
                    self._name,
                )
                error_count += 1
                if error_count > 2:
                    self._log.error(
                        "Animation '%s' for '%s' errored 3 times, aborting",
                        anim,
                        self._name,
                    )
                    log_traceback(self._log)
                    break
            else:
                remain -= 1 if repeat else 0

        if self._anim_cancel.is_set() or (anim_args.get(ANIM_KEY_REPEAT, 1) > 1
                                          and self._anim_soft_cancel.is_set()):
            self._log.debug("Cancelled animation '%s' for array '%s'", anim,
                            self._name)
        else:
            self._log.debug("Finished animation '%s' for array '%s'", anim,
                            self._name)
        common.publish_queue.put_nowait(
            PublishMessage(path=f"{self.id}/animation", content=""))
Esempio n. 4
0
 def setup(self) -> bool:
     """
     Sets up the device and makes sure it is available on the bus.
     Returns ``True`` if device is available, ``False`` otherwise.
     Subclasses may override this method but should ``super()`` it.
     """
     if self._bus.fd is None:
         self._log.error(
             "Bus '%s' not initialized for %s '%s'",
             self._bus_path,
             self._device,
             self._name,
         )
     else:
         # check device is on the line
         try:
             self._bus.write_quick(self.address)
             common.publish_queue.put_nowait(
                 PublishMessage(
                     path=f"{self._id}/address",
                     content=f"0x{self._address:02x}",
                     mqtt_retained=True,
                 )
             )
             return True
         except IOError:
             self._log.error(
                 "No ack from %s '%s' at address 0x%02x on I2C bus '%s'",
                 self._device,
                 self._name,
                 self._address,
                 self._bus_path,
             )
     return False
Esempio n. 5
0
def start() -> None:
    """
    Actions to be done in the subprocess before the loop starts
    """
    log.debug("Setting up hardware")

    for pin in [k for k in pins]:
        if not pins[pin].setup():
            del pins[pin]
            del pin_from_path[[
                k for k in pin_from_path if pin_from_path[k] == pin
            ][0]]

    if CONFIG[CONF_KEY_POLL_INT] > 0:
        log.debug("Starting polling timer with interval of %ds",
                  CONFIG[CONF_KEY_POLL_INT])
        global polling_timer
        polling_timer = Timer(CONFIG[CONF_KEY_POLL_INT],
                              polling_timer_callback)
        polling_timer.start()

    common.publish_queue.put_nowait(
        PublishMessage(
            path="gpio/polling-interval",
            content=CONFIG[CONF_KEY_POLL_INT],
            mqtt_retained=True,
        ))
    poll_all()
Esempio n. 6
0
    def publish_state(self) -> None:
        """
        Publishes the current device state
        """
        if self._setup:
            data = self._bus.read(self._address, 8)
            if data is None:
                self._log.error("%s: Failed to publish state, data read error",
                                self.name)
            else:
                self._log.trace("%s: Read raw data 0x%s from device",
                                self.name, data.hex())

                itemp = int.from_bytes(data[:2],
                                       byteorder="little",
                                       signed=False)
                itemp = int(f"0b{itemp >> 4:08b}"[-8:], base=0)
                itemp = int.from_bytes(bytes([itemp]), "big", signed=True)
                ftemp = float(itemp)
                data = int.from_bytes(data, byteorder="little", signed=False)
                ftemp += 0.5000 * float(data & (1 << 3) > 0)
                ftemp += 0.2500 * float(data & (1 << 2) > 0)
                ftemp += 0.1250 * float(data & (1 << 1) > 0)
                ftemp += 0.0625 * float(data & (1 << 0) > 0)

                if self._unit == "F":
                    ftemp = ftemp * 9 / 5 + 32

                self._log.debug("%s: Read temperature %0.3f %s", self.name,
                                ftemp, self._unit)
                common.publish_queue.put_nowait(
                    PublishMessage(path=f"{self.id}/temperature",
                                   content=ftemp))
                common.publish_queue.put_nowait(
                    PublishMessage(path=f"{self.id}/unit", content=self._unit))
        else:
            self._log.error("%s: Failed to publish state, device is not setup",
                            self.name)
Esempio n. 7
0
 def interrupt(self, state: bool) -> None:
     """
     Handles pin change interrupts
     """
     state = state ^ self._invert
     self._log.trace(
         "%s edge trigger fired with state %s logic %s for '%s' on %s",
         "Rising" if state else "Falling",
         TEXT_STATE[state],
         Logic(int(state ^ self._invert)).name,
         self.name,
         self.pin_name,
     )
     common.publish_queue.put_nowait(
         PublishMessage(path=self.path, content=TEXT_STATE[state]))
Esempio n. 8
0
 def setup(self) -> bool:
     """
     Sets up the device and makes sure it is available on the bus.
     Returns ``True`` if device is available, ``False`` otherwise.
     Subclasses may override this method but should ``super()`` it.
     """
     if self.address in self._bus.scan():
         common.publish_queue.put_nowait(
             PublishMessage(path=f"{self.id}/address",
                            content=self.address,
                            mqtt_retained=True))
         return True
     else:
         self._log.error("Device %s '%s' was not found on the bus",
                         self.device, self.name)
     return False
Esempio n. 9
0
 def publish_state(self) -> None:
     """
     Read the state from the pin and publish
     """
     if self._setup:
         state = self.get()
         if state is not None:
             self._log.debug(
                 "Read state %s logic %s from '%s' on %s",
                 TEXT_STATE[state],
                 Logic(int(state ^ self._invert)).name,
                 self.name,
                 self.pin_name,
             )
             common.publish_queue.put_nowait(
                 PublishMessage(path=self.path, content=TEXT_STATE[state]))
Esempio n. 10
0
    def report(self) -> None:
        """Report counter data"""
        end_ns = now().timestamp()
        self._timer = threading.Timer(self._interval, self.report)
        self._timer.start()
        start_ns = end_ns - self._interval

        self._log.trace("start: %s", start_ns)
        self._log.trace("  end: %s", end_ns)
        self._log.trace("self._data: %s", self._data)

        data = [
            event[1] for event in self._data
            if event[0] <= end_ns and event[0] > start_ns
        ]
        self._log.trace("data: %s", data)
        if data:
            self._data_lock.acquire()
            try:
                # keep the last record selected for this report in case it is
                # needed to calculate delta for next event
                self._data = self._data[
                    len([event
                         for event in self._data if event[0] <= end_ns]) - 1:]
            finally:
                self._data_lock.release()

            message = self._functions[self._function](data)
        else:
            if self._function == Function.RAW:
                message = self._functions[Function.RAW](data)
            else:
                message = 0

        self._log.debug(
            "Selected %d events from '%s' on %s using %s gives: %s",
            len(data),
            self.name,
            self.pin_name,
            self._function.name,
            message,
        )
        common.publish_queue.put_nowait(
            PublishMessage(path=self.path, content=message))
Esempio n. 11
0
    def begin(self) -> bool:
        """
        Setup the LED array and hardware
        """
        self._log.info(
            "Setting up '%s' on sACN %s with %d LEDs%s on universe%s%s",
            self._name,
            "multicast"
            if self._address is None else f"unicast to '{self._address}'",
            self._count,
            f" with {self._per_pixel} LEDs per pixel"
            if self._per_pixel > 1 else "",
            f" {self._universes[0]}" if len(self._universes) == 1 else
            f"s {self._universes[0]}-{self._universes[-1]}",
            f" with sync on universe {self._sync}"
            if self._sync is not None else "",
        )

        super().begin()

        global sender
        if not sender:
            try:
                import sacn as libsacn
            except ModuleNotFoundError:
                self._log.error(
                    "MQTTany's LED module requires 'sacn' to be installed, "
                    "please see the wiki for instructions on how to install requirements"
                )
                return False
            else:
                sender = libsacn.sACNsender(
                    source_name="MQTTany",
                    fps=self._fps,
                    sync_universe=self._sync
                    if self._sync is not None else 63999,
                )
                self._log.trace("Loaded sACNsender")

        global sender_started
        if not sender_started:
            sender_started = True
            sender.start()
            self._log.trace("Started sACNsender")

        for universe in self._universes:
            self._log.trace("Activating universe %d for '%s'", universe,
                            self.name)
            sender.activate_output(universe)
            if self._address is None:
                sender[universe].multicast = True
            else:
                sender[universe].destination = self._address

        common.publish_queue.put_nowait(
            PublishMessage(
                path=f"{self.id}/universe",
                content=
                f"{self._universes[0]}{f'-{self._universes[-1]}' if len(self._universes)>1 else ''}",
                mqtt_retained=True,
            ))
        common.publish_queue.put_nowait(
            PublishMessage(
                path=f"{self.id}/mode",
                content="Unicast" if self._address else "Multicast",
                mqtt_retained=True,
            ))
        if self._address is not None:
            common.publish_queue.put_nowait(
                PublishMessage(path=f"{self.id}/address",
                               content=self._address,
                               mqtt_retained=True))
        if self._sync is not None:
            common.publish_queue.put_nowait(
                PublishMessage(path=f"{self.id}/sync",
                               content=self._sync,
                               mqtt_retained=True))

        self._setup = True
        return True
Esempio n. 12
0
    def begin(self) -> bool:
        """Setup the LED array"""
        self._log.info(
            "Setting up '%s' on GPIO%02d with %d %s LEDS %s",
            self._name,
            self._pin,
            self._count,
            self._chip,
            f"with {self._per_pixel} LEDs per pixel"
            if self._per_pixel > 1 else "",
        )

        try:
            import rpi_ws281x
        except ModuleNotFoundError:
            self._log.error(
                "MQTTany's LED module requires 'rpi-ws281x' to be installed, "
                "please see the wiki for instructions on how to install requirements"
            )
            return False

        if self._pin != 10:
            if not os.access("/dev/mem", os.R_OK | os.W_OK,
                             effective_ids=True):
                self._log.error(
                    "No read/write access to '/dev/mem', try running with root privileges"
                )
                return False

        if gpio.board.lock(self._pin, gpio.common.Mode.SOC):
            try:
                self._array = rpi_ws281x.PixelStrip(
                    num=self._count * self._per_pixel,
                    pin=self._pin,
                    freq_hz=self._frequency * 1000,
                    dma=DMA_CHANNEL[self._pin],
                    invert=self._invert,
                    brightness=self._init_brightness,
                    channel=PWM_CHANNEL[self._pin],
                    strip_type=LED_COLOR_ORDERS[self._order] +
                    LED_TYPES[self._chip],
                )
                self._array.begin()
            except:
                self._log.error("An error occured while setting up '%s'",
                                self._name)
                logger.log_traceback(self._log)
                return False
            else:
                super().begin()
                common.publish_queue.put_nowait(
                    PublishMessage(path=f"{self.id}/gpio",
                                   content=self._pin,
                                   mqtt_retained=True))
                common.publish_queue.put_nowait(
                    PublishMessage(path=f"{self.id}/chip",
                                   content=self._chip,
                                   mqtt_retained=True))
                common.publish_queue.put_nowait(
                    PublishMessage(
                        path=f"{self.id}/frequency",
                        content=self._frequency,
                        mqtt_retained=True,
                    ))
                common.publish_queue.put_nowait(
                    PublishMessage(
                        path=f"{self.id}/invert",
                        content=self._invert,
                        mqtt_retained=True,
                    ))
                del self._init_brightness
                del self._chip
                del self._frequency
                del self._invert
                self._setup = True

        return self._setup
Esempio n. 13
0
 def publish_state(self) -> None:
     state = self.state_log
     common.publish_queue.put_nowait(
         PublishMessage(path=self._path, content=TEXT_STATE[state]))