Skip to content

joyfulhouse/pymoogo

pymoogo

Unofficial, reverse-engineered Python client library for Moogo smart mosquito misting devices.

Disclaimer: This is a community-created project developed through reverse engineering of the Moogo Android app. It is not affiliated with or endorsed by Moogo. Use at your own risk.

PyPI Version Python Versions License CI GitHub Sponsors Ko-fi

What It Does

pymoogo is an async Python library for controlling Moogo smart mosquito misting systems. It provides authenticated access to device discovery, spray control, schedule management, and device status monitoring. It is designed for use in custom automation scripts and as the foundation of the Moogo Smart Mosquito Misting Device Home Assistant integration.

Features

  • Async/await support — built with aiohttp for non-blocking operations
  • Session injection — accepts an external aiohttp.ClientSession (Home Assistant compatible)
  • Automatic authentication — token-based auth with automatic reauthentication
  • Device management — discover, monitor, and control Moogo devices via MoogoDevice objects
  • Schedule management — create, update, enable/disable, and delete spray schedules
  • Session persistence — export and restore auth sessions to avoid re-authenticating on restart
  • Resilience — circuit breaker pattern, exponential backoff, and rate-limit awareness
  • Full type annotations — strict mypy-compatible types throughout

Installation

See INSTALL.md for the complete guide.

pip install pymoogo
# or
uv add pymoogo

Requires Python 3.13+.

Quick Start

import asyncio
from pymoogo import MoogoClient

async def main():
    async with MoogoClient(email="your@email.com", password="your_password") as client:
        await client.authenticate()
        devices = await client.get_devices()
        if devices:
            device = devices[0]
            await device.refresh()
            print(f"Device: {device.name} | Online: {device.is_online} | Running: {device.is_running}")
            await device.start_spray()
            await asyncio.sleep(5)
            await device.stop_spray()

asyncio.run(main())

Usage

Authentication

from pymoogo import MoogoClient

client = MoogoClient(email="your@email.com", password="your_password")
await client.authenticate()

if client.is_authenticated:
    print("Authenticated successfully!")

Session persistence

Save and restore authentication sessions to avoid re-authenticating on every restart:

import json
from pymoogo import MoogoClient

# Authenticate and save session
client = MoogoClient(email="...", password="...")
auth_data = await client.authenticate()
with open("session.json", "w") as f:
    json.dump(auth_data, f)

# Later, restore the session
with open("session.json") as f:
    saved_session = json.load(f)

client = MoogoClient()
client.restore_session(saved_session)
if not client.is_authenticated:
    await client.authenticate()

Session management methods:

  • await client.authenticate() — returns session data dictionary
  • client.export_session() — export current session state for storage
  • client.restore_session(data) — restore a saved session
  • client.is_authenticated — check if session is currently valid

Session injection (Home Assistant pattern)

import aiohttp
from pymoogo import MoogoClient

session = aiohttp.ClientSession()
client = MoogoClient(email="your@email.com", password="your_password", session=session)
await client.authenticate()
devices = await client.get_devices()
# ...
await client.close()        # does NOT close the injected session
await session.close()       # caller manages session lifecycle

See docs/SESSION_INJECTION.md for full details.

Device discovery and status

devices = await client.get_devices()
for device in devices:
    await device.refresh()
    print(f"{device.name}: online={device.is_online}, running={device.is_running}")
    print(f"  temp={device.temperature}°C  humidity={device.humidity}%")
    print(f"  water={device.water_level}  liquid={device.liquid_level}")
    print(f"  firmware={device.firmware}")

Spray control

device = devices[0]
await device.start_spray()
await device.stop_spray()

# Start with duration (uses a temporary schedule; cleans up automatically)
await device.start_spray_with_duration(duration=60, cleanup=True)

start_spray() and stop_spray() poll for state confirmation (up to 10 s by default) before returning. Pass timeout and poll_interval to adjust.

Schedule management

# List schedules
schedules = await device.get_schedules()
for s in schedules:
    print(f"{s.time_str} for {s.duration}s  enabled={s.is_enabled}")

# Create
await device.create_schedule(hour=8, minute=0, duration=60,
                              repeat_set="0,1,2,3,4,5,6", enabled=True)

# Update / enable / disable
await device.update_schedule("schedule_id", duration=120, enabled=False)
await device.enable_schedule("schedule_id")
await device.disable_schedule("schedule_id")

# Bulk
await device.enable_all_schedules()
await device.disable_all_schedules()

# Delete
await device.delete_schedule("schedule_id")

Public endpoints (no authentication required)

liquid_types = await client.get_liquid_types()
recommended = await client.get_recommended_schedules()

API Reference

Full API reference and protocol notes: docs/.

Key classes

Class Description
MoogoClient High-level async client; entry point for all operations
MoogoDevice Per-device handle returned by get_devices(); exposes status and control methods
DeviceStatus Dataclass holding the raw device state snapshot
Schedule Dataclass representing a spray schedule

Data models

DeviceStatus fields: device_id, device_name, online_status, run_status, rssi, temperature (int), humidity, liquid_level, water_level, liquid_concentration, firmware, latest_spraying_duration, latest_spraying_end. Properties: is_online, is_running, signal_strength.

Schedule fields: id, hour, minute, duration, repeat_set, status. Properties: is_enabled, time_str.

Exceptions

Exception When raised
MoogoAuthError Authentication failed or credentials rejected
MoogoDeviceError Device is offline or state-change confirmation timed out
MoogoRateLimitError Rate limited (error code 10000); do not retry for 24 hours
MoogoAPIError Any other API-level error

Error codes

Code Meaning
0 Success
10000 Rate limited — 24-hour lockout, do not retry
10104 Invalid credentials
10201 Device offline
401 Unauthorized
500 Server error
from pymoogo import MoogoClient, MoogoAPIError, MoogoAuthError, MoogoDeviceError, MoogoRateLimitError

try:
    await device.start_spray()
except MoogoAuthError:
    print("Authentication failed")
except MoogoDeviceError:
    print("Device offline or timeout waiting for state change")
except MoogoRateLimitError:
    print("Rate limited — wait 24 hours before retrying")
except MoogoAPIError as e:
    print(f"API error: {e}")

Development

See docs/DEVELOPMENT.md. In short:

git clone https://github.com/joyfulhouse/pymoogo.git
cd pymoogo
uv sync
uv run pytest
uv run ruff check
uv run mypy

Support

Support Development

If this library is useful to you, please consider supporting its development:

License

This project is licensed under the MIT License — see LICENSE for details.

Related Projects

About

Python API client for Moogo smart spray systems - Community maintained

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors