Skip to main content

A simple API for RainMachine sprinkler controllers

Project description

💧 Regenmaschine: A Simple Python Library for RainMachine™

CI PyPi Version License Code Coverage Maintainability Say Thanks

regenmaschine (German for "rain machine") is a simple, clean, well-tested Python library for interacting with RainMachine™ smart sprinkler controllers. It gives developers an easy API to manage their controllers over their local LAN or remotely via the RainMachine™ cloud.

Python Versions

regenmaschine is currently supported on:

  • Python 3.7
  • Python 3.8
  • Python 3.9
  • Python 3.10

Installation

pip install regenmaschine

Usage

Creating a regenmaschine Client might be the easiest thing you do all day:

import asyncio

from aiohttp import ClientSession

from regenmaschine import Client


async def main() -> None:
    """Run!"""
    client = Client()

    # ...


asyncio.run(main())

By default, the library creates a new connection to the sprinkler controller with each coroutine. If you are calling a large number of coroutines (or merely want to squeeze out every second of runtime savings possible), an aiohttp ClientSession can be used for connection pooling:

See the module docstrings throughout the library for full info on all parameters, return types, etc.

import asyncio

from aiohttp import ClientSession

from regenmaschine import Client


async def main() -> None:
    """Run!"""
    async with ClientSession() as session:
        client = Client(session=session)


asyncio.run(main())

Loading Local (Accessible Over the LAN) Controllers

Once you have a client, you can load a local controller (i.e., one that is accessible over the LAN) very easily:

import asyncio

from aiohttp import ClientSession

from regenmaschine import Client


async def main() -> None:
    """Run!"""
    async with ClientSession() as session:
        client = Client(session=session)

        await client.load_local("192.168.1.101", "my_password", port=8080, ssl=True)

        controllers = client.controllers
        # >>> {'ab:cd:ef:12:34:56': <LocalController>}


asyncio.run(main())

Loading Remote (Accessible Over the RainMachine Cloud) Controllers

If you have 1, 2 or 100 other local controllers, you can load them in the same way – client.controllers will keep your controllers all organized.

What if you have controllers around the world and can't access them all over the same local network? No problem! regenmaschine allows you to load remote controllers very easily, as well:

import asyncio

from aiohttp import ClientSession

from regenmaschine import Client


async def main() -> None:
    """Run!"""
    async with ClientSession() as session:
        client = Client(session=session)

        await client.load_remote("rainmachine_email@host.com", "my_password")

        controllers = client.controllers
        # >>> {'xx:xx:xx:xx:xx:xx': <RemoteController>, ...}


asyncio.run(main())

Bonus tip: client.load_remote will load all controllers owned by that email address.

Using the Controller

Regardless of the type of controller you have loaded (local or remote), the same properties and methods are available to each:

import asyncio
import datetime

from aiohttp import ClientSession

from regenmaschine import Client


async def main() -> None:
    """Run!"""
    async with ClientSession() as session:
        client = Client(session=session)

        # Load a local controller:
        await client.load_local("192.168.1.101", "my_password", port=8080, ssl=True)

        # Load all remote controllers associated with an account:
        await client.load_remote("rainmachine_email@host.com", "my_password")

        # They all act the same! The only difference is that remote API calls
        # will pass through the RainMachine™ cloud:
        for mac_address, controller in client.controllers:
            # Print some client properties:
            print(f"Name: {controller.name}")
            print(f"Host: {controller.host}")
            print(f"MAC Address: {controller.mac}")
            print(f"API Version: {controller.api_version}")
            print(f"Software Version: {controller.software_version}")
            print(f"Hardware Version: {controller.hardware_version}")

            # Get all diagnostic information:
            diagnostics = await controller.diagnostics.current()

            # Get all weather parsers:
            parsers = await controller.parsers.current()

            # Get all programs:
            programs = await controller.programs.all()

            # Include inactive programs:
            programs = await controller.programs.all(include_inactive=True)

            # Get a specific program:
            program_1 = await controller.programs.get(1)

            # Enable or disable a specific program:
            await controller.programs.enable(1)
            await controller.programs.disable(1)

            # Get the next run time for all programs:
            runs = await controller.programs.next()

            # Get all running programs:
            programs = await controller.programs.running()

            # Start and stop a program:
            await controller.programs.start(1)
            await controller.programs.stop(1)

            # Get basic details about all zones:
            zones = await controller.zones.all()

            # Get advanced details about all zones:
            zones = await controller.zones.all(details=True)

            # Include inactive zones:
            zones = await controller.zones.all(include_inactive=True)

            # Get basic details about a specific zone:
            zone_1 = await controller.zones.get(1)

            # Get advanced details about a specific zone:
            zone_1 = await controller.zones.get(1, details=True)

            # Enable or disable a specific zone:
            await controller.zones.enable(1)
            await controller.zones.disable(1)

            # Start a zone for 60 seconds:
            await controller.zones.start(1, 60)

            # ...and stop it:
            await controller.zones.stop(1)

            # Get the device name:
            name = await controller.provisioning.device_name

            # Get all provisioning settings:
            settings = await controller.provisioning.settings()

            # Get all networking info related to the device:
            wifi = await controller.provisioning.wifi()

            # Get various types of active watering restrictions:
            current = await controller.restrictions.current()
            universal = await controller.restrictions.universal()
            hourly = await controller.restrictions.hourly()
            raindelay = await controller.restrictions.raindelay()

            # Restrict all watering activities for 3 days:
            await controller.restrictions.restrict(datetime.timedelta(days=3))
            # ...and then unrestrict watering activities:
            await controller.restrictions.unrestrict()

            # Get watering stats:
            today = await controller.stats.on_date(datetime.date.today())
            upcoming_days = await controller.stats.upcoming(details=True)

            # Get info on various watering activities not already covered:
            log = await controller.watering.log(datetime.date.today(), 2)
            queue = await controller.watering.queue()
            runs = await controller.watering.runs(datetime.date.today())

            # Pause all watering activities for 30 seconds:
            await controller.watering.pause_all(30)

            # Unpause all watering activities:
            await controller.watering.unpause_all()

            # Stop all watering activities:
            await controller.watering.stop_all()


asyncio.run(main())

Check out example.py, the tests, and the source files themselves for method signatures and more examples. For additional reference, the full RainMachine™ API documentation is available here.

Loading Controllers Multiple Times

It is technically possible to load a controller multiple times. Let's pretend for a moment that:

  • We have a local controller named Home (available at 192.168.1.101).
  • We have a remote controller named Grandma's House.
  • Both controllers live under our email address: user@host.com

If we load them thus:

import asyncio

from aiohttp import ClientSession

from regenmaschine import Client


async def main() -> None:
    """Run!"""
    async with ClientSession() as session:
        client = Client(session=session)

        # Load "Home" locally:
        await client.load_local("192.168.1.101", "my_password")

        # Load all of my controllers remotely:
        await client.load_remote("user@host.com", "my_password")


asyncio.run(main())

...then we will have the following:

  1. Home will be a LocalController and accessible over the LAN.
  2. Grandma's House will be a RemoteController and accessible only over the RainMachine™ cloud.

Notice that regenmaschine is smart enough to not overwrite a controller that already exists: even though Home exists as a remote controller owned by user@host.com, it had already been loaded locally. By default, regenmaschine will only load a controller if it hasn't been loaded before (locally or remotely). If you want to change this behavior, both load_local and load_remote accept an optional skip_existing parameter:

import asyncio

from aiohttp import ClientSession

from regenmaschine import Client


async def main() -> None:
    """Run!"""
    async with ClientSession() as session:
        client = Client(session=session)

        # Load all of my controllers remotely:
        await client.load_remote("user@host.com", "my_password")

        # Load "Home" locally, overwriting the existing remote controller:
        await client.load_local("192.168.1.101", "my_password", skip_existing=False)


asyncio.run(main())

Contributing

  1. Check for open features/bugs or initiate a discussion on one.
  2. Fork the repository.
  3. (optional, but highly recommended) Create a virtual environment: python3 -m venv .venv
  4. (optional, but highly recommended) Enter the virtual environment: source ./.venv/bin/activate
  5. Install the dev environment: script/setup
  6. Code your new feature or bug fix.
  7. Write tests that cover your new functionality.
  8. Run tests and ensure 100% code coverage: nox -rs coverage
  9. Update README.md with any new documentation.
  10. Add yourself to AUTHORS.md.
  11. Submit a pull request!

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

regenmaschine-2022.6.0.tar.gz (15.6 kB view details)

Uploaded Source

Built Distribution

regenmaschine-2022.6.0-py3-none-any.whl (15.5 kB view details)

Uploaded Python 3

File details

Details for the file regenmaschine-2022.6.0.tar.gz.

File metadata

  • Download URL: regenmaschine-2022.6.0.tar.gz
  • Upload date:
  • Size: 15.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.7.13 Linux/5.13.0-1025-azure

File hashes

Hashes for regenmaschine-2022.6.0.tar.gz
Algorithm Hash digest
SHA256 997aaef89730edfc4906e6fd379b854bc045cda953c29ffefef11115c1f247c8
MD5 afe851be493f4001b5058636a79644c1
BLAKE2b-256 043402ebba3b6bce96330bf70e3d3718588739706b880188e0f8dc16437bff52

See more details on using hashes here.

File details

Details for the file regenmaschine-2022.6.0-py3-none-any.whl.

File metadata

  • Download URL: regenmaschine-2022.6.0-py3-none-any.whl
  • Upload date:
  • Size: 15.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.7.13 Linux/5.13.0-1025-azure

File hashes

Hashes for regenmaschine-2022.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0eedaadf093047bf9a83f6850377b19f53279fb8ba258352c2fc91f1b2376ffb
MD5 6b0010cabd1a3d3a9b495532cda4229a
BLAKE2b-256 bdc62d74cd0ad9068ee2b075fddc0512902cd16280b6f544051bc9ce7ce72777

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page