Compare commits

..

3 Commits

Author SHA1 Message Date
Lukas Svoboda bbb6a23b6c
Merge pull request #45 from schizza/config_flow_depr_fix
Fix config flow
2025-02-13 17:33:16 +01:00
schizza d9cb2179d5 Fix config deprecation
Updated config flow to comply with new changes in HA
2025-02-13 17:32:07 +01:00
schizza 25bb26bb4d 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.
2025-02-12 13:13:46 +01:00
11 changed files with 679 additions and 307 deletions

View File

@ -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

View File

@ -1,9 +1,10 @@
"""Config flow for Sencor SWS 12500 Weather Station integration."""
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
@ -17,6 +18,7 @@ from .const import (
WINDY_API_KEY,
WINDY_ENABLED,
WINDY_LOGGER_ENABLED,
WSLINK,
)
@ -28,35 +30,56 @@ 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),
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,
}
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.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)
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.windy_data_schema = {
vol.Optional(
WINDY_API_KEY, default=self.windy_data[WINDY_API_KEY] or ""
@ -76,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",
@ -93,7 +118,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)
@ -111,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",
@ -133,18 +160,19 @@ 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)
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
class ConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Sencor SWS 12500 Weather Station."""
data_schema = {
vol.Required(API_ID): str,
vol.Required(API_KEY): str,
vol.Optional(WSLINK): bool,
vol.Optional(DEV_DBG): bool,
}
@ -170,7 +198,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",
@ -178,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()

View File

@ -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,
]

View File

@ -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": []
}

View File

@ -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):

View File

@ -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]

View File

@ -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),
),
)

View File

@ -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),
),
)

View File

@ -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",

View File

@ -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",

View File

@ -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
)