pylitterbot

repository·main·Indexed 41 days ago

https://github.com/natekspencer/pylitterbot

An unofficial Python API for controlling Whisker automated products, including Litter-Robot 3, 4, and 5, and Feeder-Robot. It provides classes for account authentication, robot control, activity and insight tracking, and a CameraClient for LR5 Pro camera management. Additionally, it includes an MCP (Model Context Protocol) server to enable AI-driven robot control via natural language assistants.

Tokens
10.7K
Snippets
36
Records
50
Agent score
52%

What's inside pylitterbot

  1. Install camera support for Litter-Robot 5 Pro

    main

    WebRTC live streaming requires the optional aiortc dependency. Install it using the camera extra:

    pip install pylitterbot[camera]
    pip install pylitterbot[camera]
  2. Install pylitterbot

    main

    Install the core package using pip:

    pip install pylitterbot

    To include the MCP (Model Context Protocol) server for AI assistant integration, install with the [mcp] extra:

    pip install pylitterbot[mcp]

    Alternatively, you can clone the repository and use uv sync to set up the environment.

  3. Set up the pylitterbot MCP server

    main

    The MCP server allows AI assistants like Claude to control your robots via natural language.

    Running the server:

    pylitterbot-mcp

    OR

    python -m pylitterbot.mcp

    Claude Desktop Configuration: Add the following to your claude_desktop_config.json. Note that credentials are stored in plaintext; ensure your config file has restricted file permissions.

    {
      "mcpServers": {
        "litter-robot": {
          "command": "python",
          "args": ["-m", "pylitterbot.mcp"],
          "env": {
            "LITTER_ROBOT_USERNAME": "your@email.com",
            "LITTER_ROBOT_PASSWORD": "your_password"
          }
        }
      }
    }
  4. Authenticate and list robots using Account

    main

    To interact with your robots, use the Account class. You must provide your Whisker account credentials to connect to the API. It is recommended to use an async context and ensure you call disconnect() to clean up the connection.

    By default, account.connect(load_robots=True) will load all robots associated with your account. You can also filter for specific robot families by passing a list of classes to the robot_types argument in the Account constructor.

    import asyncio
    from pylitterbot import Account, LitterRobot4, LitterRobot5
    
    username = "Your username"
    password = "Your password"
    
    async def main():
        # Filter for specific robot types during initialization
        account = Account(robot_types=[LitterRobot4, LitterRobot5])
    
        try:
            # Connect and load robots
            await account.connect(username=username, password=password, load_robots=True)
    
            print("Robots:")
            for robot in account.robots:
                print(robot)
        finally:
            # Always disconnect
            await account.disconnect()
    
    if __name__ == "__main__":
        asyncio.run(main())
  5. Access signed video playback via VideoPlayback

    main

    To play recorded video clips, you must use the signed details provided by CameraClient.get_video_playback(clip_id). The hls_url provided in a standard VideoClip is unsigned and will result in a 403 error.

    How to use:

    1. Call get_video_playback(clip_id) to get a VideoPlayback object.
    2. Use the hls_url attribute for the playlist URL.
    3. When requesting the .m3u8 playlist and its segments, you must include the cookie_header in your HTTP request.
  6. How LitterRobotSession handles authentication and requests

    main

    The LitterRobotSession implements a transparent authentication layer. When you call a request method like .get() or .post():

    1. It checks if the current ID token is valid using is_token_valid().
    2. If the token is invalid (and skip_auth is not set to True), it automatically calls refresh_tokens() to renew the session.
    3. It retrieves the bearer authorization string via get_bearer_authorization().
    4. It injects the Authorization: Bearer <token> header into the request.
    5. If the server returns a 500 status with a JSON body containing type: "InvalidCommandException", the session raises a pylitterbot.exceptions.InvalidCommandException with the developer message.
  7. Use LitterRobotSession for authenticated API communication

    main

    The LitterRobotSession class is the primary entry point for interacting with the Litter-Robot cloud API. It manages authentication via Cognito, handles token refreshing automatically, and provides helper methods for HTTP requests.

    It can be used as an asynchronous context manager to ensure the underlying aiohttp.ClientSession is closed correctly.

    To use it, you can either initialize it with existing tokens or use the .login(username, password) method to authenticate with credentials.

    from pylitterbot.session import LitterRobotSession
    
    async def main():
        # Using as an async context manager
        async with LitterRobotSession() as session:
            # Login with credentials
            await session.login("your_email@example.com", "your_password")
            
            # Perform requests
            data = await session.get("https://api.litter-robot.com/v2/robots")
            print(data)
    
    import asyncio
    asyncio.run(main())
  8. Control a robot and its settings

    main

    Once you have a robot instance from account.robots, you can perform various actions and modify settings.

    Common Robot Methods:

    • start_cleaning(): Initiates a clean cycle.
    • refresh(): Updates the robot's current state.
    • reset_settings(): Resets the robot to default settings.
    • set_panel_lockout(bool): Enables or disables the control panel.
    • set_night_light(bool): Toggles the night light.
    • set_power_status(bool): Toggles power.
    • set_sleep_mode(bool): Toggles sleep mode.
    • set_wait_time(int): Sets the wait time.
    • set_name(str): Updates the robot's name.
    • get_activity_history(): Retrieves recent activity.
    • get_insight(): Retrieves robot insights.
    # Example: Starting a clean cycle
    await robot.start_cleaning()
  9. Reference: MCP Server Tools

    main

    The MCP server exposes several tools categorized by function:

    Status and Info

    • get_robots
    • get_robot_status
    • get_activity_history
    • get_insight
    • get_pets

    Commands

    • start_cleaning
    • reset_robot
    • give_snack
    • set_power_status

    Settings

    • set_name
    • set_night_light
    • set_night_light_brightness
    • set_night_light_mode
    • set_panel_lockout
    • set_wait_time
    • set_sleep_mode

    Compound Operations

    • fleet_overview: Prioritized status across all robots with alerts.
    • clean_all_ready: Start cleaning on all robots in READY state.
    • sync_settings: Copy one robot's settings to all others of the same model.
    • pet_usage_report: Activity data across all robots with pet info.
    • maintenance_forecast: Estimate when each robot needs emptying.
    • household_digest: Time-bounded summary of all activity.
    • troubleshooting_report: Diagnostic bundle for a robot with errors.
    • robot_comparison: Compare settings and metrics across robots.
  10. Stream live WebRTC video with CameraStream

    main

    The CameraStream class enables WebRTC live streaming. It requires the aiortc package. Use it as an asynchronous context manager to ensure resources are cleaned up.

    Usage Pattern:

    1. Initialize CameraStream with a CameraClient instance.
    2. Register callbacks using on_video_frame(callback), on_audio_frame(callback), or on_connection_state_change(callback).
    3. Use await stream.wait_for_connection() to block until the stream is active.

    Example:

    async with CameraStream(camera_client) as stream:
        stream.on_video_frame(my_video_handler)
        await stream.wait_for_connection()
        # ... frames delivered via callback ...
    async with CameraStream(camera_client) as stream:
        stream.on_video_frame(my_video_handler)
        await stream.wait_for_connection()
        # ... frames delivered via callback ...
  11. Handle Globe Motor faults

    main

    The GlobeMotorFaultStatus enum tracks errors related to the globe motor. Use GlobeMotorFaultStatus.from_raw(raw_string) to convert API string responses into enum members. This method is robust: it handles PascalCase, strips common prefixes like MTR_ or MOTOR_, and ensures the FAULT_ prefix is present.

    from pylitterbot.enums import GlobeMotorFaultStatus
    
    # Converting raw API strings
    status = GlobeMotorFaultStatus.from_raw("MTR_PINCH")  # Returns GlobeMotorFaultStatus.FAULT_PINCH
    status = GlobeMotorFaultStatus.from_raw("overtorque")  # Returns GlobeMotorFaultStatus.FAULT_OVERTORQUE_AMP (via regex/normalization)
    status = GlobeMotorFaultStatus.from_raw("UNKNOWN_ERR") # Returns GlobeMotorFaultStatus.FAULT_UNKNOWN
  12. Manage device sleep and activity schedules with SleepSchedule

    main

    The SleepSchedule class allows you to manage and query the sleep/activity windows for a device. It supports parsing schedules from raw API data (dictionaries or lists), creating schedules from timestamps, and checking if a specific time falls within a sleep window.

    Key capabilities:

    • Parsing: Convert raw JSON-like structures into SleepSchedule objects.
    • Window Detection: Use get_window(dt) to find the start and end datetime of the current sleep period.
    • Status Checking: Use is_active(dt) to determine if the device is currently in a sleep state at a given time.
    • Day-specific configuration: Access specific daily settings via get_day(day).
    from datetime import datetime, timedelta, timezone
    from pylitterbot.sleep_schedule import SleepSchedule, DayOfWeek
    
    # Example: Parsing a schedule from a dictionary
    raw_data = {
        "MONDAY": {"sleepTime": 1320, "wakeTime": 360, "isEnabled": True},
        "TUESDAY": {"sleepTime": 1320, "wakeTime": 360, "isEnabled": True}
    }
    schedule = SleepSchedule.parse(raw_data)
    
    # Check if currently in sleep mode
    if schedule.is_active():
        print("Device is sleeping")
    
    # Get the specific time window for the current sleep period
    window = schedule.get_window()
    if window:
        start, end = window
        print(f"Sleeping from {start} to {end}")