From dc5c45483e5b6e8b87e6d33a06380cb82b121879 Mon Sep 17 00:00:00 2001 From: Lukas Svoboda Date: Wed, 19 Mar 2025 17:54:22 +0100 Subject: [PATCH 01/10] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 34 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 23 +++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0c6761a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Logs** +Provide `Developer log` if applicable + +**Provide information about your station:** + - Weather station type: + - firmware version: + +- [ ] Using PWS protocol +- [ ] Using WSLink API +- [ ] Using WSLink proxy Add-on + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..269790e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Is your feature request a new addition** +Describe what you want to achieve. How new feature should work. + +**Additional context** +Add any other context or screenshots about the feature request here. From 2b57eeb4fae36340101e2aebce60b7d1c8cfd6f3 Mon Sep 17 00:00:00 2001 From: Lukas Svoboda Date: Wed, 19 Mar 2025 18:23:59 +0100 Subject: [PATCH 02/10] Create FUNDING.yml --- .github/FUNDING.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..1d6db7e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,5 @@ +# These are supported funding model platforms + +github: schizza +ko_fi: schizza +buy_me_a_coffee: schizza From f9d80d7a0012e2a714e07df46a9266ea10f79bdb Mon Sep 17 00:00:00 2001 From: Lukas Svoboda Date: Wed, 19 Mar 2025 18:25:13 +0100 Subject: [PATCH 03/10] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0c6761a..f09af96 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' +title: '[BUG]' +labels: 'bug' assignees: '' --- From 397e44e5f2b3802ae474468fbb55a2051a163b21 Mon Sep 17 00:00:00 2001 From: Lukas Svoboda Date: Sat, 29 Mar 2025 11:27:06 +0100 Subject: [PATCH 04/10] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- .github/ISSUE_TEMPLATE/issue.md | 34 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/issue.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f09af96..4edd987 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report about: Create a report to help us improve -title: '[BUG]' -labels: 'bug' +title: "[BUG]" +labels: bug assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 0000000..5c4f806 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,34 @@ +--- +name: Issue +about: A minor issue that does not significantly affect functionality. +title: "[ISSUE]" +labels: issue +assignees: '' + +--- + +**Describe the issue** +A clear and concise description of what the issue is. + +**To Reproduce** +Steps to reproduce the behavior if any: + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Logs** +Provide `Developer log` if applicable + +**Provide information about your station:** + - Weather station type: + - firmware version: + +- [ ] Using PWS protocol +- [ ] Using WSLink API +- [ ] Using WSLink proxy Add-on + +**Additional context** +Add any other context about the problem here. From 287e74673e3cb14d7ca40c3772bbd00926e32538 Mon Sep 17 00:00:00 2001 From: Lukas Svoboda Date: Sat, 29 Mar 2025 11:30:34 +0100 Subject: [PATCH 05/10] Create config.yml --- .github/ISSUE_TEMPLATE/config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false From 601d1f3984c736c207ee130fa3c501b513eb7e30 Mon Sep 17 00:00:00 2001 From: schizza Date: Fri, 4 Apr 2025 13:10:10 +0200 Subject: [PATCH 06/10] Adds rain sensors for different time periods for WSLink Adds hourly, weekly, monthly, and yearly rain sensors to the integration for WSLink connection. The units of measurement are corrected for the rain sensor, now displaying millimeters per hour as the station is sending data in mm/h for 'rain' sensor. And cumulative precipitation is in millimeters. Also includes translations for the new sensors. --- custom_components/sws12500/const.py | 12 +++-- custom_components/sws12500/manifest.json | 2 +- custom_components/sws12500/sensors_wslink.py | 52 ++++++++++++++++++- .../sws12500/translations/cs.json | 4 ++ .../sws12500/translations/en.json | 9 ++-- 5 files changed, 69 insertions(+), 10 deletions(-) diff --git a/custom_components/sws12500/const.py b/custom_components/sws12500/const.py index 14cffb6..18ad313 100644 --- a/custom_components/sws12500/const.py +++ b/custom_components/sws12500/const.py @@ -68,6 +68,10 @@ WIND_GUST: Final = "wind_gust" WIND_DIR: Final = "wind_dir" WIND_AZIMUT: Final = "wind_azimut" RAIN: Final = "rain" +HOURLY_RAIN: Final = "hourly_rain" +WEEKLY_RAIN: Final = "weekly_rain" +MONTHLY_RAIN: Final = "monthly_rain" +YEARLY_RAIN: Final = "yearly_rain" DAILY_RAIN: Final = "daily_rain" SOLAR_RADIATION: Final = "solar_radiation" INDOOR_TEMP: Final = "indoor_temp" @@ -129,15 +133,15 @@ REMAP_WSLINK_ITEMS: dict = { "t234c2cn": CH3_CONNECTION, "t1chill": CHILL_INDEX, "t1heat": HEAT_INDEX, + "t1rainhr": HOURLY_RAIN, + "t1rainwy": WEEKLY_RAIN, + "t1rainmth": MONTHLY_RAIN, + "t1rainyr": YEARLY_RAIN, } # TODO: Add more sensors # # 'inbat' indoor battery level (1 normal, 0 low) -# 't1rainhr' hourly rain rate in mm -# 't1rainwy' weekly rain rate in mm -# 't1rainmth': monthly rain rate in mm -# 't1rainyr': yearly rain rate in mm # 't1bat': outdoor battery level (1 normal, 0 low) # 't234c1bat': CH2 battery level (1 normal, 0 low) CH2 in integration is CH1 in WSLink diff --git a/custom_components/sws12500/manifest.json b/custom_components/sws12500/manifest.json index c1aa0c4..c17c303 100644 --- a/custom_components/sws12500/manifest.json +++ b/custom_components/sws12500/manifest.json @@ -10,6 +10,6 @@ "issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues", "requirements": [], "ssdp": [], - "version": "1.5.3", + "version": "1.6.1", "zeroconf": [] } diff --git a/custom_components/sws12500/sensors_wslink.py b/custom_components/sws12500/sensors_wslink.py index 92cfe03..d052c42 100644 --- a/custom_components/sws12500/sensors_wslink.py +++ b/custom_components/sws12500/sensors_wslink.py @@ -39,6 +39,10 @@ from .const import ( WIND_GUST, WIND_SPEED, UnitOfDir, + MONTHLY_RAIN, + HOURLY_RAIN, + YEARLY_RAIN, + HOURLY_RAIN, ) from .sensors_common import WeatherSensorEntityDescription from .utils import wind_dir_to_text @@ -138,10 +142,10 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( ), WeatherSensorEntityDescription( key=RAIN, - native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL, - suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS_PER_HOUR, suggested_display_precision=2, icon="mdi:weather-pouring", translation_key=RAIN, @@ -158,6 +162,50 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( translation_key=DAILY_RAIN, value_fn=lambda data: cast("float", data), ), + WeatherSensorEntityDescription( + key=HOURLY_RAIN, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.MEASUREMENT, + suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=DAILY_RAIN, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=WEEKLY_RAIN, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.MEASUREMENT, + suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=DAILY_RAIN, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=MONTHLY_RAIN, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.MEASUREMENT, + suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=DAILY_RAIN, + value_fn=lambda data: cast("float", data), + ), + WeatherSensorEntityDescription( + key=YEARLY_RAIN, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.MEASUREMENT, + suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + suggested_display_precision=2, + icon="mdi:weather-pouring", + translation_key=DAILY_RAIN, + value_fn=lambda data: cast("float", data), + ), WeatherSensorEntityDescription( key=SOLAR_RADIATION, native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, diff --git a/custom_components/sws12500/translations/cs.json b/custom_components/sws12500/translations/cs.json index 24bf2f5..e024ec3 100644 --- a/custom_components/sws12500/translations/cs.json +++ b/custom_components/sws12500/translations/cs.json @@ -112,6 +112,10 @@ "ch4_humidity": { "name": "Vlhkost sensoru 4" }, "heat_index": { "name": "Tepelný index" }, "chill_index": { "name": "Pocitová teplota" }, + "hourly_rain": { "name": "Hodinový úhrn srážek" }, + "weekly_rain": { "name": "Týdenní úhrn srážek" }, + "monthly_rain": { "name": "Měsíční úhrn srážek" }, + "yearly_rain": { "name": "Roční úhrn srážek" }, "wind_azimut": { "name": "Azimut", "state": { diff --git a/custom_components/sws12500/translations/en.json b/custom_components/sws12500/translations/en.json index 0115968..e0ce1a6 100644 --- a/custom_components/sws12500/translations/en.json +++ b/custom_components/sws12500/translations/en.json @@ -35,7 +35,6 @@ }, "step": { - "init": { "title": "Configure SWS12500 Integration", "description": "Choose what do you want to configure. If basic access or resending data for Windy site", @@ -83,8 +82,8 @@ "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." + "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." } } } @@ -113,6 +112,10 @@ "ch4_humidity": { "name": "Channel 4 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" }, "wind_azimut": { "name": "Bearing", "state": { From ea0a5e34e33ab948a68f7553e2fce7899139f514 Mon Sep 17 00:00:00 2001 From: schizza Date: Fri, 4 Apr 2025 13:14:19 +0200 Subject: [PATCH 07/10] Update version number --- custom_components/sws12500/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/sws12500/manifest.json b/custom_components/sws12500/manifest.json index c17c303..56461c7 100644 --- a/custom_components/sws12500/manifest.json +++ b/custom_components/sws12500/manifest.json @@ -10,6 +10,6 @@ "issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues", "requirements": [], "ssdp": [], - "version": "1.6.1", + "version": "1.6.2", "zeroconf": [] } From fdcd28f96a762c1aadc15803fbac3d99f828182c Mon Sep 17 00:00:00 2001 From: schizza Date: Fri, 4 Apr 2025 14:18:23 +0200 Subject: [PATCH 08/10] Fix BUG 68 Fixing Issue #68 --- custom_components/sws12500/sensors_wslink.py | 4 +- custom_components/sws12500/utils.py | 41 +++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/custom_components/sws12500/sensors_wslink.py b/custom_components/sws12500/sensors_wslink.py index d052c42..8afd15c 100644 --- a/custom_components/sws12500/sensors_wslink.py +++ b/custom_components/sws12500/sensors_wslink.py @@ -40,9 +40,9 @@ from .const import ( WIND_SPEED, UnitOfDir, MONTHLY_RAIN, - HOURLY_RAIN, YEARLY_RAIN, HOURLY_RAIN, + WEEKLY_RAIN, ) from .sensors_common import WeatherSensorEntityDescription from .utils import wind_dir_to_text @@ -145,7 +145,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL, - suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS_PER_HOUR, + suggested_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, suggested_display_precision=2, icon="mdi:weather-pouring", translation_key=RAIN, diff --git a/custom_components/sws12500/utils.py b/custom_components/sws12500/utils.py index 3673417..30a667b 100644 --- a/custom_components/sws12500/utils.py +++ b/custom_components/sws12500/utils.py @@ -284,7 +284,46 @@ def long_term_units_in_statistics_meta(): return sensor_units -def migrate_data(sensor_id: str | None = None): +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) + updated_rows = 0 + + if not Path(DATABASE_PATH).exists(): + _LOGGER.error("Database file not found: %s", DATABASE_PATH) + return False + + conn = sqlite3.connect(DATABASE_PATH) + db = conn.cursor() + + try: + _LOGGER.info(sensor_id) + db.execute( + """ + UPDATE statistics_meta + SET unit_of_measurement = 'mm' + WHERE statistic_id = ? + AND unit_of_measurement = 'mm/d'; + """, + (sensor_id,), + ) + updated_rows = db.rowcount + conn.commit() + _LOGGER.info( + "Data migration completed successfully. Updated rows: %s for %s", + updated_rows, + sensor_id, + ) + + except sqlite3.Error as e: + _LOGGER.error("Error during data migration: %s", e) + finally: + conn.close() + return updated_rows + + +def migrate_data_old(sensor_id: str | None = None): """Migrate data from mm/d to mm.""" updated_rows = 0 From 0ab5321170478c3901ef299cec39c3eed33bca81 Mon Sep 17 00:00:00 2001 From: schizza Date: Sun, 13 Apr 2025 17:52:58 +0200 Subject: [PATCH 09/10] Corrects translation keys for rain sensors Updates the translation keys for hourly, weekly, monthly, and yearly rain sensors to ensure they accurately reflect the corresponding time periods. --- custom_components/sws12500/sensors_wslink.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/sws12500/sensors_wslink.py b/custom_components/sws12500/sensors_wslink.py index 8afd15c..dc09aa9 100644 --- a/custom_components/sws12500/sensors_wslink.py +++ b/custom_components/sws12500/sensors_wslink.py @@ -170,7 +170,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, suggested_display_precision=2, icon="mdi:weather-pouring", - translation_key=DAILY_RAIN, + translation_key=HOURLY_RAIN, value_fn=lambda data: cast("float", data), ), WeatherSensorEntityDescription( @@ -181,7 +181,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, suggested_display_precision=2, icon="mdi:weather-pouring", - translation_key=DAILY_RAIN, + translation_key=WEEKLY_RAIN, value_fn=lambda data: cast("float", data), ), WeatherSensorEntityDescription( @@ -192,7 +192,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, suggested_display_precision=2, icon="mdi:weather-pouring", - translation_key=DAILY_RAIN, + translation_key=MONTHLY_RAIN, value_fn=lambda data: cast("float", data), ), WeatherSensorEntityDescription( @@ -203,7 +203,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, suggested_display_precision=2, icon="mdi:weather-pouring", - translation_key=DAILY_RAIN, + translation_key=YEARLY_RAIN, value_fn=lambda data: cast("float", data), ), WeatherSensorEntityDescription( From 99d25bfd560239c6ddbfcfb130a63079e07b64af Mon Sep 17 00:00:00 2001 From: schizza Date: Thu, 24 Apr 2025 17:56:47 +0200 Subject: [PATCH 10/10] Adds unit migration functionality to options flow Implements a user interface to migrate units for rain sensors including migration of historic data via statistics. This provides the user with the ability to correct rain units, if they have been set incorrectly. Includes UI to select sensor and units, as well as trigger migration. --- custom_components/sws12500/config_flow.py | 188 +++++++++++++++++++--- 1 file changed, 162 insertions(+), 26 deletions(-) diff --git a/custom_components/sws12500/config_flow.py b/custom_components/sws12500/config_flow.py index 508d01e..9e6b2b2 100644 --- a/custom_components/sws12500/config_flow.py +++ b/custom_components/sws12500/config_flow.py @@ -1,13 +1,15 @@ """Config flow for Sencor SWS 12500 Weather Station integration.""" +import logging from typing import Any import voluptuous as vol from homeassistant.config_entries import ConfigFlow, OptionsFlow +from homeassistant.const import UnitOfPrecipitationDepth, UnitOfVolumetricFlux from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError -from .utils import long_term_units_in_statistics_meta, migrate_data +import homeassistant.helpers.entity_registry as er from .const import ( API_ID, @@ -15,13 +17,18 @@ from .const import ( DEV_DBG, DOMAIN, INVALID_CREDENTIALS, - SENSORS_TO_LOAD, + MIG_FROM, + MIG_TO, SENSOR_TO_MIGRATE, + SENSORS_TO_LOAD, WINDY_API_KEY, WINDY_ENABLED, WINDY_LOGGER_ENABLED, WSLINK, ) +from .utils import long_term_units_in_statistics_meta, migrate_data + +_LOGGER = logging.getLogger(__name__) class CannotConnect(HomeAssistantError): @@ -41,16 +48,23 @@ class ConfigOptionsFlowHandler(OptionsFlow): self.windy_data: dict[str, Any] = {} self.windy_data_schema = {} - self.user_data: dict[str, str] = {} + self.user_data: dict[str, Any] = {} self.user_data_schema = {} self.sensors: dict[str, Any] = {} self.migrate_schema = {} + self.migrate_sensor_select = {} + self.migrate_unit_selection = {} + self.count = 0 + self.selected_sensor = "" + + self.unit_values = [unit.value for unit in UnitOfVolumetricFlux] + self.unit_values.extend([unit.value for unit in UnitOfPrecipitationDepth]) @property def config_entry(self): return self.hass.config_entries.async_get_entry(self.handler) - def _get_entry_data(self): + async def _get_entry_data(self): """Get entry data.""" self.user_data: dict[str, Any] = { @@ -61,10 +75,10 @@ class ConfigOptionsFlowHandler(OptionsFlow): } self.user_data_schema = { - vol.Required(API_ID, default=self.user_data[API_ID] or ""): str, - vol.Required(API_KEY, default=self.user_data[API_KEY] or ""): str, - vol.Optional(WSLINK, default=self.user_data[WSLINK]): bool or False, - vol.Optional(DEV_DBG, default=self.user_data[DEV_DBG]): bool or False, + vol.Required(API_ID, default=self.user_data.get(API_ID, "")): str, + vol.Required(API_KEY, default=self.user_data.get(API_KEY, "")): str, + vol.Optional(WSLINK, default=self.user_data.get(WSLINK, False)): bool, + vol.Optional(DEV_DBG, default=self.user_data.get(DEV_DBG, False)): bool, } self.sensors: dict[str, Any] = { @@ -83,7 +97,7 @@ class ConfigOptionsFlowHandler(OptionsFlow): self.windy_data_schema = { vol.Optional( - WINDY_API_KEY, default=self.windy_data[WINDY_API_KEY] or "" + WINDY_API_KEY, default=self.windy_data.get(WINDY_API_KEY, "") ): str, vol.Optional(WINDY_ENABLED, default=self.windy_data[WINDY_ENABLED]): bool or False, @@ -93,12 +107,34 @@ class ConfigOptionsFlowHandler(OptionsFlow): ): bool or False, } - self.migrate_schema = { + self.migrate_sensor_select = { vol.Required(SENSOR_TO_MIGRATE): vol.In( - long_term_units_in_statistics_meta() or {} + await self.load_sensors_to_migrate() or {} ), + } + + self.migrate_unit_selection = { + vol.Required(MIG_FROM): vol.In(self.unit_values), + vol.Required(MIG_TO): vol.In(self.unit_values), vol.Optional("trigger_action", default=False): bool, } + # "mm/d", "mm/h", "mm", "in/d", "in/h", "in" + + async def load_sensors_to_migrate(self) -> dict[str, Any]: + """Load sensors to migrate.""" + + sensor_statistics = await long_term_units_in_statistics_meta(self.hass) + + entity_registry = er.async_get(self.hass) + sensors = entity_registry.entities.get_entries_for_config_entry_id( + self.config_entry.entry_id + ) + + return { + sensor.entity_id: f"{sensor.name or sensor.original_name} (current settings: {sensor.unit_of_measurement}, longterm stats unit: {sensor_statistics.get(sensor.entity_id)})" + for sensor in sensors + if sensor.unique_id in {"rain", "daily_rain"} + } async def async_step_init(self, user_input=None): """Manage the options - show menu first.""" @@ -110,7 +146,7 @@ class ConfigOptionsFlowHandler(OptionsFlow): """Manage basic options - credentials.""" errors = {} - self._get_entry_data() + await self._get_entry_data() if user_input is None: return self.async_show_form( @@ -147,7 +183,7 @@ class ConfigOptionsFlowHandler(OptionsFlow): """Manage windy options.""" errors = {} - self._get_entry_data() + await self._get_entry_data() if user_input is None: return self.async_show_form( @@ -160,7 +196,7 @@ class ConfigOptionsFlowHandler(OptionsFlow): errors[WINDY_API_KEY] = "windy_key_required" return self.async_show_form( step_id="windy", - data_schema=self.windy_data_schema, + data_schema=vol.Schema(self.windy_data_schema), errors=errors, ) @@ -175,15 +211,17 @@ class ConfigOptionsFlowHandler(OptionsFlow): async def async_step_migration(self, user_input=None): """Migrate sensors.""" - # hj errors = {} - self._get_entry_data() + data_schema = vol.Schema(self.migrate_sensor_select) + data_schema.schema.update() + + await self._get_entry_data() if user_input is None: return self.async_show_form( step_id="migration", - data_schema=vol.Schema(self.migrate_schema), + data_schema=vol.Schema(self.migrate_sensor_select), errors=errors, description_placeholders={ "migration_status": "-", @@ -191,18 +229,116 @@ class ConfigOptionsFlowHandler(OptionsFlow): }, ) - if user_input.get("trigger_action"): - # Akce se vykoná po zaškrtnutí - count = await self.hass.async_add_executor_job( - migrate_data, user_input.get(SENSOR_TO_MIGRATE) - ) + self.selected_sensor = user_input.get(SENSOR_TO_MIGRATE) + + return await self.async_step_migration_units() + + async def async_step_migration_units(self, user_input=None): + """Migrate unit step.""" + + registry = er.async_get(self.hass) + sensor_entry = registry.async_get(self.selected_sensor) + sensor_stats = await long_term_units_in_statistics_meta(self.hass) + + default_unit = sensor_entry.unit_of_measurement if sensor_entry else None + + if default_unit not in self.unit_values: + default_unit = self.unit_values[0] + + data_schema = vol.Schema({ + vol.Required(MIG_FROM, default=default_unit): vol.In(self.unit_values), + vol.Required(MIG_TO): vol.In(self.unit_values), + vol.Optional("trigger_action", default=False): bool, + }) + + if user_input is None: return self.async_show_form( - step_id="migration", - data_schema=vol.Schema(self.migrate_schema), + step_id="migration_units", + data_schema=data_schema, + errors={}, + description_placeholders={ + "migration_sensor": sensor_entry.original_name, + "migration_stats": sensor_stats.get(self.selected_sensor), + }, + ) + + if user_input.get("trigger_action"): + self.count = await migrate_data( + self.hass, + self.selected_sensor, + user_input.get(MIG_FROM), + user_input.get(MIG_TO), + ) + + registry.async_update_entity(self.selected_sensor, + unit_of_measurement=user_input.get(MIG_TO), + ) + + state = self.hass.states.get(self.selected_sensor) + if state: + _LOGGER.info("State attributes before update: %s", state.attributes) + attributes = dict(state.attributes) + attributes["unit_of_measurement"] = user_input.get(MIG_TO) + self.hass.states.async_set(self.selected_sensor, state.state, attributes) + _LOGGER.info("State attributes after update: %s", attributes) + + options = {**self.config_entry.options, "reload_sensor": self.selected_sensor} + self.hass.config_entries.async_update_entry(self.config_entry, options=options) + + await self.hass.config_entries.async_reload(self.config_entry.entry_id) + + await self.hass.async_block_till_done() + + _LOGGER.info("Migration complete for sensor %s: %s row updated, new measurement unit: %s, ", + self.selected_sensor, + self.count, + user_input.get(MIG_TO), + ) + + await self._get_entry_data() + sensor_entry = er.async_get(self.hass).async_get(self.selected_sensor) + sensor_stat = await self.load_sensors_to_migrate() + + return self.async_show_form( + step_id="migration_complete", + data_schema=vol.Schema({}), + errors={}, + description_placeholders={ + "migration_sensor": sensor_entry.unit_of_measurement, + "migration_stats": sensor_stat.get(self.selected_sensor), + "migration_count": self.count, + }, + ) + + # retain windy data + user_input.update(self.windy_data) + + # retain user_data + user_input.update(self.user_data) + + # retain senors + user_input.update(self.sensors) + + return self.async_create_entry(title=DOMAIN, data=user_input) + + async def async_step_migration_complete(self, user_input=None): + """Migration complete.""" + + errors = {} + + await self._get_entry_data() + sensor_entry = er.async_get(self.hass).async_get(self.selected_sensor) + sensor_stat = await self.load_sensors_to_migrate() + + if user_input is None: + return self.async_show_form( + step_id="migration_complete", + data_schema=vol.Schema({}), errors=errors, description_placeholders={ - "migration_status": user_input.get(SENSOR_TO_MIGRATE), - "migration_count": count, + "migration_sensor": sensor_entry.unit_of_measurement, + "migration_stats": sensor_stat.get(self.selected_sensor), + "migration_count": self.count, }, )