async def async_setup_entry(hass: core.HomeAssistant, entry: config_entries.ConfigEntry, async_add_entities): config = hass.data[DOMAIN][entry.entry_id] if entry.options: config.update(entry.options) session = async_get_clientsession(hass) ukhotides = UkhoTides(session, config[CONF_API_KEY]) sensors = [] for station in config[CONF_STATIONS]: coordinator = UkhoTidesDataUpdateCoordinator(hass, ukhotides, station) if CONF_STATION_NAME in coordinator.station: name = station[CONF_STATION_NAME] else: name = (await ukhotides.async_get_station( coordinator.station[CONF_STATION_ID])).name sensors.append(UkhoTidesSensor(coordinator, name)) async_add_entities(sensors, update_before_add=True)
async def test_givenValidApiKey_whenRequestStation_thenReturnStation( mock_session, mock_station_data, url, level): mock_session.get(url=url, status=HTTPStatus.OK, payload=mock_station_data) session = ClientSession() client = UkhoTides(session, "api_key", level) station = await client.async_get_station("0001") assert station == Station.from_dict(mock_station_data)
async def test_givenInvalidApiKey_whenRequestStation_thenThrowInvalidApiKeyError( mock_session, mock_station_data, url, level): mock_session.get(url=url, status=HTTPStatus.UNAUTHORIZED) session = ClientSession() client = UkhoTides(session, "api_key", level) with pytest.raises(InvalidApiKeyError): station = await client.async_get_station("0001")
async def test_givenValidApiKey_whenRequestBogusStation_thenThrowStationNotFound( mock_session, mock_station_data, url, level): mock_session.get(url=url, status=HTTPStatus.NOT_FOUND) session = ClientSession() client = UkhoTides(session, "api_key", level) with pytest.raises(StationNotFoundError): station = await client.async_get_station("0001")
async def test_givenTooManyRequests_whenRequestStation_thenThrowTooManyRequestsError( mock_session, mock_station_data, url, level): mock_session.get(url=url, status=HTTPStatus.TOO_MANY_REQUESTS) session = ClientSession() client = UkhoTides(session, "api_key", level) with pytest.raises(TooManyRequestsError): station = await client.async_get_station("0001")
async def test_givenQuotaExceeded_whenRequestStation_thenThrowApiQuotaExceededError( mock_session, mock_station_data, url, level): mock_session.get(url=url, status=HTTPStatus.FORBIDDEN) session = ClientSession() client = UkhoTides(session, "api_key", level) with pytest.raises(ApiQuotaExceededError): station = await client.async_get_station("0001")
async def test_givenValidApiKey_whenRequestTidalHeight_thenReturnTidalHeight( mock_session, mock_height_data, url, level): mock_session.get(url=url, status=HTTPStatus.OK, payload=mock_height_data) session = ClientSession() client = UkhoTides(session, "api_key", level) date_time = datetime(2020, 1, 1) height = await client.async_get_tidal_height("0001", date_time) assert height == mock_height_data["Height"]
async def test_givenValidApiKey_whenRequestTidalEventsWithInvalidDuration_thenThrowApiError( mock_session, mock_tidal_events_data, url, level): mock_session.get(url=url, status=HTTPStatus.BAD_REQUEST, payload=mock_tidal_events_data) session = ClientSession() client = UkhoTides(session, "api_key", level) with pytest.raises(ApiError): events = await client.async_get_tidal_events("0001", 20)
async def test_givenValidApiKey_whenRequestTidalEventsWithDuration_thenReturnTidalEvents( mock_session, mock_tidal_events_data, url, level): mock_session.get(url=url, status=HTTPStatus.OK, payload=mock_tidal_events_data) session = ClientSession() client = UkhoTides(session, "api_key", level) events = await client.async_get_tidal_events("0001", 4) mock_events = [TidalEvent.from_dict(e) for e in mock_tidal_events_data] assert events == mock_events
async def test_givenValidApiKey_whenRequestStationsWithName_thenReturnStations( mock_session, mock_stations_data, url, level): mock_session.get(url=url, status=HTTPStatus.OK, payload=mock_stations_data) session = ClientSession() client = UkhoTides(session, "api_key", level) stations = await client.async_get_stations("StationName") mock_stations = [ Station.from_dict(s) for s in mock_stations_data["features"] ] assert stations == mock_stations
async def async_step_user(self, user_input=None): errors = {} if user_input is not None: session = async_get_clientsession(self.hass) try: async with timeout(10): ukhotides = UkhoTides( session, user_input[CONF_API_KEY], ApiLevel[user_input[CONF_API_LEVEL]], ) stations = await ukhotides.async_get_stations() self._all_stations = {s.id: s.name for s in stations} self._stations_map = {s.id: s for s in stations} except (ApiError, ClientConnectorError, asyncio.TimeoutError, ClientError): errors["base"] = "cannot_connect" except InvalidApiKeyError: errors[CONF_API_KEY] = "invalid_api_key" except Exception: _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: self.data = user_input self.data[CONF_STATIONS] = [] return await self.async_step_station() return self.async_show_form( step_id="user", data_schema=vol.Schema({ vol.Required(CONF_API_LEVEL, default=ApiLevel.Discovery.name): vol.In([ ApiLevel.Discovery.name, ApiLevel.Foundation.name, ApiLevel.Premium.name, ]), vol.Required(CONF_API_KEY): str, }), errors=errors, )
async def test_givenValidApiKey_whenRequestTidalHeights_thenReturnTidalHeights( mock_session, mock_heights_data, url, level): mock_session.get(url=url, status=HTTPStatus.OK, payload=mock_heights_data) session = ClientSession() client = UkhoTides(session, "api_key", level) start_date = datetime(2020, 1, 1) end_date = datetime(2020, 1, 1, 2) heights = await client.async_get_tidal_heights("0001", start_date, end_date, 60) mock_heights = [TidalHeight.from_dict(h) for h in mock_heights_data] assert heights == mock_heights
async def test_givenValidApiKey_whenRequestTidalEventsForDateRange_thenReturnTidalEvents( mock_session, mock_tidal_events_data, url, level): mock_session.get(url=url, status=HTTPStatus.OK, payload=mock_tidal_events_data) session = ClientSession() client = UkhoTides(session, "api_key", level) start_date = datetime(2020, 1, 1) end_date = datetime(2020, 1, 30) events = await client.async_get_tidal_events_for_date_range( "0001", start_date, end_date) mock_events = [TidalEvent.from_dict(e) for e in mock_tidal_events_data] assert events == mock_events
async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities: Callable, discovery_info: Optional[DiscoveryInfoType] = None, ) -> None: session = async_get_clientsession(hass) ukhotides = UkhoTides(session, config[CONF_API_KEY]) sensors = [] for station in config[CONF_STATIONS]: coordinator = UkhoTidesDataUpdateCoordinator(hass, ukhotides, station) if CONF_STATION_NAME in coordinator.station: name = station[CONF_STATION_NAME] else: name = (await ukhotides.async_get_station( coordinator.station[CONF_STATION_ID])).name sensors.append(UkhoTidesSensor(coordinator, name)) async_add_entities(sensors, update_before_add=True)
async def async_step_init(self, user_input: Dict[str, Any] = None) -> Dict[str, Any]: errors: Dict[str, str] = {} session = async_get_clientsession(self.hass) self.data = self.hass.data[DOMAIN][self.config_entry.entry_id] try: async with timeout(10): api_key = self.data[CONF_API_KEY] ukhotides = UkhoTides( session, api_key, ) # Get all stations stations = await ukhotides.async_get_stations() all_stations = {s.id: s.name for s in stations} stations_map = {s.id: s for s in stations} except (ApiError, ClientConnectorError, asyncio.TimeoutError, ClientError): errors["base"] = "cannot_connect" except InvalidApiKeyError: errors[CONF_API_KEY] = "invalid_api_key" except Exception: _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: # Get currently registered entities entity_registry = await async_get_registry(self.hass) registered_entities = async_entries_for_config_entry( entity_registry, self.config_entry.entry_id) # Default values for the multi-select registered_stations_unique_to_entity = { e.unique_id: e.entity_id for e in registered_entities } registered_stations_entity_to_unique = { e.entity_id: e.unique_id for e in registered_entities } if user_input is not None: # Remove any unchecked stations removed_unique_ids = [ unique_id for unique_id in registered_stations_entity_to_unique.values() if unique_id not in user_input[CONF_STATIONS] ] for unique_id in removed_unique_ids: # Unregister from HA entity_registry.async_remove( registered_stations_unique_to_entity[unique_id]) # Add any newly checked stations self.updated_stations = [] if user_input.get(CONF_STATIONS): for entry_unique_id in user_input[CONF_STATIONS]: s = next( (item for item in self.data[CONF_STATIONS] if item[CONF_STATION_ID] == entry_unique_id), None, ) if s is not None: self.updated_stations.append(s) else: s = stations_map[entry_unique_id] self.updated_stations.append({ CONF_STATION_ID: s.id, CONF_STATION_NAME: s.name, CONF_STATION_OFFSET_HIGH: 0, CONF_STATION_OFFSET_LOW: 0, }) if not errors: if not self.updated_stations: return self.async_create_entry( title="", data={CONF_STATIONS: self.updated_stations}, ) else: return await self.async_step_station_settings() options_schema = vol.Schema({ vol.Optional( CONF_STATIONS, default=list(registered_stations_unique_to_entity.keys()), ): cv.multi_select(all_stations), }) return self.async_show_form(step_id="init", data_schema=options_schema, errors=errors)