async def test_zone_get_by_id_details(aresponses, authenticated_local_client): """Test getting advanced properties on a specific zone by ID.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/zone/1", "get", aresponses.Response(text=load_fixture("zone_id_response.json"), status=200), ) authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/zone/1/properties", "get", aresponses.Response( text=load_fixture("zone_id_properties_response.json"), status=200 ), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.zones.get(1, details=True) assert data["name"] == "Landscaping"
async def test_load_local_skip( aresponses, auth_login_json, provision_wifi_json, authenticated_local_client, event_loop, ): """Test skipping the loading of a local client if it's already loaded.""" authenticated_local_client.add( "{0}:{1}".format(TEST_HOST, TEST_PORT), "/api/4/auth/login", "post", aresponses.Response(text=json.dumps(auth_login_json), status=200), ) authenticated_local_client.add( "{0}:{1}".format(TEST_HOST, TEST_PORT), "/api/4/provision/wifi", "get", aresponses.Response(text=json.dumps(provision_wifi_json), status=200), ) async with authenticated_local_client: async with aiohttp.ClientSession(loop=event_loop) as websession: client = Client(websession) await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, True) controller = client.controllers[TEST_MAC] await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, True) assert len(client.controllers) == 1 assert client.controllers[TEST_MAC] == controller
async def test_load_remote_skip(aresponses, authenticated_remote_client): """Test skipping the loading of a remote client if it's already loaded.""" authenticated_remote_client.add( "my.rainmachine.com", "/login/auth", "post", aresponses.Response( text=load_fixture("remote_auth_login_1_response.json"), status=200 ), ) authenticated_remote_client.add( "my.rainmachine.com", "/devices/get-sprinklers", "post", aresponses.Response( text=load_fixture("remote_sprinklers_response.json"), status=200 ), ) async with authenticated_remote_client: async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_remote(TEST_EMAIL, TEST_PASSWORD, True) controller = client.controllers[TEST_MAC] await client.load_remote(TEST_EMAIL, TEST_PASSWORD, True) assert len(client.controllers) == 1 assert client.controllers[TEST_MAC] == controller
async def test_program_start_and_stop(aresponses, authenticated_local_client): """Test starting and stopping a program.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/program/1/start", "post", aresponses.Response( text=load_fixture("program_start_stop_response.json"), status=200), ) authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/program/1/stop", "post", aresponses.Response( text=load_fixture("program_start_stop_response.json"), status=200), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.programs.start(1) assert data["message"] == "OK" data = await controller.programs.stop(1) assert data["message"] == "OK"
async def test_load_local_skip(aresponses, authenticated_local_client): """Test skipping the loading of a local client if it's already loaded.""" authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/auth/login", "post", aresponses.Response(text=load_fixture("auth_login_response.json"), status=200), ) authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/provision/wifi", "get", aresponses.Response( text=load_fixture("provision_wifi_response.json"), status=200 ), ) async with authenticated_local_client: async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, True) controller = client.controllers[TEST_MAC] await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, True) assert len(client.controllers) == 1 assert client.controllers[TEST_MAC] == controller
async def test_zone_get_details(aresponses, authenticated_local_client): """Test getting all zones with details.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/zone", "get", aresponses.Response(text=load_fixture("zone_response.json"), status=200), ) authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/zone/properties", "get", aresponses.Response( text=load_fixture("zone_properties_response.json"), status=200 ), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) zones = await controller.zones.all(details=True) assert len(zones) == 2 assert zones[1]["name"] == "Landscaping" assert zones[1]["active"] is True assert zones[1]["ETcoef"] == 0.80000000000000004
async def test_watering_pause(aresponses, authenticated_local_client): """Test pausing and unpausing watering.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/watering/pauseall", "post", aresponses.Response( text=load_fixture("watering_pause_response.json"), status=200), ) authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/watering/pauseall", "post", aresponses.Response( text=load_fixture("watering_pause_response.json"), status=200), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.watering.pause_all(30) assert data["message"] == "OK" data = await controller.watering.unpause_all() assert data["message"] == "OK"
async def test_program_enable_disable(aresponses, authenticated_local_client): """Test enabling a program.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/program/1", "post", aresponses.Response( text=load_fixture("program_post_response.json"), status=200), ) authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/program/1", "post", aresponses.Response( text=load_fixture("program_post_response.json"), status=200), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) resp = await controller.programs.enable(1) assert resp["message"] == "OK" resp = await controller.programs.disable(1) assert resp["message"] == "OK"
async def async_get_controller(hass, ip_address, password, port, ssl): """Auth and fetch the mac address from the controller.""" websession = aiohttp_client.async_get_clientsession(hass) client = Client(session=websession) try: await client.load_local(ip_address, password, port=port, ssl=ssl) except RainMachineError: return None else: return get_client_controller(client)
async def test_load_local_failure(aresponses): """Test loading a local client and receiving a fail response.""" aresponses.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/auth/login", "post", aresponses.Response(text=None, status=500), ) with pytest.raises(RequestError): async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, False)
async def test_remote_error_unknown(aresponses, event_loop, remote_error_unknown): """Test that remote error handling works.""" aresponses.add( "my.rainmachine.com", "/login/auth", "post", aresponses.Response(text=json.dumps(remote_error_unknown), status=200), ) with pytest.raises(RequestError): async with aiohttp.ClientSession(loop=event_loop) as websession: client = Client(websession) await client.load_remote(TEST_EMAIL, TEST_PASSWORD)
async def test_load_local_failure(aresponses, unauthenticated_json, event_loop): """Test loading a local client and receiving a fail response.""" aresponses.add( "{0}:{1}".format(TEST_HOST, TEST_PORT), "/api/4/auth/login", "post", aresponses.Response(text=json.dumps(unauthenticated_json), status=401), ) with pytest.raises(RequestError): async with aiohttp.ClientSession(loop=event_loop) as websession: client = Client(websession) await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, False)
async def test_load_remote_failure(aresponses, unauthenticated_json, event_loop): """Test loading a remote client and receiving a fail response.""" aresponses.add( "my.rainmachine.com", "/login/auth", "post", aresponses.Response(text=json.dumps(unauthenticated_json), status=401), ) with pytest.raises(RequestError): async with aiohttp.ClientSession(loop=event_loop) as websession: client = Client(websession) await client.load_remote(TEST_EMAIL, TEST_PASSWORD)
async def test_legacy_login(authenticated_local_client): """Test loading a local client through the legacy method.""" async with authenticated_local_client: async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) assert controller._access_token == TEST_ACCESS_TOKEN assert controller.api_version == TEST_API_VERSION assert controller.hardware_version == TEST_HW_VERSION assert controller.mac == TEST_MAC assert controller.name == TEST_NAME assert controller.software_version == TEST_SW_VERSION
async def test_remote_error_unknown(aresponses): """Test that remote error handling works.""" aresponses.add( "my.rainmachine.com", "/login/auth", "post", aresponses.Response( text=load_fixture("remote_error_unknown_response.json"), status=200 ), ) with pytest.raises(RequestError): async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_remote(TEST_EMAIL, TEST_PASSWORD)
async def test_load_remote_failure(aresponses): """Test loading a remote client and receiving a fail response.""" aresponses.add( "my.rainmachine.com", "/login/auth", "post", aresponses.Response( text=load_fixture("unauthenticated_response.json"), status=401 ), ) with pytest.raises(TokenExpiredError): async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_remote(TEST_EMAIL, TEST_PASSWORD)
async def test_token_expired_explicit_exception(aresponses): """Test that the appropriate error is thrown when a token expires explicitly.""" aresponses.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/auth/login", "post", aresponses.Response( text=load_fixture("unauthenticated_response.json"), status=401 ), ) with pytest.raises(TokenExpiredError): async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, False)
async def test_token_expired_implicit_exception(authenticated_local_client): """Test that the appropriate error is thrown when a token expires implicitly.""" async with authenticated_local_client: with pytest.raises(TokenExpiredError): async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local( TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False ) controller = next(iter(client.controllers.values())) controller._access_token_expiration = datetime.now() - timedelta( hours=1 ) await controller._request("get", "random/endpoint")
async def test_request_timeout(authenticated_local_client): # noqa: D202 """Test whether the client properly raises an error on timeout.""" async def long_running_login(*args, **kwargs): # pylint: disable=unused-argument """Define a method that takes 0.5 seconds to execute.""" await asyncio.sleep(0.5) with mock.patch.object(aiohttp.ClientResponse, "json", long_running_login): async with authenticated_local_client: async with aiohttp.ClientSession() as session: with pytest.raises(RequestError): client = Client(session=session, request_timeout=0.1) await client.load_local( TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False )
async def test_load_remote(authenticated_remote_client, event_loop): """Test loading a remote client.""" async with authenticated_remote_client: async with aiohttp.ClientSession(loop=event_loop) as session: client = Client(session=session) await client.load_remote(TEST_EMAIL, TEST_PASSWORD) assert len(client.controllers) == 1 controller = client.controllers[TEST_MAC] assert controller._access_token == TEST_ACCESS_TOKEN assert controller.api_version == TEST_API_VERSION assert controller.hardware_version == TEST_HW_VERSION assert controller.mac == TEST_MAC assert controller.name == TEST_NAME assert controller.software_version == TEST_SW_VERSION
async def test_load_local(authenticated_local_client): """Test loading a local client.""" async with authenticated_local_client: async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, TEST_PORT, False) assert len(client.controllers) == 1 controller = client.controllers[TEST_MAC] assert controller._access_token == TEST_ACCESS_TOKEN assert controller.api_version == TEST_API_VERSION assert controller.hardware_version == TEST_HW_VERSION assert controller.mac == TEST_MAC assert controller.name == TEST_NAME assert controller.software_version == TEST_SW_VERSION
async def async_step_user(self, user_input=None): """Handle the start of the config flow.""" if not user_input: return self.async_show_form(step_id="user", data_schema=DATA_SCHEMA, errors={}) await self.async_set_unique_id(user_input[CONF_IP_ADDRESS]) self._abort_if_unique_id_configured() websession = aiohttp_client.async_get_clientsession(self.hass) client = Client(session=websession) try: await client.load_local( user_input[CONF_IP_ADDRESS], user_input[CONF_PASSWORD], port=user_input[CONF_PORT], ssl=user_input.get(CONF_SSL, True), ) except RainMachineError: return self.async_show_form( step_id="user", data_schema=DATA_SCHEMA, errors={CONF_PASSWORD: "******"}, ) # Unfortunately, RainMachine doesn't provide a way to refresh the # access token without using the IP address and password, so we have to # store it: return self.async_create_entry( title=user_input[CONF_IP_ADDRESS], data={ CONF_IP_ADDRESS: user_input[CONF_IP_ADDRESS], CONF_PASSWORD: user_input[CONF_PASSWORD], CONF_PORT: user_input[CONF_PORT], CONF_SSL: user_input.get(CONF_SSL, True), CONF_ZONE_RUN_TIME: user_input.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN), }, )
async def test_parsers_current(aresponses, authenticated_local_client): """Test getting all current parsers.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/parser", "get", aresponses.Response(text=load_fixture("parser_response.json"), status=200), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.parsers.current() assert len(data) == 1 assert data[0]["name"] == "NOAA Parser"
async def test_restrictions_raindelay(aresponses, authenticated_local_client): """Test getting any rain delay-related restrictions.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/restrictions/raindelay", "get", aresponses.Response( text=load_fixture("restrictions_raindelay_response.json"), status=200 ), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.restrictions.raindelay() assert data["delayCounter"] == -1
async def test_restrictions_global(aresponses, authenticated_local_client): """Test getting any global restrictions.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/restrictions/global", "get", aresponses.Response( text=load_fixture("restrictions_global_response.json"), status=200 ), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.restrictions.universal() assert data["freezeProtectTemp"] == 2
async def test_api_versions_no_explicit_session(aresponses, authenticated_local_client): """Test no explicit ClientSession.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/apiVer", "get", aresponses.Response( text=load_fixture("api_version_response.json"), status=200 ), ) client = Client() await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.api.versions() assert data["apiVer"] == "4.5.0" assert data["hwVer"] == 3 assert data["swVer"] == "4.0.925"
async def test_watering_queue(aresponses, authenticated_local_client): """Test getting the watering queue.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/watering/queue", "get", aresponses.Response( text=load_fixture("watering_queue_response.json"), status=200), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.watering.queue() assert not data
async def test_api_versions(aresponses, authenticated_local_client): """Test getting API, hardware, and software versions.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/apiVer", "get", aresponses.Response( text=load_fixture("api_version_response.json"), status=200 ), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.api.versions() assert data["apiVer"] == "4.5.0" assert data["hwVer"] == 3 assert data["swVer"] == "4.0.925"
async def test_program_get_by_id(aresponses, authenticated_local_client): """Test getting a program by its ID.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/program/1", "get", aresponses.Response(text=load_fixture("program_id_response.json"), status=200), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.programs.get(1) assert data["name"] == "Morning"
async def test_diagnostics_log(aresponses, authenticated_local_client): """Test retrieving the entire diagnostics log.""" async with authenticated_local_client: authenticated_local_client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/diag/log", "get", aresponses.Response(text=load_fixture("diag_log_response.json"), status=200), ) async with aiohttp.ClientSession() as session: client = Client(session=session) await client.load_local(TEST_HOST, TEST_PASSWORD, port=TEST_PORT, ssl=False) controller = next(iter(client.controllers.values())) data = await controller.diagnostics.log() assert data == "----"