Add Stookwijzer (#84435)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
fwestenberg 2023-01-20 13:42:47 +01:00 committed by GitHub
parent 658db7ff05
commit 29b2b6727e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 303 additions and 0 deletions

View File

@ -1249,6 +1249,9 @@ omit =
homeassistant/components/stookalert/__init__.py
homeassistant/components/stookalert/binary_sensor.py
homeassistant/components/stookalert/diagnostics.py
homeassistant/components/stookwijzer/__init__.py
homeassistant/components/stookwijzer/diagnostics.py
homeassistant/components/stookwijzer/sensor.py
homeassistant/components/stream/*
homeassistant/components/streamlabswater/*
homeassistant/components/suez_water/*

View File

@ -1139,6 +1139,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/stiebel_eltron/ @fucm
/homeassistant/components/stookalert/ @fwestenberg @frenck
/tests/components/stookalert/ @fwestenberg @frenck
/homeassistant/components/stookwijzer/ @fwestenberg
/tests/components/stookwijzer/ @fwestenberg
/homeassistant/components/stream/ @hunterjm @uvjustin @allenporter
/tests/components/stream/ @hunterjm @uvjustin @allenporter
/homeassistant/components/stt/ @pvizeli

View File

@ -0,0 +1,29 @@
"""The Stookwijzer integration."""
from __future__ import annotations
from stookwijzer import Stookwijzer
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE, Platform
from homeassistant.core import HomeAssistant
from .const import DOMAIN
PLATFORMS = [Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Stookwijzer from a config entry."""
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = Stookwijzer(
entry.data[CONF_LOCATION][CONF_LATITUDE],
entry.data[CONF_LOCATION][CONF_LONGITUDE],
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Stookwijzer config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
del hass.data[DOMAIN][entry.entry_id]
return unload_ok

View File

@ -0,0 +1,45 @@
"""Config flow to configure the Stookwijzer integration."""
from __future__ import annotations
from typing import Any
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.selector import LocationSelector
from .const import DOMAIN
class StookwijzerFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for Stookwijzer."""
VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user."""
if user_input is not None:
return self.async_create_entry(
title="Stookwijzer",
data=user_input,
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(
CONF_LOCATION,
default={
CONF_LATITUDE: self.hass.config.latitude,
CONF_LONGITUDE: self.hass.config.longitude,
},
): LocationSelector()
}
),
)

View File

@ -0,0 +1,16 @@
"""Constants for the Stookwijzer integration."""
import logging
from typing import Final
from homeassistant.backports.enum import StrEnum
DOMAIN: Final = "stookwijzer"
LOGGER = logging.getLogger(__package__)
class StookwijzerState(StrEnum):
"""Stookwijzer states for sensor entity."""
BLUE = "blauw"
ORANGE = "oranje"
RED = "rood"

View File

@ -0,0 +1,31 @@
"""Diagnostics support for Stookwijzer."""
from __future__ import annotations
from typing import Any
from stookwijzer import Stookwijzer
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
client: Stookwijzer = hass.data[DOMAIN][entry.entry_id]
last_updated = None
if client.last_updated:
last_updated = client.last_updated.isoformat()
return {
"state": client.state,
"last_updated": last_updated,
"lqi": client.lqi,
"windspeed": client.windspeed,
"weather": client.weather,
"concentrations": client.concentrations,
}

View File

@ -0,0 +1,10 @@
{
"domain": "stookwijzer",
"name": "Stookwijzer",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/stookwijzer",
"codeowners": ["@fwestenberg"],
"requirements": ["stookwijzer==1.3.0"],
"integration_type": "service",
"iot_class": "cloud_polling"
}

View File

@ -0,0 +1,65 @@
"""This integration provides support for Stookwijzer Sensor."""
from __future__ import annotations
from datetime import timedelta
from stookwijzer import Stookwijzer
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, StookwijzerState
SCAN_INTERVAL = timedelta(minutes=60)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Stookwijzer sensor from a config entry."""
client = hass.data[DOMAIN][entry.entry_id]
async_add_entities([StookwijzerSensor(client, entry)], update_before_add=True)
class StookwijzerSensor(SensorEntity):
"""Defines a Stookwijzer binary sensor."""
_attr_attribution = "Data provided by stookwijzer.nu"
_attr_device_class = SensorDeviceClass.ENUM
_attr_has_entity_name = True
_attr_translation_key = "stookwijzer"
def __init__(self, client: Stookwijzer, entry: ConfigEntry) -> None:
"""Initialize a Stookwijzer device."""
self._client = client
self._attr_options = [cls.value for cls in StookwijzerState]
self._attr_unique_id = entry.entry_id
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{entry.entry_id}")},
name="Stookwijzer",
manufacturer="stookwijzer.nu",
entry_type=DeviceEntryType.SERVICE,
configuration_url="https://www.stookwijzer.nu",
)
def update(self) -> None:
"""Update the data from the Stookwijzer handler."""
self._client.update()
@property
def available(self) -> bool:
"""Return if entity is available."""
return self._client.state is not None
@property
def native_value(self) -> str | None:
"""Return the state of the device."""
if self._client.state is None:
return None
return StookwijzerState(self._client.state).value

View File

@ -0,0 +1,23 @@
{
"config": {
"step": {
"user": {
"description": "Select the location you want to recieve the Stookwijzer information for.",
"data": {
"location": "[%key:common::config_flow::data::location%]"
}
}
}
},
"entity": {
"sensor": {
"stookwijzer": {
"state": {
"blauw": "Blue",
"oranje": "Orange",
"rood": "Red"
}
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"step": {
"user": {
"data": {
"location": "Location"
},
"description": "Select the location you want to recieve the Stookwijzer information for."
}
}
},
"entity": {
"sensor": {
"stookwijzer": {
"state": {
"blauw": "Blue",
"oranje": "Orange",
"rood": "Red"
}
}
}
}
}

View File

@ -411,6 +411,7 @@ FLOWS = {
"steam_online",
"steamist",
"stookalert",
"stookwijzer",
"subaru",
"sun",
"surepetcare",

View File

@ -5243,6 +5243,12 @@
"config_flow": true,
"iot_class": "cloud_polling"
},
"stookwijzer": {
"name": "Stookwijzer",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
},
"streamlabswater": {
"name": "StreamLabs",
"integration_type": "hub",

View File

@ -2405,6 +2405,9 @@ steamodd==4.21
# homeassistant.components.stookalert
stookalert==0.1.4
# homeassistant.components.stookwijzer
stookwijzer==1.3.0
# homeassistant.components.streamlabswater
streamlabswater==1.0.1

View File

@ -1699,6 +1699,9 @@ steamodd==4.21
# homeassistant.components.stookalert
stookalert==0.1.4
# homeassistant.components.stookwijzer
stookwijzer==1.3.0
# homeassistant.components.huawei_lte
# homeassistant.components.solaredge
# homeassistant.components.thermoworks_smoke

View File

@ -0,0 +1 @@
"""Tests for the Stookwijzer integration."""

View File

@ -0,0 +1,42 @@
"""Tests for the Stookwijzer config flow."""
from unittest.mock import patch
from homeassistant.components.stookwijzer.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
async def test_full_user_flow(hass: HomeAssistant) -> None:
"""Test the full user configuration flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result.get("type") == FlowResultType.FORM
assert result.get("step_id") == SOURCE_USER
assert "flow_id" in result
with patch(
"homeassistant.components.stookwijzer.async_setup_entry", return_value=True
) as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_LOCATION: {
CONF_LATITUDE: 1.0,
CONF_LONGITUDE: 1.1,
}
},
)
assert result2.get("type") == FlowResultType.CREATE_ENTRY
assert result2.get("data") == {
"location": {
"latitude": 1.0,
"longitude": 1.1,
},
}
assert len(mock_setup_entry.mock_calls) == 1