Python client library for Moogo smart spray system API
Project description
pymoogo
Python client library for the Moogo smart spray system API.
Note: This is a community-created project and is not an official implementation by Moogo. It was developed through reverse engineering of the Android app and is maintained independently. Use at your own risk.
Features
- Async/Await Support - Built with
aiohttpfor non-blocking operations - Session Injection - Support for external aiohttp session management (Home Assistant compatible)
- Automatic Authentication - Token-based auth with automatic reauthentication
- Device Management - Discover, monitor, and control Moogo devices
- Schedule Management - Create and manage spray schedules with custom durations
- Type Hints - Full type annotations for better IDE support
- Error Handling - Comprehensive exception handling with specific error types
Installation
uv pip install pymoogo
For development:
uv pip install pymoogo[dev]
Quick Start
import asyncio
from pymoogo import MoogoClient
async def main():
async with MoogoClient(email="your@email.com", password="your_password") as client:
# Authenticate
await client.authenticate()
# Get devices (returns MoogoDevice objects)
devices = await client.get_devices()
print(f"Found {len(devices)} devices")
# Work with device objects
if devices:
device = devices[0]
# Refresh device status
await device.refresh()
print(f"Device: {device.name}")
print(f"Online: {device.is_online}")
print(f"Running: {device.is_running}")
# Start spray using device object
await device.start_spray()
# Wait a bit
await asyncio.sleep(5)
# Stop spray
await device.stop_spray()
asyncio.run(main())
Usage Examples
Authentication
from pymoogo import MoogoClient
client = MoogoClient(email="your@email.com", password="your_password")
await client.authenticate()
# Check authentication status
if client.is_authenticated:
print("Authenticated successfully!")
Session Injection (Home Assistant Pattern)
The client supports injecting an external aiohttp.ClientSession, which is useful for Home Assistant integrations or applications that manage a shared session pool.
import aiohttp
from pymoogo import MoogoClient
# Create a shared session (e.g., provided by Home Assistant)
session = aiohttp.ClientSession()
# Inject the session into the client
client = MoogoClient(
email="your@email.com",
password="your_password",
session=session # Injected session
)
# Use the client
await client.authenticate()
devices = await client.get_devices() # Returns MoogoDevice objects
# Work with device objects
for device in devices:
await device.refresh()
print(f"{device.name}: {device.is_online}")
# Check if using injected session
print(f"Using injected session: {client.has_injected_session}")
# Close the client (does NOT close the injected session)
await client.close()
# Caller is responsible for closing the injected session
await session.close()
Key points about session injection:
- When a session is injected, the client will NOT close it when
close()is called - The caller is responsible for managing the session lifecycle
- Multiple clients can share the same session
- Use
client.has_injected_sessionto check if a session was injected
Session Persistence
Save and restore authentication sessions to avoid re-authenticating on every restart:
from pymoogo import MoogoClient
import json
# Authenticate and save session
client = MoogoClient(email="...", password="...")
auth_data = await client.authenticate()
# Save to file/database
with open("session.json", "w") as f:
json.dump(auth_data, f)
# Later, restore the session
with open("session.json", "r") as f:
saved_session = json.load(f)
client = MoogoClient()
client.restore_session(saved_session)
if client.is_authenticated:
# Use client without re-authenticating
devices = await client.get_devices()
else:
# Session expired, need to re-authenticate
await client.authenticate()
Session management methods:
await client.authenticate()- Returns session data dictionaryclient.export_session()- Export current session state for storageclient.restore_session(data)- Restore saved sessionclient.is_authenticated- Check if session is valid
Device Discovery and Status
# Get all devices as MoogoDevice objects
devices = await client.get_devices()
for device in devices:
# Refresh device status
await device.refresh()
print(f"Device: {device.name}")
print(f" ID: {device.id}")
print(f" Online: {device.is_online}")
print(f" Running: {device.is_running}")
print(f" Temperature: {device.temperature}°C")
print(f" Humidity: {device.humidity}%")
print(f" Water Level: {device.water_level}")
print(f" Liquid Level: {device.liquid_level}")
print(f" Firmware: {device.firmware}")
Spray Control
# Get device object
devices = await client.get_devices()
device = devices[0]
# Start spray using device object
await device.start_spray()
# Stop spray
await device.stop_spray()
# Start spray with duration (uses temporary schedule)
await device.start_spray_with_duration(duration=60, cleanup=True)
Schedule Management
# Get device object
devices = await client.get_devices()
device = devices[0]
# Get existing schedules
schedules = await device.get_schedules()
for schedule in schedules:
print(f"Schedule: {schedule.time_str} for {schedule.duration}s")
print(f" Enabled: {schedule.is_enabled}")
print(f" Repeat: {schedule.repeat_set}")
# Create a new schedule
# Spray every day at 8:00 AM for 60 seconds
await device.create_schedule(
hour=8,
minute=0,
duration=60,
repeat_set="0,1,2,3,4,5,6", # All days
enabled=True
)
# Update a schedule
await device.update_schedule(
schedule_id="schedule_id",
duration=120, # Change to 120 seconds
enabled=False # Disable schedule
)
# Enable/disable specific schedule
await device.enable_schedule("schedule_id")
await device.disable_schedule("schedule_id")
# Bulk operations
await device.enable_all_schedules()
await device.disable_all_schedules()
# Delete a schedule
await device.delete_schedule("schedule_id")
Public Endpoints (No Authentication Required)
# Get available liquid types
liquid_types = await client.get_liquid_types()
# Get recommended schedules
schedules = await client.get_recommended_schedules()
Data Models
DeviceStatus
@dataclass
class DeviceStatus:
device_id: str
device_name: str
online_status: int # 0=offline, 1=online
run_status: int # 0=stopped, 1=running
rssi: int
temperature: float
humidity: int
liquid_level: int
water_level: int
mix_ratio: int
firmware: str
latest_spraying_duration: Optional[int]
latest_spraying_end: Optional[int]
@property
def is_online(self) -> bool
@property
def is_running(self) -> bool
Schedule
@dataclass
class Schedule:
id: str
hour: int
minute: int
duration: int
repeat_set: str
status: int # 1=enabled, 0=disabled
@property
def is_enabled(self) -> bool
@property
def time_str(self) -> str
Exception Handling
from pymoogo import (
MoogoClient,
MoogoAPIError,
MoogoAuthError,
MoogoDeviceError,
MoogoRateLimitError,
)
# Get device object
devices = await client.get_devices()
device = devices[0]
try:
await device.start_spray()
except MoogoAuthError:
print("Authentication failed")
except MoogoDeviceError:
print("Device is offline or operation failed")
except MoogoRateLimitError:
print("Rate limited - wait 24 hours")
except MoogoAPIError as e:
print(f"API error: {e}")
Error Codes
0- Success10000- Rate limited (24-hour lockout, do not retry)10104- Invalid credentials10201- Device offline401- Unauthorized500- Server error
Development
# Clone repository
git clone https://github.com/joyfulhouse/pymoogo.git
cd pymoogo
# Install in development mode
uv sync --all-extras
# Run tests
uv run pytest
# Type checking
uv run mypy src/pymoogo
# Linting
uv run ruff check .
API Documentation
See openapi.yaml for complete API documentation.
Publishing
For information on publishing this package to PyPI, see:
- docs/PUBLISHING.md - Complete publishing guide
- docs/NEXT_STEPS.md - Quick setup checklist
License
MIT License
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
Disclaimer
This is an unofficial API client reverse-engineered from the Moogo Android app. It is not affiliated with or endorsed by Moogo.
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
File details
Details for the file pymoogo-0.1.3.tar.gz.
File metadata
- Download URL: pymoogo-0.1.3.tar.gz
- Upload date:
- Size: 177.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81f95b73cc3ab7dc64e37e40d36bd4a72ea4dd808e419d4327e8ad6c137292af
|
|
| MD5 |
5547728b45a14b3ab067c9609656d54c
|
|
| BLAKE2b-256 |
621977451f892d8db5fd0588cfc521830b7045fa20f91fe2e22262f885d12ef8
|
Provenance
The following attestation bundles were made for pymoogo-0.1.3.tar.gz:
Publisher:
publish.yml on joyfulhouse/pymoogo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymoogo-0.1.3.tar.gz -
Subject digest:
81f95b73cc3ab7dc64e37e40d36bd4a72ea4dd808e419d4327e8ad6c137292af - Sigstore transparency entry: 725305290
- Sigstore integration time:
-
Permalink:
joyfulhouse/pymoogo@719402566367666c3f6f8f0c49796bd9118ea705 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/joyfulhouse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@719402566367666c3f6f8f0c49796bd9118ea705 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pymoogo-0.1.3-py3-none-any.whl.
File metadata
- Download URL: pymoogo-0.1.3-py3-none-any.whl
- Upload date:
- Size: 23.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79858bd6dbf657fbd26930bf373070fe16fe4fdba2cc42e8f1066452c211d506
|
|
| MD5 |
397f57f522a259b45f92fbc1622ea484
|
|
| BLAKE2b-256 |
b159d785a7f5880632773f9a34844be7ccc59ecc73b25b4ec8b68753adefbae9
|
Provenance
The following attestation bundles were made for pymoogo-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on joyfulhouse/pymoogo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymoogo-0.1.3-py3-none-any.whl -
Subject digest:
79858bd6dbf657fbd26930bf373070fe16fe4fdba2cc42e8f1066452c211d506 - Sigstore transparency entry: 725305299
- Sigstore integration time:
-
Permalink:
joyfulhouse/pymoogo@719402566367666c3f6f8f0c49796bd9118ea705 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/joyfulhouse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@719402566367666c3f6f8f0c49796bd9118ea705 -
Trigger Event:
release
-
Statement type: