|
| 1 | +# CLI Migration to New LightController |
| 2 | + |
| 3 | +This document shows how to migrate CLI commands from the old LightManager to the new LightController. |
| 4 | + |
| 5 | +## Before (Old LightManager) |
| 6 | + |
| 7 | +```python |
| 8 | +# src/busylight/subcommands/on.py |
| 9 | +@on_cli.command(name="on") |
| 10 | +def activate_lights( |
| 11 | + ctx: typer.Context, |
| 12 | + color: Optional[str] = typer.Argument("green", callback=string_to_scaled_color), |
| 13 | +) -> None: |
| 14 | + """Activate lights with a color.""" |
| 15 | + logger.info("Activating lights with color: {}", color) |
| 16 | + |
| 17 | + try: |
| 18 | + ctx.obj.manager.on(color, ctx.obj.lights, timeout=ctx.obj.timeout) |
| 19 | + except (KeyboardInterrupt, TimeoutError): |
| 20 | + ctx.obj.manager.off(ctx.obj.lights) |
| 21 | + except NoLightsFoundError: |
| 22 | + typer.secho("No lights found.", fg="red") |
| 23 | + raise typer.Exit(code=1) |
| 24 | + except Exception as e: |
| 25 | + typer.secho(f"Error activating lights: {e}", fg="red") |
| 26 | + raise typer.Exit(code=1) |
| 27 | +``` |
| 28 | + |
| 29 | +## After (New LightController) |
| 30 | + |
| 31 | +```python |
| 32 | +# src/busylight/subcommands/on.py |
| 33 | +@on_cli.command(name="on") |
| 34 | +def activate_lights( |
| 35 | + ctx: typer.Context, |
| 36 | + color: Optional[str] = typer.Argument("green"), |
| 37 | +) -> None: |
| 38 | + """Activate lights with a color.""" |
| 39 | + logger.info("Activating lights with color: {}", color) |
| 40 | + |
| 41 | + try: |
| 42 | + with ctx.obj.controller as controller: |
| 43 | + # Convert light indices to selection |
| 44 | + if ctx.obj.lights: |
| 45 | + selection = controller.by_index(*ctx.obj.lights) |
| 46 | + else: |
| 47 | + selection = controller.all() |
| 48 | + |
| 49 | + # Turn on lights - much cleaner! |
| 50 | + selection.turn_on(color, timeout=ctx.obj.timeout) |
| 51 | + |
| 52 | + except (KeyboardInterrupt, TimeoutError): |
| 53 | + # Automatic cleanup happens in context manager |
| 54 | + pass |
| 55 | + except NoLightsFoundError: |
| 56 | + typer.secho("No lights found.", fg="red") |
| 57 | + raise typer.Exit(code=1) |
| 58 | + except Exception as e: |
| 59 | + typer.secho(f"Error activating lights: {e}", fg="red") |
| 60 | + raise typer.Exit(code=1) |
| 61 | +``` |
| 62 | + |
| 63 | +## Key Improvements |
| 64 | + |
| 65 | +1. **Cleaner selection**: `controller.by_index(*indices)` instead of complex light ID management |
| 66 | +2. **Fluent interface**: `selection.turn_on(color, timeout=timeout)` is much more readable |
| 67 | +3. **Automatic cleanup**: Context manager handles resource cleanup automatically |
| 68 | +4. **Better error handling**: LightController has better built-in error messages |
| 69 | +5. **Color parsing**: Handled automatically in the controller, no need for callback |
| 70 | + |
| 71 | +## Other Command Examples |
| 72 | + |
| 73 | +### Blink Command |
| 74 | +```python |
| 75 | +# Old way: |
| 76 | +effect = Effects.for_name("blink")(color, count=count) |
| 77 | +ctx.obj.manager.apply_effect(effect, duty_cycle=speed.duty_cycle, light_ids=ctx.obj.lights, timeout=ctx.obj.timeout) |
| 78 | + |
| 79 | +# New way: |
| 80 | +controller.all().blink(color, count=count, speed="slow") |
| 81 | +``` |
| 82 | + |
| 83 | +### Display Command |
| 84 | +```python |
| 85 | +# Old way: |
| 86 | +lights = ctx.obj.manager.selected_lights(ctx.obj.lights) |
| 87 | +for index, light in enumerate(lights): |
| 88 | + typer.secho(f"{index:3d} ", nl=False, fg="red") |
| 89 | + typer.secho(light.name, fg="green") |
| 90 | + |
| 91 | +# New way: |
| 92 | +selection = controller.by_index(*ctx.obj.lights) if ctx.obj.lights else controller.all() |
| 93 | +for index, name in enumerate(selection.names()): |
| 94 | + typer.secho(f"{index:3d} ", nl=False, fg="red") |
| 95 | + typer.secho(name, fg="green") |
| 96 | +``` |
| 97 | + |
| 98 | +## Global Options Update |
| 99 | + |
| 100 | +```python |
| 101 | +# OLD: src/busylight/global_options.py |
| 102 | +@dataclass |
| 103 | +class GlobalOptions: |
| 104 | + timeout: float = None |
| 105 | + dim: float = 0 |
| 106 | + lights: list[int] = field(default_factory=list) |
| 107 | + debug: bool = False |
| 108 | + manager: LightManager = field(default_factory=LightManager) |
| 109 | + |
| 110 | +# NEW: src/busylight/global_options.py |
| 111 | +@dataclass |
| 112 | +class GlobalOptions: |
| 113 | + timeout: float = None |
| 114 | + dim: float = 0 |
| 115 | + lights: list[int] = field(default_factory=list) |
| 116 | + debug: bool = False |
| 117 | + controller: LightController = field(default_factory=LightController) |
| 118 | +``` |
| 119 | + |
| 120 | +## Migration Benefits |
| 121 | + |
| 122 | +1. **Readability**: Code reads like natural language |
| 123 | +2. **Maintainability**: Easier to understand and modify |
| 124 | +3. **Testing**: Much easier to test individual components |
| 125 | +4. **Error handling**: Better built-in error messages and handling |
| 126 | +5. **Resource management**: Automatic cleanup prevents resource leaks |
| 127 | +6. **Flexibility**: Easy to add new selection methods and operations |
0 commit comments