def __init__( self, hass: HomeAssistant, session: ClientSession, api_key: str, location_key: str, forecast: bool, ) -> None: """Initialize.""" self.location_key = location_key self.forecast = forecast self.is_metric = hass.config.units.is_metric self.accuweather = AccuWeather(api_key, session, location_key=self.location_key) # Enabling the forecast download increases the number of requests per data # update, we use 40 minutes for current condition only and 80 minutes for # current condition and forecast as update interval to not exceed allowed number # of requests. We have 50 requests allowed per day, so we use 36 and leave 14 as # a reserve for restarting HA. update_interval = timedelta(minutes=40) if self.forecast: update_interval *= 2 _LOGGER.debug("Data will be update every %s", update_interval) super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
async def test_get_location(): """Test with valid location data.""" with open("tests/data/location_data.json") as file: location_data = json.load(file) session = aiohttp.ClientSession() with aioresponses() as session_mock: # pylint:disable=line-too-long session_mock.get( "https://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=32-character-string-1234567890qw&q=52.0677904%252C19.4795644", payload=location_data, headers=HEADERS, ) accuweather = AccuWeather(VALID_API_KEY, session, latitude=LATITUDE, longitude=LONGITUDE) await accuweather.async_get_location() await session.close() assert accuweather.location_name == "Piątek" assert accuweather.location_key == "268068" assert accuweather.requests_remaining == 23
async def test_api_error(): """Test with API error""" payload = { "Code": "ServiceError", "Message": "API error.", } session = aiohttp.ClientSession() with aioresponses() as session_mock: # pylint:disable=line-too-long session_mock.get( "https://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=32-character-string-1234567890qw&q=52.0677904%252C19.4795644", payload=payload, status=404, ) accuweather = AccuWeather(VALID_API_KEY, session, latitude=LATITUDE, longitude=LONGITUDE) try: await accuweather.async_get_location() except ApiError as error: assert str( error.status) == "Invalid response from AccuWeather API: 404" await session.close()
async def test_requests_exceeded_error(): """Test with requests exceeded error""" payload = { "Code": "ServiceUnavailable", "Message": "The allowed number of requests has been exceeded.", } session = aiohttp.ClientSession() with aioresponses() as session_mock: # pylint:disable=line-too-long session_mock.get( "https://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=32-character-string-1234567890qw&q=52.0677904%252C19.4795644", payload=payload, status=503, ) accuweather = AccuWeather(VALID_API_KEY, session, latitude=LATITUDE, longitude=LONGITUDE) try: await accuweather.async_get_location() except RequestsExceededError as error: assert (str(error.status) == "The allowed number of requests has been exceeded") await session.close()
async def main(): async with ClientSession() as websession: try: accuweather = AccuWeather(API_KEY, websession, latitude=LATITUDE, longitude=LONGITUDE) current_conditions = await accuweather.async_get_current_conditions( ) forecast = await accuweather.async_get_forecast(metric=True) except ( ApiError, InvalidApiKeyError, InvalidCoordinatesError, ClientError, RequestsExceededError, ) as error: print(f"Error: {error}") else: print( f"Location: {accuweather.location_name} ({accuweather.location_key})" ) print(f"Requests remaining: {accuweather.requests_remaining}") print(f"Current: {current_conditions}") print(f"Forecast: {forecast}")
async def test_invalid_coordinates_2(): """Test with invalid coordinates.""" async with ClientSession() as session: try: AccuWeather(VALID_API_KEY, session, latitude=199.99, longitude=90.0) except InvalidCoordinatesError as error: assert str(error.status) == "Your coordinates are invalid"
async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle a flow initialized by the user.""" # Under the terms of use of the API, one user can use one free API key. Due to # the small number of requests allowed, we only allow one integration instance. if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") errors = {} if user_input is not None: websession = async_get_clientsession(self.hass) try: async with timeout(10): accuweather = AccuWeather( user_input[CONF_API_KEY], websession, latitude=user_input[CONF_LATITUDE], longitude=user_input[CONF_LONGITUDE], ) await accuweather.async_get_location() except (ApiError, ClientConnectorError, asyncio.TimeoutError, ClientError): errors["base"] = "cannot_connect" except InvalidApiKeyError: errors[CONF_API_KEY] = "invalid_api_key" except RequestsExceededError: errors[CONF_API_KEY] = "requests_exceeded" else: await self.async_set_unique_id( accuweather.location_key, raise_on_progress=False ) return self.async_create_entry( title=user_input[CONF_NAME], data=user_input ) return self.async_show_form( step_id="user", data_schema=vol.Schema( { vol.Required(CONF_API_KEY): str, vol.Optional( CONF_LATITUDE, default=self.hass.config.latitude ): cv.latitude, vol.Optional( CONF_LONGITUDE, default=self.hass.config.longitude ): cv.longitude, vol.Optional( CONF_NAME, default=self.hass.config.location_name ): str, } ), errors=errors, )
async def main(): async with ClientSession() as web_session: accuweather = AccuWeather( API_KEY, web_session, latitude=LATITUDE, longitude=LONGITUDE ) current_conditions = await accuweather.async_get_current_conditions() forecast = await accuweather.async_get_forecast(metric=True) # print(f"Location: {accuweather.location_name} ({accuweather.location_key})") print(f"Requests remaining: {accuweather.requests_remaining}") print(f"Current: {current_conditions}")
async def test_invalid_api_key_1(): """Test with invalid API key.""" async with ClientSession() as session: try: AccuWeather(INVALID_API_KEY, session, latitude=LATITUDE, longitude=LONGITUDE) except InvalidApiKeyError as error: assert (str(error.status) == "Your API Key must be a 32-character hexadecimal string")
async def test_invalid_api_key_2(): """Test with invalid API key.""" session = aiohttp.ClientSession() with aioresponses() as session_mock: # pylint:disable=line-too-long session_mock.get( "https://dataservice.accuweather.com/currentconditions/v1/268068?apikey=32-character-string-1234567890qw&details=true", status=401, ) accuweather = AccuWeather(VALID_API_KEY, session, location_key=LOCATION_KEY) try: await accuweather.async_get_current_conditions() except InvalidApiKeyError as error: assert str(error.status) == "Invalid API key" await session.close()
def __init__( self, hass: HomeAssistant, session: ClientSession, api_key: str, location_key: str, forecast: bool, name: str, ) -> None: """Initialize.""" self.location_key = location_key self.forecast = forecast self.accuweather = AccuWeather(api_key, session, location_key=location_key) self.device_info = DeviceInfo( entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, location_key)}, manufacturer=MANUFACTURER, name=name, # You don't need to provide specific details for the URL, # so passing in _ characters is fine if the location key # is correct configuration_url=("http://accuweather.com/en/" f"_/_/{location_key}/" f"weather-forecast/{location_key}/"), ) # Enabling the forecast download increases the number of requests per data # update, we use 40 minutes for current condition only and 80 minutes for # current condition and forecast as update interval to not exceed allowed number # of requests. We have 50 requests allowed per day, so we use 36 and leave 14 as # a reserve for restarting HA. update_interval = timedelta(minutes=40) if self.forecast: update_interval *= 2 _LOGGER.debug("Data will be update every %s", update_interval) super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
def __init__(self, hass, session, api_key, location_key, forecast: bool): """Initialize.""" self.location_key = location_key self.forecast = forecast self.is_metric = hass.config.units.is_metric self.accuweather = AccuWeather(api_key, session, location_key=self.location_key) # Enabling the forecast download increases the number of requests per data # update, we use 32 minutes for current condition only and 64 minutes for # current condition and forecast as update interval to not exceed allowed number # of requests. We have 50 requests allowed per day, so we use 45 and leave 5 as # a reserve for restarting HA. update_interval = (timedelta( minutes=64) if self.forecast else timedelta(minutes=32)) _LOGGER.debug("Data will be update every %s", update_interval) super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
async def test_get_current_conditions(): """Test with valid current condition data.""" with open("tests/data/current_condition_data.json") as file: current_condition_data = json.load(file) with open("tests/data/location_data.json") as file: location_data = json.load(file) session = aiohttp.ClientSession() with aioresponses() as session_mock: # pylint:disable=line-too-long session_mock.get( "https://dataservice.accuweather.com/currentconditions/v1/268068?apikey=32-character-string-1234567890qw&details=true", payload=current_condition_data, headers=HEADERS, ) session_mock.get( "https://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=32-character-string-1234567890qw&q=52.0677904%252C19.4795644", payload=location_data, headers=HEADERS, ) accuweather = AccuWeather(VALID_API_KEY, session, latitude=LATITUDE, longitude=LONGITUDE) current_conditions = await accuweather.async_get_current_conditions() await session.close() assert current_conditions["WeatherIcon"] == 7 assert isinstance(current_conditions["HasPrecipitation"], bool) assert not current_conditions["HasPrecipitation"] assert not current_conditions["PrecipitationType"] assert current_conditions["Temperature"]["Metric"]["Value"] == 23.1 assert current_conditions["Temperature"]["Metric"]["Unit"] == "C" assert current_conditions["Temperature"]["Imperial"]["Value"] == 74 assert current_conditions["Temperature"]["Imperial"]["Unit"] == "F" assert accuweather.requests_remaining == 23
async def test_get_forecast(): """Test with valid forecast data.""" with open("tests/data/forecast_data.json") as file: forecast_data = json.load(file) with open("tests/data/location_data.json") as file: location_data = json.load(file) session = aiohttp.ClientSession() with aioresponses() as session_mock: # pylint:disable=line-too-long session_mock.get( "https://dataservice.accuweather.com/forecasts/v1/daily/5day/268068?apikey=32-character-string-1234567890qw&details=true&metric=True", payload=forecast_data, headers=HEADERS, ) session_mock.get( "https://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=32-character-string-1234567890qw&q=52.0677904%252C19.4795644", payload=location_data, headers=HEADERS, ) accuweather = AccuWeather(VALID_API_KEY, session, latitude=LATITUDE, longitude=LONGITUDE) forecast = await accuweather.async_get_forecast() await session.close() assert forecast[0]["IconDay"] == 15 assert forecast[0]["PrecipitationProbabilityDay"] == 57 assert forecast[0]["WindDay"]["Speed"]["Value"] == 13.0 assert forecast[0]["TemperatureMax"]["Value"] == 24.8 assert forecast[0]["TemperatureMax"]["Unit"] == "C" assert forecast[0]["Ozone"]["Value"] == 23 assert accuweather.requests_remaining == 23