From feed73081843007f5dccf299c4e27c92bb4e3b1c Mon Sep 17 00:00:00 2001 From: Ferron Nijland Date: Wed, 9 Jul 2025 16:26:18 +0200 Subject: [PATCH 1/5] Add outside battery sensor and related translations --- custom_components/sws12500/const.py | 33 +++++++++++++++++++ custom_components/sws12500/sensors_wslink.py | 11 ++++++- custom_components/sws12500/strings.json | 1 + .../sws12500/translations/cs.json | 1 + .../sws12500/translations/en.json | 2 ++ custom_components/sws12500/utils.py | 13 ++++++++ 6 files changed, 60 insertions(+), 1 deletion(-) diff --git a/custom_components/sws12500/const.py b/custom_components/sws12500/const.py index 1819c7f..28bc375 100644 --- a/custom_components/sws12500/const.py +++ b/custom_components/sws12500/const.py @@ -63,6 +63,7 @@ OUTSIDE_TEMP: Final = "outside_temp" DEW_POINT: Final = "dew_point" OUTSIDE_HUMIDITY: Final = "outside_humidity" OUTSIDE_CONNECTION: Final = "outside_connection" +OUTSIDE_BATTERY: Final = "outside_battery" WIND_SPEED: Final = "wind_speed" WIND_GUST: Final = "wind_gust" WIND_DIR: Final = "wind_dir" @@ -137,6 +138,7 @@ REMAP_WSLINK_ITEMS: dict = { "t1rainwy": WEEKLY_RAIN, "t1rainmth": MONTHLY_RAIN, "t1rainyr": YEARLY_RAIN, + "t1bat": OUTSIDE_BATTERY, } # TODO: Add more sensors @@ -177,6 +179,26 @@ class UnitOfDir(StrEnum): N = "n" +class UnitOfDir(StrEnum): + """Wind direrction azimut.""" + + 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" + N = "n" + AZIMUT: list[UnitOfDir] = [ UnitOfDir.NNE, UnitOfDir.NE, @@ -195,3 +217,14 @@ AZIMUT: list[UnitOfDir] = [ UnitOfDir.NNW, UnitOfDir.N, ] + +class UnitOfBat(StrEnum): + """Battery level unit of measure.""" + + LOW = "low" + NORMAL = "normal" + +BATLEVEL: list[UnitOfBat] = [ + UnitOfBat.LOW, + UnitOfBat.NORMAL, +] diff --git a/custom_components/sws12500/sensors_wslink.py b/custom_components/sws12500/sensors_wslink.py index dc09aa9..22aeefe 100644 --- a/custom_components/sws12500/sensors_wslink.py +++ b/custom_components/sws12500/sensors_wslink.py @@ -29,6 +29,7 @@ from .const import ( HEAT_INDEX, INDOOR_HUMIDITY, INDOOR_TEMP, + OUTSIDE_BATTERY, OUTSIDE_HUMIDITY, OUTSIDE_TEMP, RAIN, @@ -45,7 +46,7 @@ from .const import ( WEEKLY_RAIN, ) from .sensors_common import WeatherSensorEntityDescription -from .utils import wind_dir_to_text +from .utils import battery_level_to_text, wind_dir_to_text SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( WeatherSensorEntityDescription( @@ -303,4 +304,12 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( translation_key=CHILL_INDEX, value_fn=lambda data: cast("int", data), ), + WeatherSensorEntityDescription( + key=OUTSIDE_BATTERY, + name="Outside Battery", + icon="mdi:battery", + device_class=SensorDeviceClass.ENUM, + value_fn=lambda data: cast("str", battery_level_to_text(data)), + translation_key=OUTSIDE_BATTERY, + ), ) diff --git a/custom_components/sws12500/strings.json b/custom_components/sws12500/strings.json index fda0f39..7260ffa 100644 --- a/custom_components/sws12500/strings.json +++ b/custom_components/sws12500/strings.json @@ -135,6 +135,7 @@ } } }, + "outside_battery": { "name": "Outside battery level" }, "notify": { "added": { "title": "New sensors for SWS 12500 found.", diff --git a/custom_components/sws12500/translations/cs.json b/custom_components/sws12500/translations/cs.json index e024ec3..e6009ef 100644 --- a/custom_components/sws12500/translations/cs.json +++ b/custom_components/sws12500/translations/cs.json @@ -139,6 +139,7 @@ } } }, + "outside_battery": { "name": "Vnější úroveň nabití baterie" }, "notify": { "added": { "title": "Nalezeny nové senzory pro SWS 12500.", diff --git a/custom_components/sws12500/translations/en.json b/custom_components/sws12500/translations/en.json index e0ce1a6..1260904 100644 --- a/custom_components/sws12500/translations/en.json +++ b/custom_components/sws12500/translations/en.json @@ -136,6 +136,8 @@ "nw": "NW", "nnw": "NNW" } + }, + "outside_battery": { "name": "Outside battery level" } } } }, diff --git a/custom_components/sws12500/utils.py b/custom_components/sws12500/utils.py index 30a667b..b982910 100644 --- a/custom_components/sws12500/utils.py +++ b/custom_components/sws12500/utils.py @@ -20,6 +20,7 @@ from homeassistant.helpers.translation import async_get_translations from .const import ( AZIMUT, + BATLEVEL, DATABASE_PATH, DEV_DBG, OUTSIDE_HUMIDITY, @@ -180,7 +181,19 @@ def wind_dir_to_text(deg: float) -> UnitOfDir | None: return None +def battery_level_to_text(battery: int) -> str: + """Return battery level in text representation. + Returns UnitOfBat or None + """ + if battery is None: + return "unknown" + + if battery is 0: + return BATLEVEL[battery] + elif battery is 1: + return BATLEVEL[battery] + def fahrenheit_to_celsius(fahrenheit: float) -> float: """Convert Fahrenheit to Celsius.""" return (fahrenheit - 32) * 5.0 / 9.0 From 4d2dedbb11ef527d755c58199823eb99e8c20503 Mon Sep 17 00:00:00 2001 From: FerronN Date: Fri, 11 Jul 2025 10:15:44 +0200 Subject: [PATCH 2/5] Fix structure en.json --- custom_components/sws12500/translations/en.json | 1 - 1 file changed, 1 deletion(-) diff --git a/custom_components/sws12500/translations/en.json b/custom_components/sws12500/translations/en.json index 1260904..f89b307 100644 --- a/custom_components/sws12500/translations/en.json +++ b/custom_components/sws12500/translations/en.json @@ -138,7 +138,6 @@ } }, "outside_battery": { "name": "Outside battery level" } - } } }, "notify": { From cf0938a6fdb79d228a3ee680c21ff7f484b78191 Mon Sep 17 00:00:00 2001 From: FerronN Date: Fri, 11 Jul 2025 10:16:59 +0200 Subject: [PATCH 3/5] fix data parsing in sensors_wslink.py --- custom_components/sws12500/sensors_wslink.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/sws12500/sensors_wslink.py b/custom_components/sws12500/sensors_wslink.py index 22aeefe..50f8b2a 100644 --- a/custom_components/sws12500/sensors_wslink.py +++ b/custom_components/sws12500/sensors_wslink.py @@ -309,7 +309,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( name="Outside Battery", icon="mdi:battery", device_class=SensorDeviceClass.ENUM, - value_fn=lambda data: cast("str", battery_level_to_text(data)), + value_fn=lambda data: battery_level_to_text(int(data)) if data is not None and str(data).isdigit() else "unknown", translation_key=OUTSIDE_BATTERY, ), ) From de8d2a7b0c706bdba5c0de6a98e7bd4e9d8bf8a1 Mon Sep 17 00:00:00 2001 From: schizza Date: Sat, 16 Aug 2025 17:29:22 +0200 Subject: [PATCH 4/5] Update const.py --- custom_components/sws12500/const.py | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/custom_components/sws12500/const.py b/custom_components/sws12500/const.py index 28bc375..b790c88 100644 --- a/custom_components/sws12500/const.py +++ b/custom_components/sws12500/const.py @@ -158,27 +158,6 @@ DISABLED_BY_DEFAULT: Final = [ ] -class UnitOfDir(StrEnum): - """Wind direrction azimut.""" - - 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" - N = "n" - - class UnitOfDir(StrEnum): """Wind direrction azimut.""" @@ -223,8 +202,10 @@ class UnitOfBat(StrEnum): LOW = "low" NORMAL = "normal" + UNKNOWN = "unknown" -BATLEVEL: list[UnitOfBat] = [ +BATTERY_LEVEL: list[UnitOfBat] = [ UnitOfBat.LOW, UnitOfBat.NORMAL, + UnitOfBat.UNKNOWN, ] From 68da7aad9870eafcff9099d55293033bac78b36e Mon Sep 17 00:00:00 2001 From: schizza Date: Sun, 17 Aug 2025 18:33:42 +0200 Subject: [PATCH 5/5] Improves battery level representation Refactors battery level representation by using enum instead of string. Improves battery level display by adding an icon representation. Changes const BATLEVEL to BATTERY_LEVEL. --- custom_components/sws12500/utils.py | 45 ++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/custom_components/sws12500/utils.py b/custom_components/sws12500/utils.py index b982910..c78f63f 100644 --- a/custom_components/sws12500/utils.py +++ b/custom_components/sws12500/utils.py @@ -20,7 +20,7 @@ from homeassistant.helpers.translation import async_get_translations from .const import ( AZIMUT, - BATLEVEL, + BATTERY_LEVEL, DATABASE_PATH, DEV_DBG, OUTSIDE_HUMIDITY, @@ -30,6 +30,7 @@ from .const import ( SENSORS_TO_LOAD, WIND_SPEED, UnitOfDir, + UnitOfBat, ) _LOGGER = logging.getLogger(__name__) @@ -181,19 +182,33 @@ def wind_dir_to_text(deg: float) -> UnitOfDir | None: return None -def battery_level_to_text(battery: int) -> str: + +def battery_level_to_text(battery: int) -> UnitOfBat: """Return battery level in text representation. - Returns UnitOfBat or None + Returns UnitOfBat """ - if battery is None: - return "unknown" - if battery is 0: - return BATLEVEL[battery] - elif battery is 1: - return BATLEVEL[battery] - + return { + 0: UnitOfBat.LOW, + 1: UnitOfBat.NORMAL, + }.get(battery, UnitOfBat.UNKNOWN) + + +def battery_level_to_icon(battery: UnitOfBat) -> str: + """Return battery level in icon representation. + + Returns str + """ + + icons = { + UnitOfBat.LOW: "mdi:battery-alert", + UnitOfBat.NORMAL: "mdi:battery", + } + + return icons.get(battery, "mdi:battery-unknown") + + def fahrenheit_to_celsius(fahrenheit: float) -> float: """Convert Fahrenheit to Celsius.""" return (fahrenheit - 32) * 5.0 / 9.0 @@ -280,10 +295,12 @@ def long_term_units_in_statistics_meta(): db = conn.cursor() try: - db.execute(""" + db.execute( + """ SELECT statistic_id, unit_of_measurement from statistics_meta WHERE statistic_id LIKE 'sensor.weather_station_sws%' - """) + """ + ) rows = db.fetchall() sensor_units = { statistic_id: f"{statistic_id} ({unit})" for statistic_id, unit in rows @@ -299,8 +316,8 @@ def long_term_units_in_statistics_meta(): async def migrate_data(hass: HomeAssistant, sensor_id: str | None = None) -> bool: """Migrate data from mm/d to mm.""" - - _LOGGER.debug("Sensor %s is required for data migration", sensor_id) + + _LOGGER.debug("Sensor %s is required for data migration", sensor_id) updated_rows = 0 if not Path(DATABASE_PATH).exists():