Source code for tikal.low_level.toy_data

"""
Part of both the Low-Level and High-Level API: brand-agnostic data structures for toy device management.

This module defines the shared data classes used throughout the toy control system:
- Exception classes for validation errors (raised if model_name is invalid)
- :class:`ToyData` returned by the connection builder after discovery
- :class:`ToyCommands` describing a toy model's capabilities

Brand-specific model data (e.g., Lovense's model -> command mapping in ``LOVENSE_TOY_NAMES``) lives in the brand
subpackages under ``tikal/low_level/brands/``. The ``BRANDS`` mapping of brand name -> supported model names is built
from the brand registry in ``tikal/low_level/brands/__init__.py``.
"""

from dataclasses import dataclass


[docs] class ValidationError(Exception): """ Exception raised when a model_name is invalid. This exception is raised when attempting to set a model name that is not recognized. Can be raised during toy initialization or when setting a toy's model name. Example: :: try: toy.set_model_name("InvalidModel") except ValidationError as e: print(f"Invalid model: {e}") """ pass
[docs] class InvalidModelError(ValidationError): """ Exception raised when a model_name is invalid. This exception is raised when attempting to set a model name that is not recognized. Can be raised during toy initialization or when setting a toy's model name. Example: :: try: toy.set_model_name("InvalidModel") except InvalidModelError as e: print(f"Invalid model: {e}") """ pass
[docs] class BadModelError(ValidationError): """ Exception raised when a model_name is valid, but its associated commands are not accepted by the toy. This exception can mean two things: 1) A valid, but wrong model_name is being set 2) the commands being incorrect -> the Library does not handle this model correctly. Please contact the library maintainer in this case. Can be raised during toy initialization or when setting a toy's model name. """ pass
[docs] @dataclass class ToyData: """ Base class for toy discovery data. Contains the information needed to identify and connect to a toy device. You shouldn't need to instantiate this class yourself. ``ConnectionBuilder.discover_toys()`` creates instances of this class for you. Properties: name: Read-only human-readable identifier for the toy. For Bluetooth toys, this is the Bluetooth name (e.g., "LVS-B12"). toy_id: Read-only unique identifier for the toy. For Bluetooth toys, this is the Bluetooth address (e.g., "DC:F5:05:A3:6D:1E"). model_name: Model name of the toy (e.g., "Lush"). For Lovense toys, this is empty and must be set manually. brand: Read-only brand name of the toy (e.g., "Lovense"). Example: :: # Created during discovery print(lovense_data.name) # "LVS-Z36D" print(lovense_data.toy_id) # "DC:F5:05:A3:6D:1E" print(lovense_data.model_name) # "" # User selects model lovense_data.model_name = "Nora" """ _name: str _toy_id: str _model_name: str _brand: str def __post_init__(self) -> None: if not isinstance(self._name, str): raise TypeError("name must be str") if not isinstance(self._toy_id, str): raise TypeError("toy_id must be str") if not isinstance(self._model_name, str): raise TypeError("model_name must be str") if not isinstance(self._brand, str): raise TypeError("brand must be str") @property def name(self) -> str: return self._name @property def toy_id(self) -> str: return self._toy_id @property def model_name(self) -> str: return self._model_name @model_name.setter def model_name(self, value: str) -> None: if not isinstance(value, str): raise TypeError("model_name must be str") self._model_name = value @property def brand(self) -> str: return self._brand
[docs] @dataclass class ToyCommands: """ Command configuration for a toy model. Defines the available capabilities for a specific toy model. Provides names for user display and commands for protocol communication. You shouldn't need to instantiate this class, but if you use LOVENSE_TOY_NAMES you will use instances of this class. Attributes: intensity1_name: Display name for the primary capability shown to users (e.g., "Vibration", "Thrust"). intensity1_command: Command string for the primary capability sent to the toy (e.g., "Vibrate", "Thrusting"). intensity2_name: Display name for the secondary capability, or None if the toy has only one capability (e.g., "Rotation", "Air"). intensity2_command: Command string for the secondary capability, or None if the toy has no secondary capability (e.g., "Rotate", "Air:Level"). Example: :: # Check capabilities commands = LOVENSE_TOY_NAMES["Nora"] print(f"{commands.intensity1_name}: {commands.intensity1_command}") if commands.intensity2_name: print(f"{commands.intensity2_name}: {commands.intensity2_command}") """ intensity1_name: str intensity1_command: str intensity2_name: str | None = None intensity2_command: str | None = None