Add mock LED device type for testing without hardware
Virtual device with configurable LED count, RGB/RGBW mode, and simulated send latency. Includes full provider/client implementation, API schema support, and frontend add/settings modal integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -279,5 +279,8 @@ def _register_builtin_providers():
|
||||
from wled_controller.core.devices.ambiled_provider import AmbiLEDDeviceProvider
|
||||
register_provider(AmbiLEDDeviceProvider())
|
||||
|
||||
from wled_controller.core.devices.mock_provider import MockDeviceProvider
|
||||
register_provider(MockDeviceProvider())
|
||||
|
||||
|
||||
_register_builtin_providers()
|
||||
|
||||
73
server/src/wled_controller/core/devices/mock_client.py
Normal file
73
server/src/wled_controller/core/devices/mock_client.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Mock LED client — simulates an LED strip with configurable latency for testing."""
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from wled_controller.core.devices.led_client import DeviceHealth, LEDClient
|
||||
from wled_controller.utils import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class MockClient(LEDClient):
|
||||
"""LED client that simulates an LED strip without real hardware.
|
||||
|
||||
Useful for load testing, development, and CI environments.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
url: str = "",
|
||||
led_count: int = 0,
|
||||
send_latency_ms: int = 0,
|
||||
rgbw: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
self._led_count = led_count
|
||||
self._latency = send_latency_ms / 1000.0 # convert to seconds
|
||||
self._rgbw = rgbw
|
||||
self._connected = False
|
||||
|
||||
async def connect(self) -> bool:
|
||||
self._connected = True
|
||||
logger.info(
|
||||
f"Mock device connected ({self._led_count} LEDs, "
|
||||
f"{'RGBW' if self._rgbw else 'RGB'}, "
|
||||
f"{int(self._latency * 1000)}ms latency)"
|
||||
)
|
||||
return True
|
||||
|
||||
async def close(self) -> None:
|
||||
self._connected = False
|
||||
logger.info("Mock device disconnected")
|
||||
|
||||
@property
|
||||
def is_connected(self) -> bool:
|
||||
return self._connected
|
||||
|
||||
async def send_pixels(
|
||||
self,
|
||||
pixels: Union[List[Tuple[int, int, int]], np.ndarray],
|
||||
brightness: int = 255,
|
||||
) -> bool:
|
||||
if not self._connected:
|
||||
return False
|
||||
if self._latency > 0:
|
||||
await asyncio.sleep(self._latency)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def check_health(
|
||||
cls,
|
||||
url: str,
|
||||
http_client,
|
||||
prev_health: Optional[DeviceHealth] = None,
|
||||
) -> DeviceHealth:
|
||||
return DeviceHealth(
|
||||
online=True,
|
||||
latency_ms=0.0,
|
||||
last_checked=datetime.utcnow(),
|
||||
)
|
||||
43
server/src/wled_controller/core/devices/mock_provider.py
Normal file
43
server/src/wled_controller/core/devices/mock_provider.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""Mock device provider — virtual LED strip for testing."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
from wled_controller.core.devices.led_client import (
|
||||
DeviceHealth,
|
||||
DiscoveredDevice,
|
||||
LEDClient,
|
||||
LEDDeviceProvider,
|
||||
)
|
||||
from wled_controller.core.devices.mock_client import MockClient
|
||||
|
||||
|
||||
class MockDeviceProvider(LEDDeviceProvider):
|
||||
"""Provider for virtual mock LED devices."""
|
||||
|
||||
@property
|
||||
def device_type(self) -> str:
|
||||
return "mock"
|
||||
|
||||
@property
|
||||
def capabilities(self) -> set:
|
||||
return {"manual_led_count", "power_control", "brightness_control"}
|
||||
|
||||
def create_client(self, url: str, **kwargs) -> LEDClient:
|
||||
kwargs.pop("use_ddp", None)
|
||||
return MockClient(url, **kwargs)
|
||||
|
||||
async def check_health(self, url: str, http_client, prev_health=None) -> DeviceHealth:
|
||||
return DeviceHealth(online=True, latency_ms=0.0, last_checked=datetime.utcnow())
|
||||
|
||||
async def validate_device(self, url: str) -> dict:
|
||||
return {}
|
||||
|
||||
async def discover(self, timeout: float = 3.0) -> List[DiscoveredDevice]:
|
||||
return []
|
||||
|
||||
async def get_power(self, url: str, **kwargs) -> bool:
|
||||
return True
|
||||
|
||||
async def set_power(self, url: str, on: bool, **kwargs) -> None:
|
||||
pass
|
||||
Reference in New Issue
Block a user