aioshelly

raw JSON →
13.25.0 verified Fri May 01 auth: no python

Asynchronous library to control Shelly smart home devices (WiFi relays, sensors, etc.). Current version: 13.25.0. Requires Python >=3.11. Released regularly; follows Shelly firmware updates.

pip install aioshelly
error AttributeError: 'NoneType' object has no attribute 'state'
cause Called device.channels[0].state before device.async_setup() was awaited.
fix
Add 'await device.async_setup()' after get_device().
error ModuleNotFoundError: No module named 'aioshelly.device'
cause Installed version <10.0.0 where submodules didn't exist or different package.
fix
Upgrade to latest version: pip install --upgrade aioshelly
error ImportError: cannot import name 'async_get_device' from 'aioshelly'
cause async_get_device was removed in v13; use get_device instead.
fix
Change import to: from aioshelly.factory import get_device
error KeyError: 'ip_address'
cause Options dict missing required key for local device.
fix
Ensure options include 'ip_address' and 'auth' for gen1, or 'auth_key' for gen2+.
breaking In v10, top-level imports (from aioshelly import X) were removed. All classes/functions must be imported from their submodules (e.g., aioshelly.device, aioshelly.coap, aioshelly.factory).
fix Replace 'from aioshelly import ShellyDevice' with 'from aioshelly.device import ShellyDevice' or use factory.
deprecated The async_get_device() function was deprecated in v12 and removed in v13. Use get_device() from aioshelly.factory instead.
fix Replace async_get_device() with get_device() from aioshelly.factory.
gotcha Device initialization must be awaited with device.async_setup() after get_device(). Forgetting this leads to AttributeError on device.channels.
fix Always call await device.async_setup() before accessing device properties.
gotcha CoAP devices (gen1) require the local IP and auth credentials. If auth is wrong, device.update() may fail silently. For gen2/3 devices, use Cloud or local HTTP API with auth key.
fix Verify auth credentials and use correct options dict with 'ip_address' and 'auth' for gen1, or 'auth_key' for gen2+.
breaking In v11, the 'channels' attribute changed from a dict to a list. Iterating over channels now yields indexes instead of channel numbers.
fix Access channels by index: device.channels[0].state instead of device.channels[0].state (or use dict-style if still needed, but prefer indexing).

Basic example: discover and control a Shelly Plug S (gen1) using CoAP.

import asyncio
import aiohttp
from aioshelly.factory import get_device

async def main():
    async with aiohttp.ClientSession() as session:
        # For local CoAP (gen1) device:
        options = {
            'ip_address': '192.168.1.100',
            'auth': {'username': 'admin', 'password': ''}
        }
        device = await get_device(session, 'shellyplug-s-XXXX', options)
        await device.async_setup()
        print('Device name:', device.name)
        print('Is on:', device.channels[0].state)
        await device.channels[0].turn_on()
        await device.async_update()

asyncio.run(main())