Local control library for Shark robot vacuums, for use with Home Assistant
Project description
sharklocal
A Python library for local control of Shark robot vacuums, designed for use with Home Assistant integrations. No cloud connection required.
Supports two transport protocols:
- REST — Onboard HTTP API
- MQTT — Onboard broker on port 1883 using base64-encoded protobuf messages
Requirements
- Python 3.11+
aiohttp— HTTP/S transportaiomqtt— MQTT transportPyYAML— mapping configuration loading
pip install sharklocal
Quickstart
import asyncio
from sharklocal import VacuumClient
async def main():
async with VacuumClient(
"192.168.1.100",
rest_mappings="sharkiq_v1",
mqtt_mappings="sharkiq_v1",
) as vacuum:
status = await vacuum.get_status()
print(status.mode, status.battery_level)
await vacuum.start_cleaning()
asyncio.run(main())
CLI
The library includes a built-in CLI utility for discovering, testing, and controlling your vacuum. Common entry points:
python -m sharklocal <IP_ADDRESS> --probe # identify the correct mapping for your model
python -m sharklocal <IP_ADDRESS> --monitor # stream real-time MQTT status updates
python -m sharklocal <IP_ADDRESS> --cmd dock # send a direct command
See docs/cli.md for the full command reference.
Architecture
sharklocal/
├── client.py # VacuumClient — unified entry point with transport selection
├── rest_client.py # RESTVacuumClient — async HTTPS/HTTP client (aiohttp)
├── mqtt_client.py # MQTTVacuumClient — async MQTT client (aiomqtt)
├── protobuf.py # Pure-Python schema-free protobuf decoder
├── models.py # VacuumStatus, VacuumEvent, DeviceInfo, VacuumMode
├── exceptions.py # Typed exception hierarchy
└── mappings/
├── __init__.py # load_* / list_* utilities
├── base.py # RESTMappingConfig, MQTTMappingConfig dataclasses
├── rest/
│ └── sharkiq_v1.yaml
└── mqtt/
└── sharkiq_v1.yaml
Transport Selection
VacuumClient evaluates which transport to use at action call time:
- REST is tried first if the loaded REST mapping defines the action.
- MQTT is the fallback — used only when REST raises
ConnectError(host unreachable). - If neither transport supports the action,
ActionNotSupportedErroris raised.
All other exceptions (CommandError, DecoderError, etc.) propagate immediately without attempting the fallback.
Mappings
Mappings are YAML files that describe how to communicate with a specific vacuum model over REST or MQTT. Each mapping defines the connection parameters, supported actions, and response interpretation. VacuumClient tries REST first, falls back to MQTT on ConnectError, and raises ActionNotSupportedError if neither transport defines the action.
See docs/mappings.md for the full feature comparison table, transport recommendations, and details on how transport selection works.
Compatibility
Not all vacuum models support both transports, and feature coverage varies by model. See docs/compatibility.md for the list of known models and links to per-model compatibility matrices.
VacuumClient
VacuumClient is the recommended entry point. It wraps both transport clients, handles transport selection automatically, and supports real-time MQTT monitoring alongside polled REST calls.
See docs/vacuum-client.md for the full API reference: constructor options, probe(), via, the actions table, return types, real-time monitoring, and transport introspection.
Direct Transport Clients
Use the transport clients directly when you need full control.
RESTVacuumClient
from sharklocal import RESTVacuumClient, load_rest_mapping
mapping = load_rest_mapping("sharkiq_v1")
client = RESTVacuumClient("192.168.1.100", mapping)
status = await client.call("get_status") # VacuumStatus
events = await client.call("get_events") # list[VacuumEvent]
wifi = await client.call("get_wifi_status") # DeviceInfo
await client.call("start_cleaning") # True
await client.close()
MQTTVacuumClient
from sharklocal import MQTTVacuumClient, load_mqtt_mapping
mapping = load_mqtt_mapping("sharkiq_v1")
client = MQTTVacuumClient("192.168.1.100", mapping)
status = await client.call("get_status") # VacuumStatus
await client.call("start_cleaning") # True
# Monitor with a callback
stop = asyncio.Event()
await client.monitor(lambda s: print(s.mode), stop_event=stop)
Data Models
All transport clients return normalized model objects. See docs/data-models.md for field definitions, VacuumMode values, and notes on derived states like DOCKED and IDLE.
Mapping Configuration
See docs/mapping-configuration.md for annotated YAML examples for both transports, the full field reference, instructions for adding support for a new model, and how to register a custom MQTT decoder.
Exceptions
All exceptions inherit from SharklocalError.
| Exception | When raised |
|---|---|
ConnectError |
Host unreachable or connection refused |
CommandError |
HTTP error response or MQTT timeout waiting for status |
ActionNotSupportedError |
Action not defined in the configured mapping(s) |
MappingNotFoundError |
YAML mapping file not found |
DecoderError |
MQTT payload cannot be decoded |
from sharklocal import SharklocalError, ConnectError, ActionNotSupportedError
try:
status = await vacuum.get_status()
except ConnectError:
# Vacuum is offline
...
except ActionNotSupportedError:
# Mapping doesn't define this action
...
except SharklocalError:
# Catch-all for any library error
...
Testing
Install dev dependencies and run the test suite with coverage:
pip install -e ".[dev]"
python3 -m pytest --cov=sharklocal --cov-report=term-missing
The minimum required coverage is 95%. All PRs must pass before merging. See docs/testcoverage.md for the full testing guide, including how to write new tests.
Known Quirks
- The
status_water_tank_removedevent type is fired for dustbin removal on vacuums, not only water tank removal on mops. Handle accordingly in Home Assistant event translation. - The
/get/robot_idendpoint does not expose a serial number. Use themac_addressfrom/get/wifi_statusas the deviceunique_id. - MQTT
go_homeandstopsend identical payloads in thesharkiq_v1mapping — both issue the protobuf stop-and-return command. - The REST API uses a self-signed TLS certificate. SSL verification is disabled in the
sharkiq_v1mapping (verify_ssl: false). - The REST
chargingfield returns"connected"or"unconnected"as strings, not a boolean. The library normalises this toTrue/FalseonVacuumStatus.charging. - The REST
modefield alone is insufficient to determine if a vacuum is docked.mode: "ready"withcharging: "connected"means docked (VacuumMode.DOCKED);mode: "ready"withcharging: "unconnected"means the vacuum is stopped but off the dock (VacuumMode.IDLE). This combined evaluation is handled automatically by the library. mode: "exploring"means the vacuum is performing a mapping run, not cleaning. It maps toVacuumMode.EXPLORING, notVacuumMode.CLEANING.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters