fix(security)+i18n: stop health-attribute leak, mask secrets in logs, sync translations
Security (from the deep audit): - S1: health "integration_health" sensor no longer dumps the full snapshot in extra_state_attributes (readable by any HA user). public_health_snapshot() strips internal network details (source IP, internal URLs, raw add-on status); full data stays on the authenticated endpoint and admin-only diagnostics. - S2: anonymize() now masks the Ecowitt passkey/PASSKEY. - S3: Windy station id masked before logging the dataset. - S4: diagnostics redacts internal network fields (source IP, URLs, raw_status). i18n (Q1/Q2): - Sync strings.json + en.json with the actual flow: add wslink_port_setup step, the ecowitt/pocasi/wslink_port_setup menu entries, the pocasi_* and windy_key_required error keys, the stale_sensors_detected issue; fix wrong data keys (WSLINK->wslink + legacy_enabled, POCASI_CZ_SEND_INTERVAL->POCASI_SEND_INTERVAL, pocasi_enabled_checkbox->pocasi_enabled_chcekbox); translate the English ecowitt options step (was Czech); de-duplicate the legacy-battery issue description. - Q2: rename wbgt_index -> wbgt_temp to match the entity translation_key. - cs.json: add windy_key_required and fix the pocasi data/data_description keys. Tests: 300 passing, 100% coverage maintained; basedpyright clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent
bf6a02c5b3
commit
1fe4d6da45
|
|
@ -31,6 +31,13 @@ TO_REDACT = {
|
|||
"PASSWORD",
|
||||
"wsid",
|
||||
"wspw",
|
||||
# Internal network details from the health snapshot (admin-only download, but
|
||||
# diagnostics are commonly shared in bug reports).
|
||||
"home_assistant_source_ip",
|
||||
"home_assistant_url",
|
||||
"health_url",
|
||||
"info_url",
|
||||
"raw_status",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,28 @@ def _sanitize_path(path: str) -> str:
|
|||
return path
|
||||
|
||||
|
||||
# Fields under "addon" that reveal internal network topology. They must not be
|
||||
# exposed via entity attributes (readable by any HA user, incl. non-admins).
|
||||
_SENSITIVE_ADDON_FIELDS: frozenset[str] = frozenset(
|
||||
{"health_url", "info_url", "home_assistant_url", "home_assistant_source_ip", "raw_status"}
|
||||
)
|
||||
|
||||
|
||||
def public_health_snapshot(data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Return a copy of the health snapshot safe to expose to any HA user.
|
||||
|
||||
Removes internal network details (HA source IP, internal URLs, raw add-on
|
||||
status). The full snapshot stays available only via the authenticated
|
||||
`/station/health` endpoint and the admin-only (redacted) diagnostics download.
|
||||
"""
|
||||
public: dict[str, Any] = deepcopy(data)
|
||||
addon = checked(public.get("addon"), dict[str, Any])
|
||||
if addon is not None:
|
||||
for field in _SENSITIVE_ADDON_FIELDS:
|
||||
addon.pop(field, None)
|
||||
return public
|
||||
|
||||
|
||||
def _empty_forwarding_state(enabled: bool) -> dict[str, Any]:
|
||||
"""Build the default forwarding status payload."""
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ from homeassistant.util import dt as dt_util
|
|||
|
||||
from .const import DOMAIN
|
||||
from .data import SWSConfigEntry
|
||||
from .health_coordinator import HealthCoordinator
|
||||
from .health_coordinator import HealthCoordinator, public_health_snapshot
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .health_coordinator import HealthCoordinator
|
||||
|
|
@ -248,11 +248,20 @@ class HealthDiagnosticSensor( # pyright: ignore[reportIncompatibleVariableOverr
|
|||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any] | None: # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
"""Expose the full health JSON on the main health sensor for debugging."""
|
||||
"""Expose the health JSON on the main health sensor for debugging.
|
||||
|
||||
Entity attributes are readable by any HA user, so internal network details
|
||||
(source IP, internal URLs, raw add-on status) are stripped here; they remain
|
||||
available via the authenticated endpoint and the admin-only diagnostics.
|
||||
"""
|
||||
if self.entity_description.key != "integration_health":
|
||||
return None
|
||||
|
||||
return checked_or(self.coordinator.data, dict[str, Any], None)
|
||||
data = checked_or(self.coordinator.data, dict[str, Any], None)
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
return public_health_snapshot(data)
|
||||
|
||||
@cached_property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
|
|
|
|||
|
|
@ -50,7 +50,11 @@
|
|||
"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_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": {
|
||||
"init": {
|
||||
|
|
@ -58,7 +62,10 @@
|
|||
"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",
|
||||
"windy": "Windy configuration"
|
||||
"wslink_port_setup": "WSLink add-on port",
|
||||
"ecowitt": "Ecowitt configuration",
|
||||
"windy": "Windy configuration",
|
||||
"pocasi": "Pocasi Meteo CZ configuration"
|
||||
}
|
||||
},
|
||||
"basic": {
|
||||
|
|
@ -67,14 +74,16 @@
|
|||
"data": {
|
||||
"API_ID": "API ID / Station ID",
|
||||
"API_KEY": "API KEY / Password",
|
||||
"WSLINK": "WSLink API",
|
||||
"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."
|
||||
"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": {
|
||||
|
|
@ -98,28 +107,38 @@
|
|||
"data": {
|
||||
"POCASI_CZ_API_ID": "ID from your Pocasi Meteo APP",
|
||||
"POCASI_CZ_API_KEY": "Key from your Pocasi Meteo APP",
|
||||
"POCASI_CZ_SEND_INTERVAL": "Resend interval in seconds",
|
||||
"pocasi_enabled_checkbox": "Enable resending data to Pocasi Meteo",
|
||||
"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_CZ_SEND_INTERVAL": "Resend interval in seconds (minimum 12s, default 30s)",
|
||||
"pocasi_enabled_checkbox": "Enables resending data to Pocasi Meteo",
|
||||
"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": "Nastavení pro Ecowitt",
|
||||
"title": "Konfigurace pro stanice Ecowitt",
|
||||
"description": "Ecowitt configuration.",
|
||||
"title": "Ecowitt station configuration",
|
||||
"data": {
|
||||
"ecowitt_webhook_id": "Unikátní webhook ID",
|
||||
"ecowitt_enabled": "Povolit data ze stanice Ecowitt"
|
||||
"ecowitt_webhook_id": "Unique webhook ID",
|
||||
"ecowitt_enabled": "Enable Ecowitt station data"
|
||||
},
|
||||
"data_description": {
|
||||
"ecowitt_webhook_id": "Nastavení pro stanici: {url}:{port}/weatherhub/{webhook_id}",
|
||||
"ecowitt_enabled": "Povolit přijímání dat ze stanic Ecowitt"
|
||||
"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": {
|
||||
|
|
@ -361,7 +380,7 @@
|
|||
"yearly_rain": {
|
||||
"name": "Yearly precipitation"
|
||||
},
|
||||
"wbgt_index": {
|
||||
"wbgt_temp": {
|
||||
"name": "WBGT index"
|
||||
},
|
||||
"hcho": {
|
||||
|
|
@ -478,7 +497,11 @@
|
|||
"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 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."
|
||||
"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": {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
"valid_credentials_match": "API ID a API KEY nesmějí být stejné!",
|
||||
"windy_id_required": "Je vyžadováno Windy ID, pokud chcete aktivovat přeposílání dat na Windy",
|
||||
"windy_pw_required": "Je vyžadován Windy KEY, pokud chcete aktivovat přeposílání dat na Windy",
|
||||
"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."
|
||||
|
|
@ -107,15 +108,15 @@
|
|||
"data": {
|
||||
"POCASI_CZ_API_ID": "ID účtu na Počasí Meteo",
|
||||
"POCASI_CZ_API_KEY": "Klíč (Key) k účtu Počasí Meteo",
|
||||
"POCASI_CZ_SEND_INTERVAL": "Interval v sekundách",
|
||||
"POCASI_SEND_INTERVAL": "Interval v sekundách",
|
||||
"pocasi_enabled_chcekbox": "Povolit přeposílání dat na server Počasí Meteo",
|
||||
"pocasi_logger_checkbox": "Logovat data a odpovědi z Počasí Meteo"
|
||||
},
|
||||
"data_description": {
|
||||
"POCASI_API_ID": "ID získáte ve své aplikaci Počasí Meteo",
|
||||
"POCASI_API_KEY": "Klíč (Key) získáte ve své aplikaci Počasí Meteo",
|
||||
"POCASI_CZ_API_ID": "ID získáte ve své aplikaci Počasí Meteo",
|
||||
"POCASI_CZ_API_KEY": "Klíč (Key) získáte ve své aplikaci Počasí Meteo",
|
||||
"POCASI_SEND_INTERVAL": "Interval v jakém se mají data na server přeposílat (minimum 12s, defaultně 30s)",
|
||||
"pocasi_enabled_checkbox": "Zapne přeposílání data na server Počasí Meteo",
|
||||
"pocasi_enabled_chcekbox": "Zapne přeposílání data na server Počasí Meteo",
|
||||
"pocasi_logger_checkbox": "Zapnout pouze v případě, že chcete zaslat ladící informace vývojáři."
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,494 +1,513 @@
|
|||
{
|
||||
"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."
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
"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."
|
||||
},
|
||||
"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."
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"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",
|
||||
"windy": "Windy 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",
|
||||
"dev_debug_checkbox": "Developer log"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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."
|
||||
"sensor": {
|
||||
"integration_health": {
|
||||
"name": "Integration status",
|
||||
"state": {
|
||||
"online_wu": "Online PWS/WU",
|
||||
"online_wslink": "Online WSLink",
|
||||
"online_idle": "Waiting for data",
|
||||
"degraded": "Degraded",
|
||||
"error": "Error"
|
||||
}
|
||||
},
|
||||
"active_protocol": {
|
||||
"name": "Active protocol",
|
||||
"state": {
|
||||
"wu": "PWS/WU",
|
||||
"wslink": "WSLink API"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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_CZ_SEND_INTERVAL": "Resend interval in seconds",
|
||||
"pocasi_enabled_checkbox": "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_CZ_SEND_INTERVAL": "Resend interval in seconds (minimum 12s, default 30s)",
|
||||
"pocasi_enabled_checkbox": "Enables resending data to Pocasi Meteo",
|
||||
"pocasi_logger_checkbox": "Enable only if you want to send debbug data to the developer"
|
||||
}
|
||||
},
|
||||
"ecowitt": {
|
||||
"description": "Nastavení pro Ecowitt",
|
||||
"title": "Konfigurace pro stanice Ecowitt",
|
||||
"data": {
|
||||
"ecowitt_webhook_id": "Unikátní webhook ID",
|
||||
"ecowitt_enabled": "Povolit data ze stanice Ecowitt"
|
||||
},
|
||||
"data_description": {
|
||||
"ecowitt_webhook_id": "Nastavení pro stanici: {url}:{port}/weatherhub/{webhook_id}",
|
||||
"ecowitt_enabled": "Povolit přijímání dat ze stanic Ecowitt"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"integration_health": {
|
||||
"name": "Integration status",
|
||||
"state": {
|
||||
"online_wu": "Online PWS/WU",
|
||||
"online_wslink": "Online WSLink",
|
||||
"online_idle": "Waiting for data",
|
||||
"degraded": "Degraded",
|
||||
"error": "Error"
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"active_protocol": {
|
||||
"name": "Active protocol",
|
||||
"state": {
|
||||
"wu": "PWS/WU",
|
||||
"wslink": "WSLink API"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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_index": {
|
||||
"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 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 sensor detected",
|
||||
"description": "These sensors are configured but haven't reported data recently: {sensors}. They will appear as Unavailable in HA. If they are no longer connected, you can remove them via Settings -> Devices & Services -> SWS 12500."
|
||||
"notify": {
|
||||
"added": {
|
||||
"title": "New sensors for SWS 12500 found.",
|
||||
"message": "{added_sensors}\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notify": {
|
||||
"added": {
|
||||
"title": "New sensors for SWS 12500 found.",
|
||||
"message": "{added_sensors}\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ def anonymize(
|
|||
- Keep all keys, but mask sensitive values.
|
||||
- Do not raise on unexpected/missing keys.
|
||||
"""
|
||||
secrets = {"ID", "PASSWORD", "wsid", "wspw"}
|
||||
secrets = {"ID", "PASSWORD", "wsid", "wspw", "passkey", "PASSKEY"}
|
||||
|
||||
return {k: ("***" if k in secrets else v) for k, v in data.items()}
|
||||
|
||||
|
|
|
|||
|
|
@ -219,7 +219,8 @@ class WindyPush:
|
|||
headers = {"Authorization": f"Bearer {windy_station_pw}"}
|
||||
|
||||
if self.log:
|
||||
_LOGGER.info("Dataset for windy: %s", purged_data)
|
||||
# Mask the station id (a credential) before logging the dataset.
|
||||
_LOGGER.info("Dataset for windy: %s", {**purged_data, "id": "***"})
|
||||
session = async_get_clientsession(self.hass)
|
||||
try:
|
||||
async with session.get(request_url, params=purged_data, headers=headers) as resp:
|
||||
|
|
|
|||
|
|
@ -663,11 +663,39 @@ def test_sensor_native_value_with_value_fn() -> None:
|
|||
assert sensor.native_value == "online"
|
||||
|
||||
|
||||
def test_sensor_extra_state_attributes_for_integration_health() -> None:
|
||||
data = {"integration_status": "online_wu", "addon": {"online": False}}
|
||||
def test_sensor_extra_state_attributes_strips_internal_fields() -> None:
|
||||
data = {
|
||||
"integration_status": "online_wu",
|
||||
"addon": {
|
||||
"online": True,
|
||||
"name": "wslink_proxy",
|
||||
"home_assistant_source_ip": "1.2.3.4",
|
||||
"health_url": "https://1.2.3.4:443/healthz",
|
||||
"info_url": "https://1.2.3.4:443/status/internal",
|
||||
"home_assistant_url": "http://ha:8123",
|
||||
"raw_status": {"secret": "x"},
|
||||
},
|
||||
}
|
||||
coordinator = _stub_coordinator(data)
|
||||
sensor = hs.HealthDiagnosticSensor(coordinator, _description("integration_health"))
|
||||
assert sensor.extra_state_attributes == data
|
||||
|
||||
attrs = sensor.extra_state_attributes
|
||||
assert attrs is not None
|
||||
# Non-sensitive fields pass through.
|
||||
assert attrs["integration_status"] == "online_wu"
|
||||
assert attrs["addon"]["online"] is True
|
||||
assert attrs["addon"]["name"] == "wslink_proxy"
|
||||
# Internal network details are stripped from the (any-user-readable) attributes.
|
||||
for field in ("home_assistant_source_ip", "health_url", "info_url", "home_assistant_url", "raw_status"):
|
||||
assert field not in attrs["addon"]
|
||||
# The source snapshot is not mutated (deepcopy).
|
||||
assert "raw_status" in data["addon"]
|
||||
|
||||
|
||||
def test_sensor_extra_state_attributes_none_data() -> None:
|
||||
coordinator = _stub_coordinator(None)
|
||||
sensor = hs.HealthDiagnosticSensor(coordinator, _description("integration_health"))
|
||||
assert sensor.extra_state_attributes is None
|
||||
|
||||
|
||||
def test_sensor_extra_state_attributes_for_other_keys() -> None:
|
||||
|
|
@ -676,6 +704,13 @@ def test_sensor_extra_state_attributes_for_other_keys() -> None:
|
|||
assert sensor.extra_state_attributes is None
|
||||
|
||||
|
||||
def test_public_health_snapshot_handles_missing_or_nondict_addon() -> None:
|
||||
# addon missing -> returned as-is (copy).
|
||||
assert hc.public_health_snapshot({"integration_status": "x"}) == {"integration_status": "x"}
|
||||
# addon not a dict -> left untouched.
|
||||
assert hc.public_health_snapshot({"addon": "nope"}) == {"addon": "nope"}
|
||||
|
||||
|
||||
def test_sensor_device_info() -> None:
|
||||
coordinator = _stub_coordinator({})
|
||||
sensor = hs.HealthDiagnosticSensor(coordinator, _description("active_protocol"))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,27 @@
|
|||
"""Coverage for to_int / to_float edge cases."""
|
||||
"""Coverage for to_int / to_float edge cases and anonymize() masking."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from custom_components.sws12500.utils import to_float, to_int
|
||||
from custom_components.sws12500.utils import anonymize, to_float, to_int
|
||||
|
||||
|
||||
def test_anonymize_masks_all_known_secrets() -> None:
|
||||
raw = {
|
||||
"ID": "id",
|
||||
"PASSWORD": "pw",
|
||||
"wsid": "ws",
|
||||
"wspw": "wp",
|
||||
"passkey": "ecowitt-secret",
|
||||
"PASSKEY": "ecowitt-secret",
|
||||
"tempf": "68",
|
||||
}
|
||||
out = anonymize(raw)
|
||||
for key in ("ID", "PASSWORD", "wsid", "wspw", "passkey", "PASSKEY"):
|
||||
assert out[key] == "***"
|
||||
# Non-secret values pass through unchanged.
|
||||
assert out["tempf"] == "68"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
Loading…
Reference in New Issue