A simple API for RainMachine sprinkler controllers
Project description
💧 Regenmaschine: A Simple Python Library for RainMachine™
=======================================================
.. image:: https://travis-ci.org/bachya/regenmaschine.svg?branch=master
:target: https://travis-ci.org/bachya/regenmaschine
.. image:: https://img.shields.io/pypi/v/regenmaschine.svg
:target: https://pypi.python.org/pypi/regenmaschine
.. image:: https://img.shields.io/pypi/pyversions/Regenmaschine.svg
:target: https://pypi.python.org/pypi/regenmaschine
.. image:: https://img.shields.io/pypi/l/Regenmaschine.svg
:target: https://pypi.python.org/pypi/regenmaschine
.. image:: https://codecov.io/gh/bachya/regenmaschine/branch/master/graph/badge.svg
:target: https://codecov.io/gh/bachya/regenmaschine
.. image:: https://img.shields.io/codeclimate/github/bachya/regenmaschine.svg
:target: https://codeclimate.com/github/bachya/regenmaschine
.. image:: https://img.shields.io/badge/SayThanks-!-1EAEDB.svg
:target: https://saythanks.io/to/bachya
Regenmaschine (German for "rain machine") is a simple, clean, well-tested Python
library for interacting with `RainMachine™ smart sprinkler controllers
<http://www.rainmachine.com/>`_. It gives developers an easy API to manage their
controllers over a LAN or via RainMachine™'s cloud.
💧 Installation
===============
.. code-block:: bash
$ pip install regenmaschine
💧 Usage
========
Authentication & Creating a Client
----------------------------------
Authentication is the first step and an be done against the local device or the
cloud API:
.. code-block:: python
import regenmaschine as rm
# Authenticate against the local device or the remote API:
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
auth = rm.Authenticator.create_remote('<EMAIL ADDRESS>', '<PASSWORD>')
# If authentication is successful, this :code:`auth` object can then be used
# to create a client:
client = rm.Client(auth)
Diagnostics
-----------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/diagnostics>`_
.. code-block:: python
client.diagnostics.current() # Returns current diagnostic info
client.diagnostics.log() # Returns entire device log
Programs
--------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/programs>`_
.. code-block:: python
client.programs.all() # Returns all program info
client.programs.get(1) # Returns info about program with UID of 1
client.programs.next() # Returns the next run date/time for all programs
client.programs.running() # Returns all running programs
client.programs.start(7) # Starts program 7
client.programs.stop(7) # Stops program 7
Restrictions
------------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/restrictions>`_
.. code-block:: python
client.restrictions.current() # Returns currently active restrictions
client.restrictions.hourly() # Returns restrictions over the next hour
client.restrictions.raindelay() # Returns all restrictions due to rain
client.restrictions.universal() # Returns the global list of restrictions
Stats
-----
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/daily-stats>`_
.. code-block:: python
client.stats.on_date('6/29/2017') # Returns all stats for a date
client.stats.on_date('2017-06-29') # Returns all stats for a date
client.stats.on_date('1 week ago') # Returns all stats for a date
client.stats.upcoming() # Returns expected stats for the next 7 days
client.stats.upcoming(include_details=True) # Deeper look at the next 7 days
Watering
--------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/watering>`_
.. code-block:: python
client.watering.log() # Returns log of all watering
client.watering.log(details=True) # Returns full log of all watering
client.watering.log('6/29/2017', 2) # Returns log for 6/27-6/29
client.watering.log('2017-06-29', 2) # Returns log for 6/27-6/29
client.watering.log('2017-06-29', 2, details=True) # Returns full log for 6/27-6/29
client.watering.log('2 days ago', 3) # Returns log 2-5 days ago
client.watering.queue() # Returns the active queue of watering activities
client.watering.runs('6/29/2017', 2) # Alternate view of log()
client.watering.runs('2017-06-29', 2) # Alternate view of log()
client.watering.runs('2 days ago', 3) # Alternate view of log()
client.watering.stop_all() # Immediately stops all programs and zones
Weather Services
----------------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/weather-services>`_
.. code-block:: python
client.parsers.current() # Returns current weather services being used
Zones
-----
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/zones>`_
.. code-block:: python
client.zones.all() # Returns all zone info
client.zones.all(properties=True) # Returns advanced info for all zones
client.zones.get(2) # Returns info about a zone with UID of 2
client.zones.get(2, properties=True) # Returns advanced info about zone 2
client.zones.start(3, 60) # Starts zone 3 for 60 seconds
client.zones.stop(3) # Stops zone 3
# You can also simulate what a zone will do:
properties = client.zones.get(2, properties=True)
client.zones.simulate(properties)
💧 Exceptions
=============
Regenmaschine may raise any of the following:
* `Built-in Python Exceptions <https://docs.python.org/3/library/exceptions.html#bltin-exceptions>`_
* `Requests Exceptions <https://github.com/requests/requests/blob/master/requests/exceptions.py>`_
* `Regenmaschine Exceptions <https://github.com/bachya/regenmaschine/blob/master/regenmaschine/exceptions.py>`_
💧 Advanced Usage
=================
Connection Pooling
------------------
If desired, Regenmaschine can accept a session object that allows it to re-use
the same HTTP connection for every call (rather than opening up a new one each
time):
.. code-block:: python
from requests.sessions import Session
with Session() as session:
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
client = rm.Client(auth)
client.zones.all()
client.zones.get(1)
Authentication Caching
----------------------
There doesn't appear to be a limit on the number of times RainMachine™
will allow new access tokens to be generated. However, it may be desirable to
use the same credentials long term. Therefore, the :code:`auth` object can be
dumped and saved:
.. code-block:: python
# Outputs a dict:
auth_json = auth.dump()
# Outputs a string version of the dict:
auth_str = auth.dumps()
The :code:`auth` object contains the access token used to authenticate API
requests, as well as an expiration timeframe and more:
.. code-block:: python
{
"sprinkler_id": None,
"cookies": {
"access_token": "24551da62895"
},
"api_url": "https://192.168.1.100:8080/api/4",
"url": "https://192.168.1.100:8080/api/4",
"checksum": u "c5e29cdef3b1e",
"expires_in": 157680000,
"api_endpoint": "auth/login",
"access_token": u "24551da62895",
"verify_ssl": False,
"session": None,
"expiration": u "Fri, 01 Jul 2022 20:11:48 GMT",
"timeout": 10,
"status_code": 0,
"using_remote_api": False,
"data": {
"pwd": "MY_RM_PASSWORD",
"remember": 1
}
}
**TAKE NOTE:** the dumped :code:`auth` object contains the access token
needed to query the API, sprinkler IDs, RainMachine™ credentials, and other
sensitive information. *Therefore, it should be cached and stored securely*.
One common use of this mechanism would be to check the expiration date of the
access token; assuming it is still valid, a corresponding client can be
recreated quite easily:
.. code-block:: python
# The dict and the string versions can each be loaded:
auth = rm.Authenticator.load(auth_json)
auth = rm.Authenticator.loads(auth_str)
client = rm.Client(auth)
SSL Usage
---------
By default, Regenmaschine routes all API calls – local or remote – through HTTPS.
However, RainMachine devices use self-signed SSL certificates; therefore,
Regenmaschine disables verifying the validity of local SSL certificates before
processing local requests. In practice, this shouldn't be a problem. However, for the security conscious, this behavior can be changed.
First, `provide a CA-signed SSL certificate to the local device <https://support.rainmachine.com/hc/en-us/community/posts/115006512067-rovide-custom-SSL-Certificate>`_. Then, override the default local Authenticator behavior:
.. code-block:: python
# Create a local Authenticator and force it to use SSL:
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
auth.verify_ssl = True
# The client will now verify the SSL certificate on the local device before
# processing every request:
client = rm.Client(auth)
*Note:* after this, if Regenmaschine cannot recognize a CA-signed certificate
when querying the local device, a :code:`requests.exceptions.SSLError`
exception will be raised.
To disable SSL once again, re-authenticate and re-create a client:
.. code-block:: python
# Create a local Authenticator (with the default behavior):
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
# The client will now refrain from verifying the SSL connection's validity:
client = rm.Client(auth)
💧 Contributing
===============
#. Check for open features/bugs or initiate a discussion on one.
#. Fork the repository.
#. Install the dev environment: :code:`make init`.
#. Enter the virtual environment: :code:`pipenv shell`
#. Code your new feature or bug fix.
#. Write a test that covers your new functionality.
#. Run tests: :code:`make test`
#. Build new docs: :code:`make docs`
#. Add yourself to AUTHORS.rst.
#. Submit a pull request!
=======================================================
.. image:: https://travis-ci.org/bachya/regenmaschine.svg?branch=master
:target: https://travis-ci.org/bachya/regenmaschine
.. image:: https://img.shields.io/pypi/v/regenmaschine.svg
:target: https://pypi.python.org/pypi/regenmaschine
.. image:: https://img.shields.io/pypi/pyversions/Regenmaschine.svg
:target: https://pypi.python.org/pypi/regenmaschine
.. image:: https://img.shields.io/pypi/l/Regenmaschine.svg
:target: https://pypi.python.org/pypi/regenmaschine
.. image:: https://codecov.io/gh/bachya/regenmaschine/branch/master/graph/badge.svg
:target: https://codecov.io/gh/bachya/regenmaschine
.. image:: https://img.shields.io/codeclimate/github/bachya/regenmaschine.svg
:target: https://codeclimate.com/github/bachya/regenmaschine
.. image:: https://img.shields.io/badge/SayThanks-!-1EAEDB.svg
:target: https://saythanks.io/to/bachya
Regenmaschine (German for "rain machine") is a simple, clean, well-tested Python
library for interacting with `RainMachine™ smart sprinkler controllers
<http://www.rainmachine.com/>`_. It gives developers an easy API to manage their
controllers over a LAN or via RainMachine™'s cloud.
💧 Installation
===============
.. code-block:: bash
$ pip install regenmaschine
💧 Usage
========
Authentication & Creating a Client
----------------------------------
Authentication is the first step and an be done against the local device or the
cloud API:
.. code-block:: python
import regenmaschine as rm
# Authenticate against the local device or the remote API:
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
auth = rm.Authenticator.create_remote('<EMAIL ADDRESS>', '<PASSWORD>')
# If authentication is successful, this :code:`auth` object can then be used
# to create a client:
client = rm.Client(auth)
Diagnostics
-----------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/diagnostics>`_
.. code-block:: python
client.diagnostics.current() # Returns current diagnostic info
client.diagnostics.log() # Returns entire device log
Programs
--------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/programs>`_
.. code-block:: python
client.programs.all() # Returns all program info
client.programs.get(1) # Returns info about program with UID of 1
client.programs.next() # Returns the next run date/time for all programs
client.programs.running() # Returns all running programs
client.programs.start(7) # Starts program 7
client.programs.stop(7) # Stops program 7
Restrictions
------------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/restrictions>`_
.. code-block:: python
client.restrictions.current() # Returns currently active restrictions
client.restrictions.hourly() # Returns restrictions over the next hour
client.restrictions.raindelay() # Returns all restrictions due to rain
client.restrictions.universal() # Returns the global list of restrictions
Stats
-----
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/daily-stats>`_
.. code-block:: python
client.stats.on_date('6/29/2017') # Returns all stats for a date
client.stats.on_date('2017-06-29') # Returns all stats for a date
client.stats.on_date('1 week ago') # Returns all stats for a date
client.stats.upcoming() # Returns expected stats for the next 7 days
client.stats.upcoming(include_details=True) # Deeper look at the next 7 days
Watering
--------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/watering>`_
.. code-block:: python
client.watering.log() # Returns log of all watering
client.watering.log(details=True) # Returns full log of all watering
client.watering.log('6/29/2017', 2) # Returns log for 6/27-6/29
client.watering.log('2017-06-29', 2) # Returns log for 6/27-6/29
client.watering.log('2017-06-29', 2, details=True) # Returns full log for 6/27-6/29
client.watering.log('2 days ago', 3) # Returns log 2-5 days ago
client.watering.queue() # Returns the active queue of watering activities
client.watering.runs('6/29/2017', 2) # Alternate view of log()
client.watering.runs('2017-06-29', 2) # Alternate view of log()
client.watering.runs('2 days ago', 3) # Alternate view of log()
client.watering.stop_all() # Immediately stops all programs and zones
Weather Services
----------------
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/weather-services>`_
.. code-block:: python
client.parsers.current() # Returns current weather services being used
Zones
-----
More info on responses, etc: `<http://docs.rainmachine.apiary.io/#reference/zones>`_
.. code-block:: python
client.zones.all() # Returns all zone info
client.zones.all(properties=True) # Returns advanced info for all zones
client.zones.get(2) # Returns info about a zone with UID of 2
client.zones.get(2, properties=True) # Returns advanced info about zone 2
client.zones.start(3, 60) # Starts zone 3 for 60 seconds
client.zones.stop(3) # Stops zone 3
# You can also simulate what a zone will do:
properties = client.zones.get(2, properties=True)
client.zones.simulate(properties)
💧 Exceptions
=============
Regenmaschine may raise any of the following:
* `Built-in Python Exceptions <https://docs.python.org/3/library/exceptions.html#bltin-exceptions>`_
* `Requests Exceptions <https://github.com/requests/requests/blob/master/requests/exceptions.py>`_
* `Regenmaschine Exceptions <https://github.com/bachya/regenmaschine/blob/master/regenmaschine/exceptions.py>`_
💧 Advanced Usage
=================
Connection Pooling
------------------
If desired, Regenmaschine can accept a session object that allows it to re-use
the same HTTP connection for every call (rather than opening up a new one each
time):
.. code-block:: python
from requests.sessions import Session
with Session() as session:
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
client = rm.Client(auth)
client.zones.all()
client.zones.get(1)
Authentication Caching
----------------------
There doesn't appear to be a limit on the number of times RainMachine™
will allow new access tokens to be generated. However, it may be desirable to
use the same credentials long term. Therefore, the :code:`auth` object can be
dumped and saved:
.. code-block:: python
# Outputs a dict:
auth_json = auth.dump()
# Outputs a string version of the dict:
auth_str = auth.dumps()
The :code:`auth` object contains the access token used to authenticate API
requests, as well as an expiration timeframe and more:
.. code-block:: python
{
"sprinkler_id": None,
"cookies": {
"access_token": "24551da62895"
},
"api_url": "https://192.168.1.100:8080/api/4",
"url": "https://192.168.1.100:8080/api/4",
"checksum": u "c5e29cdef3b1e",
"expires_in": 157680000,
"api_endpoint": "auth/login",
"access_token": u "24551da62895",
"verify_ssl": False,
"session": None,
"expiration": u "Fri, 01 Jul 2022 20:11:48 GMT",
"timeout": 10,
"status_code": 0,
"using_remote_api": False,
"data": {
"pwd": "MY_RM_PASSWORD",
"remember": 1
}
}
**TAKE NOTE:** the dumped :code:`auth` object contains the access token
needed to query the API, sprinkler IDs, RainMachine™ credentials, and other
sensitive information. *Therefore, it should be cached and stored securely*.
One common use of this mechanism would be to check the expiration date of the
access token; assuming it is still valid, a corresponding client can be
recreated quite easily:
.. code-block:: python
# The dict and the string versions can each be loaded:
auth = rm.Authenticator.load(auth_json)
auth = rm.Authenticator.loads(auth_str)
client = rm.Client(auth)
SSL Usage
---------
By default, Regenmaschine routes all API calls – local or remote – through HTTPS.
However, RainMachine devices use self-signed SSL certificates; therefore,
Regenmaschine disables verifying the validity of local SSL certificates before
processing local requests. In practice, this shouldn't be a problem. However, for the security conscious, this behavior can be changed.
First, `provide a CA-signed SSL certificate to the local device <https://support.rainmachine.com/hc/en-us/community/posts/115006512067-rovide-custom-SSL-Certificate>`_. Then, override the default local Authenticator behavior:
.. code-block:: python
# Create a local Authenticator and force it to use SSL:
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
auth.verify_ssl = True
# The client will now verify the SSL certificate on the local device before
# processing every request:
client = rm.Client(auth)
*Note:* after this, if Regenmaschine cannot recognize a CA-signed certificate
when querying the local device, a :code:`requests.exceptions.SSLError`
exception will be raised.
To disable SSL once again, re-authenticate and re-create a client:
.. code-block:: python
# Create a local Authenticator (with the default behavior):
auth = rm.Authenticator.create_local('<DEVICE_IP_ADDRESS>', '<PASSWORD>')
# The client will now refrain from verifying the SSL connection's validity:
client = rm.Client(auth)
💧 Contributing
===============
#. Check for open features/bugs or initiate a discussion on one.
#. Fork the repository.
#. Install the dev environment: :code:`make init`.
#. Enter the virtual environment: :code:`pipenv shell`
#. Code your new feature or bug fix.
#. Write a test that covers your new functionality.
#. Run tests: :code:`make test`
#. Build new docs: :code:`make docs`
#. Add yourself to AUTHORS.rst.
#. 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-0.2.5.tar.gz
(12.9 kB
view details)
Built Distribution
File details
Details for the file regenmaschine-0.2.5.tar.gz
.
File metadata
- Download URL: regenmaschine-0.2.5.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 15530a35b943afd89921902aaa24052e219cdcf6f5d11555628388671dfdede2 |
|
MD5 | 5fd43b5b46edf1424849c0a914fb94d1 |
|
BLAKE2b-256 | f3299bfbefba8ebbb406cfe664817587a64374b0effc1c779b8c43a549f32a00 |
File details
Details for the file regenmaschine-0.2.5-py2.py3-none-any.whl
.
File metadata
- Download URL: regenmaschine-0.2.5-py2.py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5744e716255ddb3958038b06662f6ef650e0d300032fc6f0fe0f67451d104bc0 |
|
MD5 | 5ec28fe3b5702781312b287493f53242 |
|
BLAKE2b-256 | 587fb99a7bc2f9ecc5dcd489bc31be01bddc726d9981622c2b3aaf1503351742 |