From 25bb26bb4dc5df390967aed2f96d32cee60c23d6 Mon Sep 17 00:00:00 2001 From: schizza Date: Wed, 12 Feb 2025 13:13:46 +0100 Subject: [PATCH 1/2] WSLink support in beta Support for firmware 3.0 and WSLink connections. As for now the 3.0 firmware sending data only on SSL connections. --- custom_components/sws12500/__init__.py | 39 ++- custom_components/sws12500/config_flow.py | 25 +- custom_components/sws12500/const.py | 20 ++ custom_components/sws12500/manifest.json | 2 +- custom_components/sws12500/sensor.py | 268 +----------------- custom_components/sws12500/sensors_common.py | 14 + custom_components/sws12500/sensors_weather.py | 257 +++++++++++++++++ custom_components/sws12500/sensors_wslink.py | 257 +++++++++++++++++ .../sws12500/translations/cs.json | 2 + .../sws12500/translations/en.json | 2 + custom_components/sws12500/utils.py | 52 ++-- 11 files changed, 647 insertions(+), 291 deletions(-) create mode 100644 custom_components/sws12500/sensors_common.py create mode 100644 custom_components/sws12500/sensors_weather.py create mode 100644 custom_components/sws12500/sensors_wslink.py diff --git a/custom_components/sws12500/__init__.py b/custom_components/sws12500/__init__.py index 919a725..6a6e343 100644 --- a/custom_components/sws12500/__init__.py +++ b/custom_components/sws12500/__init__.py @@ -19,12 +19,15 @@ from .const import ( DOMAIN, SENSORS_TO_LOAD, WINDY_ENABLED, + WSLINK, + WSLINK_URL, ) from .utils import ( anonymize, check_disabled, loaded_sensors, remap_items, + remap_wslink_items, translated_notification, translations, update_options, @@ -51,15 +54,25 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator): async def recieved_data(self, webdata): """Handle incoming data query.""" + _wslink = self.config_entry.data.get(WSLINK) data = webdata.query + response = None - if "ID" not in data or "PASSWORD" not in data: + if not _wslink and ("ID" not in data or "PASSWORD" not in data): _LOGGER.error("Invalid request. No security data provided!") raise HTTPUnauthorized - id_data = data["ID"] - key_data = data["PASSWORD"] + if _wslink and ("wsid" not in data or "wspw" not in data): + _LOGGER.error("Invalid request. No security data provided!") + raise HTTPUnauthorized + + if _wslink: + id_data = data["wsid"] + key_data = data["wspw"] + else: + id_data = data["ID"] + key_data = data["PASSWORD"] _id = self.config_entry.options.get(API_ID) _key = self.config_entry.options.get(API_KEY) @@ -71,7 +84,11 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator): if self.config_entry.options.get(WINDY_ENABLED): response = await self.windy.push_data_to_windy(data) - remaped_items = remap_items(data) + remaped_items = ( + remap_wslink_items(data) + if self.config_entry.options.get(WSLINK) + else remap_items(data) + ) if sensors := check_disabled(self.hass, remaped_items, self.config): translate_sensors = [ @@ -98,18 +115,22 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator): if self.config_entry.options.get(DEV_DBG): _LOGGER.info("Dev log: %s", anonymize(data)) - response = response if response else "OK" - return aiohttp.web.Response(body=f"{response}", status=200) + response = response or "OK" + return aiohttp.web.Response(body=f"{response or 'OK'}", status=200) def register_path( hass: HomeAssistant, url_path: str, coordinator: WeatherDataUpdateCoordinator ): """Register path to handle incoming data.""" + + _wslink = hass.config_entries.async_get_entry(WSLINK) + try: route = hass.http.app.router.add_route( "GET", url_path, coordinator.recieved_data ) + except RuntimeError as Ex: # pylint: disable=(broad-except) if ( "Added route will never be executed, method GET is already registered" @@ -144,7 +165,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator - if not register_path(hass, DEFAULT_URL, coordinator): + _wslink = entry.data.get(WSLINK) + + _LOGGER.info("WS Link is %s", "enbled" if _wslink else "disabled") + + if not register_path(hass, DEFAULT_URL if not _wslink else WSLINK_URL, coordinator): _LOGGER.error("Fatal: path not registered!") raise PlatformNotReady diff --git a/custom_components/sws12500/config_flow.py b/custom_components/sws12500/config_flow.py index 964fdd8..00bb6cd 100644 --- a/custom_components/sws12500/config_flow.py +++ b/custom_components/sws12500/config_flow.py @@ -1,4 +1,5 @@ """Config flow for Sencor SWS 12500 Weather Station integration.""" + from typing import Any import voluptuous as vol @@ -17,6 +18,7 @@ from .const import ( WINDY_API_KEY, WINDY_ENABLED, WINDY_LOGGER_ENABLED, + WSLINK, ) @@ -38,22 +40,30 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): self.user_data: dict[str, str] = { API_ID: self.config_entry.options.get(API_ID), API_KEY: self.config_entry.options.get(API_KEY), + WSLINK: self.config_entry.options.get(WSLINK), DEV_DBG: self.config_entry.options.get(DEV_DBG), } self.windy_data: dict[str, Any] = { WINDY_API_KEY: self.config_entry.options.get(WINDY_API_KEY), - WINDY_ENABLED: self.config_entry.options.get(WINDY_ENABLED) if isinstance(self.config_entry.options.get(WINDY_ENABLED), bool) else False, - WINDY_LOGGER_ENABLED: self.config_entry.options.get(WINDY_LOGGER_ENABLED) if isinstance(self.config_entry.options.get(WINDY_LOGGER_ENABLED), bool) else False, + WINDY_ENABLED: self.config_entry.options.get(WINDY_ENABLED) + if isinstance(self.config_entry.options.get(WINDY_ENABLED), bool) + else False, + WINDY_LOGGER_ENABLED: self.config_entry.options.get(WINDY_LOGGER_ENABLED) + if isinstance(self.config_entry.options.get(WINDY_LOGGER_ENABLED), bool) + else False, } self.sensors: dict[str, Any] = { - SENSORS_TO_LOAD: self.config_entry.options.get(SENSORS_TO_LOAD) if isinstance(self.config_entry.options.get(SENSORS_TO_LOAD), list) else [] + SENSORS_TO_LOAD: self.config_entry.options.get(SENSORS_TO_LOAD) + if isinstance(self.config_entry.options.get(SENSORS_TO_LOAD), list) + else [] } self.user_data_schema = { vol.Required(API_ID, default=self.user_data[API_ID] or ""): str, vol.Required(API_KEY, default=self.user_data[API_KEY] or ""): str, + vol.Optional(WSLINK, default=self.user_data[WSLINK]): bool, vol.Optional(DEV_DBG, default=self.user_data[DEV_DBG]): bool, } @@ -93,7 +103,7 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): # retain windy data user_input.update(self.windy_data) - #retain sensors + # retain sensors user_input.update(self.sensors) return self.async_create_entry(title=DOMAIN, data=user_input) @@ -133,7 +143,7 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): # retain user_data user_input.update(self.user_data) - #retain senors + # retain senors user_input.update(self.sensors) return self.async_create_entry(title=DOMAIN, data=user_input) @@ -145,6 +155,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data_schema = { vol.Required(API_ID): str, vol.Required(API_KEY): str, + vol.Optional(WSLINK): bool, vol.Optional(DEV_DBG): bool, } @@ -170,7 +181,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): elif user_input[API_KEY] == user_input[API_ID]: errors["base"] = "valid_credentials_match" else: - return self.async_create_entry(title=DOMAIN, data=user_input, options=user_input) + return self.async_create_entry( + title=DOMAIN, data=user_input, options=user_input + ) return self.async_show_form( step_id="user", diff --git a/custom_components/sws12500/const.py b/custom_components/sws12500/const.py index 393b2e5..9a60f76 100644 --- a/custom_components/sws12500/const.py +++ b/custom_components/sws12500/const.py @@ -5,6 +5,7 @@ from typing import Final DOMAIN = "sws12500" DEFAULT_URL = "/weatherstation/updateweatherstation.php" +WSLINK_URL = "/data/upload.php" WINDY_URL = "https://stations.windy.com/pws/update/" ICON = "mdi:weather" @@ -15,6 +16,7 @@ API_ID = "API_ID" SENSORS_TO_LOAD: Final = "sensors_to_load" DEV_DBG: Final = "dev_debug_checkbox" +WSLINK: Final = "wslink" WINDY_API_KEY = "WINDY_API_KEY" WINDY_ENABLED: Final = "windy_enabled_checkbox" @@ -53,6 +55,7 @@ PURGE_DATA: Final = [ "dailyrainin", ] + BARO_PRESSURE: Final = "baro_pressure" OUTSIDE_TEMP: Final = "outside_temp" DEW_POINT: Final = "dew_point" @@ -99,6 +102,22 @@ REMAP_ITEMS: dict = { "soilmoisture3": CH4_HUMIDITY, } +REMAP_WSLINK_ITEMS: dict = { + "intem": INDOOR_TEMP, + "inhum": INDOOR_HUMIDITY, + "t1temp": OUTSIDE_TEMP, + "t1hum": OUTSIDE_HUMIDITY, + "t1dew": DEW_POINT, + "t1wdir": WIND_DIR, + "t1ws": WIND_SPEED, + "t1wg": WIND_GUST, + "t1rainra": RAIN, + "t1raindy": DAILY_RAIN, + "t1solrad": SOLAR_RADIATION, + "rbar": BARO_PRESSURE, + "uvi": UV +} + DISABLED_BY_DEFAULT: Final = [ CH2_TEMP, CH2_HUMIDITY, @@ -148,3 +167,4 @@ AZIMUT: list[UnitOfDir] = [ UnitOfDir.NNW, UnitOfDir.N, ] + diff --git a/custom_components/sws12500/manifest.json b/custom_components/sws12500/manifest.json index 48e7af5..7c141b2 100644 --- a/custom_components/sws12500/manifest.json +++ b/custom_components/sws12500/manifest.json @@ -10,6 +10,6 @@ "issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues", "requirements": [], "ssdp": [], - "version": "0.1.3.5", + "version": "0.1.4.0", "zeroconf": [] } diff --git a/custom_components/sws12500/sensor.py b/custom_components/sws12500/sensor.py index 79c4af6..fc6888d 100644 --- a/custom_components/sws12500/sensor.py +++ b/custom_components/sws12500/sensor.py @@ -1,29 +1,9 @@ """Sensors definition for SWS12500.""" -from collections.abc import Callable -from dataclasses import dataclass import logging -from typing import Any, cast -from homeassistant.components.sensor import ( - RestoreSensor, - SensorDeviceClass, - SensorEntity, - SensorEntityDescription, - SensorStateClass, -) +from homeassistant.components.sensor import RestoreSensor, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - DEGREE, - PERCENTAGE, - UV_INDEX, - UnitOfIrradiance, - UnitOfPrecipitationDepth, - UnitOfPressure, - UnitOfSpeed, - UnitOfTemperature, - UnitOfVolumetricFlux, -) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo, generate_entity_id @@ -32,258 +12,25 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import WeatherDataUpdateCoordinator from .const import ( - BARO_PRESSURE, - CH2_HUMIDITY, - CH2_TEMP, - CH3_TEMP, - CH3_HUMIDITY, - CH4_TEMP, - CH4_HUMIDITY, CHILL_INDEX, - DAILY_RAIN, - DEW_POINT, DOMAIN, HEAT_INDEX, - INDOOR_HUMIDITY, - INDOOR_TEMP, OUTSIDE_HUMIDITY, OUTSIDE_TEMP, - RAIN, SENSORS_TO_LOAD, - SOLAR_RADIATION, - UV, WIND_AZIMUT, WIND_DIR, - WIND_GUST, WIND_SPEED, - UnitOfDir, + WSLINK, ) -from .utils import heat_index, wind_dir_to_text, chill_index +from .sensors_common import WeatherSensorEntityDescription +from .sensors_weather import SENSOR_TYPES_WEATHER_API +from .sensors_wslink import SENSOR_TYPES_WSLINK +from .utils import chill_index, heat_index _LOGGER = logging.getLogger(__name__) -@dataclass(frozen=True, kw_only=True) -class WeatherSensorEntityDescription(SensorEntityDescription): - """Describe Weather Sensor entities.""" - - value_fn: Callable[[Any], int | float | str | None] - - -SENSOR_TYPES: tuple[WeatherSensorEntityDescription, ...] = ( - WeatherSensorEntityDescription( - key=INDOOR_TEMP, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, - translation_key=INDOOR_TEMP, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=INDOOR_HUMIDITY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:thermometer", - device_class=SensorDeviceClass.HUMIDITY, - translation_key=INDOOR_HUMIDITY, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=OUTSIDE_TEMP, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, - translation_key=OUTSIDE_TEMP, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=OUTSIDE_HUMIDITY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:thermometer", - device_class=SensorDeviceClass.HUMIDITY, - translation_key=OUTSIDE_HUMIDITY, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=DEW_POINT, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:thermometer-lines", - device_class=SensorDeviceClass.TEMPERATURE, - translation_key=DEW_POINT, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=BARO_PRESSURE, - native_unit_of_measurement=UnitOfPressure.INHG, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:thermometer-lines", - device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, - suggested_unit_of_measurement=UnitOfPressure.HPA, - translation_key=BARO_PRESSURE, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=WIND_SPEED, - native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.WIND_SPEED, - suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, - icon="mdi:weather-windy", - translation_key=WIND_SPEED, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=WIND_GUST, - native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.WIND_SPEED, - suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, - icon="mdi:windsock", - translation_key=WIND_GUST, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=WIND_DIR, - native_unit_of_measurement=DEGREE, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=None, - icon="mdi:sign-direction", - translation_key=WIND_DIR, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=WIND_AZIMUT, - icon="mdi:sign-direction", - value_fn=lambda data: cast(str, wind_dir_to_text(data)), - device_class=SensorDeviceClass.ENUM, - options=list(UnitOfDir), - translation_key=WIND_AZIMUT, - ), - WeatherSensorEntityDescription( - key=RAIN, - native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, - device_class=SensorDeviceClass.PRECIPITATION, - state_class=SensorStateClass.TOTAL, - suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, - suggested_display_precision=2, - icon="mdi:weather-pouring", - translation_key=RAIN, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=DAILY_RAIN, - native_unit_of_measurement=UnitOfVolumetricFlux.INCHES_PER_DAY, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, - suggested_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_DAY, - suggested_display_precision=2, - icon="mdi:weather-pouring", - translation_key=DAILY_RAIN, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=SOLAR_RADIATION, - native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.IRRADIANCE, - icon="mdi:weather-sunny", - translation_key=SOLAR_RADIATION, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=UV, - name=UV, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=UV_INDEX, - icon="mdi:sunglasses", - translation_key=UV, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=CH2_TEMP, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, - suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, - icon="mdi:weather-sunny", - translation_key=CH2_TEMP, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=CH2_HUMIDITY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.HUMIDITY, - icon="mdi:weather-sunny", - translation_key=CH2_HUMIDITY, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=CH3_TEMP, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, - suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, - icon="mdi:weather-sunny", - translation_key=CH3_TEMP, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=CH3_HUMIDITY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.HUMIDITY, - icon="mdi:weather-sunny", - translation_key=CH3_HUMIDITY, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=CH4_TEMP, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, - suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, - icon="mdi:weather-sunny", - translation_key=CH4_TEMP, - value_fn=lambda data: cast(float, data), - ), - WeatherSensorEntityDescription( - key=CH4_HUMIDITY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.HUMIDITY, - icon="mdi:weather-sunny", - translation_key=CH4_HUMIDITY, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=HEAT_INDEX, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, - suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, - icon="mdi:weather-sunny", - translation_key=HEAT_INDEX, - value_fn=lambda data: cast(int, data), - ), - WeatherSensorEntityDescription( - key=CHILL_INDEX, - native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, - suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, - suggested_display_precision=2, - icon="mdi:weather-sunny", - translation_key=CHILL_INDEX, - value_fn=lambda data: cast(int, data), - ), -) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -295,6 +42,9 @@ async def async_setup_entry( sensors_to_load: list = [] sensors: list = [] + _wslink = config_entry.data.get(WSLINK) + + SENSOR_TYPES = SENSOR_TYPES_WSLINK if _wslink else SENSOR_TYPES_WEATHER_API # Check if we have some sensors to load. if sensors_to_load := config_entry.options.get(SENSORS_TO_LOAD): diff --git a/custom_components/sws12500/sensors_common.py b/custom_components/sws12500/sensors_common.py new file mode 100644 index 0000000..c5443e5 --- /dev/null +++ b/custom_components/sws12500/sensors_common.py @@ -0,0 +1,14 @@ +"""Common classes for sensors.""" + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any + +from homeassistant.components.sensor import SensorEntityDescription + + +@dataclass(frozen=True, kw_only=True) +class WeatherSensorEntityDescription(SensorEntityDescription): + """Describe Weather Sensor entities.""" + + value_fn: Callable[[Any], int | float | str | None] diff --git a/custom_components/sws12500/sensors_weather.py b/custom_components/sws12500/sensors_weather.py new file mode 100644 index 0000000..9912633 --- /dev/null +++ b/custom_components/sws12500/sensors_weather.py @@ -0,0 +1,257 @@ +"""Sensor entities for the SWS12500 integration for old endpoint.""" + +from typing import cast + +from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass +from homeassistant.const import ( + DEGREE, + PERCENTAGE, + UV_INDEX, + UnitOfIrradiance, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfVolumetricFlux, +) + +from .const import ( + BARO_PRESSURE, + CH2_HUMIDITY, + CH2_TEMP, + CH3_HUMIDITY, + CH3_TEMP, + CH4_HUMIDITY, + CH4_TEMP, + CHILL_INDEX, + DAILY_RAIN, + DEW_POINT, + HEAT_INDEX, + INDOOR_HUMIDITY, + INDOOR_TEMP, + OUTSIDE_HUMIDITY, + OUTSIDE_TEMP, + RAIN, + SOLAR_RADIATION, + UV, + WIND_AZIMUT, + WIND_DIR, + WIND_GUST, + WIND_SPEED, + UnitOfDir, +) +from .sensors_common import WeatherSensorEntityDescription +from .utils import wind_dir_to_text + +SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = ( + WeatherSensorEntityDescription( + key=INDOOR_TEMP, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.TEMPERATURE, + translation_key=INDOOR_TEMP, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=INDOOR_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.HUMIDITY, + translation_key=INDOOR_HUMIDITY, + value_fn=lambda data: cast("int", data), + ), + WeatherSensorEntityDescription( + key=OUTSIDE_TEMP, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.TEMPERATURE, + translation_key=OUTSIDE_TEMP, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=OUTSIDE_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.HUMIDITY, + translation_key=OUTSIDE_HUMIDITY, + value_fn=lambda data: cast(int, data), + ), + WeatherSensorEntityDescription( + key=DEW_POINT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer-lines", + device_class=SensorDeviceClass.TEMPERATURE, + translation_key=DEW_POINT, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=BARO_PRESSURE, + native_unit_of_measurement=UnitOfPressure.INHG, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer-lines", + device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, + suggested_unit_of_measurement=UnitOfPressure.HPA, + translation_key=BARO_PRESSURE, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=WIND_SPEED, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.WIND_SPEED, + suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + icon="mdi:weather-windy", + translation_key=WIND_SPEED, + value_fn=lambda data: cast(int, data), + ), + WeatherSensorEntityDescription( + key=WIND_GUST, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.WIND_SPEED, + suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + icon="mdi:windsock", + translation_key=WIND_GUST, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=WIND_DIR, + native_unit_of_measurement=DEGREE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=None, + icon="mdi:sign-direction", + translation_key=WIND_DIR, + value_fn=lambda data: cast(int, data), + ), + WeatherSensorEntityDescription( + key=WIND_AZIMUT, + icon="mdi:sign-direction", + value_fn=lambda data: cast(str, wind_dir_to_text(data)), + device_class=SensorDeviceClass.ENUM, + options=list(UnitOfDir), + translation_key=WIND_AZIMUT, + ), + WeatherSensorEntityDescription( + key=RAIN, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.TOTAL, + suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=RAIN, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=DAILY_RAIN, + native_unit_of_measurement=UnitOfVolumetricFlux.INCHES_PER_DAY, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, + suggested_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_DAY, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=DAILY_RAIN, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=SOLAR_RADIATION, + native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.IRRADIANCE, + icon="mdi:weather-sunny", + translation_key=SOLAR_RADIATION, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=UV, + name=UV, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UV_INDEX, + icon="mdi:sunglasses", + translation_key=UV, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=CH2_TEMP, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.TEMPERATURE, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + icon="mdi:weather-sunny", + translation_key=CH2_TEMP, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=CH2_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.HUMIDITY, + icon="mdi:weather-sunny", + translation_key=CH2_HUMIDITY, + value_fn=lambda data: cast(int, data), + ), + WeatherSensorEntityDescription( + key=CH3_TEMP, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.TEMPERATURE, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + icon="mdi:weather-sunny", + translation_key=CH3_TEMP, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=CH3_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.HUMIDITY, + icon="mdi:weather-sunny", + translation_key=CH3_HUMIDITY, + value_fn=lambda data: cast(int, data), + ), + WeatherSensorEntityDescription( + key=CH4_TEMP, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.TEMPERATURE, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + icon="mdi:weather-sunny", + translation_key=CH4_TEMP, + value_fn=lambda data: cast(float, data), + ), + WeatherSensorEntityDescription( + key=CH4_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.HUMIDITY, + icon="mdi:weather-sunny", + translation_key=CH4_HUMIDITY, + value_fn=lambda data: cast(int, data), + ), + WeatherSensorEntityDescription( + key=HEAT_INDEX, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.TEMPERATURE, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + icon="mdi:weather-sunny", + translation_key=HEAT_INDEX, + value_fn=lambda data: cast(int, data), + ), + WeatherSensorEntityDescription( + key=CHILL_INDEX, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.TEMPERATURE, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + suggested_display_precision=2, + icon="mdi:weather-sunny", + translation_key=CHILL_INDEX, + value_fn=lambda data: cast(int, data), + ), +) diff --git a/custom_components/sws12500/sensors_wslink.py b/custom_components/sws12500/sensors_wslink.py new file mode 100644 index 0000000..880ec53 --- /dev/null +++ b/custom_components/sws12500/sensors_wslink.py @@ -0,0 +1,257 @@ +"""Sensor entities for the SWS12500 integration for old endpoint.""" + +from typing import cast + +from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass +from homeassistant.const import ( + DEGREE, + PERCENTAGE, + UV_INDEX, + UnitOfIrradiance, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfVolumetricFlux, +) + +from .const import ( + BARO_PRESSURE, + CH2_HUMIDITY, + CH2_TEMP, + CH3_HUMIDITY, + CH3_TEMP, + CH4_HUMIDITY, + CH4_TEMP, + CHILL_INDEX, + DAILY_RAIN, + DEW_POINT, + HEAT_INDEX, + INDOOR_HUMIDITY, + INDOOR_TEMP, + OUTSIDE_HUMIDITY, + OUTSIDE_TEMP, + RAIN, + SOLAR_RADIATION, + UV, + WIND_AZIMUT, + WIND_DIR, + WIND_GUST, + WIND_SPEED, + UnitOfDir, +) +from .sensors_common import WeatherSensorEntityDescription +from .utils import wind_dir_to_text + +SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( + WeatherSensorEntityDescription( + key=INDOOR_TEMP, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.TEMPERATURE, + translation_key=INDOOR_TEMP, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=INDOOR_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.HUMIDITY, + translation_key=INDOOR_HUMIDITY, + value_fn=lambda data: cast("int", data), + ), + WeatherSensorEntityDescription( + key=OUTSIDE_TEMP, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.TEMPERATURE, + translation_key=OUTSIDE_TEMP, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=OUTSIDE_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer", + device_class=SensorDeviceClass.HUMIDITY, + translation_key=OUTSIDE_HUMIDITY, + value_fn=lambda data: cast("int", data), + ), + WeatherSensorEntityDescription( + key=DEW_POINT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer-lines", + device_class=SensorDeviceClass.TEMPERATURE, + translation_key=DEW_POINT, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=BARO_PRESSURE, + native_unit_of_measurement=UnitOfPressure.HPA, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:thermometer-lines", + device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, + suggested_unit_of_measurement=UnitOfPressure.HPA, + translation_key=BARO_PRESSURE, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=WIND_SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.WIND_SPEED, + suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + icon="mdi:weather-windy", + translation_key=WIND_SPEED, + value_fn=lambda data: cast("int", data), + ), + WeatherSensorEntityDescription( + key=WIND_GUST, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.WIND_SPEED, + suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + icon="mdi:windsock", + translation_key=WIND_GUST, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=WIND_DIR, + native_unit_of_measurement=DEGREE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=None, + icon="mdi:sign-direction", + translation_key=WIND_DIR, + value_fn=lambda data: cast("int", data), + ), + WeatherSensorEntityDescription( + key=WIND_AZIMUT, + icon="mdi:sign-direction", + value_fn=lambda data: cast("str", wind_dir_to_text(data)), + device_class=SensorDeviceClass.ENUM, + options=list(UnitOfDir), + translation_key=WIND_AZIMUT, + ), + WeatherSensorEntityDescription( + key=RAIN, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.TOTAL, + suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=RAIN, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=DAILY_RAIN, + native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, + suggested_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_DAY, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=DAILY_RAIN, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=SOLAR_RADIATION, + native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.IRRADIANCE, + icon="mdi:weather-sunny", + translation_key=SOLAR_RADIATION, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=UV, + name=UV, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UV_INDEX, + icon="mdi:sunglasses", + translation_key=UV, + value_fn=lambda data: cast("float", data), + ), + # WeatherSensorEntityDescription( + # key=CH2_TEMP, + # native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + # state_class=SensorStateClass.MEASUREMENT, + # device_class=SensorDeviceClass.TEMPERATURE, + # suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + # icon="mdi:weather-sunny", + # translation_key=CH2_TEMP, + # value_fn=lambda data: cast(float, data), + # ), + # WeatherSensorEntityDescription( + # key=CH2_HUMIDITY, + # native_unit_of_measurement=PERCENTAGE, + # state_class=SensorStateClass.MEASUREMENT, + # device_class=SensorDeviceClass.HUMIDITY, + # icon="mdi:weather-sunny", + # translation_key=CH2_HUMIDITY, + # value_fn=lambda data: cast(int, data), + # ), + # WeatherSensorEntityDescription( + # key=CH3_TEMP, + # native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + # state_class=SensorStateClass.MEASUREMENT, + # device_class=SensorDeviceClass.TEMPERATURE, + # suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + # icon="mdi:weather-sunny", + # translation_key=CH3_TEMP, + # value_fn=lambda data: cast(float, data), + # ), + # WeatherSensorEntityDescription( + # key=CH3_HUMIDITY, + # native_unit_of_measurement=PERCENTAGE, + # state_class=SensorStateClass.MEASUREMENT, + # device_class=SensorDeviceClass.HUMIDITY, + # icon="mdi:weather-sunny", + # translation_key=CH3_HUMIDITY, + # value_fn=lambda data: cast(int, data), + # ), + # WeatherSensorEntityDescription( + # key=CH4_TEMP, + # native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + # state_class=SensorStateClass.MEASUREMENT, + # device_class=SensorDeviceClass.TEMPERATURE, + # suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + # icon="mdi:weather-sunny", + # translation_key=CH4_TEMP, + # value_fn=lambda data: cast(float, data), + # ), + # WeatherSensorEntityDescription( + # key=CH4_HUMIDITY, + # native_unit_of_measurement=PERCENTAGE, + # state_class=SensorStateClass.MEASUREMENT, + # device_class=SensorDeviceClass.HUMIDITY, + # icon="mdi:weather-sunny", + # translation_key=CH4_HUMIDITY, + # value_fn=lambda data: cast(int, data), + # ), + WeatherSensorEntityDescription( + key=HEAT_INDEX, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.TEMPERATURE, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + icon="mdi:weather-sunny", + translation_key=HEAT_INDEX, + value_fn=lambda data: cast("int", data), + ), + WeatherSensorEntityDescription( + key=CHILL_INDEX, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.TEMPERATURE, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, + suggested_display_precision=2, + icon="mdi:weather-sunny", + translation_key=CHILL_INDEX, + value_fn=lambda data: cast("int", data), + ), +) diff --git a/custom_components/sws12500/translations/cs.json b/custom_components/sws12500/translations/cs.json index dfc2b13..e996974 100644 --- a/custom_components/sws12500/translations/cs.json +++ b/custom_components/sws12500/translations/cs.json @@ -10,6 +10,7 @@ "data": { "API_ID": "API ID / ID stanice", "API_KEY": "API KEY / Heslo", + "wslink": "WSLink API", "dev_debug_checkbox": "Developer log" }, "description": "Zadejte API ID a API KEY, aby meteostanice mohla komunikovat s HomeAssistantem", @@ -32,6 +33,7 @@ "data": { "API_ID": "API ID / ID Stanice", "API_KEY": "API KEY / Heslo", + "wslink": "WSLink API", "dev_debug_checkbox": "Developer log" }, "description": "Zadejte API ID a API KEY, aby meteostanice mohla komunikovat s HomeAssistantem", diff --git a/custom_components/sws12500/translations/en.json b/custom_components/sws12500/translations/en.json index 976657a..b07b7a9 100644 --- a/custom_components/sws12500/translations/en.json +++ b/custom_components/sws12500/translations/en.json @@ -10,6 +10,7 @@ "data": { "API_ID": "API ID / Station ID", "API_KEY": "API KEY / Password", + "WSLINK": "WSLink API", "dev_debug_checkbox": "Developer log" }, "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant", @@ -32,6 +33,7 @@ "data": { "API_ID": "API ID / Station ID", "API_KEY": "API KEY / Password", + "WSLINK": "WSLink API", "dev_debug_checkbox": "Developer log" }, "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant", diff --git a/custom_components/sws12500/utils.py b/custom_components/sws12500/utils.py index 38b26ca..1747e75 100644 --- a/custom_components/sws12500/utils.py +++ b/custom_components/sws12500/utils.py @@ -1,25 +1,27 @@ """Utils for SWS12500.""" import logging +import math +from typing import Any + +import numpy as np from homeassistant.components import persistent_notification from homeassistant.config_entries import ConfigEntry +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.translation import async_get_translations -from homeassistant.const import UnitOfTemperature -from typing import Any -import math -import numpy as np from .const import ( AZIMUT, DEV_DBG, - REMAP_ITEMS, - SENSORS_TO_LOAD, - UnitOfDir, - OUTSIDE_TEMP, OUTSIDE_HUMIDITY, + OUTSIDE_TEMP, + REMAP_ITEMS, + REMAP_WSLINK_ITEMS, + SENSORS_TO_LOAD, WIND_SPEED, + UnitOfDir, ) _LOGGER = logging.getLogger(__name__) @@ -99,7 +101,7 @@ def anonymize(data): anonym = {} for k in data: - if k not in ("ID", "PASSWORD"): + if k not in ("ID", "PASSWORD", "wsid", "wspw"): anonym[k] = data[k] return anonym @@ -115,6 +117,16 @@ def remap_items(entities): return items +def remap_wslink_items(entities): + """Remap items in query for WSLink API.""" + items = {} + for item in entities: + if item in REMAP_WSLINK_ITEMS: + items[REMAP_WSLINK_ITEMS[item]] = entities[item] + + return items + + def loaded_sensors(config_entry: ConfigEntry) -> list | None: """Get loaded sensors.""" @@ -201,12 +213,16 @@ def chill_index(data: Any) -> UnitOfTemperature: temp = float(data[OUTSIDE_TEMP]) wind = float(data[WIND_SPEED]) - - return round( - ( - (35.7 + (0.6215 * temp)) - - (35.75 * (wind**0.16)) - + (0.4275 * (temp * (wind**0.16))) - ), - 2, - ) if temp < 50 and wind > 3 else temp + + return ( + round( + ( + (35.7 + (0.6215 * temp)) + - (35.75 * (wind**0.16)) + + (0.4275 * (temp * (wind**0.16))) + ), + 2, + ) + if temp < 50 and wind > 3 + else temp + ) From d9cb2179d50e62c6a3613bc7a291d2a3aab863a9 Mon Sep 17 00:00:00 2001 From: schizza Date: Thu, 13 Feb 2025 17:32:07 +0100 Subject: [PATCH 2/2] Fix config deprecation Updated config flow to comply with new changes in HA --- custom_components/sws12500/config_flow.py | 60 ++++++++++++++--------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/custom_components/sws12500/config_flow.py b/custom_components/sws12500/config_flow.py index 00bb6cd..102d3d9 100644 --- a/custom_components/sws12500/config_flow.py +++ b/custom_components/sws12500/config_flow.py @@ -4,7 +4,7 @@ from typing import Any import voluptuous as vol -from homeassistant import config_entries +from homeassistant.config_entries import ConfigFlow, OptionsFlow from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError @@ -30,12 +30,25 @@ class InvalidAuth(HomeAssistantError): """Invalid auth exception.""" -class ConfigOptionsFlowHandler(config_entries.OptionsFlow): +class ConfigOptionsFlowHandler(OptionsFlow): """Handle WeatherStation ConfigFlow.""" - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + def __init__(self) -> None: """Initialize flow.""" - self.config_entry = config_entry + super().__init__() + + self.windy_data: dict[str, Any] = {} + self.windy_data_schema = {} + self.user_data: dict[str, str] = {} + self.user_data_schema = {} + self.sensors: dict[str, Any] = {} + + @property + def config_entry(self): + return self.hass.config_entries.async_get_entry(self.handler) + + def _get_entry_data(self): + """Get entry data.""" self.user_data: dict[str, str] = { API_ID: self.config_entry.options.get(API_ID), @@ -44,6 +57,19 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): DEV_DBG: self.config_entry.options.get(DEV_DBG), } + self.user_data_schema = { + vol.Required(API_ID, default=self.user_data[API_ID] or ""): str, + vol.Required(API_KEY, default=self.user_data[API_KEY] or ""): str, + vol.Optional(WSLINK, default=self.user_data[WSLINK]): bool, + vol.Optional(DEV_DBG, default=self.user_data[DEV_DBG]): bool, + } + + self.sensors: dict[str, Any] = { + SENSORS_TO_LOAD: self.config_entry.options.get(SENSORS_TO_LOAD) + if isinstance(self.config_entry.options.get(SENSORS_TO_LOAD), list) + else [] + } + self.windy_data: dict[str, Any] = { WINDY_API_KEY: self.config_entry.options.get(WINDY_API_KEY), WINDY_ENABLED: self.config_entry.options.get(WINDY_ENABLED) @@ -54,19 +80,6 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): else False, } - self.sensors: dict[str, Any] = { - SENSORS_TO_LOAD: self.config_entry.options.get(SENSORS_TO_LOAD) - if isinstance(self.config_entry.options.get(SENSORS_TO_LOAD), list) - else [] - } - - self.user_data_schema = { - vol.Required(API_ID, default=self.user_data[API_ID] or ""): str, - vol.Required(API_KEY, default=self.user_data[API_KEY] or ""): str, - vol.Optional(WSLINK, default=self.user_data[WSLINK]): bool, - vol.Optional(DEV_DBG, default=self.user_data[DEV_DBG]): bool, - } - self.windy_data_schema = { vol.Optional( WINDY_API_KEY, default=self.windy_data[WINDY_API_KEY] or "" @@ -86,6 +99,8 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): """Manage basic options - credentials.""" errors = {} + self._get_entry_data() + if user_input is None: return self.async_show_form( step_id="basic", @@ -121,6 +136,8 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): """Manage windy options.""" errors = {} + self._get_entry_data() + if user_input is None: return self.async_show_form( step_id="windy", @@ -149,7 +166,7 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): return self.async_create_entry(title=DOMAIN, data=user_input) -class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): +class ConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for Sencor SWS 12500 Weather Station.""" data_schema = { @@ -191,10 +208,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors=errors, ) + @staticmethod @callback - def async_get_options_flow( - config_entry: config_entries.ConfigEntry, - ) -> ConfigOptionsFlowHandler: + def async_get_options_flow(config_entry) -> ConfigOptionsFlowHandler: """Get the options flow for this handler.""" - return ConfigOptionsFlowHandler(config_entry) + return ConfigOptionsFlowHandler()