from __future__ import annotations from dataclasses import dataclass from datetime import timedelta import typer from ..api import ProtectApiClient from ..cli import base from ..data import Doorlock app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of doorlock to select for subcommands") @dataclass class DoorlockContext(base.CliContext): devices: dict[str, Doorlock] device: Doorlock | None = None ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app) @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: str | None = ARG_DEVICE_ID) -> None: """ Doorlock device CLI. Returns full list of Doorlocks without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = DoorlockContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.doorlocks, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.doorlocks.get(device_id)) is None: typer.secho("Invalid doorlock ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def camera(ctx: typer.Context, camera_id: str | None = typer.Argument(None)) -> None: """Returns or sets tha paired camera for a doorlock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device if camera_id is None: base.print_unifi_obj(obj.camera, ctx.obj.output_format) else: protect: ProtectApiClient = ctx.obj.protect if (camera_obj := protect.bootstrap.cameras.get(camera_id)) is None: typer.secho("Invalid camera ID") raise typer.Exit(1) base.run(ctx, obj.set_paired_camera(camera_obj)) @app.command() def set_status_light(ctx: typer.Context, enabled: bool) -> None: """Sets status light for the lock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.set_status_light(enabled)) @app.command() def set_auto_close_time( ctx: typer.Context, duration: int = typer.Argument(..., min=0, max=3600), ) -> None: """Sets auto-close time for the lock (in seconds). 0 = disabled.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.set_auto_close_time(timedelta(seconds=duration))) @app.command() def unlock(ctx: typer.Context) -> None: """Unlocks the lock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.open_lock()) @app.command() def lock(ctx: typer.Context) -> None: """Locks the lock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.close_lock()) @app.command() def calibrate(ctx: typer.Context, force: bool = base.OPTION_FORCE) -> None: """ Calibrate the doorlock. Door must be open and lock unlocked. """ base.require_device_id(ctx) obj: Doorlock = ctx.obj.device if force or typer.confirm("Is the door open and unlocked?"): base.run(ctx, obj.calibrate())