diff --git a/custom_components/sws12500/__init__.py b/custom_components/sws12500/__init__.py index 06e26bf..931e411 100644 --- a/custom_components/sws12500/__init__.py +++ b/custom_components/sws12500/__init__.py @@ -39,13 +39,12 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.event import async_track_time_interval +from .conflicts import effective_protocols, update_protocol_conflict_issue from .const import ( DEFAULT_URL, DOMAIN, - ECOWITT_ENABLED, ECOWITT_URL_PREFIX, HEALTH_URL, - LEGACY_ENABLED, POCASI_CZ_ENABLED, SENSORS_TO_LOAD, WINDY_ENABLED, @@ -81,8 +80,8 @@ def register_path( raise ConfigEntryNotReady _wslink: bool = checked_or(config.options.get(WSLINK), bool, False) - _ecowitt_enabled: bool = checked_or(config.options.get(ECOWITT_ENABLED), bool, False) - _legacy: bool = checked_or(config.options.get(LEGACY_ENABLED), bool, True) + # Legacy and Ecowitt share one entity namespace, so only one may be wired up. + _legacy, _ecowitt_enabled = effective_protocols(config) # Load registred routes routes: Routes | None = hass_data.get("routes", None) @@ -157,8 +156,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: SWSConfigEntry) -> bool: last_options=dict(entry.options), ) _wslink = checked_or(entry.options.get(WSLINK), bool, False) - _legacy = checked_or(entry.options.get(LEGACY_ENABLED), bool, True) - _ecowitt_enabled = checked_or(entry.options.get(ECOWITT_ENABLED), bool, False) + # Legacy and Ecowitt share one entity namespace, so only one may be wired up. + _legacy, _ecowitt_enabled = effective_protocols(entry) _ecowitt_path = ECOWITT_URL_PREFIX + "/{webhook_id}" _LOGGER.debug("WS Link is %s", "enabled" if _wslink else "disabled") @@ -197,6 +196,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: SWSConfigEntry) -> bool: entry.async_on_unload(entry.add_update_listener(update_listener)) update_legacy_battery_issue(hass, entry) + update_protocol_conflict_issue(hass, entry) return True diff --git a/custom_components/sws12500/config_flow.py b/custom_components/sws12500/config_flow.py index bb3a3d0..e070b8c 100644 --- a/custom_components/sws12500/config_flow.py +++ b/custom_components/sws12500/config_flow.py @@ -14,6 +14,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import selector from homeassistant.helpers.network import get_url +from .conflicts import ERROR_MUTUALLY_EXCLUSIVE from .const import ( API_ID, API_KEY, @@ -171,7 +172,11 @@ class ConfigOptionsFlowHandler(OptionsFlow): ) if user_input.get(LEGACY_ENABLED): - if user_input[API_ID] in INVALID_CREDENTIALS or user_input.get(API_ID, "") == "": + # Both endpoints remap onto the same internal sensor keys, so enabling the + # legacy one while Ecowitt is active would corrupt those entities. + if self.ecowitt.get(ECOWITT_ENABLED): + errors["base"] = ERROR_MUTUALLY_EXCLUSIVE + elif user_input[API_ID] in INVALID_CREDENTIALS or user_input.get(API_ID, "") == "": errors[API_ID] = "valid_credentials_api" elif user_input[API_KEY] in INVALID_CREDENTIALS or user_input.get(API_KEY, "") == "": errors[API_KEY] = "valid_credentials_key" @@ -261,35 +266,38 @@ class ConfigOptionsFlowHandler(OptionsFlow): if not (webhook := self.ecowitt.get(ECOWITT_WEBHOOK_ID)): webhook = secrets.token_hex(8) - if user_input is None: - url: URL = URL(get_url(self.hass)) + if user_input is not None: + # Both endpoints remap onto the same internal sensor keys, so enabling + # Ecowitt while the legacy endpoint is active would corrupt those entities. + if user_input.get(ECOWITT_ENABLED) and self.user_data.get(LEGACY_ENABLED): + errors["base"] = ERROR_MUTUALLY_EXCLUSIVE + else: + return self.async_create_entry(title=DOMAIN, data=self.retain_data(user_input)) - host = url.host or "UNKNOWN" + url: URL = URL(get_url(self.hass)) + host = url.host or "UNKNOWN" - ecowitt_schema = { - vol.Required( - ECOWITT_WEBHOOK_ID, - default=webhook, - ): str, - vol.Optional( - ECOWITT_ENABLED, - default=self.ecowitt.get(ECOWITT_ENABLED, False), - ): bool, - } + ecowitt_schema = { + vol.Required( + ECOWITT_WEBHOOK_ID, + default=webhook, + ): str, + vol.Optional( + ECOWITT_ENABLED, + default=self.ecowitt.get(ECOWITT_ENABLED, False), + ): bool, + } - return self.async_show_form( - step_id="ecowitt", - data_schema=vol.Schema(ecowitt_schema), - description_placeholders={ - "url": host, - "port": str(url.port), - "webhook_id": webhook, - }, - errors=errors, - ) - - user_input = self.retain_data(user_input) - return self.async_create_entry(title=DOMAIN, data=user_input) + return self.async_show_form( + step_id="ecowitt", + data_schema=vol.Schema(ecowitt_schema), + description_placeholders={ + "url": host, + "port": str(url.port), + "webhook_id": webhook, + }, + errors=errors, + ) async def async_step_wslink_port_setup(self, user_input: Any = None) -> ConfigFlowResult: """WSLink Addon port setup.""" diff --git a/custom_components/sws12500/conflicts.py b/custom_components/sws12500/conflicts.py new file mode 100644 index 0000000..26e6be9 --- /dev/null +++ b/custom_components/sws12500/conflicts.py @@ -0,0 +1,90 @@ +"""Protocol conflict handling. + +The legacy (PWS/WSLink) and Ecowitt endpoints both remap their payloads onto the *same* +internal sensor keys (`REMAP_ITEMS` / `REMAP_WSLINK_ITEMS` vs `REMAP_ECOWITT_COMPAT`) and +push them through the same coordinator. Running both at once is therefore unsound: + +1. Units clash. The entity descriptions are chosen by the WSLink flag alone, so Ecowitt's + imperial payload (`tempf`, `baromrelin`, `windspeedmph`, ...) is rendered with the + WSLink set's metric units - 18 of the 26 Ecowitt-mapped keys disagree. +2. Payloads blank each other. `async_set_updated_data` *replaces* the coordinator payload, + so every Ecowitt push blanks the keys only the legacy protocol reports (and vice versa), + flipping those entities to `unknown` on every other push. +3. One entity, two sources. A single `sensor.outside_temp` would alternate between whatever + the two endpoints report. + +The initial config flow already keeps them exclusive (the `pws` step disables Ecowitt, the +`ecowitt` step disables legacy); only the options flow could turn both on. This module is +the single place that states the rule, so the config flow, the route wiring and the device +model all agree on it. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Final + +from py_typecheck import checked_or + +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import issue_registry as ir +from homeassistant.helpers.issue_registry import IssueSeverity + +from .const import DOMAIN, ECOWITT_ENABLED, LEGACY_ENABLED + +if TYPE_CHECKING: + # Type-only: `data` imports `effective_protocols` from here at runtime, so a + # runtime import back into `data` would close an import cycle. + from .data import SWSConfigEntry + +# Shown in the config/options flow when a submit would enable both protocols. +ERROR_MUTUALLY_EXCLUSIVE: Final = "protocols_mutually_exclusive" + + +@callback +def protocols_conflict(entry: SWSConfigEntry) -> bool: + """Return whether this entry has both protocols enabled.""" + return checked_or(entry.options.get(LEGACY_ENABLED), bool, True) and checked_or( + entry.options.get(ECOWITT_ENABLED), bool, False + ) + + +@callback +def effective_protocols(entry: SWSConfigEntry) -> tuple[bool, bool]: + """Return the `(legacy, ecowitt)` state to actually wire up. + + Options written before this rule existed may still have both flags set. Rather than + ingesting two protocols into one entity namespace, legacy wins - matching the + precedence already documented in `health_coordinator._configured_protocol` - and + `update_protocol_conflict_issue` tells the user what happened. + """ + legacy = checked_or(entry.options.get(LEGACY_ENABLED), bool, True) + ecowitt = checked_or(entry.options.get(ECOWITT_ENABLED), bool, False) + + if legacy and ecowitt: + return True, False + return legacy, ecowitt + + +def _issue_id(entry: SWSConfigEntry) -> str: + """Return the Repairs issue id for this config entry.""" + return f"protocol_conflict_{entry.entry_id}" + + +@callback +def update_protocol_conflict_issue(hass: HomeAssistant, entry: SWSConfigEntry) -> None: + """Create or clear the Repairs issue for a legacy/Ecowitt conflict.""" + + issue_id = _issue_id(entry) + + if protocols_conflict(entry): + ir.async_create_issue( + hass, + DOMAIN, + issue_id=issue_id, + is_persistent=True, + is_fixable=False, + severity=IssueSeverity.ERROR, + translation_key="protocol_conflict", + ) + else: + ir.async_delete_issue(hass, DOMAIN, issue_id=issue_id) diff --git a/custom_components/sws12500/data.py b/custom_components/sws12500/data.py index 7528359..02f16c6 100644 --- a/custom_components/sws12500/data.py +++ b/custom_components/sws12500/data.py @@ -19,7 +19,8 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import dt as dt_util -from .const import DOMAIN, ECOWITT_ENABLED, WSLINK +from .conflicts import effective_protocols +from .const import DOMAIN, WSLINK if TYPE_CHECKING: from homeassistant.components.binary_sensor import BinarySensorEntityDescription @@ -69,12 +70,17 @@ def _station_model(entry: SWSConfigEntry) -> str: """Return the device model label reflecting the running station type. Ecowitt (with the learned model when available), else WSLink, else PWS. + + Uses the *effective* protocols so a stale both-enabled config reports the endpoint + that is actually wired up, rather than one that is being ignored. """ - if checked_or(entry.options.get(ECOWITT_ENABLED), bool, False): + legacy, ecowitt = effective_protocols(entry) + + if ecowitt: runtime = getattr(entry, "runtime_data", None) model = getattr(runtime, "ecowitt_model", None) if runtime is not None else None return f"Ecowitt {model}" if model else "Ecowitt" - if checked_or(entry.options.get(WSLINK), bool, False): + if legacy and checked_or(entry.options.get(WSLINK), bool, False): return "WSLink" return "PWS" diff --git a/custom_components/sws12500/strings.json b/custom_components/sws12500/strings.json index 87ac5cc..5520bde 100644 --- a/custom_components/sws12500/strings.json +++ b/custom_components/sws12500/strings.json @@ -1,561 +1,567 @@ { - "config": { - "error": { - "valid_credentials_api": "Provide valid API ID.", - "valid_credentials_key": "Provide valid API KEY.", - "valid_credentials_match": "API ID and API KEY should not be the same." - }, - "step": { - "user": { - "title": "Choose your station type", - "description": "Choose the type of your station. If you don't have Eccowit station, choose PWS/WSLink", - "menu_options": { - "pws": "PWS/WSLink (Sencor, Garni, Bresser, other - Weather Underground compatible)", - "ecowitt": "Ecowitt" - } - }, - "pws": { - "title": "PWS/WSLink credentials.", - "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant.", - "data": { - "API_ID": "API ID / Station ID", - "API_KEY": "API KEY / Password", - "wslink": "WSLink Protocol", - "dev_debug_checkbox": "Developer log" - }, - "data_description": { - "API_ID": "API ID is the Station ID you set in the Weather Station.", - "API_KEY": "API KEY is the password you set in the Weather Station.", - "wslink": "Enable WSLink Protocol if the station is set to send data via WSLink. If you are unsure, use https://test-station.schizza.cz/", - "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer." - } - }, - "ecowitt": { - "title": "Ecowitt configuration.", - "description": "No API ID/KEY needed. Set your Ecowitt station to send data to the enndpoint below.", - "data": { - "ecowitt_webhook_id": "Unique webhook ID", - "ecowitt_enabled": "Enable Ecowitt station data" - }, - "data_description": { - "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", - "ecowitt_enabled": "Enable receiving data from Ecowitt stations" - } - } - } + "config": { + "error": { + "valid_credentials_api": "Provide valid API ID.", + "valid_credentials_key": "Provide valid API KEY.", + "valid_credentials_match": "API ID and API KEY should not be the same.", + "protocols_mutually_exclusive": "The legacy (PWS/WSLink) and Ecowitt endpoints cannot be enabled at the same time - they feed the same sensor entities, which would mix up units and blank readings. Disable one of them first." }, - "options": { - "error": { - "valid_credentials_api": "Provide valid API ID.", - "valid_credentials_key": "Provide valid API KEY.", - "valid_credentials_match": "API ID and API KEY should not be the same.", - "windy_id_required": "Windy API ID is required if you want to enable this function.", - "windy_pw_required": "Windy API password is required if you want to enable this function.", - "windy_key_required": "Windy station ID and password are required if you want to enable this function.", - "pocasi_id_required": "Pocasi Meteo API ID is required if you want to enable this function.", - "pocasi_key_required": "Pocasi Meteo API key is required if you want to enable this function.", - "pocasi_send_minimum": "The resend interval must be at least 12 seconds." + "step": { + "user": { + "title": "Choose your station type", + "description": "Choose the type of your station. If you don't have Eccowit station, choose PWS/WSLink", + "menu_options": { + "pws": "PWS/WSLink (Sencor, Garni, Bresser, other - Weather Underground compatible)", + "ecowitt": "Ecowitt" + } + }, + "pws": { + "title": "PWS/WSLink credentials.", + "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant.", + "data": { + "API_ID": "API ID / Station ID", + "API_KEY": "API KEY / Password", + "wslink": "WSLink Protocol", + "dev_debug_checkbox": "Developer log" }, - "step": { - "init": { - "title": "Configure SWS12500 Integration", - "description": "Choose what do you want to configure. If basic access or resending data for Windy site", - "menu_options": { - "basic": "Basic - configure credentials for Weather Station", - "wslink_port_setup": "WSLink add-on port", - "ecowitt": "Ecowitt configuration", - "windy": "Windy configuration", - "pocasi": "Pocasi Meteo CZ configuration" - } - }, - "basic": { - "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant", - "title": "Configure credentials", - "data": { - "API_ID": "API ID / Station ID", - "API_KEY": "API KEY / Password", - "wslink": "WSLink API", - "legacy_enabled": "Enable PWS/WSLink endpoint", - "dev_debug_checkbox": "Developer log" - }, - "data_description": { - "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer.", - "API_ID": "API ID is the Station ID you set in the Weather Station.", - "API_KEY": "API KEY is the password you set in the Weather Station.", - "wslink": "Enable WSLink API if the station is set to send data via WSLink.", - "legacy_enabled": "Enable the PWS/WSLink endpoint. Turn off for an Ecowitt-only setup." - } - }, - "windy": { - "description": "Resend weather data to your Windy stations.", - "title": "Configure Windy", - "data": { - "WINDY_STATION_ID": "Station ID obtained form Windy", - "WINDY_STATION_PWD": "Station password obtained from Windy", - "windy_enabled_checkbox": "Enable resending data to Windy", - "windy_logger_checkbox": "Log Windy data and responses" - }, - "data_description": { - "WINDY_STATION_ID": "Windy station ID obtained from https://stations.windy.com/stations", - "WINDY_STATION_PWD": "Windy station password obtained from https://stations.windy.com/stations", - "windy_logger_checkbox": "Enable only if you want to send debuging data to the developer." - } - }, - "pocasi": { - "description": "Resend data to Pocasi Meteo CZ", - "title": "Configure Pocasi Meteo CZ", - "data": { - "POCASI_CZ_API_ID": "ID from your Pocasi Meteo APP", - "POCASI_CZ_API_KEY": "Key from your Pocasi Meteo APP", - "POCASI_SEND_INTERVAL": "Resend interval in seconds", - "pocasi_enabled_chcekbox": "Enable resending data to Pocasi Meteo", - "pocasi_logger_checkbox": "Log data and responses" - }, - "data_description": { - "POCASI_CZ_API_ID": "You can obtain your ID in Pocasi Meteo App", - "POCASI_CZ_API_KEY": "You can obtain your KEY in Pocasi Meteo App", - "POCASI_SEND_INTERVAL": "Resend interval in seconds (minimum 12s, default 30s)", - "pocasi_enabled_chcekbox": "Enables resending data to Pocasi Meteo", - "pocasi_logger_checkbox": "Enable only if you want to send debbug data to the developer" - } - }, - "ecowitt": { - "description": "Ecowitt configuration.", - "title": "Ecowitt station configuration", - "data": { - "ecowitt_webhook_id": "Unique webhook ID", - "ecowitt_enabled": "Enable Ecowitt station data" - }, - "data_description": { - "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", - "ecowitt_enabled": "Enable receiving data from Ecowitt stations" - } - }, - "wslink_port_setup": { - "title": "WSLink add-on port", - "description": "Set the external port of the WSLink proxy add-on so the integration can reach its health endpoint.", - "data": { - "WSLINK_ADDON_PORT": "WSLink add-on port" - }, - "data_description": { - "WSLINK_ADDON_PORT": "The external TCP port the WSLink proxy add-on listens on (default 443)." - } - }, - "migration": { - "title": "Statistic migration.", - "description": "For the correct functioning of long-term statistics, it is necessary to migrate the sensor unit in the long-term statistics. The original unit of long-term statistics for daily precipitation was in mm/d, however, the station only sends data in mm without time differentiation.\n\n The sensor to be migrated is for daily precipitation. If the correct value is already in the list for the daily precipitation sensor (mm), then the migration is already complete.\n\n Migration result for the sensor: {migration_status}, a total of {migration_count} rows converted.", - "data": { - "sensor_to_migrate": "Sensor to migrate", - "trigger_action": "Trigger migration" - }, - "data_description": { - "sensor_to_migrate": "Select the correct sensor for statistics migration.\nThe sensor values will be preserved, they will not be recalculated, only the unit in the long-term statistics will be changed.", - "trigger_action": "Trigger the sensor statistics migration after checking." - } - } + "data_description": { + "API_ID": "API ID is the Station ID you set in the Weather Station.", + "API_KEY": "API KEY is the password you set in the Weather Station.", + "wslink": "Enable WSLink Protocol if the station is set to send data via WSLink. If you are unsure, use https://test-station.schizza.cz/", + "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer." } - }, - "entity": { - "binary_sensor": { - "outside_battery": { - "name": "Outside battery" - }, - "indoor_battery": { - "name": "Console battery" - }, - "ch2_battery": { - "name": "Channel 2 battery" - }, - "ch3_battery": { - "name": "Channel 3 battery" - }, - "ch4_battery": { - "name": "Channel 4 battery" - }, - "ch5_battery": { - "name": "Channel 5 battery" - }, - "ch6_battery": { - "name": "Channel 6 battery" - }, - "ch7_battery": { - "name": "Channel 7 battery" - }, - "ch8_battery": { - "name": "Channel 8 battery" - } + }, + "ecowitt": { + "title": "Ecowitt configuration.", + "description": "No API ID/KEY needed. Set your Ecowitt station to send data to the enndpoint below.", + "data": { + "ecowitt_webhook_id": "Unique webhook ID", + "ecowitt_enabled": "Enable Ecowitt station data" }, - "sensor": { - "ecowitt_absolute_pressure": { - "name": "Absolute pressure" - }, - "ecowitt_rain_rate": { - "name": "Rain rate" - }, - "ecowitt_event_rain": { - "name": "Event rain" - }, - "ecowitt_hourly_rain": { - "name": "Hourly rain" - }, - "ecowitt_weekly_rain": { - "name": "Weekly rain" - }, - "ecowitt_monthly_rain": { - "name": "Monthly rain" - }, - "ecowitt_yearly_rain": { - "name": "Yearly rain" - }, - "ecowitt_total_rain": { - "name": "Total rain" - }, - "ecowitt_24h_rain": { - "name": "24h rain" - }, - "ecowitt_feels_like": { - "name": "Feels like" - }, - "ecowitt_indoor_dewpoint": { - "name": "Indoor dew point" - }, - "ecowitt_console_co2": { - "name": "Console CO₂" - }, - "ecowitt_console_co2_24h": { - "name": "Console CO₂ (24h avg)" - }, - "ecowitt_co2": { - "name": "CO₂" - }, - "ecowitt_co2_24h": { - "name": "CO₂ (24h avg)" - }, - "integration_health": { - "name": "Integration status", - "state": { - "online_wu": "Online PWS/WU", - "online_wslink": "Online WSLink", - "online_ecowitt": "Online Ecowitt", - "online_idle": "Waiting for data", - "degraded": "Degraded", - "error": "Error" - } - }, - "active_protocol": { - "name": "Active protocol", - "state": { - "wu": "PWS/WU", - "wslink": "WSLink API", - "ecowitt": "Ecowitt" - } - }, - "wslink_addon_status": { - "name": "WSLink Addon Status", - "state": { - "online": "Running", - "offline": "Offline" - } - }, - "wslink_addon_name": { - "name": "WSLink Addon Name" - }, - "wslink_addon_version": { - "name": "WSLink Addon Version" - }, - "wslink_addon_listen_port": { - "name": "WSLink Addon Listen Port" - }, - "wslink_upstream_ha_port": { - "name": "WSLink Addon Upstream HA Port" - }, - "route_wu_enabled": { - "name": "PWS/WU Protocol" - }, - "route_wslink_enabled": { - "name": "WSLink Protocol" - }, - "last_ingress_time": { - "name": "Last access time" - }, - "last_ingress_protocol": { - "name": "Last access protocol", - "state": { - "wu": "PWS/WU", - "wslink": "WSLink API", - "ecowitt": "Ecowitt" - } - }, - "last_ingress_route_enabled": { - "name": "Last ingress route enabled" - }, - "last_ingress_accepted": { - "name": "Last access", - "state": { - "accepted": "Accepted", - "rejected": "Rejected" - } - }, - "last_ingress_authorized": { - "name": "Last access authorization", - "state": { - "authorized": "Authorized", - "unauthorized": "Unauthorized", - "unknown": "Unknown" - } - }, - "last_ingress_reason": { - "name": "Last access reason" - }, - "forward_windy_enabled": { - "name": "Forwarding to Windy" - }, - "forward_windy_status": { - "name": "Forwarding status to Windy", - "state": { - "disabled": "Disabled", - "idle": "Waiting to send", - "ok": "Ok" - } - }, - "forward_pocasi_enabled": { - "name": "Forwarding to Počasí Meteo" - }, - "forward_pocasi_status": { - "name": "Forwarding status to Počasí Meteo", - "state": { - "disabled": "Disabled", - "idle": "Waiting to send", - "ok": "Ok" - } - }, - "indoor_temp": { - "name": "Indoor temperature" - }, - "indoor_humidity": { - "name": "Indoor humidity" - }, - "outside_temp": { - "name": "Outside Temperature" - }, - "outside_humidity": { - "name": "Outside humidity" - }, - "uv": { - "name": "UV index" - }, - "baro_pressure": { - "name": "Barometric pressure" - }, - "dew_point": { - "name": "Dew point" - }, - "wind_speed": { - "name": "Wind speed" - }, - "wind_dir": { - "name": "Wind direction" - }, - "wind_gust": { - "name": "Wind gust" - }, - "rain": { - "name": "Rain" - }, - "daily_rain": { - "name": "Daily precipitation" - }, - "solar_radiation": { - "name": "Solar irradiance" - }, - "ch2_temp": { - "name": "Channel 2 temperature" - }, - "ch2_humidity": { - "name": "Channel 2 humidity" - }, - "ch3_temp": { - "name": "Channel 3 temperature" - }, - "ch3_humidity": { - "name": "Channel 3 humidity" - }, - "ch4_temp": { - "name": "Channel 4 temperature" - }, - "ch4_humidity": { - "name": "Channel 4 humidity" - }, - "ch5_temp": { - "name": "Channel 5 temperature" - }, - "ch5_humidity": { - "name": "Channel 5 humidity" - }, - "ch6_temp": { - "name": "Channel 6 temperature" - }, - "ch6_humidity": { - "name": "Channel 6 humidity" - }, - "ch7_temp": { - "name": "Channel 7 temperature" - }, - "ch7_humidity": { - "name": "Channel 7 humidity" - }, - "ch8_temp": { - "name": "Channel 8 temperature" - }, - "ch8_humidity": { - "name": "Channel 8 humidity" - }, - "heat_index": { - "name": "Apparent temperature" - }, - "chill_index": { - "name": "Wind chill" - }, - "hourly_rain": { - "name": "Hourly precipitation" - }, - "weekly_rain": { - "name": "Weekly precipitation" - }, - "monthly_rain": { - "name": "Monthly precipitation" - }, - "yearly_rain": { - "name": "Yearly precipitation" - }, - "wbgt_temp": { - "name": "WBGT index" - }, - "hcho": { - "name": "Formaldehyde (HCHO)" - }, - "voc": { - "name": "VOC level", - "state": { - "unhealthy": "Unhealthy", - "poor": "Poor", - "moderate": "Moderate", - "good": "Good", - "excellent": "Excellent" - } - }, - "t9_battery": { - "name": "HCHO/VOC sensor battery" - }, - "wind_azimut": { - "name": "Bearing", - "state": { - "n": "N", - "nne": "NNE", - "ne": "NE", - "ene": "ENE", - "e": "E", - "ese": "ESE", - "se": "SE", - "sse": "SSE", - "s": "S", - "ssw": "SSW", - "sw": "SW", - "wsw": "WSW", - "w": "W", - "wnw": "WNW", - "nw": "NW", - "nnw": "NNW" - } - }, - "outside_battery": { - "name": "Outside battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch2_battery": { - "name": "Channel 2 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch3_battery": { - "name": "Channel 3 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch4_battery": { - "name": "Channel 4 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch5_battery": { - "name": "Channel 5 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch6_battery": { - "name": "Channel 6 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch7_battery": { - "name": "Channel 7 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch8_battery": { - "name": "Channel 8 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "indoor_battery": { - "name": "Console battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - } - } - }, - "issues": { - "legacy_battery_sensor_deprecated": { - "title": "Legacy battery sensor detected", - "description": "Old battery sensor entities ({entities}) were found in the entity registry. They have been replaced by binary battery sensors and will be removed in version {remove_version}. Please delete the legacy entities manually via Settings -> Devices & Services -> SWS 12500." - }, - "stale_sensors_detected": { - "title": "Stale sensors detected", - "description": "These sensors have not reported data for an extended period and may be misconfigured or out of range: {sensors}. If you no longer use them, remove them from the integration options." - } - }, - "notify": { - "added": { - "title": "New sensors for SWS 12500 found.", - "message": "{added_sensors}\n" + "data_description": { + "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", + "ecowitt_enabled": "Enable receiving data from Ecowitt stations" } + } } + }, + "options": { + "error": { + "valid_credentials_api": "Provide valid API ID.", + "valid_credentials_key": "Provide valid API KEY.", + "valid_credentials_match": "API ID and API KEY should not be the same.", + "windy_id_required": "Windy API ID is required if you want to enable this function.", + "windy_pw_required": "Windy API password is required if you want to enable this function.", + "windy_key_required": "Windy station ID and password are required if you want to enable this function.", + "pocasi_id_required": "Pocasi Meteo API ID is required if you want to enable this function.", + "pocasi_key_required": "Pocasi Meteo API key is required if you want to enable this function.", + "pocasi_send_minimum": "The resend interval must be at least 12 seconds.", + "protocols_mutually_exclusive": "The legacy (PWS/WSLink) and Ecowitt endpoints cannot be enabled at the same time - they feed the same sensor entities, which would mix up units and blank readings. Disable one of them first." + }, + "step": { + "init": { + "title": "Configure SWS12500 Integration", + "description": "Choose what do you want to configure. If basic access or resending data for Windy site", + "menu_options": { + "basic": "Basic - configure credentials for Weather Station", + "wslink_port_setup": "WSLink add-on port", + "ecowitt": "Ecowitt configuration", + "windy": "Windy configuration", + "pocasi": "Pocasi Meteo CZ configuration" + } + }, + "basic": { + "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant", + "title": "Configure credentials", + "data": { + "API_ID": "API ID / Station ID", + "API_KEY": "API KEY / Password", + "wslink": "WSLink API", + "legacy_enabled": "Enable PWS/WSLink endpoint", + "dev_debug_checkbox": "Developer log" + }, + "data_description": { + "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer.", + "API_ID": "API ID is the Station ID you set in the Weather Station.", + "API_KEY": "API KEY is the password you set in the Weather Station.", + "wslink": "Enable WSLink API if the station is set to send data via WSLink.", + "legacy_enabled": "Enable the PWS/WSLink endpoint. Turn off for an Ecowitt-only setup." + } + }, + "windy": { + "description": "Resend weather data to your Windy stations.", + "title": "Configure Windy", + "data": { + "WINDY_STATION_ID": "Station ID obtained form Windy", + "WINDY_STATION_PWD": "Station password obtained from Windy", + "windy_enabled_checkbox": "Enable resending data to Windy", + "windy_logger_checkbox": "Log Windy data and responses" + }, + "data_description": { + "WINDY_STATION_ID": "Windy station ID obtained from https://stations.windy.com/stations", + "WINDY_STATION_PWD": "Windy station password obtained from https://stations.windy.com/stations", + "windy_logger_checkbox": "Enable only if you want to send debuging data to the developer." + } + }, + "pocasi": { + "description": "Resend data to Pocasi Meteo CZ", + "title": "Configure Pocasi Meteo CZ", + "data": { + "POCASI_CZ_API_ID": "ID from your Pocasi Meteo APP", + "POCASI_CZ_API_KEY": "Key from your Pocasi Meteo APP", + "POCASI_SEND_INTERVAL": "Resend interval in seconds", + "pocasi_enabled_chcekbox": "Enable resending data to Pocasi Meteo", + "pocasi_logger_checkbox": "Log data and responses" + }, + "data_description": { + "POCASI_CZ_API_ID": "You can obtain your ID in Pocasi Meteo App", + "POCASI_CZ_API_KEY": "You can obtain your KEY in Pocasi Meteo App", + "POCASI_SEND_INTERVAL": "Resend interval in seconds (minimum 12s, default 30s)", + "pocasi_enabled_chcekbox": "Enables resending data to Pocasi Meteo", + "pocasi_logger_checkbox": "Enable only if you want to send debbug data to the developer" + } + }, + "ecowitt": { + "description": "Ecowitt configuration.", + "title": "Ecowitt station configuration", + "data": { + "ecowitt_webhook_id": "Unique webhook ID", + "ecowitt_enabled": "Enable Ecowitt station data" + }, + "data_description": { + "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", + "ecowitt_enabled": "Enable receiving data from Ecowitt stations" + } + }, + "wslink_port_setup": { + "title": "WSLink add-on port", + "description": "Set the external port of the WSLink proxy add-on so the integration can reach its health endpoint.", + "data": { + "WSLINK_ADDON_PORT": "WSLink add-on port" + }, + "data_description": { + "WSLINK_ADDON_PORT": "The external TCP port the WSLink proxy add-on listens on (default 443)." + } + }, + "migration": { + "title": "Statistic migration.", + "description": "For the correct functioning of long-term statistics, it is necessary to migrate the sensor unit in the long-term statistics. The original unit of long-term statistics for daily precipitation was in mm/d, however, the station only sends data in mm without time differentiation.\n\n The sensor to be migrated is for daily precipitation. If the correct value is already in the list for the daily precipitation sensor (mm), then the migration is already complete.\n\n Migration result for the sensor: {migration_status}, a total of {migration_count} rows converted.", + "data": { + "sensor_to_migrate": "Sensor to migrate", + "trigger_action": "Trigger migration" + }, + "data_description": { + "sensor_to_migrate": "Select the correct sensor for statistics migration.\nThe sensor values will be preserved, they will not be recalculated, only the unit in the long-term statistics will be changed.", + "trigger_action": "Trigger the sensor statistics migration after checking." + } + } + } + }, + "entity": { + "binary_sensor": { + "outside_battery": { + "name": "Outside battery" + }, + "indoor_battery": { + "name": "Console battery" + }, + "ch2_battery": { + "name": "Channel 2 battery" + }, + "ch3_battery": { + "name": "Channel 3 battery" + }, + "ch4_battery": { + "name": "Channel 4 battery" + }, + "ch5_battery": { + "name": "Channel 5 battery" + }, + "ch6_battery": { + "name": "Channel 6 battery" + }, + "ch7_battery": { + "name": "Channel 7 battery" + }, + "ch8_battery": { + "name": "Channel 8 battery" + } + }, + "sensor": { + "ecowitt_absolute_pressure": { + "name": "Absolute pressure" + }, + "ecowitt_rain_rate": { + "name": "Rain rate" + }, + "ecowitt_event_rain": { + "name": "Event rain" + }, + "ecowitt_hourly_rain": { + "name": "Hourly rain" + }, + "ecowitt_weekly_rain": { + "name": "Weekly rain" + }, + "ecowitt_monthly_rain": { + "name": "Monthly rain" + }, + "ecowitt_yearly_rain": { + "name": "Yearly rain" + }, + "ecowitt_total_rain": { + "name": "Total rain" + }, + "ecowitt_24h_rain": { + "name": "24h rain" + }, + "ecowitt_feels_like": { + "name": "Feels like" + }, + "ecowitt_indoor_dewpoint": { + "name": "Indoor dew point" + }, + "ecowitt_console_co2": { + "name": "Console CO₂" + }, + "ecowitt_console_co2_24h": { + "name": "Console CO₂ (24h avg)" + }, + "ecowitt_co2": { + "name": "CO₂" + }, + "ecowitt_co2_24h": { + "name": "CO₂ (24h avg)" + }, + "integration_health": { + "name": "Integration status", + "state": { + "online_wu": "Online PWS/WU", + "online_wslink": "Online WSLink", + "online_ecowitt": "Online Ecowitt", + "online_idle": "Waiting for data", + "degraded": "Degraded", + "error": "Error" + } + }, + "active_protocol": { + "name": "Active protocol", + "state": { + "wu": "PWS/WU", + "wslink": "WSLink API", + "ecowitt": "Ecowitt" + } + }, + "wslink_addon_status": { + "name": "WSLink Addon Status", + "state": { + "online": "Running", + "offline": "Offline" + } + }, + "wslink_addon_name": { + "name": "WSLink Addon Name" + }, + "wslink_addon_version": { + "name": "WSLink Addon Version" + }, + "wslink_addon_listen_port": { + "name": "WSLink Addon Listen Port" + }, + "wslink_upstream_ha_port": { + "name": "WSLink Addon Upstream HA Port" + }, + "route_wu_enabled": { + "name": "PWS/WU Protocol" + }, + "route_wslink_enabled": { + "name": "WSLink Protocol" + }, + "last_ingress_time": { + "name": "Last access time" + }, + "last_ingress_protocol": { + "name": "Last access protocol", + "state": { + "wu": "PWS/WU", + "wslink": "WSLink API", + "ecowitt": "Ecowitt" + } + }, + "last_ingress_route_enabled": { + "name": "Last ingress route enabled" + }, + "last_ingress_accepted": { + "name": "Last access", + "state": { + "accepted": "Accepted", + "rejected": "Rejected" + } + }, + "last_ingress_authorized": { + "name": "Last access authorization", + "state": { + "authorized": "Authorized", + "unauthorized": "Unauthorized", + "unknown": "Unknown" + } + }, + "last_ingress_reason": { + "name": "Last access reason" + }, + "forward_windy_enabled": { + "name": "Forwarding to Windy" + }, + "forward_windy_status": { + "name": "Forwarding status to Windy", + "state": { + "disabled": "Disabled", + "idle": "Waiting to send", + "ok": "Ok" + } + }, + "forward_pocasi_enabled": { + "name": "Forwarding to Počasí Meteo" + }, + "forward_pocasi_status": { + "name": "Forwarding status to Počasí Meteo", + "state": { + "disabled": "Disabled", + "idle": "Waiting to send", + "ok": "Ok" + } + }, + "indoor_temp": { + "name": "Indoor temperature" + }, + "indoor_humidity": { + "name": "Indoor humidity" + }, + "outside_temp": { + "name": "Outside Temperature" + }, + "outside_humidity": { + "name": "Outside humidity" + }, + "uv": { + "name": "UV index" + }, + "baro_pressure": { + "name": "Barometric pressure" + }, + "dew_point": { + "name": "Dew point" + }, + "wind_speed": { + "name": "Wind speed" + }, + "wind_dir": { + "name": "Wind direction" + }, + "wind_gust": { + "name": "Wind gust" + }, + "rain": { + "name": "Rain" + }, + "daily_rain": { + "name": "Daily precipitation" + }, + "solar_radiation": { + "name": "Solar irradiance" + }, + "ch2_temp": { + "name": "Channel 2 temperature" + }, + "ch2_humidity": { + "name": "Channel 2 humidity" + }, + "ch3_temp": { + "name": "Channel 3 temperature" + }, + "ch3_humidity": { + "name": "Channel 3 humidity" + }, + "ch4_temp": { + "name": "Channel 4 temperature" + }, + "ch4_humidity": { + "name": "Channel 4 humidity" + }, + "ch5_temp": { + "name": "Channel 5 temperature" + }, + "ch5_humidity": { + "name": "Channel 5 humidity" + }, + "ch6_temp": { + "name": "Channel 6 temperature" + }, + "ch6_humidity": { + "name": "Channel 6 humidity" + }, + "ch7_temp": { + "name": "Channel 7 temperature" + }, + "ch7_humidity": { + "name": "Channel 7 humidity" + }, + "ch8_temp": { + "name": "Channel 8 temperature" + }, + "ch8_humidity": { + "name": "Channel 8 humidity" + }, + "heat_index": { + "name": "Apparent temperature" + }, + "chill_index": { + "name": "Wind chill" + }, + "hourly_rain": { + "name": "Hourly precipitation" + }, + "weekly_rain": { + "name": "Weekly precipitation" + }, + "monthly_rain": { + "name": "Monthly precipitation" + }, + "yearly_rain": { + "name": "Yearly precipitation" + }, + "wbgt_temp": { + "name": "WBGT index" + }, + "hcho": { + "name": "Formaldehyde (HCHO)" + }, + "voc": { + "name": "VOC level", + "state": { + "unhealthy": "Unhealthy", + "poor": "Poor", + "moderate": "Moderate", + "good": "Good", + "excellent": "Excellent" + } + }, + "t9_battery": { + "name": "HCHO/VOC sensor battery" + }, + "wind_azimut": { + "name": "Bearing", + "state": { + "n": "N", + "nne": "NNE", + "ne": "NE", + "ene": "ENE", + "e": "E", + "ese": "ESE", + "se": "SE", + "sse": "SSE", + "s": "S", + "ssw": "SSW", + "sw": "SW", + "wsw": "WSW", + "w": "W", + "wnw": "WNW", + "nw": "NW", + "nnw": "NNW" + } + }, + "outside_battery": { + "name": "Outside battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch2_battery": { + "name": "Channel 2 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch3_battery": { + "name": "Channel 3 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch4_battery": { + "name": "Channel 4 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch5_battery": { + "name": "Channel 5 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch6_battery": { + "name": "Channel 6 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch7_battery": { + "name": "Channel 7 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch8_battery": { + "name": "Channel 8 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "indoor_battery": { + "name": "Console battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + } + } + }, + "issues": { + "legacy_battery_sensor_deprecated": { + "title": "Legacy battery sensor detected", + "description": "Old battery sensor entities ({entities}) were found in the entity registry. They have been replaced by binary battery sensors and will be removed in version {remove_version}. Please delete the legacy entities manually via Settings -> Devices & Services -> SWS 12500." + }, + "stale_sensors_detected": { + "title": "Stale sensors detected", + "description": "These sensors have not reported data for an extended period and may be misconfigured or out of range: {sensors}. If you no longer use them, remove them from the integration options." + }, + "protocol_conflict": { + "title": "Two incompatible protocols enabled", + "description": "The legacy (PWS/WSLink) and Ecowitt endpoints are both enabled. They map onto the same sensor entities, so running both mixes up measurement units and makes readings disappear between updates. The legacy endpoint is being used and Ecowitt data is ignored until you choose one: go to Settings -> Devices & Services -> SWS 12500 -> Configure and disable either the legacy endpoint or Ecowitt." + } + }, + "notify": { + "added": { + "title": "New sensors for SWS 12500 found.", + "message": "{added_sensors}\n" + } + } } diff --git a/custom_components/sws12500/translations/cs.json b/custom_components/sws12500/translations/cs.json index da6e4c1..411674d 100644 --- a/custom_components/sws12500/translations/cs.json +++ b/custom_components/sws12500/translations/cs.json @@ -3,7 +3,8 @@ "error": { "valid_credentials_api": "Vyplňte platné API ID.", "valid_credentials_key": "Vyplňte platný API KEY.", - "valid_credentials_match": "API ID a API KEY nesmějí být stejné!" + "valid_credentials_match": "API ID a API KEY nesmějí být stejné!", + "protocols_mutually_exclusive": "Starý endpoint (PWS/WSLink) a Ecowitt nelze zapnout současně – plní stejné entity senzorů, což by pomíchalo jednotky a mazalo naměřené hodnoty. Nejdřív jeden z nich vypni." }, "step": { "user": { @@ -54,7 +55,8 @@ "windy_key_required": "Pro aktivaci je vyžadováno Windy ID i heslo stanice.", "pocasi_id_required": "Je vyžadován Počasí ID, pokud chcete aktivovat přeposílání dat na Počasí Meteo CZ", "pocasi_key_required": "Klíč k účtu Počasí Meteo je povinný.", - "pocasi_send_minimum": "Minimální interval pro přeposílání je 12 sekund." + "pocasi_send_minimum": "Minimální interval pro přeposílání je 12 sekund.", + "protocols_mutually_exclusive": "Starý endpoint (PWS/WSLink) a Ecowitt nelze zapnout současně – plní stejné entity senzorů, což by pomíchalo jednotky a mazalo naměřené hodnoty. Nejdřív jeden z nich vypni." }, "step": { "init": { @@ -551,6 +553,10 @@ "stale_sensors_detected": { "title": "Detekovány nečinné sensory", "description": "Tyto senzory jsou nastavené, ale nereportují data: {sensors}. V HA budou viditelné jako Nedostupné. Pokud už nejsou přítomné, můžeš je odstranit přes Nastavení –> Zařízení a Služby –> SWS 12500." + }, + "protocol_conflict": { + "title": "Zapnuté dva neslučitelné protokoly", + "description": "Současně je zapnutý starý endpoint (PWS/WSLink) i Ecowitt. Oba plní stejné entity senzorů, takže jejich souběh míchá měrné jednotky a hodnoty mezi aktualizacemi mizí. Používá se starý endpoint a data z Ecowittu se ignorují, dokud si nevybereš jeden: jdi do Nastavení -> Zařízení a služby -> SWS 12500 -> Konfigurovat a vypni buď starý endpoint, nebo Ecowitt." } }, "notify": { diff --git a/custom_components/sws12500/translations/en.json b/custom_components/sws12500/translations/en.json index 87ac5cc..5520bde 100644 --- a/custom_components/sws12500/translations/en.json +++ b/custom_components/sws12500/translations/en.json @@ -1,561 +1,567 @@ { - "config": { - "error": { - "valid_credentials_api": "Provide valid API ID.", - "valid_credentials_key": "Provide valid API KEY.", - "valid_credentials_match": "API ID and API KEY should not be the same." - }, - "step": { - "user": { - "title": "Choose your station type", - "description": "Choose the type of your station. If you don't have Eccowit station, choose PWS/WSLink", - "menu_options": { - "pws": "PWS/WSLink (Sencor, Garni, Bresser, other - Weather Underground compatible)", - "ecowitt": "Ecowitt" - } - }, - "pws": { - "title": "PWS/WSLink credentials.", - "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant.", - "data": { - "API_ID": "API ID / Station ID", - "API_KEY": "API KEY / Password", - "wslink": "WSLink Protocol", - "dev_debug_checkbox": "Developer log" - }, - "data_description": { - "API_ID": "API ID is the Station ID you set in the Weather Station.", - "API_KEY": "API KEY is the password you set in the Weather Station.", - "wslink": "Enable WSLink Protocol if the station is set to send data via WSLink. If you are unsure, use https://test-station.schizza.cz/", - "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer." - } - }, - "ecowitt": { - "title": "Ecowitt configuration.", - "description": "No API ID/KEY needed. Set your Ecowitt station to send data to the enndpoint below.", - "data": { - "ecowitt_webhook_id": "Unique webhook ID", - "ecowitt_enabled": "Enable Ecowitt station data" - }, - "data_description": { - "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", - "ecowitt_enabled": "Enable receiving data from Ecowitt stations" - } - } - } + "config": { + "error": { + "valid_credentials_api": "Provide valid API ID.", + "valid_credentials_key": "Provide valid API KEY.", + "valid_credentials_match": "API ID and API KEY should not be the same.", + "protocols_mutually_exclusive": "The legacy (PWS/WSLink) and Ecowitt endpoints cannot be enabled at the same time - they feed the same sensor entities, which would mix up units and blank readings. Disable one of them first." }, - "options": { - "error": { - "valid_credentials_api": "Provide valid API ID.", - "valid_credentials_key": "Provide valid API KEY.", - "valid_credentials_match": "API ID and API KEY should not be the same.", - "windy_id_required": "Windy API ID is required if you want to enable this function.", - "windy_pw_required": "Windy API password is required if you want to enable this function.", - "windy_key_required": "Windy station ID and password are required if you want to enable this function.", - "pocasi_id_required": "Pocasi Meteo API ID is required if you want to enable this function.", - "pocasi_key_required": "Pocasi Meteo API key is required if you want to enable this function.", - "pocasi_send_minimum": "The resend interval must be at least 12 seconds." + "step": { + "user": { + "title": "Choose your station type", + "description": "Choose the type of your station. If you don't have Eccowit station, choose PWS/WSLink", + "menu_options": { + "pws": "PWS/WSLink (Sencor, Garni, Bresser, other - Weather Underground compatible)", + "ecowitt": "Ecowitt" + } + }, + "pws": { + "title": "PWS/WSLink credentials.", + "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant.", + "data": { + "API_ID": "API ID / Station ID", + "API_KEY": "API KEY / Password", + "wslink": "WSLink Protocol", + "dev_debug_checkbox": "Developer log" }, - "step": { - "init": { - "title": "Configure SWS12500 Integration", - "description": "Choose what do you want to configure. If basic access or resending data for Windy site", - "menu_options": { - "basic": "Basic - configure credentials for Weather Station", - "wslink_port_setup": "WSLink add-on port", - "ecowitt": "Ecowitt configuration", - "windy": "Windy configuration", - "pocasi": "Pocasi Meteo CZ configuration" - } - }, - "basic": { - "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant", - "title": "Configure credentials", - "data": { - "API_ID": "API ID / Station ID", - "API_KEY": "API KEY / Password", - "wslink": "WSLink API", - "legacy_enabled": "Enable PWS/WSLink endpoint", - "dev_debug_checkbox": "Developer log" - }, - "data_description": { - "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer.", - "API_ID": "API ID is the Station ID you set in the Weather Station.", - "API_KEY": "API KEY is the password you set in the Weather Station.", - "wslink": "Enable WSLink API if the station is set to send data via WSLink.", - "legacy_enabled": "Enable the PWS/WSLink endpoint. Turn off for an Ecowitt-only setup." - } - }, - "windy": { - "description": "Resend weather data to your Windy stations.", - "title": "Configure Windy", - "data": { - "WINDY_STATION_ID": "Station ID obtained form Windy", - "WINDY_STATION_PWD": "Station password obtained from Windy", - "windy_enabled_checkbox": "Enable resending data to Windy", - "windy_logger_checkbox": "Log Windy data and responses" - }, - "data_description": { - "WINDY_STATION_ID": "Windy station ID obtained from https://stations.windy.com/stations", - "WINDY_STATION_PWD": "Windy station password obtained from https://stations.windy.com/stations", - "windy_logger_checkbox": "Enable only if you want to send debuging data to the developer." - } - }, - "pocasi": { - "description": "Resend data to Pocasi Meteo CZ", - "title": "Configure Pocasi Meteo CZ", - "data": { - "POCASI_CZ_API_ID": "ID from your Pocasi Meteo APP", - "POCASI_CZ_API_KEY": "Key from your Pocasi Meteo APP", - "POCASI_SEND_INTERVAL": "Resend interval in seconds", - "pocasi_enabled_chcekbox": "Enable resending data to Pocasi Meteo", - "pocasi_logger_checkbox": "Log data and responses" - }, - "data_description": { - "POCASI_CZ_API_ID": "You can obtain your ID in Pocasi Meteo App", - "POCASI_CZ_API_KEY": "You can obtain your KEY in Pocasi Meteo App", - "POCASI_SEND_INTERVAL": "Resend interval in seconds (minimum 12s, default 30s)", - "pocasi_enabled_chcekbox": "Enables resending data to Pocasi Meteo", - "pocasi_logger_checkbox": "Enable only if you want to send debbug data to the developer" - } - }, - "ecowitt": { - "description": "Ecowitt configuration.", - "title": "Ecowitt station configuration", - "data": { - "ecowitt_webhook_id": "Unique webhook ID", - "ecowitt_enabled": "Enable Ecowitt station data" - }, - "data_description": { - "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", - "ecowitt_enabled": "Enable receiving data from Ecowitt stations" - } - }, - "wslink_port_setup": { - "title": "WSLink add-on port", - "description": "Set the external port of the WSLink proxy add-on so the integration can reach its health endpoint.", - "data": { - "WSLINK_ADDON_PORT": "WSLink add-on port" - }, - "data_description": { - "WSLINK_ADDON_PORT": "The external TCP port the WSLink proxy add-on listens on (default 443)." - } - }, - "migration": { - "title": "Statistic migration.", - "description": "For the correct functioning of long-term statistics, it is necessary to migrate the sensor unit in the long-term statistics. The original unit of long-term statistics for daily precipitation was in mm/d, however, the station only sends data in mm without time differentiation.\n\n The sensor to be migrated is for daily precipitation. If the correct value is already in the list for the daily precipitation sensor (mm), then the migration is already complete.\n\n Migration result for the sensor: {migration_status}, a total of {migration_count} rows converted.", - "data": { - "sensor_to_migrate": "Sensor to migrate", - "trigger_action": "Trigger migration" - }, - "data_description": { - "sensor_to_migrate": "Select the correct sensor for statistics migration.\nThe sensor values will be preserved, they will not be recalculated, only the unit in the long-term statistics will be changed.", - "trigger_action": "Trigger the sensor statistics migration after checking." - } - } + "data_description": { + "API_ID": "API ID is the Station ID you set in the Weather Station.", + "API_KEY": "API KEY is the password you set in the Weather Station.", + "wslink": "Enable WSLink Protocol if the station is set to send data via WSLink. If you are unsure, use https://test-station.schizza.cz/", + "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer." } - }, - "entity": { - "binary_sensor": { - "outside_battery": { - "name": "Outside battery" - }, - "indoor_battery": { - "name": "Console battery" - }, - "ch2_battery": { - "name": "Channel 2 battery" - }, - "ch3_battery": { - "name": "Channel 3 battery" - }, - "ch4_battery": { - "name": "Channel 4 battery" - }, - "ch5_battery": { - "name": "Channel 5 battery" - }, - "ch6_battery": { - "name": "Channel 6 battery" - }, - "ch7_battery": { - "name": "Channel 7 battery" - }, - "ch8_battery": { - "name": "Channel 8 battery" - } + }, + "ecowitt": { + "title": "Ecowitt configuration.", + "description": "No API ID/KEY needed. Set your Ecowitt station to send data to the enndpoint below.", + "data": { + "ecowitt_webhook_id": "Unique webhook ID", + "ecowitt_enabled": "Enable Ecowitt station data" }, - "sensor": { - "ecowitt_absolute_pressure": { - "name": "Absolute pressure" - }, - "ecowitt_rain_rate": { - "name": "Rain rate" - }, - "ecowitt_event_rain": { - "name": "Event rain" - }, - "ecowitt_hourly_rain": { - "name": "Hourly rain" - }, - "ecowitt_weekly_rain": { - "name": "Weekly rain" - }, - "ecowitt_monthly_rain": { - "name": "Monthly rain" - }, - "ecowitt_yearly_rain": { - "name": "Yearly rain" - }, - "ecowitt_total_rain": { - "name": "Total rain" - }, - "ecowitt_24h_rain": { - "name": "24h rain" - }, - "ecowitt_feels_like": { - "name": "Feels like" - }, - "ecowitt_indoor_dewpoint": { - "name": "Indoor dew point" - }, - "ecowitt_console_co2": { - "name": "Console CO₂" - }, - "ecowitt_console_co2_24h": { - "name": "Console CO₂ (24h avg)" - }, - "ecowitt_co2": { - "name": "CO₂" - }, - "ecowitt_co2_24h": { - "name": "CO₂ (24h avg)" - }, - "integration_health": { - "name": "Integration status", - "state": { - "online_wu": "Online PWS/WU", - "online_wslink": "Online WSLink", - "online_ecowitt": "Online Ecowitt", - "online_idle": "Waiting for data", - "degraded": "Degraded", - "error": "Error" - } - }, - "active_protocol": { - "name": "Active protocol", - "state": { - "wu": "PWS/WU", - "wslink": "WSLink API", - "ecowitt": "Ecowitt" - } - }, - "wslink_addon_status": { - "name": "WSLink Addon Status", - "state": { - "online": "Running", - "offline": "Offline" - } - }, - "wslink_addon_name": { - "name": "WSLink Addon Name" - }, - "wslink_addon_version": { - "name": "WSLink Addon Version" - }, - "wslink_addon_listen_port": { - "name": "WSLink Addon Listen Port" - }, - "wslink_upstream_ha_port": { - "name": "WSLink Addon Upstream HA Port" - }, - "route_wu_enabled": { - "name": "PWS/WU Protocol" - }, - "route_wslink_enabled": { - "name": "WSLink Protocol" - }, - "last_ingress_time": { - "name": "Last access time" - }, - "last_ingress_protocol": { - "name": "Last access protocol", - "state": { - "wu": "PWS/WU", - "wslink": "WSLink API", - "ecowitt": "Ecowitt" - } - }, - "last_ingress_route_enabled": { - "name": "Last ingress route enabled" - }, - "last_ingress_accepted": { - "name": "Last access", - "state": { - "accepted": "Accepted", - "rejected": "Rejected" - } - }, - "last_ingress_authorized": { - "name": "Last access authorization", - "state": { - "authorized": "Authorized", - "unauthorized": "Unauthorized", - "unknown": "Unknown" - } - }, - "last_ingress_reason": { - "name": "Last access reason" - }, - "forward_windy_enabled": { - "name": "Forwarding to Windy" - }, - "forward_windy_status": { - "name": "Forwarding status to Windy", - "state": { - "disabled": "Disabled", - "idle": "Waiting to send", - "ok": "Ok" - } - }, - "forward_pocasi_enabled": { - "name": "Forwarding to Počasí Meteo" - }, - "forward_pocasi_status": { - "name": "Forwarding status to Počasí Meteo", - "state": { - "disabled": "Disabled", - "idle": "Waiting to send", - "ok": "Ok" - } - }, - "indoor_temp": { - "name": "Indoor temperature" - }, - "indoor_humidity": { - "name": "Indoor humidity" - }, - "outside_temp": { - "name": "Outside Temperature" - }, - "outside_humidity": { - "name": "Outside humidity" - }, - "uv": { - "name": "UV index" - }, - "baro_pressure": { - "name": "Barometric pressure" - }, - "dew_point": { - "name": "Dew point" - }, - "wind_speed": { - "name": "Wind speed" - }, - "wind_dir": { - "name": "Wind direction" - }, - "wind_gust": { - "name": "Wind gust" - }, - "rain": { - "name": "Rain" - }, - "daily_rain": { - "name": "Daily precipitation" - }, - "solar_radiation": { - "name": "Solar irradiance" - }, - "ch2_temp": { - "name": "Channel 2 temperature" - }, - "ch2_humidity": { - "name": "Channel 2 humidity" - }, - "ch3_temp": { - "name": "Channel 3 temperature" - }, - "ch3_humidity": { - "name": "Channel 3 humidity" - }, - "ch4_temp": { - "name": "Channel 4 temperature" - }, - "ch4_humidity": { - "name": "Channel 4 humidity" - }, - "ch5_temp": { - "name": "Channel 5 temperature" - }, - "ch5_humidity": { - "name": "Channel 5 humidity" - }, - "ch6_temp": { - "name": "Channel 6 temperature" - }, - "ch6_humidity": { - "name": "Channel 6 humidity" - }, - "ch7_temp": { - "name": "Channel 7 temperature" - }, - "ch7_humidity": { - "name": "Channel 7 humidity" - }, - "ch8_temp": { - "name": "Channel 8 temperature" - }, - "ch8_humidity": { - "name": "Channel 8 humidity" - }, - "heat_index": { - "name": "Apparent temperature" - }, - "chill_index": { - "name": "Wind chill" - }, - "hourly_rain": { - "name": "Hourly precipitation" - }, - "weekly_rain": { - "name": "Weekly precipitation" - }, - "monthly_rain": { - "name": "Monthly precipitation" - }, - "yearly_rain": { - "name": "Yearly precipitation" - }, - "wbgt_temp": { - "name": "WBGT index" - }, - "hcho": { - "name": "Formaldehyde (HCHO)" - }, - "voc": { - "name": "VOC level", - "state": { - "unhealthy": "Unhealthy", - "poor": "Poor", - "moderate": "Moderate", - "good": "Good", - "excellent": "Excellent" - } - }, - "t9_battery": { - "name": "HCHO/VOC sensor battery" - }, - "wind_azimut": { - "name": "Bearing", - "state": { - "n": "N", - "nne": "NNE", - "ne": "NE", - "ene": "ENE", - "e": "E", - "ese": "ESE", - "se": "SE", - "sse": "SSE", - "s": "S", - "ssw": "SSW", - "sw": "SW", - "wsw": "WSW", - "w": "W", - "wnw": "WNW", - "nw": "NW", - "nnw": "NNW" - } - }, - "outside_battery": { - "name": "Outside battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch2_battery": { - "name": "Channel 2 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch3_battery": { - "name": "Channel 3 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch4_battery": { - "name": "Channel 4 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch5_battery": { - "name": "Channel 5 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch6_battery": { - "name": "Channel 6 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch7_battery": { - "name": "Channel 7 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "ch8_battery": { - "name": "Channel 8 battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - }, - "indoor_battery": { - "name": "Console battery level", - "state": { - "normal": "OK", - "low": "Low", - "unknown": "Unknown / drained out" - } - } - } - }, - "issues": { - "legacy_battery_sensor_deprecated": { - "title": "Legacy battery sensor detected", - "description": "Old battery sensor entities ({entities}) were found in the entity registry. They have been replaced by binary battery sensors and will be removed in version {remove_version}. Please delete the legacy entities manually via Settings -> Devices & Services -> SWS 12500." - }, - "stale_sensors_detected": { - "title": "Stale sensors detected", - "description": "These sensors have not reported data for an extended period and may be misconfigured or out of range: {sensors}. If you no longer use them, remove them from the integration options." - } - }, - "notify": { - "added": { - "title": "New sensors for SWS 12500 found.", - "message": "{added_sensors}\n" + "data_description": { + "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", + "ecowitt_enabled": "Enable receiving data from Ecowitt stations" } + } } + }, + "options": { + "error": { + "valid_credentials_api": "Provide valid API ID.", + "valid_credentials_key": "Provide valid API KEY.", + "valid_credentials_match": "API ID and API KEY should not be the same.", + "windy_id_required": "Windy API ID is required if you want to enable this function.", + "windy_pw_required": "Windy API password is required if you want to enable this function.", + "windy_key_required": "Windy station ID and password are required if you want to enable this function.", + "pocasi_id_required": "Pocasi Meteo API ID is required if you want to enable this function.", + "pocasi_key_required": "Pocasi Meteo API key is required if you want to enable this function.", + "pocasi_send_minimum": "The resend interval must be at least 12 seconds.", + "protocols_mutually_exclusive": "The legacy (PWS/WSLink) and Ecowitt endpoints cannot be enabled at the same time - they feed the same sensor entities, which would mix up units and blank readings. Disable one of them first." + }, + "step": { + "init": { + "title": "Configure SWS12500 Integration", + "description": "Choose what do you want to configure. If basic access or resending data for Windy site", + "menu_options": { + "basic": "Basic - configure credentials for Weather Station", + "wslink_port_setup": "WSLink add-on port", + "ecowitt": "Ecowitt configuration", + "windy": "Windy configuration", + "pocasi": "Pocasi Meteo CZ configuration" + } + }, + "basic": { + "description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant", + "title": "Configure credentials", + "data": { + "API_ID": "API ID / Station ID", + "API_KEY": "API KEY / Password", + "wslink": "WSLink API", + "legacy_enabled": "Enable PWS/WSLink endpoint", + "dev_debug_checkbox": "Developer log" + }, + "data_description": { + "dev_debug_checkbox": " Enable only if you want to send debuging data to the developer.", + "API_ID": "API ID is the Station ID you set in the Weather Station.", + "API_KEY": "API KEY is the password you set in the Weather Station.", + "wslink": "Enable WSLink API if the station is set to send data via WSLink.", + "legacy_enabled": "Enable the PWS/WSLink endpoint. Turn off for an Ecowitt-only setup." + } + }, + "windy": { + "description": "Resend weather data to your Windy stations.", + "title": "Configure Windy", + "data": { + "WINDY_STATION_ID": "Station ID obtained form Windy", + "WINDY_STATION_PWD": "Station password obtained from Windy", + "windy_enabled_checkbox": "Enable resending data to Windy", + "windy_logger_checkbox": "Log Windy data and responses" + }, + "data_description": { + "WINDY_STATION_ID": "Windy station ID obtained from https://stations.windy.com/stations", + "WINDY_STATION_PWD": "Windy station password obtained from https://stations.windy.com/stations", + "windy_logger_checkbox": "Enable only if you want to send debuging data to the developer." + } + }, + "pocasi": { + "description": "Resend data to Pocasi Meteo CZ", + "title": "Configure Pocasi Meteo CZ", + "data": { + "POCASI_CZ_API_ID": "ID from your Pocasi Meteo APP", + "POCASI_CZ_API_KEY": "Key from your Pocasi Meteo APP", + "POCASI_SEND_INTERVAL": "Resend interval in seconds", + "pocasi_enabled_chcekbox": "Enable resending data to Pocasi Meteo", + "pocasi_logger_checkbox": "Log data and responses" + }, + "data_description": { + "POCASI_CZ_API_ID": "You can obtain your ID in Pocasi Meteo App", + "POCASI_CZ_API_KEY": "You can obtain your KEY in Pocasi Meteo App", + "POCASI_SEND_INTERVAL": "Resend interval in seconds (minimum 12s, default 30s)", + "pocasi_enabled_chcekbox": "Enables resending data to Pocasi Meteo", + "pocasi_logger_checkbox": "Enable only if you want to send debbug data to the developer" + } + }, + "ecowitt": { + "description": "Ecowitt configuration.", + "title": "Ecowitt station configuration", + "data": { + "ecowitt_webhook_id": "Unique webhook ID", + "ecowitt_enabled": "Enable Ecowitt station data" + }, + "data_description": { + "ecowitt_webhook_id": "Set your Ecowitt station to send data to the endpoint: {url}:{port}/weatherhub/{webhook_id}", + "ecowitt_enabled": "Enable receiving data from Ecowitt stations" + } + }, + "wslink_port_setup": { + "title": "WSLink add-on port", + "description": "Set the external port of the WSLink proxy add-on so the integration can reach its health endpoint.", + "data": { + "WSLINK_ADDON_PORT": "WSLink add-on port" + }, + "data_description": { + "WSLINK_ADDON_PORT": "The external TCP port the WSLink proxy add-on listens on (default 443)." + } + }, + "migration": { + "title": "Statistic migration.", + "description": "For the correct functioning of long-term statistics, it is necessary to migrate the sensor unit in the long-term statistics. The original unit of long-term statistics for daily precipitation was in mm/d, however, the station only sends data in mm without time differentiation.\n\n The sensor to be migrated is for daily precipitation. If the correct value is already in the list for the daily precipitation sensor (mm), then the migration is already complete.\n\n Migration result for the sensor: {migration_status}, a total of {migration_count} rows converted.", + "data": { + "sensor_to_migrate": "Sensor to migrate", + "trigger_action": "Trigger migration" + }, + "data_description": { + "sensor_to_migrate": "Select the correct sensor for statistics migration.\nThe sensor values will be preserved, they will not be recalculated, only the unit in the long-term statistics will be changed.", + "trigger_action": "Trigger the sensor statistics migration after checking." + } + } + } + }, + "entity": { + "binary_sensor": { + "outside_battery": { + "name": "Outside battery" + }, + "indoor_battery": { + "name": "Console battery" + }, + "ch2_battery": { + "name": "Channel 2 battery" + }, + "ch3_battery": { + "name": "Channel 3 battery" + }, + "ch4_battery": { + "name": "Channel 4 battery" + }, + "ch5_battery": { + "name": "Channel 5 battery" + }, + "ch6_battery": { + "name": "Channel 6 battery" + }, + "ch7_battery": { + "name": "Channel 7 battery" + }, + "ch8_battery": { + "name": "Channel 8 battery" + } + }, + "sensor": { + "ecowitt_absolute_pressure": { + "name": "Absolute pressure" + }, + "ecowitt_rain_rate": { + "name": "Rain rate" + }, + "ecowitt_event_rain": { + "name": "Event rain" + }, + "ecowitt_hourly_rain": { + "name": "Hourly rain" + }, + "ecowitt_weekly_rain": { + "name": "Weekly rain" + }, + "ecowitt_monthly_rain": { + "name": "Monthly rain" + }, + "ecowitt_yearly_rain": { + "name": "Yearly rain" + }, + "ecowitt_total_rain": { + "name": "Total rain" + }, + "ecowitt_24h_rain": { + "name": "24h rain" + }, + "ecowitt_feels_like": { + "name": "Feels like" + }, + "ecowitt_indoor_dewpoint": { + "name": "Indoor dew point" + }, + "ecowitt_console_co2": { + "name": "Console CO₂" + }, + "ecowitt_console_co2_24h": { + "name": "Console CO₂ (24h avg)" + }, + "ecowitt_co2": { + "name": "CO₂" + }, + "ecowitt_co2_24h": { + "name": "CO₂ (24h avg)" + }, + "integration_health": { + "name": "Integration status", + "state": { + "online_wu": "Online PWS/WU", + "online_wslink": "Online WSLink", + "online_ecowitt": "Online Ecowitt", + "online_idle": "Waiting for data", + "degraded": "Degraded", + "error": "Error" + } + }, + "active_protocol": { + "name": "Active protocol", + "state": { + "wu": "PWS/WU", + "wslink": "WSLink API", + "ecowitt": "Ecowitt" + } + }, + "wslink_addon_status": { + "name": "WSLink Addon Status", + "state": { + "online": "Running", + "offline": "Offline" + } + }, + "wslink_addon_name": { + "name": "WSLink Addon Name" + }, + "wslink_addon_version": { + "name": "WSLink Addon Version" + }, + "wslink_addon_listen_port": { + "name": "WSLink Addon Listen Port" + }, + "wslink_upstream_ha_port": { + "name": "WSLink Addon Upstream HA Port" + }, + "route_wu_enabled": { + "name": "PWS/WU Protocol" + }, + "route_wslink_enabled": { + "name": "WSLink Protocol" + }, + "last_ingress_time": { + "name": "Last access time" + }, + "last_ingress_protocol": { + "name": "Last access protocol", + "state": { + "wu": "PWS/WU", + "wslink": "WSLink API", + "ecowitt": "Ecowitt" + } + }, + "last_ingress_route_enabled": { + "name": "Last ingress route enabled" + }, + "last_ingress_accepted": { + "name": "Last access", + "state": { + "accepted": "Accepted", + "rejected": "Rejected" + } + }, + "last_ingress_authorized": { + "name": "Last access authorization", + "state": { + "authorized": "Authorized", + "unauthorized": "Unauthorized", + "unknown": "Unknown" + } + }, + "last_ingress_reason": { + "name": "Last access reason" + }, + "forward_windy_enabled": { + "name": "Forwarding to Windy" + }, + "forward_windy_status": { + "name": "Forwarding status to Windy", + "state": { + "disabled": "Disabled", + "idle": "Waiting to send", + "ok": "Ok" + } + }, + "forward_pocasi_enabled": { + "name": "Forwarding to Počasí Meteo" + }, + "forward_pocasi_status": { + "name": "Forwarding status to Počasí Meteo", + "state": { + "disabled": "Disabled", + "idle": "Waiting to send", + "ok": "Ok" + } + }, + "indoor_temp": { + "name": "Indoor temperature" + }, + "indoor_humidity": { + "name": "Indoor humidity" + }, + "outside_temp": { + "name": "Outside Temperature" + }, + "outside_humidity": { + "name": "Outside humidity" + }, + "uv": { + "name": "UV index" + }, + "baro_pressure": { + "name": "Barometric pressure" + }, + "dew_point": { + "name": "Dew point" + }, + "wind_speed": { + "name": "Wind speed" + }, + "wind_dir": { + "name": "Wind direction" + }, + "wind_gust": { + "name": "Wind gust" + }, + "rain": { + "name": "Rain" + }, + "daily_rain": { + "name": "Daily precipitation" + }, + "solar_radiation": { + "name": "Solar irradiance" + }, + "ch2_temp": { + "name": "Channel 2 temperature" + }, + "ch2_humidity": { + "name": "Channel 2 humidity" + }, + "ch3_temp": { + "name": "Channel 3 temperature" + }, + "ch3_humidity": { + "name": "Channel 3 humidity" + }, + "ch4_temp": { + "name": "Channel 4 temperature" + }, + "ch4_humidity": { + "name": "Channel 4 humidity" + }, + "ch5_temp": { + "name": "Channel 5 temperature" + }, + "ch5_humidity": { + "name": "Channel 5 humidity" + }, + "ch6_temp": { + "name": "Channel 6 temperature" + }, + "ch6_humidity": { + "name": "Channel 6 humidity" + }, + "ch7_temp": { + "name": "Channel 7 temperature" + }, + "ch7_humidity": { + "name": "Channel 7 humidity" + }, + "ch8_temp": { + "name": "Channel 8 temperature" + }, + "ch8_humidity": { + "name": "Channel 8 humidity" + }, + "heat_index": { + "name": "Apparent temperature" + }, + "chill_index": { + "name": "Wind chill" + }, + "hourly_rain": { + "name": "Hourly precipitation" + }, + "weekly_rain": { + "name": "Weekly precipitation" + }, + "monthly_rain": { + "name": "Monthly precipitation" + }, + "yearly_rain": { + "name": "Yearly precipitation" + }, + "wbgt_temp": { + "name": "WBGT index" + }, + "hcho": { + "name": "Formaldehyde (HCHO)" + }, + "voc": { + "name": "VOC level", + "state": { + "unhealthy": "Unhealthy", + "poor": "Poor", + "moderate": "Moderate", + "good": "Good", + "excellent": "Excellent" + } + }, + "t9_battery": { + "name": "HCHO/VOC sensor battery" + }, + "wind_azimut": { + "name": "Bearing", + "state": { + "n": "N", + "nne": "NNE", + "ne": "NE", + "ene": "ENE", + "e": "E", + "ese": "ESE", + "se": "SE", + "sse": "SSE", + "s": "S", + "ssw": "SSW", + "sw": "SW", + "wsw": "WSW", + "w": "W", + "wnw": "WNW", + "nw": "NW", + "nnw": "NNW" + } + }, + "outside_battery": { + "name": "Outside battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch2_battery": { + "name": "Channel 2 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch3_battery": { + "name": "Channel 3 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch4_battery": { + "name": "Channel 4 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch5_battery": { + "name": "Channel 5 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch6_battery": { + "name": "Channel 6 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch7_battery": { + "name": "Channel 7 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "ch8_battery": { + "name": "Channel 8 battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + }, + "indoor_battery": { + "name": "Console battery level", + "state": { + "normal": "OK", + "low": "Low", + "unknown": "Unknown / drained out" + } + } + } + }, + "issues": { + "legacy_battery_sensor_deprecated": { + "title": "Legacy battery sensor detected", + "description": "Old battery sensor entities ({entities}) were found in the entity registry. They have been replaced by binary battery sensors and will be removed in version {remove_version}. Please delete the legacy entities manually via Settings -> Devices & Services -> SWS 12500." + }, + "stale_sensors_detected": { + "title": "Stale sensors detected", + "description": "These sensors have not reported data for an extended period and may be misconfigured or out of range: {sensors}. If you no longer use them, remove them from the integration options." + }, + "protocol_conflict": { + "title": "Two incompatible protocols enabled", + "description": "The legacy (PWS/WSLink) and Ecowitt endpoints are both enabled. They map onto the same sensor entities, so running both mixes up measurement units and makes readings disappear between updates. The legacy endpoint is being used and Ecowitt data is ignored until you choose one: go to Settings -> Devices & Services -> SWS 12500 -> Configure and disable either the legacy endpoint or Ecowitt." + } + }, + "notify": { + "added": { + "title": "New sensors for SWS 12500 found.", + "message": "{added_sensors}\n" + } + } } diff --git a/tests/test_data.py b/tests/test_data.py index d346d18..822255c 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -4,7 +4,7 @@ from __future__ import annotations from types import SimpleNamespace -from custom_components.sws12500.const import DOMAIN, ECOWITT_ENABLED, WSLINK +from custom_components.sws12500.const import DOMAIN, ECOWITT_ENABLED, LEGACY_ENABLED, WSLINK from custom_components.sws12500.data import ( SWSRuntimeData, _station_model, @@ -75,11 +75,11 @@ def test_station_model_ecowitt_without_learned_model(): Covers both the missing-runtime_data and the model-is-None paths. """ - no_runtime = SimpleNamespace(options={ECOWITT_ENABLED: True}) + no_runtime = SimpleNamespace(options={ECOWITT_ENABLED: True, LEGACY_ENABLED: False}) assert _station_model(no_runtime) == "Ecowitt" # type: ignore[arg-type] runtime_no_model = SimpleNamespace( - options={ECOWITT_ENABLED: True}, + options={ECOWITT_ENABLED: True, LEGACY_ENABLED: False}, runtime_data=SimpleNamespace(ecowitt_model=None), ) assert _station_model(runtime_no_model) == "Ecowitt" # type: ignore[arg-type] @@ -88,19 +88,23 @@ def test_station_model_ecowitt_without_learned_model(): def test_station_model_ecowitt_with_learned_model(): """Ecowitt enabled with a learned model -> "Ecowitt ".""" entry = SimpleNamespace( - options={ECOWITT_ENABLED: True}, + options={ECOWITT_ENABLED: True, LEGACY_ENABLED: False}, runtime_data=SimpleNamespace(ecowitt_model="GW1000"), ) assert _station_model(entry) == "Ecowitt GW1000" # type: ignore[arg-type] -def test_station_model_ecowitt_takes_precedence_over_wslink(): - """When both flags are set the ecowitt model wins.""" +def test_station_model_legacy_wins_over_ecowitt_on_conflict(): + """A stale both-enabled config reports the endpoint that is actually wired up. + + `effective_protocols` resolves the conflict in favour of legacy, so the device model + must not advertise an Ecowitt station whose data is being ignored. + """ entry = SimpleNamespace( - options={ECOWITT_ENABLED: True, WSLINK: True}, + options={ECOWITT_ENABLED: True, LEGACY_ENABLED: True, WSLINK: True}, runtime_data=SimpleNamespace(ecowitt_model="WS3900"), ) - assert _station_model(entry) == "Ecowitt WS3900" # type: ignore[arg-type] + assert _station_model(entry) == "WSLink" # type: ignore[arg-type] def test_build_device_info_shared_identity():