Skip to main content

A library to authenticate and interact with STIHL iMow mowers using their WebAPI

Project description

STIHL iMow unofficial Python API wrapper

PyPI version shields.io Docs on GitHub pages CI PyPI download total PyPI pyversions PyPI license

This unofficial Python API was created to provide an interface to interact with the STIHL iMow mower WebAPI. This wrapper is able to receive the current state from the mowers and to send actions.
I wrote this library to implement an integration for the Home Assistant Smart Home System, which you can find here.

iMOW compatibility

STIHL uses different webapps for their iMOW generations. Currently only the iMOW RMI series are supported by this library, because i'm not able to reverse engineer the newer generation. This is simply because I do not own them.

If you use this webapp, https://app.imow.sithl.com, this library should work for your mower.

Also see here: Issue #13

Getting Started

These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.

API Documentation is available on: https://chrishapunkt.github.io/stihl-imow-webapi/imow

If you want to
"Buy Me A Coffee"

Prerequisites

Python 3.8+ is required to run this application, other than that there are no prerequisites for the project, as the dependencies are included in the repository.

Installing

To install the library is as simple as cloning the repository and running

pip install -e .

It is recommended to create an virtual environment prior to installing this library. Alternatively, you can also install this library via Pip:

pip install imow-webapi

And have fun!

Usage

Python Import and Usage

Import the module and instantiate the IMowApi() constructor with credentials. Afterwards, initiate the get_token() method. Or place credentials in the get_token() method.

from imow.api import IMowApi
from imow.common.actions import IMowActions
import asyncio
import aiohttp


async def main():
    async with aiohttp.ClientSession() as session:
        api = IMowApi(aiohttp_session=session, lang="de")
        # save token for later use if you want to recreate IMowApi(token=my_token) because the created token is valid for
        # 30 days
        token, expire_time = await api.get_token("email@account.stihl", "supersecret", return_expire_time=True)
        
        print(await api.get_token())
        
        mowers = await api.receive_mowers()
        mower = mowers[0]
        
        print(f"{mower.name} @ {mower.coordinateLatitude},{mower.coordinateLongitude}")
        print(f"Currently: {mower.stateMessage['short']}")
        await mower.update_setting("gpsProtectionEnabled", True)
        
        print(mower.stateMessage)
        print(mower.machineState)
        await mower.intent(IMowActions.TO_DOCKING)
        print(await mower.update_from_upstream())
        print(await mower.get_startpoints())
        

if __name__ == "__main__":
    asyncio.run(main())
Selection of outputs from above statements:
> Mährlin @ 54.123456,10.12345
> Currently: Hood blocked
> {'short': 'Hood blocked', 'long': 'The hood is blocked. Please check the hood and press the OK button on your machine (M1120).', 'legacyMessage': 'Abschaltung Automatikmode durch Bumper', 'errorId': '', 'error': False}
> HOOD_BLOCKED
> <imow.common.mowerstate.MowerState object at 0x000001B034C245F8>

Example: Receive startpoints and intent mowing

Save the following as myscript.sh and execute chmod +x myscript.sh. Make sure you install the api via pip3 install imow-webapi
Afterwards you can execute via ./myscript.sh

#!/usr/bin/env python3
from imow.api import IMowApi
from imow.common.actions import IMowActions
import asyncio
import aiohttp
import logging

logger = logging.getLogger("imow")
# Enable DEBUG output
logging.basicConfig(level=logging.DEBUG)

async def main():
    async with aiohttp.ClientSession() as session:
        api = IMowApi(aiohttp_session=session, lang="de")
        # save token for later use if you want to recreate IMowApi(token=my_token) because the created token is valid for
        # 30 days
        token, expire_time = await api.get_token("email@account.stihl", "supersecret", return_expire_time=True)
    
        print(await api.get_token())
    
        mowers = await api.receive_mowers()
        mower = mowers[0]
    
        print(f"{mower.name} @ {mower.coordinateLatitude},{mower.coordinateLongitude}")
        print(f"Currently: {mower.stateMessage['short']}")
    
        startpoints = await mower.get_startpoints()
        for i in range(len(startpoints)):
            print("Startpoint {}: {}".format(i, startpoints[i]))
        
        # if your mower supports the "startMowing" call, use this action (i.e iMow 600 series)
        await mower.intent(IMowActions.START_MOWING, starttime="2023-08-12 20:50")
        # await mower.intent(IMowActions.START_MOWING, endtime="2023-08-12 22:50")
        # await mower.intent(IMowActions.START_MOWING, starttime="2023-08-12 20:50", endtime="2023-08-12 22:50")

        # if your mower supports the "startMowingFromPoint" call, use this action (i.e iMow 400 series)
        await mower.intent(IMowActions.START_MOWING_FROM_POINT, duration=50)
        # await mower.intent(IMowActions.START_MOWING_FROM_POINT, startpoint=2)
        # await mower.intent(IMowActions.START_MOWING_FROM_POINT, duration=50, startpoint=2)


if __name__ == "__main__":
    asyncio.run(main())

Testing

For unit testing run pytest -s tests/test_unit*. For upstream integration testing, provide a /secrets.py with the following contents:

EMAIL = "email@account.stihl"
PASSWORD = "supersecret"
MOWER_NAME = "MyRobot"

and run pytest -s tests/test_integration* or pytest -s.

Built With

  • aiohttp
  • BeautifulSoup
  • asyncio

Versioning

Navigate to tags on this repository to see all available versions.

Authors

Mail Address GitHub Profile
chris@homeset.de ChrisHaPunkt

License

This project is licensed under the MIT License - see the LICENSE.md license file for more details.

Acknowledges

Thanks to

for repo structure inspiration

Changelog

Version 0.8.4 (2023-12-09)

Fix

  • Loosen the version requirements for dependency libs
  • update the tested python version placed inside setup and PyPi to python 3.12

Version 0.8.2 (2023-08-26)

Fix

  • validation of values in keyword arguments

BREAKING CHANGE

The iMow action IMowActions.START_MOWING now creates a startMowing action and no longer a startMowingFromPoint action. To issue a startMowingFromPoint, the new IMowAction.START_MOWING_FROM_POINT needs to be used.

Added

  • keywork arguments starttime and endtime in IMow.intent() call to be used with IMowActions.START_MOWING
  • Updated Readme with IMow.intent exampes
  • added debug log output in intent() call

Version 0.7.10 (2023-05-13)

  • Bugfix http_session not present on logout

Version 0.7.9 (2023-05-13)

  • remove unnecessary http_session closes

Version 0.7.8 (2022-03-06)

  • Implement a logout function ImowAPI.api_logout(). Use within re-authentication

Version 0.7.7 (2022-03-06)

  • Use more async withs

Version 0.7.6 (2022-03-05)

Dependency updates

  • Update dependencies to latest

Version 0.7.4 (2021-09-06)

Bugfxes

  • Allow handling of timestamps on mower intents (StartTime/Endtime)

Version 0.7.3 (2021-07-03)

Bugfxes

  • Always return a MowerState on settings update

Version 0.7.2 (2021-07-03)

await mower.update_setting("gpsProtectionEnabled", True)

Features

  • Possibility to update a specific settings for a mower like gpsProtection on/off

Version 0.7.0 (2021-06-30)

Breaking Changes

  • IMowApi.intent Parameter mower_action_id is renamed to mower_external_id to match the upstream api expectation.

Features

  • If an api_request is intended, and the used access_token expires in less than one day, it's automatically renewed.

Version 0.6.0 (2021-06-28)

  • mower.machineError = 'M1120',
    mower.machineState = 'HOOD_BLOCKED',
    mower.stateMessage: dict = {
        'short': 'Hood blocked',
        'long': 'The hood is blocked. Please check the hood and press the OK button on your machine (M1120).',
        'legacyMessage': 'Abschaltung Automatikmode durch Bumper',
        'errorId': 'M1120',
        'error': True
    }
    

Breaking Changes

  • Migrated all own MowerState attributes to camelCase to match the upstream attributes style.
    - MowerState.stateMessage = None
    - MowerState.machineError = None
    - MowerState.machineState = None
    

Version 0.5.2 (2021-06-15)

Bugfixes

  • Also quote password string in auth request to support more special chars

Version 0.5.1 (2021-06-13)

Features

  • mower.machine_error = 'M1120',
    mower.machine_state = 'HOOD_BLOCKED',
    mower.state_message: dict = {
        'short': 'Hood blocked',
        'long': 'The hood is blocked. Please check the hood and press the OK button on your machine (M1120).',
        'legacyMessage': 'Abschaltung Automatikmode durch Bumper',
        'errorId': 'M1120',
        'error': True
    }
    
    Provide a machine usable string from the short message in english

Version 0.5.0 (2021-06-12)

Breaking Changes

  • The MowerTask class is removed in favor of the new state_message property on th MowerState object.
  • The MowerState.get_current_task() method now returns the short property of the state message instead of a MowerTask property and is now longer an async method.

Features

  • mower.state_message -> dict
    {
        'short': 'Hood blocked',
        'long': 'The hood is blocked. Please check the hood and press the OK button on your machine (M1120).',
        'errorId': 'M1120',
        'error': True
    }
    
    The MowerState Class now provides a state_message property which gives a short andlong text for description (Besides an error indication and errorId). All error and status codes are now dynamically matched and human readable available. This makes the MowerTask obsolete and it is removed with this release.
  • api = IMowApi(lang="en") The imow api can now be instanced with a language code (fallback to en). The state_message property displays the messages in the corresponding language.

Version 0.4.5 (2021-06-01)

Features

  • Add 2 new identified Tasks within MowerTask (Thanks to @lausser)
  • Add check_api_maintenance() method to IMowAPI Class. Check if the api server is currently under maintenance. This method is automatically called if the api server returns a 500 error response for any request.
  • One should call the new close() method for the IMowAPI Class when finishing the api interactions to correctly close the http session.

Bugfixes

Version 0.4.4 (2021-05-28)

Features

  • Add validate_token() method to IMowAPI Class. Test if a token is valid and is able to call the webapi.

Version 0.4.3 (2021-05-27)

Features

  • Allow IMowAPI Class to use predefined aiohttp session when instantiating

Version 0.4.1 (2021-05-24)

Changes

  • Even more asynchronously with switch from requests to aiohttp

Version 0.4.0 (2021-05-17)

Breaking Changes

  • Reworked everything to use asyncio where possible. See Readme.md for new usage example.
  • Renamed MowerState method update to update_from_upstream
  • Renamed MowerState method get_status to get_current_status

Version 0.3.0 (2021-05-17)

Breaking Changes

  • Renamed Class/Enum MowerState to MowerTask because it describes the current task not the state
  • Renamed Class Mower to MowerState because it's just a snapshot of the last upstream state

Features

Bugfixes

  • Return a valid error message and raise if provided login credentials are wrong

Version 0.2.2 (2021-05-00)

  • Initial release

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

imow-webapi-0.8.4.tar.gz (27.0 kB view details)

Uploaded Source

Built Distribution

imow_webapi-0.8.4-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

Details for the file imow-webapi-0.8.4.tar.gz.

File metadata

  • Download URL: imow-webapi-0.8.4.tar.gz
  • Upload date:
  • Size: 27.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.12.0

File hashes

Hashes for imow-webapi-0.8.4.tar.gz
Algorithm Hash digest
SHA256 d92c68d3e4329268f673a40708b968d4a0453ec93925ab4e7e9d1e4c0dafa1d6
MD5 bfbf3ddb54a016ffe5c81049e4217e9a
BLAKE2b-256 c7ee57190917ef291b965811821ef0149b49216b461878299e293ee1c51971fb

See more details on using hashes here.

File details

Details for the file imow_webapi-0.8.4-py3-none-any.whl.

File metadata

  • Download URL: imow_webapi-0.8.4-py3-none-any.whl
  • Upload date:
  • Size: 22.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.12.0

File hashes

Hashes for imow_webapi-0.8.4-py3-none-any.whl
Algorithm Hash digest
SHA256 404c82730317aca9fc7ece7d316ab2ae36511b0af2538e27ed3cd9f191d93919
MD5 fc8fb8e21c38db6409f860c3ea6e3bd8
BLAKE2b-256 d74f2cc68c7bfa979a5ddfec9c00853058e51559c26094dd371e1b367f0b9afd

See more details on using hashes here.

Supported by

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