mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Add Wolflink integration (#34104)
* WOLF Smart-set integration * Removed translations. Changed device class of timestamp. Added new test for unknown exception * Remove unit_of_measurement from hours sensor * Code cleanup. Pull Request comments fixes * ConnectError import change. Removed DEVICE_CLASS_TIMESTAMP * Add unique id guard with tests. Use common translations. Move device_id resolution to config_flow. * Remove debug print
This commit is contained in:
parent
d0d4e08a2a
commit
bedb0753f3
@ -934,6 +934,9 @@ omit =
|
|||||||
homeassistant/components/wiffi/*
|
homeassistant/components/wiffi/*
|
||||||
homeassistant/components/wink/*
|
homeassistant/components/wink/*
|
||||||
homeassistant/components/wirelesstag/*
|
homeassistant/components/wirelesstag/*
|
||||||
|
homeassistant/components/wolflink/__init__.py
|
||||||
|
homeassistant/components/wolflink/sensor.py
|
||||||
|
homeassistant/components/wolflink/const.py
|
||||||
homeassistant/components/worldtidesinfo/sensor.py
|
homeassistant/components/worldtidesinfo/sensor.py
|
||||||
homeassistant/components/worxlandroid/sensor.py
|
homeassistant/components/worxlandroid/sensor.py
|
||||||
homeassistant/components/x10/light.py
|
homeassistant/components/x10/light.py
|
||||||
|
@ -459,6 +459,7 @@ homeassistant/components/websocket_api/* @home-assistant/core
|
|||||||
homeassistant/components/wiffi/* @mampfes
|
homeassistant/components/wiffi/* @mampfes
|
||||||
homeassistant/components/withings/* @vangorra
|
homeassistant/components/withings/* @vangorra
|
||||||
homeassistant/components/wled/* @frenck
|
homeassistant/components/wled/* @frenck
|
||||||
|
homeassistant/components/wolflink/* @adamkrol93
|
||||||
homeassistant/components/workday/* @fabaff
|
homeassistant/components/workday/* @fabaff
|
||||||
homeassistant/components/worldclock/* @fabaff
|
homeassistant/components/worldclock/* @fabaff
|
||||||
homeassistant/components/xbox_live/* @MartinHjelmare
|
homeassistant/components/xbox_live/* @MartinHjelmare
|
||||||
|
103
homeassistant/components/wolflink/__init__.py
Normal file
103
homeassistant/components/wolflink/__init__.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
"""The Wolf SmartSet Service integration."""
|
||||||
|
from _datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from httpcore import ConnectError
|
||||||
|
from wolf_smartset.token_auth import InvalidAuth
|
||||||
|
from wolf_smartset.wolf_client import WolfClient
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
COORDINATOR,
|
||||||
|
DEVICE_GATEWAY,
|
||||||
|
DEVICE_ID,
|
||||||
|
DEVICE_NAME,
|
||||||
|
DOMAIN,
|
||||||
|
PARAMETERS,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass: HomeAssistant, config: dict):
|
||||||
|
"""Set up the Wolf SmartSet Service component."""
|
||||||
|
hass.data[DOMAIN] = {}
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
"""Set up Wolf SmartSet Service from a config entry."""
|
||||||
|
username = entry.data[CONF_USERNAME]
|
||||||
|
password = entry.data[CONF_PASSWORD]
|
||||||
|
device_name = entry.data[DEVICE_NAME]
|
||||||
|
device_id = entry.data[DEVICE_ID]
|
||||||
|
gateway_id = entry.data[DEVICE_GATEWAY]
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Setting up wolflink integration for device: %s (id: %s, gateway: %s)",
|
||||||
|
device_name,
|
||||||
|
device_id,
|
||||||
|
gateway_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
wolf_client = WolfClient(username, password)
|
||||||
|
|
||||||
|
parameters = await fetch_parameters(wolf_client, gateway_id, device_id)
|
||||||
|
|
||||||
|
async def async_update_data():
|
||||||
|
"""Update all stored entities for Wolf SmartSet."""
|
||||||
|
try:
|
||||||
|
values = await wolf_client.fetch_value(gateway_id, device_id, parameters)
|
||||||
|
return {v.value_id: v.value for v in values}
|
||||||
|
except ConnectError as exception:
|
||||||
|
raise UpdateFailed(f"Error communicating with API: {exception}")
|
||||||
|
except InvalidAuth:
|
||||||
|
raise UpdateFailed("Invalid authentication during update.")
|
||||||
|
|
||||||
|
coordinator = DataUpdateCoordinator(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
name="wolflink",
|
||||||
|
update_method=async_update_data,
|
||||||
|
update_interval=timedelta(minutes=1),
|
||||||
|
)
|
||||||
|
|
||||||
|
await coordinator.async_refresh()
|
||||||
|
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = {}
|
||||||
|
hass.data[DOMAIN][entry.entry_id][PARAMETERS] = parameters
|
||||||
|
hass.data[DOMAIN][entry.entry_id][COORDINATOR] = coordinator
|
||||||
|
hass.data[DOMAIN][entry.entry_id][DEVICE_ID] = device_id
|
||||||
|
|
||||||
|
hass.async_create_task(
|
||||||
|
hass.config_entries.async_forward_entry_setup(entry, "sensor")
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
"""Unload a config entry."""
|
||||||
|
unload_ok = await hass.config_entries.async_forward_entry_unload(entry, "sensor")
|
||||||
|
if unload_ok:
|
||||||
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_parameters(client: WolfClient, gateway_id: int, device_id: int):
|
||||||
|
"""
|
||||||
|
Fetch all available parameters with usage of WolfClient.
|
||||||
|
|
||||||
|
By default Reglertyp entity is removed because API will not provide value for this parameter.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
fetched_parameters = await client.fetch_parameters(gateway_id, device_id)
|
||||||
|
return [param for param in fetched_parameters if param.name != "Reglertyp"]
|
||||||
|
except ConnectError as exception:
|
||||||
|
raise UpdateFailed(f"Error communicating with API: {exception}")
|
||||||
|
except InvalidAuth:
|
||||||
|
raise UpdateFailed("Invalid authentication during update.")
|
93
homeassistant/components/wolflink/config_flow.py
Normal file
93
homeassistant/components/wolflink/config_flow.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
"""Config flow for Wolf SmartSet Service integration."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from httpcore import ConnectError
|
||||||
|
import voluptuous as vol
|
||||||
|
from wolf_smartset.token_auth import InvalidAuth
|
||||||
|
from wolf_smartset.wolf_client import WolfClient
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
|
|
||||||
|
from .const import ( # pylint:disable=unused-import
|
||||||
|
DEVICE_GATEWAY,
|
||||||
|
DEVICE_ID,
|
||||||
|
DEVICE_NAME,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
USER_SCHEMA = vol.Schema(
|
||||||
|
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for Wolf SmartSet Service."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize with empty username and password."""
|
||||||
|
self.username = None
|
||||||
|
self.password = None
|
||||||
|
self.fetched_systems = None
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Handle the initial step to get connection parameters."""
|
||||||
|
errors = {}
|
||||||
|
if user_input is not None:
|
||||||
|
wolf_client = WolfClient(
|
||||||
|
user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self.fetched_systems = await wolf_client.fetch_system_list()
|
||||||
|
except ConnectError:
|
||||||
|
errors["base"] = "cannot_connect"
|
||||||
|
except InvalidAuth:
|
||||||
|
errors["base"] = "invalid_auth"
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
_LOGGER.exception("Unexpected exception")
|
||||||
|
errors["base"] = "unknown"
|
||||||
|
else:
|
||||||
|
self.username = user_input[CONF_USERNAME]
|
||||||
|
self.password = user_input[CONF_PASSWORD]
|
||||||
|
return await self.async_step_device()
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user", data_schema=USER_SCHEMA, errors=errors
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_device(self, user_input=None):
|
||||||
|
"""Allow user to select device from devices connected to specified account."""
|
||||||
|
errors = {}
|
||||||
|
if user_input is not None:
|
||||||
|
device_name = user_input[DEVICE_NAME]
|
||||||
|
system = [
|
||||||
|
device for device in self.fetched_systems if device.name == device_name
|
||||||
|
]
|
||||||
|
device_id = system[0].id
|
||||||
|
await self.async_set_unique_id(device_id)
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=user_input[DEVICE_NAME],
|
||||||
|
data={
|
||||||
|
CONF_USERNAME: self.username,
|
||||||
|
CONF_PASSWORD: self.password,
|
||||||
|
DEVICE_NAME: device_name,
|
||||||
|
DEVICE_GATEWAY: system[0].gateway,
|
||||||
|
DEVICE_ID: device_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
data_schema = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(DEVICE_NAME): vol.In(
|
||||||
|
[info.name for info in self.fetched_systems]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="device", data_schema=data_schema, errors=errors
|
||||||
|
)
|
93
homeassistant/components/wolflink/const.py
Normal file
93
homeassistant/components/wolflink/const.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
"""Constants for the Wolf SmartSet Service integration."""
|
||||||
|
|
||||||
|
DOMAIN = "wolflink"
|
||||||
|
|
||||||
|
COORDINATOR = "coordinator"
|
||||||
|
PARAMETERS = "parameters"
|
||||||
|
DEVICE_ID = "device_id"
|
||||||
|
DEVICE_GATEWAY = "device_gateway"
|
||||||
|
DEVICE_NAME = "device_name"
|
||||||
|
|
||||||
|
STATES = {
|
||||||
|
"Ein": "ein",
|
||||||
|
"Deaktiviert": "deaktiviert",
|
||||||
|
"Aus": "aus",
|
||||||
|
"Standby": "standby",
|
||||||
|
"Auto": "auto",
|
||||||
|
"Permanent": "permanent",
|
||||||
|
"Initialisierung": "initialisierung",
|
||||||
|
"Antilegionellenfunktion": "antilegionellenfunktion",
|
||||||
|
"Fernschalter ein": "fernschalter_ein",
|
||||||
|
"1x Warmwasser": "1_x_warmwasser",
|
||||||
|
"Bereit, keine Ladung": "bereit_keine_ladung",
|
||||||
|
"Solarbetrieb": "solarbetrieb",
|
||||||
|
"Reduzierter Betrieb": "reduzierter_betrieb",
|
||||||
|
"SmartHome": "smart_home",
|
||||||
|
"SmartGrid": "smart_grid",
|
||||||
|
"Ruhekontakt": "ruhekontakt",
|
||||||
|
"Vorspülen": "vorspulen",
|
||||||
|
"Zünden": "zunden",
|
||||||
|
"Stabilisierung": "stabilisierung",
|
||||||
|
"Ventilprüfung": "ventilprufung",
|
||||||
|
"Nachspülen": "nachspulen",
|
||||||
|
"Softstart": "softstart",
|
||||||
|
"Taktsperre": "taktsperre",
|
||||||
|
"Betrieb ohne Brenner": "betrieb_ohne_brenner",
|
||||||
|
"Abgasklappe": "abgasklappe",
|
||||||
|
"Störung": "storung",
|
||||||
|
"Gradienten Überwachung": "gradienten_uberwachung",
|
||||||
|
"Gasdruck": "gasdruck",
|
||||||
|
"Spreizung hoch": "spreizung_hoch",
|
||||||
|
"Spreizung KF": "spreizung_kf",
|
||||||
|
"Test": "test",
|
||||||
|
"Start": "start",
|
||||||
|
"Frost Heizkreis": "frost_heizkreis",
|
||||||
|
"Frost Warmwasser": "frost_warmwasser",
|
||||||
|
"Schornsteinfeger": "schornsteinfeger",
|
||||||
|
"Kombibetrieb": "kombibetrieb",
|
||||||
|
"Parallelbetrieb": "parallelbetrieb",
|
||||||
|
"Warmwasserbetrieb": "warmwasserbetrieb",
|
||||||
|
"Warmwassernachlauf": "warmwassernachlauf",
|
||||||
|
"Mindest-Kombizeit": "mindest_kombizeit",
|
||||||
|
"Heizbetrieb": "heizbetrieb",
|
||||||
|
"Nachlauf Heizkreispumpe": "nachlauf_heizkreispumpe",
|
||||||
|
"Frostschutz": "frostschutz",
|
||||||
|
"Kaskadenbetrieb": "kaskadenbetrieb",
|
||||||
|
"GLT-Betrieb": "glt_betrieb",
|
||||||
|
"Kalibration": "kalibration",
|
||||||
|
"Kalibration Heizbetrieb": "kalibration_heizbetrieb",
|
||||||
|
"Kalibration Warmwasserbetrieb": "kalibration_warmwasserbetrieb",
|
||||||
|
"Kalibration Kombibetrieb": "kalibration_kombibetrieb",
|
||||||
|
"Warmwasser Schnellstart": "warmwasser_schnellstart",
|
||||||
|
"Externe Deaktivierung": "externe_deaktivierung",
|
||||||
|
"Heizung": "heizung",
|
||||||
|
"Warmwasser": "warmwasser",
|
||||||
|
"Kombigerät": "kombigerat",
|
||||||
|
"Kombigerät mit Solareinbindung": "kombigerat_mit_solareinbindung",
|
||||||
|
"Heizgerät mit Speicher": "heizgerat_mit_speicher",
|
||||||
|
"Nur Heizgerät": "nur_heizgerat",
|
||||||
|
"Aktiviert": "ktiviert",
|
||||||
|
"Sparen": "sparen",
|
||||||
|
"Estrichtrocknung": "estrichtrocknung",
|
||||||
|
"Telefonfernschalter": "telefonfernschalter",
|
||||||
|
"Partymodus": "partymodus",
|
||||||
|
"Urlaubsmodus": "urlaubsmodus",
|
||||||
|
"Automatik ein": "automatik_ein",
|
||||||
|
"Automatik aus": "automatik_aus",
|
||||||
|
"Permanentbetrieb": "permanentbetrieb",
|
||||||
|
"Sparbetrieb": "sparbetrieb",
|
||||||
|
"AutoOnCool": "auto_on_cool",
|
||||||
|
"AutoOffCool": "auto_off_cool",
|
||||||
|
"PermCooling": "perm_cooling",
|
||||||
|
"Absenkbetrieb": "absenkbetrieb",
|
||||||
|
"Eco": "eco",
|
||||||
|
"Absenkstop": "absenkstop",
|
||||||
|
"AT Abschaltung": "at_abschaltung",
|
||||||
|
"RT Abschaltung": "rt_abschaltung",
|
||||||
|
"AT Frostschutz": "at_frostschutz",
|
||||||
|
"RT Frostschutz": "rt_frostschutz",
|
||||||
|
"DHWPrior": "dhw_prior",
|
||||||
|
"Cooling": "cooling",
|
||||||
|
"TPW": "tpw",
|
||||||
|
"Warmwasservorrang": "warmwasservorrang",
|
||||||
|
}
|
8
homeassistant/components/wolflink/manifest.json
Normal file
8
homeassistant/components/wolflink/manifest.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"domain": "wolflink",
|
||||||
|
"name": "Wolf SmartSet Service",
|
||||||
|
"config_flow": true,
|
||||||
|
"documentation": "https://www.home-assistant.io/integrations/wolflink",
|
||||||
|
"requirements": ["wolf_smartset==0.1.4"],
|
||||||
|
"codeowners": ["@adamkrol93"]
|
||||||
|
}
|
182
homeassistant/components/wolflink/sensor.py
Normal file
182
homeassistant/components/wolflink/sensor.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
"""The Wolf SmartSet sensors."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from wolf_smartset.models import (
|
||||||
|
HoursParameter,
|
||||||
|
ListItemParameter,
|
||||||
|
Parameter,
|
||||||
|
PercentageParameter,
|
||||||
|
Pressure,
|
||||||
|
SimpleParameter,
|
||||||
|
Temperature,
|
||||||
|
)
|
||||||
|
|
||||||
|
from homeassistant.components.wolflink.const import (
|
||||||
|
COORDINATOR,
|
||||||
|
DEVICE_ID,
|
||||||
|
DOMAIN,
|
||||||
|
PARAMETERS,
|
||||||
|
STATES,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
PRESSURE_BAR,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
TIME_HOURS,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
"""Set up all entries for Wolf Platform."""
|
||||||
|
|
||||||
|
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
|
||||||
|
parameters = hass.data[DOMAIN][config_entry.entry_id][PARAMETERS]
|
||||||
|
device_id = hass.data[DOMAIN][config_entry.entry_id][DEVICE_ID]
|
||||||
|
|
||||||
|
entities = []
|
||||||
|
for parameter in parameters:
|
||||||
|
if isinstance(parameter, Temperature):
|
||||||
|
entities.append(WolfLinkTemperature(coordinator, parameter, device_id))
|
||||||
|
if isinstance(parameter, Pressure):
|
||||||
|
entities.append(WolfLinkPressure(coordinator, parameter, device_id))
|
||||||
|
if isinstance(parameter, PercentageParameter):
|
||||||
|
entities.append(WolfLinkPercentage(coordinator, parameter, device_id))
|
||||||
|
if isinstance(parameter, ListItemParameter):
|
||||||
|
entities.append(WolfLinkState(coordinator, parameter, device_id))
|
||||||
|
if isinstance(parameter, HoursParameter):
|
||||||
|
entities.append(WolfLinkHours(coordinator, parameter, device_id))
|
||||||
|
if isinstance(parameter, SimpleParameter):
|
||||||
|
entities.append(WolfLinkSensor(coordinator, parameter, device_id))
|
||||||
|
|
||||||
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
|
|
||||||
|
class WolfLinkSensor(Entity):
|
||||||
|
"""Base class for all Wolf entities."""
|
||||||
|
|
||||||
|
def __init__(self, coordinator, wolf_object: Parameter, device_id):
|
||||||
|
"""Initialize."""
|
||||||
|
self.coordinator = coordinator
|
||||||
|
self.wolf_object = wolf_object
|
||||||
|
self.device_id = device_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name."""
|
||||||
|
return f"{self.wolf_object.name}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return the state."""
|
||||||
|
return self.coordinator.data[self.wolf_object.value_id]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
return {
|
||||||
|
"parameter_id": self.wolf_object.parameter_id,
|
||||||
|
"value_id": self.wolf_object.value_id,
|
||||||
|
"parent": self.wolf_object.parent,
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return a unique_id for this entity."""
|
||||||
|
return f"{self.device_id}:{self.wolf_object.parameter_id}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
"""Return True if entity is available."""
|
||||||
|
return self.coordinator.last_update_success
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
"""No need to poll. Coordinator notifies entity of updates."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""When entity is added to hass."""
|
||||||
|
self.async_on_remove(
|
||||||
|
self.coordinator.async_add_listener(self.async_write_ha_state)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Update the sensor."""
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
_LOGGER.debug("Updating %s", self.coordinator.data[self.wolf_object.value_id])
|
||||||
|
|
||||||
|
|
||||||
|
class WolfLinkHours(WolfLinkSensor):
|
||||||
|
"""Class for hour based entities."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
"""Icon to display in the front Aend."""
|
||||||
|
return "mdi:clock"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return the unit the value is expressed in."""
|
||||||
|
return TIME_HOURS
|
||||||
|
|
||||||
|
|
||||||
|
class WolfLinkTemperature(WolfLinkSensor):
|
||||||
|
"""Class for temperature based entities."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the device_class."""
|
||||||
|
return DEVICE_CLASS_TEMPERATURE
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return the unit the value is expressed in."""
|
||||||
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
|
|
||||||
|
class WolfLinkPressure(WolfLinkSensor):
|
||||||
|
"""Class for pressure based entities."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the device_class."""
|
||||||
|
return DEVICE_CLASS_PRESSURE
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return the unit the value is expressed in."""
|
||||||
|
return PRESSURE_BAR
|
||||||
|
|
||||||
|
|
||||||
|
class WolfLinkPercentage(WolfLinkSensor):
|
||||||
|
"""Class for percentage based entities."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return the unit the value is expressed in."""
|
||||||
|
return self.wolf_object.unit
|
||||||
|
|
||||||
|
|
||||||
|
class WolfLinkState(WolfLinkSensor):
|
||||||
|
"""Class for entities which has defined list of state."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the device class."""
|
||||||
|
return "wolflink__state"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return the state converting with supported values."""
|
||||||
|
state = self.coordinator.data[self.wolf_object.value_id]
|
||||||
|
resolved_state = [
|
||||||
|
item for item in self.wolf_object.items if item.value == int(state)
|
||||||
|
]
|
||||||
|
if resolved_state:
|
||||||
|
resolved_name = resolved_state[0].name
|
||||||
|
return STATES.get(resolved_name, resolved_name)
|
||||||
|
return state
|
27
homeassistant/components/wolflink/strings.json
Normal file
27
homeassistant/components/wolflink/strings.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"username": "[%key:common::config_flow::data::username%]",
|
||||||
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
|
},
|
||||||
|
"title": "WOLF SmartSet connection"
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"data": {
|
||||||
|
"device_name": "Device"
|
||||||
|
},
|
||||||
|
"title": "Select WOLF device"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
homeassistant/components/wolflink/strings.sensor.json
Normal file
87
homeassistant/components/wolflink/strings.sensor.json
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"state": {
|
||||||
|
"wolflink__state": {
|
||||||
|
"ein": "Enabled",
|
||||||
|
"deaktiviert": "Inactive",
|
||||||
|
"aus": "Disabled",
|
||||||
|
"standby": "Standby",
|
||||||
|
"auto": "Auto",
|
||||||
|
"permanent": "Permament",
|
||||||
|
"initialisierung": "Initialization",
|
||||||
|
"antilegionellenfunktion": "Anti-legionella Function",
|
||||||
|
"fernschalter_ein": "Remote control enabled",
|
||||||
|
"1_x_warmwasser": "1 x DHW",
|
||||||
|
"bereit_keine_ladung": "Ready, not loading",
|
||||||
|
"solarbetrieb": "Solar mode",
|
||||||
|
"reduzierter_betrieb": "Limited mode",
|
||||||
|
"smart_home": "SmartHome",
|
||||||
|
"smart_grid": "SmartGrid",
|
||||||
|
"ruhekontakt": "Rest contact",
|
||||||
|
"vorspulen": "Entry rinsing",
|
||||||
|
"zunden": "Ignition",
|
||||||
|
"stabilisierung": "Stablization",
|
||||||
|
"ventilprufung": "Valve test",
|
||||||
|
"nachspulen": "Post-flush",
|
||||||
|
"softstart": "Soft start",
|
||||||
|
"taktsperre": "Anti-cycle",
|
||||||
|
"betrieb_ohne_brenner": "Working without burner",
|
||||||
|
"abgasklappe": "Flue gas damper",
|
||||||
|
"storung": "Fault",
|
||||||
|
"gradienten_uberwachung": "Gradient monitoring",
|
||||||
|
"gasdruck": "Gas pressure",
|
||||||
|
"spreizung_hoch": "dT too wide",
|
||||||
|
"spreizung_kf": "Spread KF",
|
||||||
|
"test": "Test",
|
||||||
|
"start": "Start",
|
||||||
|
"frost_heizkreis": "Heating circuit frost",
|
||||||
|
"frost_warmwasser": "DHW frost",
|
||||||
|
"schornsteinfeger": "Emissions test",
|
||||||
|
"kombibetrieb": "Combi mode",
|
||||||
|
"parallelbetrieb": "Parallel mode",
|
||||||
|
"warmwasserbetrieb": "DHW mode",
|
||||||
|
"warmwassernachlauf": "DHW run-on",
|
||||||
|
"heizbetrieb": "Heating mode",
|
||||||
|
"nachlauf_heizkreispumpe": "Heating circuit pump run-on",
|
||||||
|
"frostschutz": "Frost protection",
|
||||||
|
"kaskadenbetrieb": "Cascade operation",
|
||||||
|
"glt_betrieb": "BMS mode",
|
||||||
|
"kalibration": "Calibration",
|
||||||
|
"kalibration_heizbetrieb": "Heating mode calibration",
|
||||||
|
"kalibration_warmwasserbetrieb": "DHW calibration",
|
||||||
|
"kalibration_kombibetrieb": "Combi mode calibration",
|
||||||
|
"warmwasser_schnellstart": "DHW quick start",
|
||||||
|
"externe_deaktivierung": "External deactivation",
|
||||||
|
"heizung": "Heating",
|
||||||
|
"warmwasser": "DHW",
|
||||||
|
"kombigerat": "Combi boiler",
|
||||||
|
"kombigerat_mit_solareinbindung": "Combi boiler with solar integration",
|
||||||
|
"heizgerat_mit_speicher": "Boiler with cylinder",
|
||||||
|
"nur_heizgerat": "Boiler only",
|
||||||
|
"aktiviert": "Activated",
|
||||||
|
"sparen": "Economy",
|
||||||
|
"estrichtrocknung": "Screed drying",
|
||||||
|
"telefonfernschalter": "Telephone remote switch",
|
||||||
|
"partymodus": "Party mode",
|
||||||
|
"urlaubsmodus": "Holiday mode",
|
||||||
|
"automatik_ein": "Automatic ON",
|
||||||
|
"automatik_aus": "Automatic OFF",
|
||||||
|
"permanentbetrieb": "Permanent mode",
|
||||||
|
"sparbetrieb": "Economy mode",
|
||||||
|
"auto_on_cool": "AutoOnCool",
|
||||||
|
"auto_off_cool": "AutoOffCool",
|
||||||
|
"perm_cooling": "PermCooling",
|
||||||
|
"absenkbetrieb": "Setback mode",
|
||||||
|
"eco": "Eco",
|
||||||
|
"absenkstop": "Setback stop",
|
||||||
|
"at_abschaltung": "OT shutdown",
|
||||||
|
"rt_abschaltung": "RT shutdown",
|
||||||
|
"at_frostschutz": "OT frost protection",
|
||||||
|
"rt_frostschutz": "RT frost protection",
|
||||||
|
"dhw_prior": "DHWPrior",
|
||||||
|
"cooling": "Cooling",
|
||||||
|
"tpw": "TPW",
|
||||||
|
"warmwasservorrang": "DHW priority",
|
||||||
|
"mindest_kombizeit": "Minimum combi time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
homeassistant/components/wolflink/translations/en.json
Normal file
28
homeassistant/components/wolflink/translations/en.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Device is already configured"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "Failed to connect, please try again",
|
||||||
|
"invalid_auth": "Invalid authentication",
|
||||||
|
"unknown": "Unexpected error"
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"username": "Username",
|
||||||
|
"password": "Password"
|
||||||
|
},
|
||||||
|
"title": "WOLF SmartSet connection"
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"data": {
|
||||||
|
"device_name": "Device"
|
||||||
|
},
|
||||||
|
"title": "Select WOLF device"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Wolf SmartSet Service"
|
||||||
|
}
|
||||||
|
}
|
@ -186,6 +186,7 @@ FLOWS = [
|
|||||||
"wiffi",
|
"wiffi",
|
||||||
"withings",
|
"withings",
|
||||||
"wled",
|
"wled",
|
||||||
|
"wolflink",
|
||||||
"xiaomi_aqara",
|
"xiaomi_aqara",
|
||||||
"xiaomi_miio",
|
"xiaomi_miio",
|
||||||
"zerproc",
|
"zerproc",
|
||||||
|
@ -2212,6 +2212,9 @@ withings-api==2.1.6
|
|||||||
# homeassistant.components.wled
|
# homeassistant.components.wled
|
||||||
wled==0.4.3
|
wled==0.4.3
|
||||||
|
|
||||||
|
# homeassistant.components.wolflink
|
||||||
|
wolf_smartset==0.1.4
|
||||||
|
|
||||||
# homeassistant.components.xbee
|
# homeassistant.components.xbee
|
||||||
xbee-helper==0.0.7
|
xbee-helper==0.0.7
|
||||||
|
|
||||||
|
@ -978,6 +978,9 @@ withings-api==2.1.6
|
|||||||
# homeassistant.components.wled
|
# homeassistant.components.wled
|
||||||
wled==0.4.3
|
wled==0.4.3
|
||||||
|
|
||||||
|
# homeassistant.components.wolflink
|
||||||
|
wolf_smartset==0.1.4
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
# homeassistant.components.rest
|
# homeassistant.components.rest
|
||||||
# homeassistant.components.startca
|
# homeassistant.components.startca
|
||||||
|
1
tests/components/wolflink/__init__.py
Normal file
1
tests/components/wolflink/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for the Wolf SmartSet Service integration."""
|
139
tests/components/wolflink/test_config_flow.py
Normal file
139
tests/components/wolflink/test_config_flow.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
"""Test the Wolf SmartSet Service config flow."""
|
||||||
|
from httpcore import ConnectError
|
||||||
|
from wolf_smartset.models import Device
|
||||||
|
from wolf_smartset.token_auth import InvalidAuth
|
||||||
|
|
||||||
|
from homeassistant import config_entries, data_entry_flow, setup
|
||||||
|
from homeassistant.components.wolflink.const import (
|
||||||
|
DEVICE_GATEWAY,
|
||||||
|
DEVICE_ID,
|
||||||
|
DEVICE_NAME,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
|
|
||||||
|
from tests.async_mock import patch
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
CONFIG = {
|
||||||
|
DEVICE_NAME: "test-device",
|
||||||
|
DEVICE_ID: 1234,
|
||||||
|
DEVICE_GATEWAY: 5678,
|
||||||
|
CONF_USERNAME: "test-username",
|
||||||
|
CONF_PASSWORD: "test-password",
|
||||||
|
}
|
||||||
|
|
||||||
|
INPUT_CONFIG = {
|
||||||
|
CONF_USERNAME: CONFIG[CONF_USERNAME],
|
||||||
|
CONF_PASSWORD: CONFIG[CONF_PASSWORD],
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVICE = Device(CONFIG[DEVICE_ID], CONFIG[DEVICE_GATEWAY], CONFIG[DEVICE_NAME])
|
||||||
|
|
||||||
|
|
||||||
|
async def test_show_form(hass):
|
||||||
|
"""Test we get the form."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_step_form(hass):
|
||||||
|
"""Test we get the second step of config."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.wolflink.config_flow.WolfClient.fetch_system_list",
|
||||||
|
return_value=[DEVICE],
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=INPUT_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "device"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_create_entry(hass):
|
||||||
|
"""Test entity creation from device step."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.wolflink.config_flow.WolfClient.fetch_system_list",
|
||||||
|
return_value=[DEVICE],
|
||||||
|
), patch("homeassistant.components.wolflink.async_setup_entry", return_value=True):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=INPUT_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
result_create_entry = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"device_name": CONFIG[DEVICE_NAME]},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result_create_entry["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result_create_entry["title"] == CONFIG[DEVICE_NAME]
|
||||||
|
assert result_create_entry["data"] == CONFIG
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_invalid_auth(hass):
|
||||||
|
"""Test we handle invalid auth."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.wolflink.config_flow.WolfClient.fetch_system_list",
|
||||||
|
side_effect=InvalidAuth,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=INPUT_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["errors"] == {"base": "invalid_auth"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_cannot_connect(hass):
|
||||||
|
"""Test we handle cannot connect error."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.wolflink.config_flow.WolfClient.fetch_system_list",
|
||||||
|
side_effect=ConnectError,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=INPUT_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_unknown_exception(hass):
|
||||||
|
"""Test we handle cannot connect error."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.wolflink.config_flow.WolfClient.fetch_system_list",
|
||||||
|
side_effect=Exception,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=INPUT_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["errors"] == {"base": "unknown"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_already_configured_error(hass):
|
||||||
|
"""Test already configured while creating entry."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.wolflink.config_flow.WolfClient.fetch_system_list",
|
||||||
|
return_value=[DEVICE],
|
||||||
|
), patch("homeassistant.components.wolflink.async_setup_entry", return_value=True):
|
||||||
|
|
||||||
|
MockConfigEntry(
|
||||||
|
domain=DOMAIN, unique_id=CONFIG[DEVICE_ID], data=CONFIG
|
||||||
|
).add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=INPUT_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
result_create_entry = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"device_name": CONFIG[DEVICE_NAME]},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result_create_entry["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result_create_entry["reason"] == "already_configured"
|
Loading…
x
Reference in New Issue
Block a user