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/examples/config.json b/examples/config.json index 8da2c7d..6838078 100644 --- a/examples/config.json +++ b/examples/config.json @@ -1,5 +1,4 @@ { - "username": "xxx@xxx.xx", - "password": "xxx", - "serial": "xxxxxxxxx" + "token": "", + "serial": "123456789" } \ No newline at end of file diff --git a/examples/print_network.py b/examples/print_network.py index 3c45774..b48c39b 100755 --- a/examples/print_network.py +++ b/examples/print_network.py @@ -2,9 +2,15 @@ # -*- coding: utf-8 -*- import json +import logging from pyIndego import IndegoClient +logging.basicConfig(filename="pyIndego.log", level=logging.DEBUG) +_LOGGER = logging.getLogger(__name__) +_LOGGER.setLevel(logging.DEBUG) + def country_operator(mcc, mnc): + """get the country and network operator from network ID's""" if mcc == 262: country = "Germany" if mnc == 1: @@ -40,30 +46,34 @@ def country_operator(mcc, mnc): def main(config): + """example of how to instantiate a indego object and get the network info""" with IndegoClient(**config) as indego: indego.update_network() - - (country, operator) = country_operator(indego.network.mcc, indego.network.mnc) - if country is not None: - print("Country is:", country) - if operator is not None: - print("Operator is:", operator) + + if indego.network is not None: + (country, operator) = country_operator(indego.network.mcc, indego.network.mnc) + if country is not None: + print("Country is:", country) + if operator is not None: + print("Operator is:", operator) + else: + print("Operator is unknown") else: - print("Operator is unknown") + print("Country and operator are unknown") + + print("Signal strength (rssi):", indego.network.rssi) + + print("Available Networks:") + for i in range(indego.network.networkCount): + (country, operator) = country_operator(int(str(indego.network.networks[i])[:3]), int(str(indego.network.networks[i])[3:5])) + if (country is not None) and (operator is not None): + print("\t", country, ":", operator) + else: + print("\tmcc =", str(indego.network.networks[i])[:3], ": mnc =", str(indego.network.networks[i])[3:5]) else: - print("Country and operator are unknown") - - print("Signal strength (rssi):", indego.network.rssi) - - print("Available Networks:") - for i in range(indego.network.networkCount): - (country, operator) = country_operator(int(str(indego.network.networks[i])[:3]), int(str(indego.network.networks[i])[3:5])) - if (country is not None) and (operator is not None): - print("\t", country, ":", operator) - else: - print("\tmcc =", str(indego.network.networks[i])[:3], ": mnc =", str(indego.network.networks[i])[3:5]) - + print("Error getting network info") + if __name__ == "__main__": - with open("config.json", "r") as config_file: - config = json.load(config_file) - main(config) + with open("config.json", "r",encoding="utf-8") as config_file: + _config = json.load(config_file) + main(_config) diff --git a/examples/print_next_mowing.py b/examples/print_next_mowing.py index 6b46860..f1763af 100755 --- a/examples/print_next_mowing.py +++ b/examples/print_next_mowing.py @@ -2,20 +2,29 @@ # -*- coding: utf-8 -*- import json +import logging from datetime import datetime, timezone, timedelta from pyIndego import IndegoClient +logging.basicConfig(filename="pyIndego.log", level=logging.DEBUG) +_LOGGER = logging.getLogger(__name__) +_LOGGER.setLevel(logging.DEBUG) + def main(config): + """example of how to instantiate a indego object and get the mowing time""" with IndegoClient(**config) as indego: - + indego.update_next_mow() print("Next mowing:", indego.next_mow) - - nowDate = datetime.now(timezone.utc) - if (indego.next_mow - nowDate) < timedelta(hours=2, minutes=30): - print("Less than two and a half hours before mowing.") + + now_date = datetime.now(timezone.utc) + if indego.next_mow is not None: + if (indego.next_mow - now_date) < timedelta(hours=2, minutes=30): + print("Less than two and a half hours before mowing.") + else: + print("Error getting mowing time") if __name__ == "__main__": - with open("config.json", "r") as config_file: - config = json.load(config_file) - main(config) + with open("config.json", "r", encoding="utf-8") as config_file: + _config = json.load(config_file) + main(_config) diff --git a/examples/print_predictive_schedule.py b/examples/print_predictive_schedule.py index 5112feb..85a3d93 100755 --- a/examples/print_predictive_schedule.py +++ b/examples/print_predictive_schedule.py @@ -2,32 +2,41 @@ # -*- coding: utf-8 -*- from datetime import datetime +import logging import json from pyIndego import IndegoClient +logging.basicConfig(filename="pyIndego.log", level=logging.DEBUG) +_LOGGER = logging.getLogger(__name__) +_LOGGER.setLevel(logging.DEBUG) + def main(config): + """example of how to instantiate a indego object and get the schedule information""" with IndegoClient(**config) as indego: indego.update_predictive_schedule() - + print("Times where SmartMowing is planing to mow the lawn:") - - for i in range(datetime.now().weekday(), datetime.now().weekday()+7): - for j in range(len(indego.predictive_schedule.schedule_days)): - if (indego.predictive_schedule.schedule_days[j].day == (i % 7)): - print("\t{}".format(indego.predictive_schedule.schedule_days[j].day_name)) - for k in range(len(indego.predictive_schedule.schedule_days[j].slots)): - print('\t\t{:%H:%M} - {:%H:%M}'.format(indego.predictive_schedule.schedule_days[j].slots[k].start, indego.predictive_schedule.schedule_days[j].slots[k].end)) - - print("Times that are excluded for mowing from SmartMowing:") - - for i in range(datetime.now().weekday(), datetime.now().weekday()+7): - for j in range(len(indego.predictive_schedule.exclusion_days)): - if (indego.predictive_schedule.exclusion_days[j].day == (i % 7)): - print("\t{}".format(indego.predictive_schedule.exclusion_days[j].day_name)) - for k in range(len(indego.predictive_schedule.exclusion_days[j].slots)): - print('\t\t{:%H:%M} - {:%H:%M} {}'.format(indego.predictive_schedule.exclusion_days[j].slots[k].start, indego.predictive_schedule.exclusion_days[j].slots[k].end, indego.predictive_schedule.exclusion_days[j].slots[k].Attr)) + + if indego.predictive_schedule is not None: + for i in range(datetime.now().weekday(), datetime.now().weekday()+7): + for j in range(len(indego.predictive_schedule.schedule_days)): + if (indego.predictive_schedule.schedule_days[j].day == (i % 7)): + print("\t{}".format(indego.predictive_schedule.schedule_days[j].day_name)) + for k in range(len(indego.predictive_schedule.schedule_days[j].slots)): + print('\t\t{:%H:%M} - {:%H:%M}'.format(indego.predictive_schedule.schedule_days[j].slots[k].start, indego.predictive_schedule.schedule_days[j].slots[k].end)) + + print("Times that are excluded for mowing from SmartMowing:") + + for i in range(datetime.now().weekday(), datetime.now().weekday()+7): + for j in range(len(indego.predictive_schedule.exclusion_days)): + if (indego.predictive_schedule.exclusion_days[j].day == (i % 7)): + print("\t{}".format(indego.predictive_schedule.exclusion_days[j].day_name)) + for k in range(len(indego.predictive_schedule.exclusion_days[j].slots)): + print('\t\t{:%H:%M} - {:%H:%M} {}'.format(indego.predictive_schedule.exclusion_days[j].slots[k].start, indego.predictive_schedule.exclusion_days[j].slots[k].end, indego.predictive_schedule.exclusion_days[j].slots[k].Attr)) + else: + print("Error getting predictive schedule info") if __name__ == "__main__": - with open("config.json", "r") as config_file: - config = json.load(config_file) - main(config) + with open("config.json", "r",encoding="utf-8") as config_file: + _config = json.load(config_file) + main(_config) diff --git a/pyIndego/const.py b/pyIndego/const.py index e27ecf6..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." @@ -132,7 +134,6 @@ class Methods(Enum): 99999: "Offline", } - MOWER_STATE_DESCRIPTION = { 0: "Docked", 101: "Docked", @@ -216,7 +217,6 @@ class Methods(Enum): "firmware.updateComplete": "Software update complete", } - DAY_MAPPING = { 0: "monday", 1: "tuesday", diff --git a/pyIndego/indego_async_client.py b/pyIndego/indego_async_client.py index fb3a4b4..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, ) @@ -61,10 +61,23 @@ def __init__( self._session = aiohttp.ClientSession(raise_for_status=False) self._should_close_session = True + async def __aenter__(self): + """Enter for async with.""" + await self.start() + return self + async def __aexit__(self, exc_type, exc_value, traceback): """Exit for async with.""" await self.close() + async def start(self): + """get the OAuth-token""" + if self._token_refresh_method is not None: + _LOGGER.debug("Refreshing token") + self._token = await self._token_refresh_method() + else: + _LOGGER.debug("Token refresh is NOT available") + async def close(self): """Close the aiohttp session.""" if self._should_close_session: @@ -472,16 +485,12 @@ async def _request( # noqa: C901 _LOGGER.warning("Five attempts done, please try again later") return None - if self._token_refresh_method is not None: - _LOGGER.debug("Refreshing token") - self._token = await self._token_refresh_method() - else: - _LOGGER.debug("Token refresh is NOT available") + await self.start() 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", diff --git a/test_new.py b/test_new.py index a281674..e3caaba 100644 --- a/test_new.py +++ b/test_new.py @@ -1,8 +1,6 @@ import json import logging -import pytz import pytz # $ pip install pytz -import tzlocal # $ pip install tzlocal from pyIndego import IndegoClient from datetime import datetime import time @@ -86,7 +84,7 @@ def main(config): #print("=[indego.update_next_mow]===") #indego.update_next_mow() #print(indego.next_mow) - + # Wakes mower! # # print(" ") # print("=[operating data]====") @@ -108,7 +106,7 @@ def main(config): #print("=[updates]====") #indego.update_updates_available() #print(indego.update_available) - + #print(" ") #print("=[user]====") #indego.update_user() @@ -118,7 +116,7 @@ def main(config): if __name__ == "__main__": with open("config.json", "r") as config_file: config = json.load(config_file) - + for i in range(10): now = datetime.now() current_time = now.strftime("%H:%M:%S") diff --git a/test_new_aio.py b/test_new_aio.py index a0388b4..c8d29c4 100644 --- a/test_new_aio.py +++ b/test_new_aio.py @@ -21,7 +21,7 @@ async def main(config): async with IndegoAsyncClient(**config) as indego: #Update and cache all calls from API #await indego.update_all() - + # await indego.update_alerts() # print("alerts: ", indego.alerts) ###### FUNCTION! print("alert count: ", indego.alerts_count) @@ -53,7 +53,7 @@ async def main(config): # await indego.update_predictive_calendar() # print("predictive calendar: ", indego.predictive_calendar) -# +# # await indego.update_predictive_schedule() # print("predictive schedule: ", indego.predictive_schedule) @@ -72,18 +72,18 @@ async def main(config): # print("serial: ", indego.serial) #print("xPos: ", indego.state.xPos) #print("yPos: ", indego.state.yPos) - + f = open('percentage.log', 'a') - + from time import gmtime, strftime logtime = strftime("%H:%M", gmtime()) print(logtime , end = ' ') print("Mowed: ", indego.state.mowed) - - - - + + + + f.write(str(logtime) + " - ") f.write(str(indego.state.mowed) + "\n") f.close() @@ -102,7 +102,7 @@ async def main(config): # print("state description: ", indego.state_description) # print("state description detail: ", indego.state_description_detail) - + # await indego.update_updates_available() # print("update available: ", indego.update_available) @@ -110,11 +110,9 @@ async def main(config): # print("user: ", indego.user) - if __name__ == "__main__": with open("config.json", "r") as config_file: config = json.load(config_file) # config.pop("serial") # config["username"] = "sdjbfajhbsdf" - loop = asyncio.get_event_loop() - loop.run_until_complete(main(config)) + asyncio.run(main(config)) \ No newline at end of file diff --git a/tests/test_indego.py b/tests/test_indego.py index 70db5ee..7932939 100644 --- a/tests/test_indego.py +++ b/tests/test_indego.py @@ -5,6 +5,7 @@ from socket import error as SocketError import pytest +import pytest_asyncio from aiohttp import ( ClientOSError, ClientResponseError, @@ -43,6 +44,8 @@ _LOGGER = logging.getLogger(__name__) +pytest_plugins = ('pytest_asyncio',) + alert = { "alm_sn": "test_sn", "alert_id": "5efda84ffbf591182723be89", @@ -366,7 +369,7 @@ "mow_trig": True, } -test_config = {"username": "testname", "password": "testpassword", "api_url": "", "serial": "123456789"} +test_config = {"serial": "123456789", "token": "testtoken"} class AsyncMock(MagicMock): @@ -666,6 +669,7 @@ def test_date_parsing(self, date_str, date_dt): (False, IndegoAsyncClient.update_all, "user", None, None), ], ) + @pytest.mark.asyncio async def test_client_update_functions( self, sync, func, attr, ret_value, assert_value ): @@ -674,8 +678,6 @@ async def test_client_update_functions( resp = MockResponseSync(ret_value, 200) with patch("requests.request", return_value=resp): indego = IndegoClient(**test_config) - indego._online = True - indego._userid = "test_user_id" func(indego) assert getattr(indego, attr) == assert_value if attr == "state": @@ -687,9 +689,6 @@ async def test_client_update_functions( "pyIndego.IndegoAsyncClient.start", return_value=True ): async with IndegoAsyncClient(**test_config) as indego: - indego._contextid = "askdjfbaks" - indego._online = True - indego._userid = "test_user_id" await func(indego) assert getattr(indego, attr) == assert_value if attr == "state": @@ -749,6 +748,7 @@ async def test_client_update_functions( ), ], ) + @pytest.mark.asyncio async def test_client_update_state_params( self, sync, func, param, attr, ret_value, assert_value ): @@ -757,8 +757,6 @@ async def test_client_update_state_params( resp = MockResponseSync(ret_value, 200) with patch("requests.request", return_value=resp): indego = IndegoClient(**test_config) - indego._online = True - indego._userid = "test_user_id" func(indego, **param) assert getattr(indego, attr) == assert_value else: @@ -767,9 +765,6 @@ async def test_client_update_state_params( "pyIndego.IndegoAsyncClient.start", return_value=True ): async with IndegoAsyncClient(**test_config) as indego: - indego._contextid = "askdjfbaks" - indego._online = True - indego._userid = "test_user_id" await func(indego, **param) assert getattr(indego, attr) == assert_value @@ -780,14 +775,13 @@ async def test_client_update_state_params( (False, IndegoAsyncClient.update_user, "user", user, User(**user)), ], ) + @pytest.mark.asyncio async def test_client_replace(self, sync, func, attr, ret_value, assert_value): """Test the base client functions with 200.""" if sync: resp = MockResponseSync(ret_value, 200) with patch("requests.request", return_value=resp): indego = IndegoClient(**test_config) - indego._online = True - indego._userid = "test_user_id" func(indego) assert getattr(indego, attr) == assert_value func(indego) @@ -798,9 +792,6 @@ async def test_client_replace(self, sync, func, attr, ret_value, assert_value): "pyIndego.IndegoAsyncClient.start", return_value=True ): async with IndegoAsyncClient(**test_config) as indego: - indego._contextid = "askdjfbaks" - indego._online = True - indego._userid = "test_user_id" await func(indego) assert getattr(indego, attr) == assert_value await func(indego) @@ -825,22 +816,19 @@ async def test_client_replace(self, sync, func, attr, ret_value, assert_value): (False, 504, IndegoAsyncClient.update_user, "user", user), ], ) + @pytest.mark.asyncio async def test_client_responses(self, sync, response, func, attr, ret_value): """Test the request functions with different responses.""" if sync: resp = MockResponseSync(ret_value, response) with patch("requests.request", return_value=resp): indego = IndegoClient(**test_config) - indego._online = True - indego._userid = "test_user_id" func(indego) assert getattr(indego, attr) == None else: resp = MockResponseAsync(ret_value, response) with patch("aiohttp.ClientSession.request", return_value=resp): async with IndegoAsyncClient(**test_config) as indego: - indego._online = True - indego._userid = "test_user_id" await func(indego) assert getattr(indego, attr) == None @@ -858,14 +846,13 @@ async def test_client_responses(self, sync, response, func, attr, ret_value): (SocketError), ], ) + @pytest.mark.asyncio async def test_a_client_response_errors(self, error): """Test the request functions with different responses.""" with patch("aiohttp.ClientSession.request", side_effect=error), patch( "asyncio.sleep", new_callable=AsyncMock ): async with IndegoAsyncClient(**test_config) as indego: - indego._online = True - indego._userid = "test_user_id" resp = await indego._request( method=Methods.GET, path="alerts", timeout=1 ) @@ -880,8 +867,6 @@ def test_client_response_errors(self, error): "time.sleep", new_callable=SyncMock ): indego = IndegoClient(**test_config) - indego._online = True - indego._userid = "test_user_id" resp = indego._request(method=Methods.GET, path="alerts", timeout=1) assert resp is None @@ -894,13 +879,12 @@ def test_client_response_errors(self, error): (None, False, 0, ValueError), ], ) + @pytest.mark.asyncio async def test_alert_functions(self, alerts, loaded, index, error): """Test the function for handling alerts.""" resp = MockResponseSync(True, 200) with patch("requests.request", return_value=resp): indego = IndegoClient(**test_config) - indego._online = True - indego._userid = "test_user_id" indego.alerts = alerts indego._alerts_loaded = loaded try: @@ -944,8 +928,6 @@ async def test_alert_functions(self, alerts, loaded, index, error): "pyIndego.IndegoAsyncClient.start", return_value=True ): async with IndegoAsyncClient(**test_config) as indego: - indego._online = True - indego._userid = "test_user_id" indego.alerts = alerts indego._alerts_loaded = loaded try: @@ -1003,13 +985,12 @@ async def test_alert_functions(self, alerts, loaded, index, error): ("pred_cal", {"cals": 1}, ValueError), ], ) + @pytest.mark.asyncio async def test_commands(self, command, param, error): """Test the function for handling alerts.""" resp = MockResponseSync(True, 200) with patch("requests.request", return_value=resp): indego = IndegoClient(**test_config) - indego._online = True - indego._userid = "test_user_id" if command == "command": try: indego.put_command(param) @@ -1043,8 +1024,6 @@ async def test_commands(self, command, param, error): "pyIndego.IndegoAsyncClient.start", return_value=True ): async with IndegoAsyncClient(**test_config) as indego: - indego._online = True - indego._userid = "test_user_id" if command == "command": try: await indego.put_command(param) @@ -1073,46 +1052,11 @@ async def test_commands(self, command, param, error): except error: assert True - @pytest.mark.parametrize( - "config", - [ - ({"username": "testname", "password": "testpassword", "api_url": ""}), - ( - { - "username": "testname", - "password": "testpassword", - "serial": "123456789", - "api_url": "", - } - ), - ], - ) - async def test_login(self, config): - """Test the function for handling alerts.""" - resp_json = {"contextId": "98765", "userId": "12345"} - resp_get = [{"alm_sn": "123456789"}] - resp_login_s = MockResponseSync(resp_json, 200) - with patch("requests.request", return_value=resp_login_s), patch( - "pyIndego.IndegoClient.get", return_value=resp_get - ): - indego = IndegoClient(**config) - indego.login() - assert indego._userid == "12345" - assert indego.serial == "123456789" - - resp_login_a = MockResponseAsync(resp_json, 200) - with patch("aiohttp.ClientSession.request", return_value=resp_login_a), patch( - "pyIndego.IndegoAsyncClient.start", return_value=True - ), patch("pyIndego.IndegoAsyncClient.get", return_value=resp_get): - async with IndegoAsyncClient(**config) as indego: - await indego.login() - assert indego._userid == "12345" - assert indego.serial == "123456789" - @pytest.mark.parametrize( "config, param, error", [(None, None, ValueError), (None, "test.svg", None), ("test.svg", None, None)], ) + @pytest.mark.asyncio async def test_download(self, config, param, error): """Test the function for download map.""" conf = test_config.copy()