87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
"""Native GPIO driver for H7992 switch."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
from python_app.hardware_full.switch_drivers.gpio_uapi import GpioOutputLines
|
|
|
|
|
|
_POSITION_TO_AB = (
|
|
(0, 0),
|
|
(0, 1),
|
|
(1, 0),
|
|
(1, 1),
|
|
)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class H7992Driver:
|
|
"""Drive H7992 using two GPIO lines (A/B)."""
|
|
|
|
name: str
|
|
positions: int = 4
|
|
default_position: int = 0
|
|
gpio_chip: str = "/dev/gpiochip0"
|
|
pin_a: int = 17
|
|
pin_b: int = 27
|
|
_lines: GpioOutputLines | None = field(init=False, default=None, repr=False)
|
|
_is_open: bool = field(init=False, default=False, repr=False)
|
|
_current_position: int = field(init=False, default=0, repr=False)
|
|
|
|
def __post_init__(self) -> None:
|
|
"""Initialize runtime state."""
|
|
self._lines: GpioOutputLines | None = None
|
|
self._is_open = False
|
|
self._current_position = 0
|
|
|
|
def open(self) -> None:
|
|
"""Open GPIO lines and switch to default position."""
|
|
if self._is_open:
|
|
return
|
|
|
|
self._validate()
|
|
self._lines = GpioOutputLines(
|
|
chip=self.gpio_chip,
|
|
offsets=(self.pin_a, self.pin_b),
|
|
consumer=f"radar_{self.name}",
|
|
)
|
|
self._lines.open()
|
|
self._is_open = True
|
|
self.switch_to(self.default_position)
|
|
|
|
def close(self) -> None:
|
|
"""Close GPIO lines."""
|
|
if self._lines is not None:
|
|
self._lines.close()
|
|
self._lines = None
|
|
self._is_open = False
|
|
|
|
def position_count(self) -> int:
|
|
"""Return number of supported positions."""
|
|
return self.positions
|
|
|
|
def switch_to(self, position: int) -> None:
|
|
"""Switch hardware to selected position."""
|
|
if not self._is_open or self._lines is None:
|
|
raise RuntimeError(f"Switch driver is not open for {self.name}")
|
|
if position < 0 or position >= self.positions:
|
|
raise ValueError(f"Position out of range for {self.name}: {position}")
|
|
|
|
self._lines.set_values(_POSITION_TO_AB[position])
|
|
self._current_position = int(position)
|
|
|
|
@property
|
|
def current_position(self) -> int:
|
|
"""Return current position."""
|
|
return self._current_position
|
|
|
|
def _validate(self) -> None:
|
|
"""Validate H7992 driver parameters."""
|
|
if self.positions <= 0 or self.positions > 4:
|
|
raise ValueError(f"H7992 positions must be in range [1,4] for {self.name}")
|
|
if self.default_position < 0 or self.default_position >= self.positions:
|
|
raise ValueError(f"default_position out of range for {self.name}")
|
|
if self.pin_a < 0 or self.pin_b < 0 or self.pin_a == self.pin_b:
|
|
raise ValueError(f"pin_a/pin_b are invalid for {self.name}")
|