diff --git a/README.md b/README.md index cf38505..e9b20d0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ For use in Home Assistant: https://github.com/jm-73/Indego ## Basic information needed -The library requires python 3.7 or above. +The library requires python 3.8 or above. Required information | Description -----------------------|------------ @@ -100,7 +100,7 @@ Calendar(cal=3, days=[CalendarDay(day=0, day_name='monday', slots=[CalendarSlot( ``` ### indego.update_config() -Updates indego.config with settings for region, border cut, pin lock, id for the wire, bump and alarm mode. This call doesnt work on some Indegos, this function gives an error on Indego 1000, while it works on newer models (e.g., Indego S+ 400). +Updates indego.config with settings for region, border cut, pin lock, id for the wire, bump and alarm mode. This call doesn't work on some Indegos, this function gives an error on Indego 1000, while it works on newer models (e.g., Indego S+ 400). ```python Config(region=0, language=1, border_cut=0, is_pin_set=True, wire_id=4, bump_sensitivity=0, alarm_mode=True) @@ -174,7 +174,7 @@ Security(enabled=True, autolock=False) ``` ### indego.update_setup() -Updates the indego.setup with information if the Indego is set up, has pincode and wome other unknown values. +Updates the indego.setup with information if the Indego is set up, has pincode and some other unknown values. ```python Setup(hasOwner=True, hasPin=True, hasMap=True, hasAutoCal=False, hasIntegrityCheckPassed=True) @@ -197,7 +197,7 @@ State(state=64513, map_update_available=True, mowed=78, mowmode=0, xPos=162, yPo ### indego.update_state(force=False, longpoll=True, longpoll_timeout=120) Updates the indego.state with state of mower, % lawn mowed, position, runtime, map coordinates. -When longpoll is set to True, the indego.state must contain a value. It should contain the current state of the mower (you must run a "regular" update.state first). You send the current value to the API, and the API answers back when the state chenges. +When longpoll is set to True, the indego.state must contain a value. It should contain the current state of the mower (you must run a "regular" update.state first). You send the current value to the API, and the API answers back when the state changes. This function can be used instead of polling the status every couple of seconds: place one longpoll status request with a timeout of max. 300 seconds and the function will provide its return value when the status has been updated. As soon as an answer is received, the next longpoll status request can be placed. This should save traffic on both ends. @@ -237,19 +237,19 @@ Set all alerts to read, the function loops through the alert_id's from indego.al ### indego.put_command(command) Send commands. -Command |Description +Command |Description ------------|-------------------- -put_command('mow') |Start mowing -put_command('pause') |Pause mower +put_command('mow') |Start mowing +put_command('pause') |Pause mower put_command('returnToDock') |Return mower to dock ### indego.put_mow_mode(command) Send command. Accepted commands: -Command |Description +Command |Description ------------|-------------------- -put_mow_mode('true') |Smart Mow enabled -put_mow_mode('false') |Smart Mow disabled +put_mow_mode('true') |Smart Mow enabled +put_mow_mode('false') |Smart Mow disabled ## Not implemented yet @@ -311,7 +311,7 @@ Response: ## Attributes for reading data from locally cached API data -All functions that doesnt contain "update" first in name is collecting data from locally stored variables in the function. No API calls to Bosch or mower. +All functions that doesn't contain "update" first in name is collecting data from locally stored variables in the function. No API calls to Bosch or mower. attributes | Description -------------------------|----------------------------- @@ -324,7 +324,7 @@ AlmName | Show name. BareToolNumber | Show the model number. Battery | Show battery information. BatteryAmbientTemp | Show the ambient temp of the battery. -BatteryCycles | Dont know what this value is? +BatteryCycles | Don't know what this value is? BatteryDischarge | Show the current drawn in Ah. BatteryPercent | Show the raw value for percentage left. For Gen 1 this seems to be the battery voltage. For Gen 2 it seems to be the actual percentage left in the battery. BatteryPercentAdjusted | Show the adjusted value for percentage left. Calculated for Gen 1, and the actual percentage value for Gen 2. @@ -333,13 +333,13 @@ BatteryVoltage | Show voltage for the battery. For Gen 1 mowers this value seems ConvertBoschDateTime | Convert Bosch abbreviation for time to std 24h time Country | Show country for the account. Displayname | Show name for the account. -Email | Show email adress for the account. +Email | Show email address for the account. FirmwareAvailable | Show if there are any firmware updates available. FriendlyAlertErrorCode | Show user friendly alert error code description to be shown in HA GUI. -Garden | Dont know what this is? -HmiKeysn | Dont know what this is? +Garden | Don't know what this is? +HmiKeysn | Don't know what this is? Language | Show language for the account. -MapSvgCacheTs | Dont know what this is... +MapSvgCacheTs | Don't know what this is... MapUpdateAvailable | Show if there is an update of the map image. ModelDescription | Show user friendly model name. ModelVoltage | Show the predefined voltage limits in order to calculate battery percentage. @@ -351,11 +351,11 @@ MowerState | Show current state MowerStateDescription | Show simple description of current state. States available are Docked, Mowing, Stuck, Diagnostics mode, End of life, Software update. MowerStateDescriptionDetailed | Show description in detail of current state. MowingModeDescription | Show the user friendly mow mode description. -NeedsService | Show needs service flag. Dont know when it is used. +NeedsService | Show needs service flag. Don't know when it is used. NextMow | Show next planned mow session. -OptIn | Dont know what this are for? -OptInApp | Dont know what this are for? -Runtime | Show session and total rutime and charge time in minutes. +OptIn | Don't know what this are for? +OptInApp | Don't know what this are for? +Runtime | Show session and total runtime and charge time in minutes. RuntimeSession | show session runtime and charge time in minutes RuntimeTotal | Show total runtime and charge time in hours Serial | Show serial number @@ -369,7 +369,7 @@ YPos | Show y-position of mower. ### Not properly implemented yet - update_predicitive_calendar() + update_predictive_calendar() Get the calender for predicted mow sessions update_user_adjustment() @@ -419,3 +419,23 @@ put delete /alerts/ ``` +# Contributing +The project development is done in a poetry virtual environment. + +## setup your environment +To start development please install [poetry](https://python-poetry.org/docs/). + +* Install all dependency by running `poetry install`. +* activate the virtual environment by running `poetry shell` +* Run `python test_new.py` to test your setup. + +## setup your personal info + +Open [config.json](./config.json) and type in the information for your `indego`. + +```javascript +{ + "token": "mytoken", + "serial": "myserial" +} +``` \ No newline at end of file diff --git a/pyIndego/const.py b/pyIndego/const.py index 5bbb8c0..5464073 100644 --- a/pyIndego/const.py +++ b/pyIndego/const.py @@ -1,7 +1,7 @@ """Constants for pyIndego.""" from enum import Enum -import random -import string +from pyIndego.version import __version__ + class Methods(Enum): """Enum with HTTP methods.""" @@ -20,12 +20,14 @@ class Methods(Enum): CONTENT_TYPE = "Content-Type" COMMANDS = ("mow", "pause", "returnToDock") -DEFAULT_HEADER = { +DEFAULT_HEADERS = { CONTENT_TYPE: CONTENT_TYPE_JSON, # We need to change the user-agent! - # The Microsoft Azure proxy seems to block all requests (HTTP 403) for the default 'python-requests' user-agent. - # We also need to use a random agent for each client: https://github.com/jm-73/pyIndego/issues/119 - "User-Agent": ''.join(random.choices(string.ascii_uppercase + string.digits, k=12)) + # The Microsoft Azure proxy WAF seems to block all requests (HTTP 403) for the default 'python-requests' user-agent. + # See issues: + # - https://github.com/jm-73/pyIndego/issues/119 + # - https://github.com/jm-73/Indego/issues/204 + 'User-Agent': "HomeAssistant/Indego (%s)" % __version__ } DEFAULT_LOOKUP_VALUE = "Not in database." diff --git a/pyIndego/indego_async_client.py b/pyIndego/indego_async_client.py index 35dfcb8..6057c41 100644 --- a/pyIndego/indego_async_client.py +++ b/pyIndego/indego_async_client.py @@ -18,7 +18,7 @@ COMMANDS, CONTENT_TYPE_JSON, DEFAULT_CALENDAR, - DEFAULT_HEADER, + DEFAULT_HEADERS, DEFAULT_URL, Methods, ) @@ -490,7 +490,7 @@ async def _request( # noqa: C901 url = f"{self._api_url}{path}" if not headers: - headers = DEFAULT_HEADER.copy() + headers = self._default_headers.copy() headers["Authorization"] = "Bearer %s" % self._token try: diff --git a/pyIndego/indego_base_client.py b/pyIndego/indego_base_client.py index c4e7c4b..b2e4d9b 100644 --- a/pyIndego/indego_base_client.py +++ b/pyIndego/indego_base_client.py @@ -7,6 +7,7 @@ import requests from .const import ( + DEFAULT_HEADERS, DEFAULT_CALENDAR, DEFAULT_LOOKUP_VALUE, DEFAULT_URL, @@ -55,6 +56,7 @@ def __init__( api_url (str, optional): url for the api, defaults to DEFAULT_URL. raise_request_exceptions (bool): Should unexpected API request exception be raised or not. Default False to keep things backwards compatible. """ + self._default_headers = DEFAULT_HEADERS.copy() self._token = token self._token_refresh_method = token_refresh_method self._serial = serial @@ -470,6 +472,12 @@ def _update_battery_percentage_adjusted(self): self.generic_data.model_voltage ) + def set_default_header(self, key: str, value: str): + if value is None or value == "": + return + self._default_headers[key] = value + _LOGGER.debug("Default request headers updated: '%s'", self._default_headers) + def __repr__(self): """Create a string representing the mower.""" str1 = ( diff --git a/pyIndego/indego_client.py b/pyIndego/indego_client.py index 95efd17..31cacff 100644 --- a/pyIndego/indego_client.py +++ b/pyIndego/indego_client.py @@ -12,7 +12,7 @@ CONTENT_TYPE, CONTENT_TYPE_JSON, DEFAULT_CALENDAR, - DEFAULT_HEADER, + DEFAULT_HEADERS, Methods, ) from .indego_base_client import IndegoBaseClient @@ -405,7 +405,7 @@ def _request( # noqa: C901 url = f"{self._api_url}{path}" if not headers: - headers = DEFAULT_HEADER.copy() + headers = self._default_headers.copy() headers["Authorization"] = "Bearer %s" % self._token try: diff --git a/pyIndego/version.py b/pyIndego/version.py new file mode 100644 index 0000000..48fb20a --- /dev/null +++ b/pyIndego/version.py @@ -0,0 +1 @@ +__version__ = "3.1.3" \ No newline at end of file diff --git a/setup.py b/setup.py index 0db3b34..2de681b 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,21 @@ """Setup for pyIndego.""" +import os from setuptools import find_packages, setup -with open("README.md", "r") as fh: +current_dir = os.path.dirname(os.path.realpath(__file__)) + +# Little hack to load the version.py file without loading the __init__.py. +# As that would fail (when deps are not yet installed). +__version__ = None +with open(os.path.join(current_dir, "pyIndego", "version.py"), "r") as fh: + exec(fh.read()) + +with open(os.path.join(current_dir, "README.md"), "r") as fh: long_description = fh.read() setup( name="pyIndego", - version="3.1.1", + version=__version__, author="jm-73, sander1988", author_email="jens@myretyr.se", description="API for Bosch Indego mower",