[door] add doorcontroller with mock pins for testing

This commit is contained in:
2026-07-31 00:36:11 +02:00
parent d37526e39a
commit 717ce951a6
2 changed files with 72 additions and 10 deletions

View File

@@ -20,6 +20,7 @@ from app.controllers import (
)
from app.services.auth import create_first_user, token_router
from app.services.database import create_db_and_tables, get_db_session
from app.services.door import DoorController, init_controller
from app.services.scanner import BackgroundScanner
from app.services.settings import verify_settings
@@ -37,7 +38,13 @@ async def lifespan(app: FastAPI):
verify_settings()
create_db_and_tables()
create_first_user(db=get_db_session())
init_controller(
DoorController(
lock_pin=os.getenv("LOCK_PIN", 17),
unlock_pin=os.getenv("UNLOCK_PIN", 18),
mock_factory=os.getenv("MOCK_GPIO", True),
),
)
if not os.getenv("DISABLE_CARDS"):
scanner.start()

View File

@@ -1,6 +1,9 @@
import logging
from datetime import date, datetime, timedelta
from time import sleep
from gpiozero import OutputDevice
from gpiozero.pins.mock import MockFactory
from sqlalchemy import exc
from sqlmodel import select
@@ -9,25 +12,77 @@ from app.services.database import Session, add_and_refresh
logger = logging.getLogger(__name__)
doorIsOpen = True
# I think this could also be gpio controlled
# See: https://github.com/technyon/nuki_hub#gpio-lock-control-optional
# TODO: add sensor pin
class DoorController:
def __init__(
self,
lock_pin: str = 17, # connected to 20 on the esp
unlock_pin: str = 18, # connected to 21 on the esp
mock_factory: bool = False,
):
self._is_open: bool = False
self._lock_pin = lock_pin
self._unlock_pin = unlock_pin
if mock_factory:
from gpiozero import Device
Device.pin_factory = MockFactory()
self._lock = OutputDevice(pin=lock_pin, active_high=False)
self._unlock = OutputDevice(pin=unlock_pin, active_high=False)
logger.info(
"DoorController started. lock=%s unlock=%s factory=%s",
lock_pin,
unlock_pin,
type(self._lock.pin_factory).__name__,
)
def open(self):
self._unlock.on()
sleep(0.4)
self._unlock.off()
self._is_open = True
logger.info("Door unlocked!")
def close(self):
self._lock.on()
sleep(0.4)
self._lock.off()
self._is_open = False
logger.info("Door locked!")
def is_open(self):
return self._is_open
_contoller: DoorController | None = None
def init_controller(ctrl: DoorController):
global _contoller
_contoller = ctrl
def get_controller():
if _contoller is None:
raise RuntimeError("DoorController not initialized.")
return _contoller
def openDoor():
global doorIsOpen
doorIsOpen = True
logger.info("Still needs gpio out")
get_controller().open()
def closeDoor():
global doorIsOpen
doorIsOpen = False
logger.info("Still needs gpio out")
get_controller().close()
def isDoorOpen():
return doorIsOpen
return get_controller().is_open()
def decrementOneshot(db: Session, oneshot: OneShotAccess):