PySwitchbot

repository·main·Indexed 41 days ago

https://github.com/sblibs/pyswitchbot

A Python library for controlling SwitchBot IoT devices via Cloud APIs using OAuth and local BLE connections. Version 2.9.0 supports device discovery and control for SwitchBot Locks (WoLock/Lock-Pro) and Curtains (WoCurtain/Curtain 3), including helpers for OAuth authorization-code flows and encryption key retrieval for locks.

Tokens
1.7K
Snippets
5
Records
5
Agent score
2%

What's inside PySwitchbot

  1. Obtain encryption key for Switchbot Locks

    main

    Switchbot Locks require an encryption key and a Key ID for control. You can manually retrieve these using the provided helper script.

    Usage:

    python3 scripts/get_encryption_key.py MAC USERNAME
    • MAC: The MAC address of the lock.
    • USERNAME: Your SwitchBot account username.
    • The script will prompt for your password.

    Troubleshooting:

    • Authentication failed:
      • If 2FA is enabled, you must temporarily disable it to retrieve the key.
      • If using Apple/Google Sign-in, ensure you have set a password in the SwitchBot app.
      • Ensure the username matches your login identifier (email vs phone number).
    • Status code 190 (SwitchbotApiError): This indicates the account is not the device owner. Keys are only returned to the owning account, not shared/family members.
    $ python3 scripts/get_encryption_key.py MAC USERNAME
    # Key ID: XX
    # Encryption key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2. Implement OAuth account access

    main

    pySwitchbot provides helpers for the SwitchBot authorization-code flow. The application must provide a SwitchBot-issued client_id and a registered redirect_uri.

    Because SwitchBot treats these as public clients (no client secret or PKCE), you must generate and verify an unpredictable, single-use state to protect against request forgery.

    Workflow:

    1. Generate a state using secrets.token_urlsafe(32).
    2. Use build_oauth_authorize_url to create the authorization URL.
    3. On callback, verify that the returned callback_state matches your stored state.
    4. Use exchange_oauth_code to obtain the token mapping.
    5. Use the access_token with fetch_cloud_devices_by_token to retrieve devices.

    Error Handling:

    • SwitchbotAuthenticationError: Raised on HTTP 401 or 403 responses.
    • SwitchbotApiError: Raised on other API failures.
    • SwitchbotAccountConnectionError: Raised on transport or availability failures.
    import secrets
    from switchbot import (
        build_oauth_authorize_url,
        exchange_oauth_code,
        fetch_cloud_devices_by_token,
    )
    
    state = secrets.token_urlsafe(32)
    authorize_url = build_oauth_authorize_url(client_id, redirect_uri, state)
    
    # ... user authorizes and is redirected back ...
    
    # On callback:
    if callback_state != state:
        raise ValueError("OAuth state mismatch")
    
    token = await exchange_oauth_code(
        session,
        client_id,
        redirect_uri,
        authorization_code,
    )
    devices = await fetch_cloud_devices_by_token(session, token["access_token"])
  3. Control a Switchbot Lock (WoLock/Lock-Pro)

    main

    To control a Switchbot Lock, you need its BLE MAC address, the KEY_ID, the ENC_KEY (obtained via the encryption key script), and the specific SwitchbotModel (e.g., SwitchbotModel.LOCK_PRO).

    Use GetSwitchbotDevices().get_locks() to find the device instance, then instantiate lock.SwitchbotLock to call .unlock() or .lock().

    import asyncio
    from switchbot.discovery import GetSwitchbotDevices
    from switchbot.devices import lock
    from switchbot.const import SwitchbotModel
    
    BLE_MAC = "XX:XX:XX:XX:XX:XX"
    KEY_ID = "XX"
    ENC_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    LOCK_MODEL = SwitchbotModel.LOCK_PRO
    
    async def main():
        wolock = await GetSwitchbotDevices().get_locks()
        # To unlock:
        await lock.SwitchbotLock(
            wolock[BLE_MAC].device, KEY_ID, ENC_KEY, model=LOCK_MODEL
        ).unlock()
        # To lock:
        # await lock.SwitchbotLock(wolock[BLE_MAC].device, KEY_ID, ENC_KEY, model=LOCK_MODEL).lock()
    
    asyncio.run(main())
  4. Control a Switchbot Curtain (WoCurtain/Curtain 3)

    main

    You can control curtains by discovering local BLE advertisement data.

    1. Use GetSwitchbotDevices().discover() to scan for nearby Switchbot devices.
    2. Identify your device's BLE address from the returned data.
    3. Extract the device object from the advertisement data.
    4. Instantiate curtain.SwitchbotCurtain using that device object.
    5. Use methods like .set_position(value) (where value is typically 0-100) to control the curtain.
    import asyncio
    from pprint import pprint
    from switchbot import GetSwitchbotDevices
    from switchbot.devices import curtain
    
    async def main():
        # Discover local BLE devices
        advertisement_data = await GetSwitchbotDevices().discover()
    
        # Identify device by BLE address
        ble_address = "9915077C-C6FD-5FF6-27D3-45087898790B"
        ble_device = advertisement_data[ble_address].device
        
        # Construct curtain device
        curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=False)
    
        # Interact with device
        pprint(await curtain_device.get_device_data())
        await curtain_device.set_position(100)
    
    if __name__ == "__main__":
        asyncio.run(main())