Example #1
0
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
Example #10
0
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
Example #11
0
    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,
        )
Example #12
0
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
Example #13
0
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
Example #14
0
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)
Example #15
0
    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)