pax_global_header00006660000000000000000000000064145452267520014526gustar00rootroot0000000000000052 comment=7f3c210b54dcd418128a489f8417432c9d6dc997 rfverbruggen-rachiopy-7f3c210/000077500000000000000000000000001454522675200163675ustar00rootroot00000000000000rfverbruggen-rachiopy-7f3c210/.all-contributorsrc000066400000000000000000000011231454522675200222150ustar00rootroot00000000000000{ "files": [ "README.md" ], "imageSize": 100, "commit": false, "commitType": "docs", "commitConvention": "angular", "contributors": [ { "login": "brg468", "name": "Brian Rogers", "avatar_url": "https://avatars.githubusercontent.com/u/19143191?v=4", "profile": "https://github.com/brg468", "contributions": [ "code", "doc", "test" ] } ], "contributorsPerLine": 7, "skipCi": true, "repoType": "github", "repoHost": "https://github.com", "projectName": "rachiopy", "projectOwner": "rfverbruggen" } rfverbruggen-rachiopy-7f3c210/.all-contributorsrc.json000066400000000000000000000001021454522675200231610ustar00rootroot00000000000000{ "projectName": "rachiopy", "projectOwner": "rfverbruggen" } rfverbruggen-rachiopy-7f3c210/.github/000077500000000000000000000000001454522675200177275ustar00rootroot00000000000000rfverbruggen-rachiopy-7f3c210/.github/workflows/000077500000000000000000000000001454522675200217645ustar00rootroot00000000000000rfverbruggen-rachiopy-7f3c210/.github/workflows/codeql-analysis.yml000066400000000000000000000046211454522675200256020ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ dev, master ] pull_request: # The branches below must be a subset of the branches above branches: [ dev ] schedule: - cron: '28 6 * * 6' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 rfverbruggen-rachiopy-7f3c210/.github/workflows/lint.yml000066400000000000000000000015551454522675200234630ustar00rootroot00000000000000name: Run linters on: [push, pull_request] jobs: test: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - name: Set up Python 3.x uses: actions/setup-python@v2 with: python-version: 3.x - name: Install dependencies run: | python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f requirements_test.txt ]; then pip install -r requirements_test.txt; fi - name: Run pylint run: pylint rachiopy tests - name: Run flake8 run: flake8 - name: Run pydocstyle run: pydocstyle {posargs:rachiopy tests} rfverbruggen-rachiopy-7f3c210/.github/workflows/publish.yml000066400000000000000000000022451454522675200241600ustar00rootroot00000000000000name: Publish Python distributions to PyPI on: release: types: [published] jobs: build-n-publish: name: Build and publish Python distributions to PyPI runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/project/RachioPy permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - uses: actions/checkout@master - name: Set up Python 3.x uses: actions/setup-python@v2 with: python-version: 3.x - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -r requirements.txt - name: Run unit tests run: python -m unittest discover -v tests - name: Build distribution run: python setup.py sdist bdist_wheel - name: Publish distribution to Test PyPI if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@release/v1 with: repository-url: https://test.pypi.org/legacy/ - name: Publish distribution to PyPI if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@release/v1 rfverbruggen-rachiopy-7f3c210/.github/workflows/publish_test.yml000066400000000000000000000020041454522675200252100ustar00rootroot00000000000000name: Publish Python distributions to Test PyPI on: push: branches: - dev jobs: build-n-publish: name: Build and publish Python distributions to Test PyPI runs-on: ubuntu-latest environment: name: pypi url: https://test.pypi.org/project/RachioPy permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - uses: actions/checkout@master - name: Set up Python 3.x uses: actions/setup-python@v2 with: python-version: 3.x - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -r requirements.txt - name: Run unit tests run: python -m unittest discover -v tests - name: Build distribution run: python setup.py sdist bdist_wheel - name: Publish package distributions to TestPyPI uses: pypa/gh-action-pypi-publish@release/v1 with: repository-url: https://test.pypi.org/legacy/ rfverbruggen-rachiopy-7f3c210/.github/workflows/test.yml000066400000000000000000000016351454522675200234730ustar00rootroot00000000000000name: Run unit tests on: [push, pull_request] jobs: test: # The type of runner that the job will run on runs-on: ubuntu-latest strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f requirements_test.txt ]; then pip install -r requirements_test.txt; fi - name: Run unittests run: python -m unittest discover -v tests rfverbruggen-rachiopy-7f3c210/.gitignore000066400000000000000000000013531454522675200203610ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg .python-version # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ # VSCode .vscode/rfverbruggen-rachiopy-7f3c210/.hound.yml000066400000000000000000000000621454522675200203030ustar00rootroot00000000000000fail_on_violations: false python: enabled: truerfverbruggen-rachiopy-7f3c210/.pylintrc000066400000000000000000000000401454522675200202260ustar00rootroot00000000000000[MESSAGES CONTROL] disable=R0801rfverbruggen-rachiopy-7f3c210/.readthedocs.yml000066400000000000000000000003711454522675200214560ustar00rootroot00000000000000# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py rfverbruggen-rachiopy-7f3c210/LICENSE.txt000066400000000000000000000020631454522675200202130ustar00rootroot00000000000000MIT License Copyright (c) 2017 Robbert Verbruggen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rfverbruggen-rachiopy-7f3c210/README.md000066400000000000000000000027451454522675200176560ustar00rootroot00000000000000# Rachiopy [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-) This python package provides a interface to the Rachio public API. ## Usage ```python from rachiopy import Rachio r = Rachio("authtoken") r.person.info() ``` For the complete documentation visit [read the docs](https://rachiopy.readthedocs.io/en/latest/). ## Contributors
Brian Rogers
Brian Rogers

đŸ’ģ 📖 âš ī¸
rfverbruggen-rachiopy-7f3c210/docs/000077500000000000000000000000001454522675200173175ustar00rootroot00000000000000rfverbruggen-rachiopy-7f3c210/docs/Makefile000066400000000000000000000011721454522675200207600ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) rfverbruggen-rachiopy-7f3c210/docs/conf.py000066400000000000000000000036401454522675200206210ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) sys.setrecursionlimit(1500) # -- Project information ----------------------------------------------------- project = 'RachioPy' copyright = '2020, Robbert Verbruggen' author = 'Robbert Verbruggen' # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'classic' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # set the master doc master_doc = 'index' rfverbruggen-rachiopy-7f3c210/docs/device.rst000066400000000000000000000001171454522675200213070ustar00rootroot00000000000000Device module =================== .. automodule:: rachiopy.device :members:rfverbruggen-rachiopy-7f3c210/docs/flexschedulerule.rst000066400000000000000000000001471454522675200234160ustar00rootroot00000000000000Flexschedulerule module ======================= .. automodule:: rachiopy.flexschedulerule :members:rfverbruggen-rachiopy-7f3c210/docs/index.rst000066400000000000000000000023641454522675200211650ustar00rootroot00000000000000=================== Welcome to RachioPy =================== This python package provides a interface to the Rachio public API. Library Installation ==================== .. code-block:: bash $ pip install rachiopy Getting Started =============== .. code-block:: python from rachiopy import Rachio r = Rachio("8e600a4c-0027-4a9a-9bda-dc8d5c90350d") resp, content = r.person.info() print (resp["status"]) print (content["id"]) This prints: .. code-block:: text 200 ccd8bb4d-d7ef-407c-b029-1c578c7a98d8 Source code =========== The project is hosted on GitHub Please feel free to file an issue on the bug tracker if you have found a bug or have some suggestion in order to improve the library. Dependencies ============ - Python 3.6+ - requests Authors and License =================== The rachiopy package is written mostly by Robbert Verbruggen. It's MIT licensed and freely available. Feel free to improve this package and send a pull request to GitHub. Table Of Contents ================= .. toctree:: :maxdepth: 2 person device zone schedulerule flexschedulerule notification rachioobject Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` rfverbruggen-rachiopy-7f3c210/docs/make.bat000066400000000000000000000013701454522675200207250ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd rfverbruggen-rachiopy-7f3c210/docs/notification.rst000066400000000000000000000001331454522675200225340ustar00rootroot00000000000000Notification module =================== .. automodule:: rachiopy.notification :members:rfverbruggen-rachiopy-7f3c210/docs/person.rst000066400000000000000000000001171454522675200213560ustar00rootroot00000000000000Person module =================== .. automodule:: rachiopy.person :members:rfverbruggen-rachiopy-7f3c210/docs/rachioobject.rst000066400000000000000000000001331454522675200225020ustar00rootroot00000000000000RachioObject module =================== .. automodule:: rachiopy.rachioobject :members:rfverbruggen-rachiopy-7f3c210/docs/schedulerule.rst000066400000000000000000000001331454522675200225320ustar00rootroot00000000000000Schedulerule module =================== .. automodule:: rachiopy.schedulerule :members:rfverbruggen-rachiopy-7f3c210/docs/zone.rst000066400000000000000000000001131454522675200210170ustar00rootroot00000000000000Zone module =================== .. automodule:: rachiopy.zone :members:rfverbruggen-rachiopy-7f3c210/rachiopy/000077500000000000000000000000001454522675200202055ustar00rootroot00000000000000rfverbruggen-rachiopy-7f3c210/rachiopy/__init__.py000066400000000000000000000022521454522675200223170ustar00rootroot00000000000000"""Main rachiopy module.""" from rachiopy.rachioobject import RachioObject from rachiopy.person import Person from rachiopy.device import Device from rachiopy.flexschedulerule import FlexSchedulerule from rachiopy.notification import Notification from rachiopy.schedulerule import Schedulerule from rachiopy.zone import Zone from rachiopy.valve import Valve from rachiopy.summary import SummaryServce from rachiopy.program import Program class Rachio(RachioObject): """Object representing the Rachio API.""" # pylint: disable=too-many-instance-attributes def __init__(self, authtoken: str): """Initialze the Rachio API wrapper. :param authtoken: The API authentication token. :type authtoken: str """ super().__init__(authtoken) self.person = Person(authtoken) self.device = Device(authtoken) self.flexschedulerule = FlexSchedulerule(authtoken) self.notification = Notification(authtoken) self.schedulerule = Schedulerule(authtoken) self.zone = Zone(authtoken) self.valve = Valve(authtoken) self.summary = SummaryServce(authtoken) self.program = Program(authtoken) rfverbruggen-rachiopy-7f3c210/rachiopy/device.py000066400000000000000000000173151454522675200220250ustar00rootroot00000000000000"""Device module handling /device/ API calls.""" from rachiopy.rachioobject import RachioObject class Device(RachioObject): """Device class with /device/ API calls.""" def get(self, dev_id: str): """Retrieve the information for a device entity. For more info of the content in the response see: https://rachio.readme.io/docs/publicdeviceid :param dev_id: Device's unique id :type dev_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"device/{dev_id}" return self.get_request(path) def current_schedule(self, dev_id: str): """Retrieve current schedule running, if any. For more info of the content in the response see: https://rachio.readme.io/docs/publicdeviceidcurrent_schedule :param dev_id: Device's unique id :type dev_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"device/{dev_id}/current_schedule" return self.get_request(path) def event(self, dev_id: str, starttime: int, endtime: int): """Retrieve events for a device entity. For more info of the content in the response see: https://rachio.readme.io/docs/publicdeviceideventstarttimestarttimeendtimeendtim :param dev_id: Device's unique id :type dev_id: str :param starttime: Query start time milliseconds unix epoch :type starttime: int :param endtime: Query end time milliseconds unix epoch :type endtime: int :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"device/{dev_id}/event?startTime={starttime}&endTime={endtime}" return self.get_request(path) def forecast(self, dev_id: str, units="US"): """Retrieve current and predicted forecast. For more info of the content in the response see: https://rachio.readme.io/docs/publicdeviceidforecastunitsunits :param dev_id: Device's unique id :type dev_id: str :param units: Forecast data units, one of US or METRIC, defaults to US :type units: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ assert units in ["US", "METRIC"], "units must be either US or METRIC" path = f"device/{dev_id}/forecast?units={units}" return self.get_request(path) def stop_water(self, dev_id: str): """Stop all watering on device. For more info of the content in the response see: https://rachio.readme.io/docs/devicestop_water :param dev_id: Device's unique id :type dev_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"id": dev_id} return self.put_request("device/stop_water", payload) def rain_delay(self, dev_id: str, duration: int): """Rain delay device. For more info of the content in the response see: https://rachio.readme.io/docs/devicestop_water :param dev_id: Device's unique id :type dev_id: str :param duration: Duration in seconds (Range is 0 - 604800 (7 days) ) :type duration: int :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ assert 0 <= duration <= 604800, "duration must be between 0 and 604800" payload = {"id": dev_id, "duration": duration} return self.put_request("device/rain_delay", payload) def turn_on(self, dev_id: str): """Turn ON all features of the device. schedules, weather intelligence, water budget, etc. For more info of the content in the response see: https://rachio.readme.io/docs/publicdeviceon-1 :param dev_id: Device's unique id :type dev_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"id": dev_id} return self.put_request("device/on", payload) def turn_off(self, dev_id: str): """Turn OFF all features of the device. schedules, weather intelligence, water budget, etc. For more info of the content in the response see: https://rachio.readme.io/docs/publicdeviceoff-1 :param dev_id: Device's unique id :type dev_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"id": dev_id} return self.put_request("device/off", payload) def pause_zone_run(self, dev_id: str, duration: int): """Pause a zone run for device. For more info of the content in the response see: https://rachio.readme.io/docs/publicdevicepause_zone_run :param dev_id: Device's unique id :type dev_id: str :param duration: Duration in seconds (Range is 0 - 3600 (1 hour) ) :type duration: int :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ assert 0 <= duration <= 3600, "duration must be between 0 and 3600" payload = {"id": dev_id, "duration": duration} return self.put_request("device/pause_zone_run", payload) def resume_zone_run(self, dev_id: str): """Resume a zone run for device. For more info of the content in the response see: https://rachio.readme.io/docs/publicdeviceresume_zone_run :param dev_id: Device's unique id :type dev_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"id": dev_id} return self.put_request("device/resume_zone_run", payload) rfverbruggen-rachiopy-7f3c210/rachiopy/flexschedulerule.py000066400000000000000000000016271454522675200241300ustar00rootroot00000000000000"""Flexschedulerule module handling /flexschedulerule/ API calls.""" from rachiopy.rachioobject import RachioObject class FlexSchedulerule(RachioObject): """FlexSchedulerule class with methods for /flexschedulerule/ calls.""" def get(self, flex_sched_rule_id: str): """Retrieve the information for a flexscheduleRule entity. For more info of the content in the response see: https://rachio.readme.io/docs/publicflexscheduleruleid :param flex_sched_rule_id: FlexScheduleRule's unique id :type flex_sched_rule_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ path = f"flexschedulerule/{flex_sched_rule_id}" return self.get_request(path) rfverbruggen-rachiopy-7f3c210/rachiopy/notification.py000066400000000000000000000113471454522675200232530ustar00rootroot00000000000000"""Notification module handling /notification/ API calls.""" from rachiopy.rachioobject import RachioObject class Notification(RachioObject): """Notification class with methods for /notification/ API calls.""" def get_webhook_event_type(self): """Retrieve the list of events types. Event types that are available to any webhook for subscription. For more info of the content in the response see: https://rachio.readme.io/docs/publicnotificationwebhook_event_type :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ return self.get_request("notification/webhook_event_type") def get_device_webhook(self, dev_id: str): """Retrieve all webhooks for a device. For more info of the content in the response see: https://rachio.readme.io/docs/publicnotificationdeviceidwebhook :param dev_id: Device's unique id :type dev_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ path = f"notification/{dev_id}/webhook" return self.get_request(path) def add(self, dev_id: str, external_id: str, url: str, event_types): """Add a webhook to a device. externalId can be used as opaque data that is tied to your company, and passed back in each webhook event response. For more info of the content in the response see: https://rachio.readme.io/docs/publicnotificationwebhook :param dev_id: Device's unique id :type dev_id: str :param external_id: External company ID :type exteranl_id: str :param url: External webhook URL :type url: str :param event_type: Event types :type event_type: Object[] :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ payload = { "device": {"id": dev_id}, "externalId": external_id, "url": url, "eventTypes": event_types, } return self.post_request("notification/webhook", payload) def update(self, hook_id: str, external_id: str, url: str, event_types): """Update a webhook. For more info of the content in the response see: https://rachio.readme.io/docs/publicnotificationwebhook-1 :param hook_id: Webhook's unique id :type hook_id: str :param external_id: External company ID :type exteranl_id: str :param url: External webhook URL :type url: str :param event_type: Event types :type event_type: Object[] :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ payload = { "id": hook_id, "externalId": external_id, "url": url, "eventTypes": event_types, } return self.put_request("notification/webhook", payload) def delete(self, hook_id: str): """Remove a webhook. For more info of the content in the response see: https://rachio.readme.io/docs/publicnotificationwebhookid :param hook_id: Webhook's unique id :type hook_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ path = f"notification/webhook/{hook_id}" return self.delete_request(path) def get(self, hook_id: str): """Get a webhook. For more info of the content in the response see: https://rachio.readme.io/docs/publicnotificationdeviceidwebhook :param hook_id: Webhook's unique id :type hook_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ path = f"notification/webhook/{hook_id}" return self.get_request(path) rfverbruggen-rachiopy-7f3c210/rachiopy/person.py000066400000000000000000000025601454522675200220700ustar00rootroot00000000000000"""Person module handling /person/ API calls.""" from rachiopy.rachioobject import RachioObject class Person(RachioObject): """Person class with methods for /person/ API calls.""" def info(self): """Retrieve the id for the person entity currently logged in. For more info of the content in the response see: https://rachio.readme.io/docs/publicpersoninfo :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self.get_request("person/info") def get(self, user_id: str): """Retrieve the information for a person entity. For more info of the content in the response see: https://rachio.readme.io/docs/publicpersonid :param user_id: Person's unique id :type user_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"person/{user_id}" return self.get_request(path) rfverbruggen-rachiopy-7f3c210/rachiopy/program.py000066400000000000000000000066211454522675200222330ustar00rootroot00000000000000"""Program module for the smart hose timer.""" from rachiopy.rachioobject import RachioObject class Program(RachioObject): """Program class for the smart hose timer.""" def list_programs(self, valve_id: str): """Retreive the list of programs (schedules) for a valve. For more info of the content in the response see: https://rachio.readme.io/docs/programservice_listprograms :param valve_id: Valve's unique id :type valve_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ path = f"program/listPrograms/{valve_id}" return self.valve_get_request(path) def get_program(self, program_id: str): """Retreive the information for a specific program. For more info of the content in the response see: https://rachio.readme.io/docs/programservice_getprogram :param program_id: Program's unique id :type program_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"program/getProgram/{program_id}" return self.valve_get_request(path) def create_skip_overrides(self, program_id: str, timestamp: str): """Create manual skips for the specific program run time. You can retrieve the runtimes from SummaryService.getValveDayViews For more info of the content in the response see: https://rachio.readme.io/docs/programservice_createskipoverrides :param program_id: Program's unique id :type program_id: str :param timestamp: Timestamp of the run to skip :type timestamp: timestamp :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"programId": program_id, "timestamp": timestamp} return self.valve_post_request("program/createSkipOverrides", payload) def delete_skip_overrides(self, program_id: str, timestamp: str): """Cancel program skips for the specified program run time. You can retrieve upcoming skips from SummaryService.getValveDayViews For more info of the content in the response see: https://rachio.readme.io/docs/programservice_deleteskipoverrides :param program_id: Program's unique id :type program_id: str :param timestamp: Timestamp of the run skip to delete :type timestamp: timestamp :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"programId": program_id, "timestamp": timestamp} return self.valve_post_request("program/deleteSkipOverrides", payload) rfverbruggen-rachiopy-7f3c210/rachiopy/rachioobject.py000066400000000000000000000147361454522675200232260ustar00rootroot00000000000000"""RachioObject module containing a helper class for all API calls.""" import json from requests import Session _API_URL = "https://api.rach.io/1/public" _VALVE_URL = "https://cloud-rest.rach.io" class RachioObject: """The Rachio base object.""" def __init__(self, authtoken: str, http_session=None, timeout=25): """Rachioobject class initializer. :param authtoken: The API authentication token. :type authtoken: str :param http_session: The HTTP Session :type http_session: Session :param timeout: How long to wait for the server to send data before giving up, as a float, or a (connect timeout, read timeout) tuple. :type timeout: float :type timeout: tuple """ self.authtoken = authtoken self._headers = { "Content-Type": "application/json", "Authorization": f"Bearer {authtoken}", } self._http_session = http_session or Session() self.timeout = timeout def _request(self, path: str, method: str, body=None): """Make a request to the API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ if body is not None: body = json.dumps(body) url = f"{_API_URL}/{path}" response = self._http_session.request( method, url, headers=self._headers, data=body, timeout=self.timeout ) content_type = response.headers.get("content-type") headers = {k.lower(): v for k, v in response.headers.items()} headers["status"] = response.status_code if content_type and content_type.startswith("application/json"): return headers, response.json() return headers, response.text def get_request(self, path: str, body=None): """Make a GET request to the API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._request(path, "GET", body) def put_request(self, path: str, body=None): """Make a PUT request to the API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._request(path, "PUT", body) def post_request(self, path: str, body=None): """Make a POST request to the API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._request(path, "POST", body) def delete_request(self, path: str, body=None): """Make a DELETE request to the API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._request(path, "DELETE", body) def _valve_request(self, path: str, method: str, body=None): """Make a request to the API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ if body is not None: body = json.dumps(body) url = f"{_VALVE_URL}/{path}" response = self._http_session.request( method, url, headers=self._headers, data=body, timeout=self.timeout ) content_type = response.headers.get("content-type") headers = {k.lower(): v for k, v in response.headers.items()} headers["status"] = response.status_code if content_type and content_type.startswith("application/json"): return headers, response.json() return headers, response.text def valve_get_request(self, path: str, body=None): """Make a GET request to the valve API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._valve_request(path, "GET", body) def valve_put_request(self, path: str, body=None): """Make a PUT request to the valve API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._valve_request(path, "PUT", body) def valve_post_request(self, path: str, body=None): """Make a POST request to the valve API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._valve_request(path, "POST", body) def valve_delete_request(self, path: str, body=None): """Make a DELETE request to the valve API. :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ return self._valve_request(path, "DELETE", body) rfverbruggen-rachiopy-7f3c210/rachiopy/schedulerule.py000066400000000000000000000063041454522675200232460ustar00rootroot00000000000000"""Schedulerule module handling /scheduerule/ API calls.""" from decimal import Decimal from rachiopy.rachioobject import RachioObject class Schedulerule(RachioObject): """Schedulerule class with methods for /schedulerule/ API calls.""" def skip(self, sched_rule_id: str): """Skip a schedule rule (watering time). For more info of the content in the response see: https://rachio.readme.io/docs/scheduleruleskip :param sched_rule_id: Schedule rule's unique id :type sched_rule_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ payload = {"id": sched_rule_id} return self.put_request("schedulerule/skip", payload) def start(self, sched_rule_id: str): """Start a schedule rule (watering time). For more info of the content in the response see: https://rachio.readme.io/docs/schedulerulestart :param sched_rule_id: Schedule rule's unique id :type sched_rule_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ payload = {"id": sched_rule_id} return self.put_request("schedulerule/start", payload) def seasonal_adjustment(self, sched_rule_id: str, adjustment: Decimal): """Seasonal adjustment for a schedule rule (watering time). This adjustment amount will be applied to the overall run time of the selected schedule while overriding any current adjustments. For more info of the content in the response see: https://rachio.readme.io/docs/publicscheduleruleseasonal_adjustment :param sched_rule_id: Schedule rule's unique id :type sched_rule_id: str :param adjustment: Seasonal adjustment percent from 100% to -100% (valid data range is 1 to -1) :type adjustment: Decimal :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ payload = {"id": sched_rule_id, "adjustment": adjustment} return self.put_request("schedulerule/seasonal_adjustment", payload) def get(self, sched_rule_id: str): """Retrieve the information for a scheduleRule entity. For more info of the content in the response see: https://rachio.readme.io/docs/publicscheduleruleid :param sched_rule_id: Schedule rule's unique id :type sched_rule_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ path = f"schedulerule/{sched_rule_id}" return self.get_request(path) rfverbruggen-rachiopy-7f3c210/rachiopy/summary.py000066400000000000000000000020641454522675200222560ustar00rootroot00000000000000"""Smart Hose Timer scheudle summary calls.""" from rachiopy.rachioobject import RachioObject class SummaryServce(RachioObject): """Scheudle summary class.""" def get_valve_day_views(self, base_id: str, start, end): """List historical and upcoming valve runs and skips. For more info of the content in the response see: https://rachio.readme.io/docs/summaryservice_getvalvedayviews :param base_id: Base's unique id :type dev_id: str :param start: Start date :type start: Object[] :param end: End date :type end: Object[] :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body. :rtype: tuple """ payload = { "resourceId": {"baseStationId": base_id}, "start": start, "end": end, } return self.valve_post_request("summary/getValveDayViews", payload) rfverbruggen-rachiopy-7f3c210/rachiopy/valve.py000066400000000000000000000123311454522675200216740ustar00rootroot00000000000000"""Valve Service.""" from rachiopy.rachioobject import RachioObject class Valve(RachioObject): """Valve class for smart hose timer.""" def get_base_station(self, base_id: str): """Retreive the information for a specific base station. For more info of the content in the response see: https://rachio.readme.io/docs/valveservice_getbasestation :param base_id: Base station's unique id :type user_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"valve/getBaseStation/{base_id}" return self.valve_get_request(path) def get_valve(self, valve_id: str): """Retrieve the information for a specific smart valve. For more info of the content in the response see: https://rachio.readme.io/docs/valveservice_getvalve :param valve_id: Valve's unique id :type user_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"valve/getValve/{valve_id}" return self.valve_get_request(path) def list_base_stations(self, user_id: str): """Retrieve all base stations for a given user ID. For more info of the content in the response see: https://rachio.readme.io/docs/valveservice_listbasestations :param user_id: Person's unique id :type user_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"valve/listBaseStations/{user_id}" return self.valve_get_request(path) def list_valves(self, base_id: str): """Retreive all valves on a given base station. For more info of the content in the response see: https://rachio.readme.io/docs/valveservice_listvalves :param base_id: Base station's unique id :type user_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"valve/listValves/{base_id}" return self.valve_get_request(path) def set_default_runtime(self, valve_id: str, duration: int): """Set the runtime for a valve when the button is pressed. For more info of the content in the response see: https://rachio.readme.io/docs/valveservice_setdefaultruntime :param valve_id: Valve's unique id :type user_id: str :param duration: Duration in seconds :type duration: int :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"valveId": valve_id, "defaultRuntimeSeconds": duration} return self.valve_put_request("valve/setDefaultRuntime", payload) def start_watering(self, valve_id: str, duration: int): """Start a valve. For more info of the content in the response see: https://rachio.readme.io/docs/valveservice_startwatering :param valve_id: Valve's unique id :type user_id: str :param duration: Duration in seconds :type duration: int :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ assert 0 <= duration <= 86400, "duration must be in range 0-86400" payload = {"valveId": valve_id, "durationSeconds": duration} return self.valve_put_request("valve/startWatering", payload) def stop_watering(self, valve_id: str): """Stop a valve. For more info of the content in the response see: https://rachio.readme.io/docs/valveservice_stopwatering :param valve_id: Valve's unique id :type user_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"valveId": valve_id} return self.valve_put_request("valve/stopWatering", payload) rfverbruggen-rachiopy-7f3c210/rachiopy/zone.py000066400000000000000000000122761454522675200215420ustar00rootroot00000000000000"""Zone module handling /zone/ API calls.""" from decimal import Decimal from rachiopy.rachioobject import RachioObject class Zone(RachioObject): """Zone class with methods for /zone/ API calls.""" def start(self, zone_id: str, duration: int): """Start a zone. For more info of the content in the response see: https://rachio.readme.io/docs/zonestart :param zone_id: Zone's unique id :type zone_id: str :param duration: Duration in seconds (Range is 0 - 10800 (3 Hours) ) :type duration: int :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ assert 0 <= duration <= 10800, "duration must be in range 0-10800" payload = {"id": zone_id, "duration": duration} return self.put_request("zone/start", payload) def start_multiple(self, zones): """Start multiple zones. For more info of the content in the response see: https://rachio.readme.io/docs/publiczonestart_multiple :param zones: Zone's unique id, duration, and sort order :type zones: Object[] :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"zones": zones} return self.put_request("zone/start_multiple", payload) def schedule(self): """Create an empty zone schedule.""" return ZoneSchedule(self) def set_moisture_percent(self, zone_id: str, percent: Decimal): """Set zone moisture percent. For more info of the content in the response see: https://rachio.readme.io/docs/publiczonesetmoisturepercent :param zone_id: Zone's unique id :type zone_id: str :param percent: Soil moisture percent (Range is 0 - 1 ) :type percent: Decimal :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ assert 0 <= percent <= 1, "percent must be in range 0.0-1.0" payload = {"id": zone_id, "percent": percent} return self.put_request("zone/setMoisturePercent", payload) def set_moisture_level(self, zone_id: str, level: Decimal): """Set zone moisture level. For more info of the content in the response see: https://rachio.readme.io/docs/publiczonesetmoisturelevel :param zone_id: Zone's unique id :type zone_id: str :param level: Soil moisture level in mm (Range is 0 - Maximum Moisture in mm (depth of water + (10% depth of water)) :type level: Decimal :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ payload = {"id": zone_id, "level": level} return self.put_request("zone/setMoistureLevel", payload) def get(self, zone_id: str): """Retrieve the information for a zone entity. For more info of the content in the response see: https://rachio.readme.io/docs/publiczoneid :param zone_id: Zone's unique id :type zone_id: str :return: The return value is a tuple of (response, content), the first being and instance of the httplib2.Response class, the second being a string that contains the response entity body (Python object if it contains JSON). :rtype: tuple """ path = f"zone/{zone_id}" return self.get_request(path) class ZoneSchedule: """Help with starting multiple zones.""" def __init__(self, zone_api: Zone): """Zoneschedule class initializer.""" self._api = zone_api self._zones = [] def enqueue(self, zone_id: str, duration: int): """Add a zone and duration to the schedule. :param zone_id: Zone's unique id :type zone_id: str :param duration: Duration in seconds (Range is 0 - 10800 (3 Hours) ) :type duration: int """ self._zones.append((zone_id, duration)) def start(self): """Start the schedule.""" zones = [ {"id": data[0], "duration": data[1], "sortOrder": count} for (count, data) in enumerate(self._zones, 1) ] self._api.start_multiple(zones) def __enter__(self): """Allow a schedule to be created in a with block.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Allow the schedule to be executed by leaving with block.""" self.start() rfverbruggen-rachiopy-7f3c210/requirements.txt000066400000000000000000000000111454522675200216430ustar00rootroot00000000000000requests rfverbruggen-rachiopy-7f3c210/requirements_test.txt000066400000000000000000000000541454522675200227110ustar00rootroot00000000000000flake8 pydocstyle pylint coverage jsonschemarfverbruggen-rachiopy-7f3c210/setup.py000066400000000000000000000026461454522675200201110ustar00rootroot00000000000000"""Rachiopy setup script.""" from setuptools import find_packages, setup from datetime import datetime from pathlib import Path NOW = datetime.now().strftime("%m%d%Y%H%M%S") VERSION = "1.1.0" GITHUB_USERNAME = "rfverbruggen" GITHUB_REPOSITORY = "rachiopy" GITHUB_PATH = f"{GITHUB_USERNAME}/{GITHUB_REPOSITORY}" GITHUB_URL = f"https://github.com/{GITHUB_PATH}" DOWNLOAD_URL = f"{GITHUB_URL}/archive/{VERSION}.tar.gz" PROJECT_URLS = {"Bug Reports": f"{GITHUB_URL}/issues"} PACKAGES = find_packages(exclude=["tests", "tests.*"]) # read the contents of your README file this_directory = Path(__file__).parent long_description = (this_directory / "README.md").read_text() setup( name="RachioPy", version=VERSION, author="Robbert Verbruggen", author_email="rfverbruggen@icloud.com", packages=PACKAGES, install_requires=["requests"], url=GITHUB_URL, download_url=DOWNLOAD_URL, project_urls=PROJECT_URLS, license="MIT", description="A Python module for the Rachio API.", long_description=long_description, long_description_content_type='text/markdown', platforms="Cross Platform", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Topic :: Software Development", ], ) rfverbruggen-rachiopy-7f3c210/tests/000077500000000000000000000000001454522675200175315ustar00rootroot00000000000000rfverbruggen-rachiopy-7f3c210/tests/__init__.py000066400000000000000000000000341454522675200216370ustar00rootroot00000000000000"""Rachiopy test module.""" rfverbruggen-rachiopy-7f3c210/tests/constants.py000066400000000000000000000007541454522675200221250ustar00rootroot00000000000000"""Constants for test execution.""" from unittest.mock import Mock from requests import Response BASE_API_URL = "https://api.rach.io/1/public" VALVE_API_URL = "https://cloud-rest.rach.io" AUTHTOKEN = "1c1d9f3d-39c9-42b1-abc0-066f5a05cdef" RESPONSE200 = Mock(spec=Response) RESPONSE200.status_code = 200 RESPONSE200.headers = {"content-type": "application/json"} RESPONSE200.json.return_value = {} RESPONSE204 = Mock(spec=Response) RESPONSE204.headers = {} RESPONSE204.status_code = 204 rfverbruggen-rachiopy-7f3c210/tests/test_device.py000066400000000000000000000174561454522675200224160ustar00rootroot00000000000000"""Device object test module""" import unittest import uuid import json from unittest.mock import patch from random import randrange from rachiopy import Device from tests.constants import BASE_API_URL, AUTHTOKEN, RESPONSE200, RESPONSE204 class TestDeviceMethods(unittest.TestCase): """Class containing the Device object test cases.""" def setUp(self): self.device = Device(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.device.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_get(self, mock): """Test if the get method works as expected.""" mock.return_value = RESPONSE200 deviceid = str(uuid.uuid4()) self.device.get(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/{deviceid}", ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_current_schedule(self, mock): """Test if the current schedule method works as expected.""" mock.return_value = RESPONSE200 deviceid = str(uuid.uuid4()) self.device.current_schedule(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/{deviceid}/current_schedule", ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_event(self, mock): """Test if the event method works as expected.""" mock.return_value = RESPONSE200 deviceid = str(uuid.uuid4()) starttime = 1414818000000 endtime = 1415739608103 self.device.event(deviceid, starttime, endtime) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/" f"{deviceid}/event?startTime=" f"{starttime}&endTime=" f"{endtime}", ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_forecast(self, mock): """Test if the forecast method works as expected.""" mock.return_value = RESPONSE200 deviceid = str(uuid.uuid4()) self.device.forecast(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/{deviceid}/forecast?units=US", ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) self.device.forecast(deviceid, "US") args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/{deviceid}/forecast?units=US", ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) self.device.forecast(deviceid, "METRIC") args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/{deviceid}/forecast?units=METRIC", ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) # Check that values should be within range. self.assertRaises(AssertionError, self.device.forecast, deviceid, "") @patch("requests.Session.request") def test_stop_water(self, mock): """Test if the stop water method works as expected.""" mock.return_value = RESPONSE204 deviceid = str(uuid.uuid4()) self.device.stop_water(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/stop_water", ) self.assertEqual(args[0], "PUT") self.assertEqual(kwargs["data"], json.dumps({"id": deviceid})) @patch("requests.Session.request") def test_rain_delay(self, mock): """Test if the rain delay method works as expected.""" mock.return_value = RESPONSE204 deviceid = str(uuid.uuid4()) duration = randrange(604800) self.device.rain_delay(deviceid, duration) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/rain_delay", ) self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps({"id": deviceid, "duration": duration}) ) # Check that values should be within range. self.assertRaises(AssertionError, self.device.rain_delay, deviceid, -1) self.assertRaises( AssertionError, self.device.rain_delay, deviceid, 604801 ) @patch("requests.Session.request") def test_turn_on(self, mock): """Test if the turn on method works as expected.""" mock.return_value = RESPONSE204 deviceid = str(uuid.uuid4()) self.device.turn_on(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/on", ) self.assertEqual(args[0], "PUT") self.assertEqual(kwargs["data"], json.dumps({"id": deviceid})) @patch("requests.Session.request") def test_turn_off(self, mock): """Test if the turn off method works as expected.""" mock.return_value = RESPONSE204 deviceid = str(uuid.uuid4()) self.device.turn_off(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/off", ) self.assertEqual(args[0], "PUT") self.assertEqual(kwargs["data"], json.dumps({"id": deviceid})) @patch("requests.Session.request") def test_pause_zone_run(self, mock): """Test if the pause zone run method works as expected.""" mock.return_value = RESPONSE204 deviceid = str(uuid.uuid4()) duration = randrange(3600) self.device.pause_zone_run(deviceid, duration) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/pause_zone_run", ) self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps({"id": deviceid, "duration": duration}) ) # Check that values should be within range. self.assertRaises( AssertionError, self.device.pause_zone_run, deviceid, -1 ) self.assertRaises( AssertionError, self.device.pause_zone_run, deviceid, 3601 ) @patch("requests.Session.request") def test_resume_zone_run(self, mock): """Test if the resume zone run method works as expected.""" mock.return_value = RESPONSE204 deviceid = str(uuid.uuid4()) self.device.resume_zone_run(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/device/resume_zone_run", ) self.assertEqual(args[0], "PUT") self.assertEqual(kwargs["data"], json.dumps({"id": deviceid})) rfverbruggen-rachiopy-7f3c210/tests/test_flexschedulerule.py000066400000000000000000000022011454522675200245000ustar00rootroot00000000000000"""FlexScheduleRule object test module""" import unittest from unittest.mock import patch import uuid from rachiopy import FlexSchedulerule from tests.constants import BASE_API_URL, AUTHTOKEN, RESPONSE200 class TestFlexScheduleRuleMethods(unittest.TestCase): """Class containing the FlexScheduleRule object test cases.""" def setUp(self): self.flexschedulerule = FlexSchedulerule(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.flexschedulerule.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_get(self, mock): """Test if the get method works as expected.""" mock.return_value = RESPONSE200 flexscheduleruleid = uuid.uuid4() self.flexschedulerule.get(flexscheduleruleid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/flexschedulerule/{flexscheduleruleid}", ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) rfverbruggen-rachiopy-7f3c210/tests/test_notification.py000066400000000000000000000111611454522675200236300ustar00rootroot00000000000000"""Notification object test module""" import unittest from unittest.mock import patch import uuid import json from rachiopy import Notification from tests.constants import BASE_API_URL, AUTHTOKEN, RESPONSE200, RESPONSE204 class TestNotificationMethods(unittest.TestCase): """Class containing the Notification object test cases.""" def setUp(self): self.notification = Notification(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.notification.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_get_webhook_eventtype(self, mock): """Test if the get webhook eventtype method works as expected.""" mock.return_value = RESPONSE200 self.notification.get_webhook_event_type() args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/notification/webhook_event_type" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_get_device_webhook(self, mock): """Test if the get device webhook method works as expected.""" mock.return_value = RESPONSE200 deviceid = str(uuid.uuid4()) self.notification.get_device_webhook(deviceid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/notification/{deviceid}/webhook" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_add(self, mock): """Test if the add method works as expected.""" mock.return_value = RESPONSE200 deviceid = str(uuid.uuid4()) externalid = "Test ID" url = "https://www.mydomain.com/another_webhook_new_url" eventtypes = [{"id": "1"}, {"id": "2"}] self.notification.add(deviceid, externalid, url, eventtypes) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/notification/webhook") self.assertEqual(args[0], "POST") self.assertEqual( kwargs["data"], json.dumps( { "device": {"id": deviceid}, "externalId": externalid, "url": url, "eventTypes": eventtypes, } ), ) @patch("requests.Session.request") def test_update(self, mock): """Test if the update method works as expected.""" mock.return_value = RESPONSE200 hookid = str(uuid.uuid4()) externalid = "Test ID" url = "https://www.mydomain.com/another_webhook_new_url" eventtypes = [{"id": "1"}, {"id": "2"}] self.notification.update(hookid, externalid, url, eventtypes) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/notification/webhook") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps( { "id": hookid, "externalId": externalid, "url": url, "eventTypes": eventtypes, } ), ) @patch("requests.Session.request") def test_delete(self, mock): """Test if the delete method works as expected.""" mock.return_value = RESPONSE204 hookid = str(uuid.uuid4()) self.notification.delete(hookid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/notification/webhook/{hookid}" ) self.assertEqual(args[0], "DELETE") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_get(self, mock): """Test if the get method works as expected.""" mock.return_value = RESPONSE200 hookid = str(uuid.uuid4()) self.notification.get(hookid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/notification/webhook/{hookid}" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) rfverbruggen-rachiopy-7f3c210/tests/test_person.py000066400000000000000000000026631454522675200224570ustar00rootroot00000000000000"""Person object test module""" import unittest from unittest.mock import patch import uuid from rachiopy import Person from tests.constants import BASE_API_URL, AUTHTOKEN, RESPONSE200 class TestPersonMethods(unittest.TestCase): """Class containing the Rachio object test cases.""" def setUp(self): self.person = Person(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.person.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_info(self, mock): """Test if the info method works as expected.""" mock.return_value = RESPONSE200 self.person.info() args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/person/info") self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_get(self, mock): """Test if the get method works as expected.""" mock.return_value = RESPONSE200 personid = uuid.uuid4() self.person.get(personid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/person/{personid}") self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) rfverbruggen-rachiopy-7f3c210/tests/test_program.py000066400000000000000000000060671454522675200226220ustar00rootroot00000000000000"""Program object test module""" import unittest import uuid import json from unittest.mock import patch from rachiopy import Program from tests.constants import VALVE_API_URL, AUTHTOKEN, RESPONSE200 class TestProgramMethods(unittest.TestCase): """Class containing the Program object tests.""" def setUp(self): self.program = Program(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.program.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_list_programs(self, mock): """Test if the list programs method works as expected.""" mock.return_value = RESPONSE200 valveid = str(uuid.uuid4()) self.program.list_programs(valveid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{VALVE_API_URL}/program/listPrograms/{valveid}" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_get_program(self, mock): """Test if the get program method works as expected.""" mock.return_value = RESPONSE200 programid = str(uuid.uuid4()) self.program.get_program(programid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{VALVE_API_URL}/program/getProgram/{programid}" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_create_skip_overrides(self, mock): """Test if the create skip overrides method works as expected.""" mock.return_value = RESPONSE200 programid = str(uuid.uuid4()) timestamp = 1414818000000 self.program.create_skip_overrides(programid, timestamp) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{VALVE_API_URL}/program/createSkipOverrides" ) self.assertEqual(args[0], "POST") self.assertEqual( kwargs["data"], json.dumps({"programId": programid, "timestamp": timestamp}), ) @patch("requests.Session.request") def test_delete_skip_overrides(self, mock): """Test if the delete skip overrides method works as expected.""" mock.return_value = RESPONSE200 programid = str(uuid.uuid4()) timestamp = 1414818000000 self.program.delete_skip_overrides(programid, timestamp) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{VALVE_API_URL}/program/deleteSkipOverrides" ) self.assertEqual(args[0], "POST") self.assertEqual( kwargs["data"], json.dumps({"programId": programid, "timestamp": timestamp}), ) rfverbruggen-rachiopy-7f3c210/tests/test_rachio.py000066400000000000000000000017011454522675200224060ustar00rootroot00000000000000"""Rachio object test module""" import unittest from rachiopy import Rachio from tests.constants import AUTHTOKEN class TestRachioMethods(unittest.TestCase): """Class containing the Rachio object test cases.""" def test_init(self): """Test if the constructor works as expected.""" rachio = Rachio(AUTHTOKEN) self.assertEqual(rachio.authtoken, AUTHTOKEN) self.assertEqual(rachio.person.authtoken, AUTHTOKEN) self.assertEqual(rachio.device.authtoken, AUTHTOKEN) self.assertEqual(rachio.zone.authtoken, AUTHTOKEN) self.assertEqual(rachio.schedulerule.authtoken, AUTHTOKEN) self.assertEqual(rachio.flexschedulerule.authtoken, AUTHTOKEN) self.assertEqual(rachio.notification.authtoken, AUTHTOKEN) self.assertEqual(rachio.valve.authtoken, AUTHTOKEN) self.assertEqual(rachio.summary.authtoken, AUTHTOKEN) self.assertEqual(rachio.program.authtoken, AUTHTOKEN) rfverbruggen-rachiopy-7f3c210/tests/test_schedulerule.py000066400000000000000000000056521454522675200236360ustar00rootroot00000000000000"""ScheduleRule object test module""" import unittest from unittest.mock import patch import uuid import json from rachiopy import Schedulerule from tests.constants import BASE_API_URL, AUTHTOKEN, RESPONSE200, RESPONSE204 class TestScheduleRuleMethods(unittest.TestCase): """Class containing the ScheduleRule object test cases.""" def setUp(self): self.schedulerule = Schedulerule(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.schedulerule.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_get(self, mock): """Test if the get method works as expected.""" mock.return_value = RESPONSE200 scheduleruleid = str(uuid.uuid4()) self.schedulerule.get(scheduleruleid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/schedulerule/{scheduleruleid}" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_skip(self, mock): """Test if the skip method works as expected.""" mock.return_value = RESPONSE204 scheduleruleid = str(uuid.uuid4()) self.schedulerule.skip(scheduleruleid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/schedulerule/skip") self.assertEqual(args[0], "PUT") self.assertEqual(kwargs["data"], json.dumps({"id": scheduleruleid})) @patch("requests.Session.request") def test_start(self, mock): """Test if the start method works as expected.""" mock.return_value = RESPONSE204 scheduleruleid = str(uuid.uuid4()) self.schedulerule.start(scheduleruleid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/schedulerule/start") self.assertEqual(args[0], "PUT") self.assertEqual(kwargs["data"], json.dumps({"id": scheduleruleid})) @patch("requests.Session.request") def test_seasonal_adjustment(self, mock): """Test if the seasonal adjustment method works as expected.""" mock.return_value = RESPONSE200 scheduleruleid = str(uuid.uuid4()) adjustment = 0.2 self.schedulerule.seasonal_adjustment(scheduleruleid, adjustment) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{BASE_API_URL}/schedulerule/seasonal_adjustment" ) self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps({"id": scheduleruleid, "adjustment": adjustment}), ) rfverbruggen-rachiopy-7f3c210/tests/test_summary.py000066400000000000000000000026141454522675200226420ustar00rootroot00000000000000"""Summary object test module""" import unittest import uuid import json from unittest.mock import patch from rachiopy import SummaryServce from tests.constants import VALVE_API_URL, AUTHTOKEN, RESPONSE200 class TestSummaryMethod(unittest.TestCase): """Class containing the Summary object test.""" def setUp(self): self.summary = SummaryServce(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.summary.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_get_valve_day_views(self, mock): """Test if the get day views method works as expected.""" mock.return_value = RESPONSE200 deviceid = str(uuid.uuid4()) start = {"year": 2023, "month": 1, "day": 1} end = {"year": 2023, "month": 1, "day": 30} self.summary.get_valve_day_views(deviceid, start, end) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{VALVE_API_URL}/summary/getValveDayViews") self.assertEqual(args[0], "POST") self.assertEqual( kwargs["data"], json.dumps( { "resourceId": {"baseStationId": deviceid}, "start": start, "end": end, } ), ) rfverbruggen-rachiopy-7f3c210/tests/test_valve.py000066400000000000000000000135111454522675200222600ustar00rootroot00000000000000"""Valve object test module""" import unittest from unittest.mock import patch import uuid import json from random import randrange from rachiopy import Valve from tests.constants import VALVE_API_URL, AUTHTOKEN, RESPONSE200, RESPONSE204 class TestValveMethods(unittest.TestCase): """Class containing the Valve object test cases.""" def setUp(self): self.valve = Valve(AUTHTOKEN) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.valve.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_get_valve(self, mock): """Test if the get_valve method works as expected.""" mock.return_value = RESPONSE200 valveid = uuid.uuid4() self.valve.get_valve(valveid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{VALVE_API_URL}/valve/getValve/{valveid}") self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_get_base_station(self, mock): """Test if the get_base_station method works as expected.""" mock.return_value = RESPONSE200 baseid = str(uuid.uuid4()) self.valve.get_base_station(baseid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{VALVE_API_URL}/valve/getBaseStation/{baseid}" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_list_base_stations(self, mock): """Test if the list_base_stations method works as expected.""" mock.return_value = RESPONSE200 userid = str(uuid.uuid4()) self.valve.list_base_stations(userid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual( args[1], f"{VALVE_API_URL}/valve/listBaseStations/{userid}" ) self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_list_valves(self, mock): """Test if the list_valves method works as expected.""" mock.return_value = RESPONSE200 baseid = str(uuid.uuid4()) self.valve.list_valves(baseid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{VALVE_API_URL}/valve/listValves/{baseid}") self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_set_default_runtime(self, mock): """Test if the set_default_runtime method works as expected.""" mock.return_value = RESPONSE200 valveid = str(uuid.uuid4()) duration = randrange(86400) self.valve.set_default_runtime(valveid, duration) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{VALVE_API_URL}/valve/setDefaultRuntime") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps( {"valveId": valveid, "defaultRuntimeSeconds": duration} ), ) @patch("requests.Session.request") def test_set_default_runtime_exception(self, mock): """Test if the set_default_runtime method catches incorrect values.""" mock.return_value = RESPONSE200 valveid = str(uuid.uuid4()) duration1 = randrange(-50, -1) duration2 = randrange(86401, 86500) # Check that values should be within range. self.assertRaises( AssertionError, self.valve.start_watering, valveid, duration1 ) self.assertRaises( AssertionError, self.valve.start_watering, valveid, duration2 ) @patch("requests.Session.request") def test_start_watering(self, mock): """Test if the start_watering method works as expected.""" mock.return_value = RESPONSE204 valveid = str(uuid.uuid4()) duration = randrange(86400) self.valve.start_watering(valveid, duration) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{VALVE_API_URL}/valve/startWatering") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps({"valveId": valveid, "durationSeconds": duration}), ) @patch("requests.Session.request") def test_start_watering_exception(self, mock): """Test if the start_watering method catches incorrect values.""" mock.return_value = RESPONSE204 valveid = str(uuid.uuid4()) duration1 = randrange(-50, -1) duration2 = randrange(86401, 86500) # Check that values should be within range. self.assertRaises( AssertionError, self.valve.start_watering, valveid, duration1 ) self.assertRaises( AssertionError, self.valve.start_watering, valveid, duration2 ) @patch("requests.Session.request") def test_stop_watering(self, mock): """Test if the stop_watering method works as expected.""" mock.return_value = RESPONSE204 valveid = str(uuid.uuid4()) self.valve.stop_watering(valveid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{VALVE_API_URL}/valve/stopWatering") self.assertEqual(args[0], "PUT") self.assertEqual(kwargs["data"], json.dumps({"valveId": valveid})) rfverbruggen-rachiopy-7f3c210/tests/test_zone.py000066400000000000000000000155571454522675200221320ustar00rootroot00000000000000"""Zone object test module""" import unittest from unittest.mock import patch import uuid import random import json from random import randrange from rachiopy import Zone from rachiopy.zone import ZoneSchedule from tests.constants import BASE_API_URL, AUTHTOKEN, RESPONSE200, RESPONSE204 class TestZoneMethods(unittest.TestCase): """Class containing the Zone object test cases.""" def setUp(self): self.zone = Zone(AUTHTOKEN) zone1id = str(uuid.uuid4()) zone2id = str(uuid.uuid4()) zone3id = str(uuid.uuid4()) duration1 = randrange(10800) duration2 = randrange(10800) duration3 = randrange(10800) self.zones = [] self.zones.append((zone1id, duration1)) self.zones.append((zone2id, duration2)) self.zones.append((zone3id, duration3)) def test_init(self): """Test if the constructor works as expected.""" self.assertEqual(self.zone.authtoken, AUTHTOKEN) @patch("requests.Session.request") def test_get(self, mock): """Test if the get method works as expected.""" mock.return_value = RESPONSE200 zoneid = uuid.uuid4() self.zone.get(zoneid) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/zone/{zoneid}") self.assertEqual(args[0], "GET") self.assertEqual(kwargs["data"], None) @patch("requests.Session.request") def test_start(self, mock): """Test if the start method works as expected.""" mock.return_value = RESPONSE204 zoneid = str(uuid.uuid4()) duration = randrange(10800) self.zone.start(zoneid, duration) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/zone/start") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps({"id": zoneid, "duration": duration}) ) # Check that values should be within range. self.assertRaises(AssertionError, self.zone.start, zoneid, -1) self.assertRaises(AssertionError, self.zone.start, zoneid, 10801) @patch("requests.Session.request") def test_start_multiple(self, mock): """Test if the start multiple method works as expected.""" mock.return_value = RESPONSE204 zones = [ {"id": data[0], "duration": data[1], "sortOrder": count} for (count, data) in enumerate(self.zones, 1) ] self.zone.start_multiple(zones) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/zone/start_multiple") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps( { "zones": [ { "id": data[0], "duration": data[1], "sortOrder": count, } for (count, data) in enumerate(self.zones, 1) ] } ), ) @patch("requests.Session.request") def test_set_moisture_percent(self, mock): """Test if the set moisture percent method works as expected.""" mock.return_value = RESPONSE204 zoneid = str(uuid.uuid4()) percent = round(random.random(), 1) self.zone.set_moisture_percent(zoneid, percent) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/zone/setMoisturePercent") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps({"id": zoneid, "percent": percent}) ) # Check that values should be within range. self.assertRaises( AssertionError, self.zone.set_moisture_percent, zoneid, -0.1 ) self.assertRaises( AssertionError, self.zone.set_moisture_percent, zoneid, 1.1 ) @patch("requests.Session.request") def test_set_moisture_level(self, mock): """Test if the set moisture level method works as expected.""" mock.return_value = RESPONSE204 zoneid = str(uuid.uuid4()) level = round(random.uniform(0.0, 100.0), 2) self.zone.set_moisture_level(zoneid, level) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/zone/setMoistureLevel") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps({"id": zoneid, "level": level}) ) @patch("requests.Session.request") def test_zoneschedule(self, mock): """Test if the zoneschedule helper class works as expected.""" mock.return_value = RESPONSE204 zoneschedule = self.zone.schedule() for zone in self.zones: zoneschedule.enqueue(zone[0], zone[1]) zoneschedule.start() args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/zone/start_multiple") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps( { "zones": [ { "id": data[0], "duration": data[1], "sortOrder": count, } for (count, data) in enumerate(self.zones, 1) ] } ), ) @patch("requests.Session.request") def test_zoneschedule_with_statement(self, mock): """Test if the zoneschedule with statement works as expected.""" mock.return_value = RESPONSE204 with ZoneSchedule(self.zone) as zoneschedule: for zone in self.zones: zoneschedule.enqueue(zone[0], zone[1]) args, kwargs = mock.call_args # Check that the mock function is called with the rights args. self.assertEqual(args[1], f"{BASE_API_URL}/zone/start_multiple") self.assertEqual(args[0], "PUT") self.assertEqual( kwargs["data"], json.dumps( { "zones": [ { "id": data[0], "duration": data[1], "sortOrder": count, } for (count, data) in enumerate(self.zones, 1) ] } ), ) rfverbruggen-rachiopy-7f3c210/tox.ini000066400000000000000000000015641454522675200177100ustar00rootroot00000000000000[tox] envlist = clean, py{38,39,310,311,312}, pylint, flake8, pydocstyle, stats ignore_basepython_conflict = true [testenv:clean] commands= coverage erase [testenv] basepython = {env:PYTHON3_PATH:python3} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements_test.txt commands = {envbindir}/python -m unittest discover -v [] coverage run -a setup.py test [testenv:pylint] deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements_test.txt commands = pylint {posargs} rachiopy tests [testenv:flake8] deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements_test.txt commands = flake8 {posargs} [testenv:pydocstyle] deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements_test.txt commands = pydocstyle {posargs:rachiopy tests} [testenv:stats] commands= coverage report coverage html