from __future__ import annotations import asyncio import logging from os import scandir import time import typing from .const import GpioPattern _LOGGER = logging.getLogger(__name__) try: import gpiod is_gpiod_v1 = hasattr(gpiod.chip, "OPEN_BY_PATH") except ImportError: gpiod = None if gpiod is None: # No gpiod library def _send_gpio_pattern(chip: str, pattern: list[GpioPattern]) -> None: raise NotImplementedError("GPIO not supported on this platform") elif is_gpiod_v1: # gpiod <= 1.5.4 def _send_gpio_pattern(chip: str, pattern: list[GpioPattern]) -> None: gpiod_chip = gpiod.chip(chip, gpiod.chip.OPEN_BY_PATH) lines = gpiod_chip.get_lines(pattern[0].pins.keys()) config = gpiod.line_request() config.consumer = "universal-silabs-flasher" config.request_type = gpiod.line_request.DIRECTION_OUTPUT try: # Open the pins and set their initial states _LOGGER.debug("Sending GPIO pattern %r", pattern[0]) lines.request(config, [int(v) for v in pattern[0].pins.values()]) time.sleep(pattern[0].delay_after) # Send all subsequent states for p in pattern[1:]: _LOGGER.debug("Sending GPIO pattern %r", p) lines.set_values([int(v) for v in p.pins.values()]) time.sleep(p.delay_after) finally: # Clean up and ensure the GPIO pins are reset to inputs lines.set_direction_input() lines.release() else: # gpiod >= 2.0.2 def _send_gpio_pattern(chip: str, pattern: list[GpioPattern]) -> None: _LOGGER.debug("Sending GPIO pattern %r", pattern[0]) with gpiod.request_lines( path=chip, consumer="universal-silabs-flasher", config={ # Set initial states pin: gpiod.LineSettings( direction=gpiod.line.Direction.OUTPUT, output_value=gpiod.line.Value(state), ) for pin, state in pattern[0].pins.items() }, ) as request: time.sleep(pattern[0].delay_after) try: # Send all subsequent states for p in pattern[1:]: _LOGGER.debug("Sending GPIO pattern %r", p) request.set_values( { pin: gpiod.line.Value(int(state)) for pin, state in p.pins.items() } ) time.sleep(p.delay_after) finally: # Clean up and ensure the GPIO pins are reset to inputs request.reconfigure_lines( { pin: gpiod.LineSettings(direction=gpiod.line.Direction.INPUT) for pin in pattern[0].pins.keys() } ) def _generate_gpio_chips() -> typing.Iterable[str]: for entry in scandir("/dev/"): if is_gpiod_v1: if entry.name.startswith("gpiochip"): yield entry.path else: if gpiod.is_gpiochip_device(entry.path): yield entry.path def _find_gpiochip_by_label(label: str) -> str: for path in _generate_gpio_chips(): try: if is_gpiod_v1: chip = gpiod.chip(path, gpiod.chip.OPEN_BY_PATH) if chip.label == label: return path else: with gpiod.Chip(path) as chip: if chip.get_info().label == label: return path except PermissionError: pass raise RuntimeError("No matching gpiochip device found") async def find_gpiochip_by_label(label: str) -> str: result = await asyncio.get_running_loop().run_in_executor( None, _find_gpiochip_by_label, label ) return result async def send_gpio_pattern(chip: str, pattern: list[GpioPattern]) -> None: await asyncio.get_running_loop().run_in_executor( None, _send_gpio_pattern, chip, pattern )