Compare commits

...

30 Commits
v1.5.0 ... main

Author SHA1 Message Date
Lukas Svoboda ee7be37ba6 Add shared hass.data keys/constants for cleaner integration state storage (local) 2026-01-20 20:01:29 +01:00
Lukas Svoboda 8034ada12f Add shared hass.data keys/constants for cleaner integration state storage 2026-01-20 19:59:29 +01:00
schizza 99d25bfd56 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.
2025-04-24 17:56:47 +02:00
Lukas Svoboda af87fd0719
Merge pull request #69 from schizza/wslink_name_fix1
WSLINK_name_fix
2025-04-13 17:54:53 +02:00
schizza 0ab5321170 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.
2025-04-13 17:52:58 +02:00
schizza fdcd28f96a Fix BUG 68
Fixing Issue #68
2025-04-04 14:18:23 +02:00
Lukas Svoboda 093a7915f0
Merge pull request #67 from schizza/add_sensors_for_wslink
Adds rain sensors for different time periods for WSLink
2025-04-04 13:15:01 +02:00
schizza ea0a5e34e3 Update version number 2025-04-04 13:14:19 +02:00
Lukas Svoboda fdd4cacddf
Merge branch 'main' into add_sensors_for_wslink 2025-04-04 13:11:45 +02:00
schizza 601d1f3984 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.
2025-04-04 13:10:10 +02:00
Lukas Svoboda 287e74673e
Create config.yml 2025-03-29 11:30:34 +01:00
Lukas Svoboda 397e44e5f2
Update issue templates 2025-03-29 11:27:06 +01:00
Lukas Svoboda f9d80d7a00
Update bug_report.md 2025-03-19 18:25:13 +01:00
Lukas Svoboda 2b57eeb4fa
Create FUNDING.yml 2025-03-19 18:23:59 +01:00
Lukas Svoboda dc5c45483e
Update issue templates 2025-03-19 17:54:22 +01:00
Lukas Svoboda 3d112757d0
Update const.py
Update path to database as it differs in development installation.
2025-03-15 15:44:57 +01:00
Lukas Svoboda 189777fbdb
Merge pull request #63 from schizza/migrate_data
Fixes rainfall unit inconsistency

Updates daily rain sensor to use consistent measurement units and corrects device class and suggested unit.

Adds database path constant and data migration function

Defines DATABASE_PATH constant for database file location Introduces migrate_data function to update unit of measurement in long statistics.

Adds sensor migration feature

Introduces a migration step for sensor statistics
Updates schema and translations to support migration Fixes data migration from mm/d to mm
2025-03-15 15:13:59 +01:00
schizza 8e97fc7404 Different values displayed on station/cloud and HA - Total rainfall
Fixes #62

Fixes rainfall unit inconsistency

Updates daily rain sensor to use consistent measurement units and corrects device class and suggested unit.

Fixes #62

Adds database path constant and data migration function

Defines DATABASE_PATH constant for database file location
Introduces migrate_data function to update unit of measurement in long statistics.

Adds sensor migration feature

Introduces a migration step for sensor statistics
Updates schema and translations to support migration
Fixes data migration from mm/d to mm
2025-03-15 15:10:49 +01:00
Lukas Svoboda 908a60ade4
Merge pull request #61 from schizza/invalid_native_temp
Corrected temperature unit in outside_temp.
2025-03-11 17:40:28 +01:00
schizza 47143d9ac2 Corrected temperature unit in outside_temp.
Changes temperature unit from Celsius to Fahrenheit in the weather sensor descriptions.
2025-03-11 17:39:09 +01:00
Lukas Svoboda 41e2415e69
Update manifest.json 2025-03-10 08:45:32 +01:00
Lukas Svoboda 7ca4578c0f
Merge pull request #59 from schizza/wslink_sensors_add
Adds sensors for WSLink and improves data handling
2025-03-10 08:44:11 +01:00
schizza 37c3e2e77f Adds sensors for WSLink and improves data handling
Includes new sensor connection status constants and mappings
Updates WeatherSensor to handle null data values
Switches temperature unit to Celsius for WSLink sensors
Refines heat and chill index calculations with unit conversions
2025-03-10 08:42:17 +01:00
Lukas Svoboda cd5abc0db3
Merge pull request #52 from schizza/readme_update
readme_update
2025-02-21 19:06:26 +01:00
Lukas Svoboda 0c33ac02da
Merge branch 'main' into readme_update 2025-02-21 19:06:02 +01:00
schizza 3cd21e0143 readme_update 2025-02-21 18:05:15 +00:00
schizza 1dd51f4ed9 gitignore 2025-02-19 17:50:50 +01:00
Lukas Svoboda 7257587b2f
Update minor version 2025-02-18 19:43:36 +01:00
Lukas Svoboda c131eddf39
Merge pull request #51 from schizza/config_flow_fix
Improves configuration defaults and UI descriptions

Sets default values for optional fields in configuration flow
Enhances descriptions and titles in UI for better clarity
Updates translations
2025-02-18 19:42:20 +01:00
schizza a3dc4f9986 Improves configuration defaults and UI descriptions
Sets default values for optional fields in configuration flow
Enhances descriptions and titles in UI for better clarity
Updates translations
2025-02-18 19:40:46 +01:00
19 changed files with 810 additions and 149 deletions

5
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,5 @@
# These are supported funding model platforms
github: schizza
ko_fi: schizza
buy_me_a_coffee: schizza

34
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,34 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
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.

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

View File

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

34
.github/ISSUE_TEMPLATE/issue.md vendored Normal file
View File

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

4
.gitignore vendored
View File

@ -161,3 +161,7 @@ cython_debug/
.DS_Store .DS_Store
HA HA
.devcontainer
.gitignore
.vscode

View File

@ -1,24 +1,33 @@
# Integrates your SWS 12500 weather station seamlessly into Home Assistant # Integrates your Sencor SWS 12500 or 16600, GARNI, BRESSER weather stations seamlessly into Home Assistant
This integration will listen for data from your station and passes them to respective sensors. It also provides the ability to push data to Windy API. This integration will listen for data from your station and passes them to respective sensors. It also provides the ability to push data to Windy API.
_This custom component replaces [old integration via Node-RED and proxy server](https://github.com/schizza/WeatherStation-SWS12500)._ _This custom component replaces [old integration via Node-RED and proxy server](https://github.com/schizza/WeatherStation-SWS12500)._
# Warning - FW 3.0 for SWS 12500! ## Warning - WSLink APP (applies also for SWS 12500 with firmware >3.0)
As by now, it is strongly recommended **not to update to FW 3.0.** For stations that are using WSLink app to setup station and WSLink API for resending data (SWS 12500 manufactured in 2024 and later). You will need to install [WSLink SSL proxy addon](https://github.com/schizza/wslink-addon) to your Home Assistant if you are not running your Home Assistant instance in SSL mode or you do not have SSL proxy for your Home Assistant.
This firmware seems to be buggy in sending data to Home Assistant withou SSL settings.
_**I am working of this issue and it is not resloved by now.**_
## Requirements ## Requirements
- [Sencor SWS 12500 Weather Station](https://www.sencor.cz/profesionalni-meteorologicka-stanice/sws-12500). - Weather station that supports sending data to custom server in their API [(list of supported stations.)](#list-of-supported-stations)
- Configure station to send data directly to Home Assistant. - Configure station to send data directly to Home Assistant.
- If you want to push data to Windy, you have to create an account at [Windy](https://stations.windy.com). - If you want to push data to Windy, you have to create an account at [Windy](https://stations.windy.com).
## List of supported stations
- [Sencor SWS 12500 Weather Station](https://www.sencor.cz/profesionalni-meteorologicka-stanice/sws-12500)
- [Sencor SWS 16600 WiFi SH](https://www.sencor.cz/meteorologicka-stanice/sws-16600)
- Bresser stations that support custom server upload. [for example, this is known to work](https://www.bresser.com/p/bresser-wi-fi-clearview-weather-station-with-7-in-1-sensor-7002586)
- Garni stations with WSLink support or custom server support.
## Installation ## Installation
### If your station's firmware is 1.0 or your station is configured as described in this README and you still can not see any data incoming to Home Assistant please [read here](https://github.com/schizza/SWS-12500-custom-component/issues/17) and [here](firmware_bug.md) ### If your SWS12500 station's firmware is 1.0 or your station is configured as described in this README and you still can not see any data incoming to Home Assistant please [read here](https://github.com/schizza/SWS-12500-custom-component/issues/17) and [here](firmware_bug.md)
### For stations that send through WSLink API
Make sure you have your Home Assistant cofigured in SSL mode or use [WSLink SSL proxy addon](https://github.com/schizza/wslink-addon) to bypass SSL configuration of whole Home Assistant.
### HACS installation ### HACS installation
@ -44,11 +53,15 @@ For manual installation you must have an access to your Home Assistant's `/confi
## Configure your station in AP mode ## Configure your station in AP mode
> This configuration example is for Sencor SWS12500 with FW < 3.0
> For WSLink read [this notes.](#wslink-notes)
1. Hold the Wi-Fi button on the back of the station for 6 seconds until the AP will flash on the display. 1. Hold the Wi-Fi button on the back of the station for 6 seconds until the AP will flash on the display.
2. Select your station from available APs on your computer. 2. Select your station from available APs on your computer.
3. Connect to the station's setup page: `http://192.168.1.1` from your browser. 3. Connect to the station's setup page: `http://192.168.1.1` from your browser.
4. In the third URL section fill in the address to your local Home Assistant installation. 4. In the third URL section fill in the address to your local Home Assistant installation.
5. Create new `ID` and `KEY`. You can use [online tool](https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx) to generate random keys. _(you will need them to configure integation to Home Assistatnt)_ 5. Create new `ID` and `KEY`. You can use [online tool](https://randomkeygen.com) to generate random keys. _(you will need them to configure integration to Home Assistant)_
6. Save your configuration. 6. Save your configuration.
![station_setup](README/station_hint.png) ![station_setup](README/station_hint.png)
@ -61,7 +74,7 @@ API_KEY: PASSWORD in station's config
![config dialog](README/cfg_dialog.png) ![config dialog](README/cfg_dialog.png)
If you chanage `API ID` or `API KEY` in the station, you have to reconfigure integration to accept data from your station. If you change `API ID` or `API KEY` in the station, you have to reconfigure integration to accept data from your station.
- In `Settings` -> `Devices & services` find SWS12500 and click `Configure`. - In `Settings` -> `Devices & services` find SWS12500 and click `Configure`.
- In dialog box choose `Basic - Configure credentials` - In dialog box choose `Basic - Configure credentials`
@ -85,3 +98,23 @@ As soon as the integration is added into Home Assistant it will listen for incom
![enable windy](README/windy_cfg.png) ![enable windy](README/windy_cfg.png)
- You are done. - You are done.
## WSLink notes
While your station is using WSLink you have to have Home Assistant in SSL mode or behind SSL proxy server.
You can bypass whole SSL settings by using [WSLink SSL proxy addon](https://github.com/schizza/wslink-addon) which is made exactly for this integration to support WSLink on unsecured installations of Home Assistant.
### Configuration
- Set your station as [mentioned above](#configure-your-station-in-ap-mode) while changing `HA port` to be the port number you set in the addon (443 for example) not port of your Home Assistant instance. And that will do the trick!
```plain
HomeAssistant is at 192.0.0.2:8123
WSLink proxy addon listening on port 4443
you will set URL in station to: 192.0.0.2:4443
```
- Your station will be sending data to this SSL proxy and addon will handle the rest.
_Most of the stations does not care about self-signed certificates on the server side._

View File

@ -97,6 +97,10 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator):
self.hass, DOMAIN, f"sensor.{t_key}", key="name", category="entity" self.hass, DOMAIN, f"sensor.{t_key}", key="name", category="entity"
) )
for t_key in sensors for t_key in sensors
if await translations(
self.hass, DOMAIN, f"sensor.{t_key}", key="name", category="entity"
)
is not None
] ]
human_readable = "\n".join(translate_sensors) human_readable = "\n".join(translate_sensors)

View File

@ -1,12 +1,15 @@
"""Config flow for Sencor SWS 12500 Weather Station integration.""" """Config flow for Sencor SWS 12500 Weather Station integration."""
import logging
from typing import Any from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, OptionsFlow from homeassistant.config_entries import ConfigFlow, OptionsFlow
from homeassistant.const import UnitOfPrecipitationDepth, UnitOfVolumetricFlux
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.entity_registry as er
from .const import ( from .const import (
API_ID, API_ID,
@ -14,12 +17,18 @@ from .const import (
DEV_DBG, DEV_DBG,
DOMAIN, DOMAIN,
INVALID_CREDENTIALS, INVALID_CREDENTIALS,
MIG_FROM,
MIG_TO,
SENSOR_TO_MIGRATE,
SENSORS_TO_LOAD, SENSORS_TO_LOAD,
WINDY_API_KEY, WINDY_API_KEY,
WINDY_ENABLED, WINDY_ENABLED,
WINDY_LOGGER_ENABLED, WINDY_LOGGER_ENABLED,
WSLINK, WSLINK,
) )
from .utils import long_term_units_in_statistics_meta, migrate_data
_LOGGER = logging.getLogger(__name__)
class CannotConnect(HomeAssistantError): class CannotConnect(HomeAssistantError):
@ -39,29 +48,37 @@ class ConfigOptionsFlowHandler(OptionsFlow):
self.windy_data: dict[str, Any] = {} self.windy_data: dict[str, Any] = {}
self.windy_data_schema = {} self.windy_data_schema = {}
self.user_data: dict[str, str] = {} self.user_data: dict[str, Any] = {}
self.user_data_schema = {} self.user_data_schema = {}
self.sensors: dict[str, Any] = {} 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 @property
def config_entry(self): def config_entry(self):
return self.hass.config_entries.async_get_entry(self.handler) return self.hass.config_entries.async_get_entry(self.handler)
def _get_entry_data(self): async def _get_entry_data(self):
"""Get entry data.""" """Get entry data."""
self.user_data: dict[str, Any] = { self.user_data: dict[str, Any] = {
API_ID: self.config_entry.options.get(API_ID), API_ID: self.config_entry.options.get(API_ID),
API_KEY: self.config_entry.options.get(API_KEY), API_KEY: self.config_entry.options.get(API_KEY),
WSLINK: self.config_entry.options.get(WSLINK), WSLINK: self.config_entry.options.get(WSLINK, False),
DEV_DBG: self.config_entry.options.get(DEV_DBG), DEV_DBG: self.config_entry.options.get(DEV_DBG, False),
} }
self.user_data_schema = { self.user_data_schema = {
vol.Required(API_ID, default=self.user_data[API_ID] or ""): str, vol.Required(API_ID, default=self.user_data.get(API_ID, "")): str,
vol.Required(API_KEY, default=self.user_data[API_KEY] or ""): str, vol.Required(API_KEY, default=self.user_data.get(API_KEY, "")): str,
vol.Optional(WSLINK, default=self.user_data[WSLINK]): bool, vol.Optional(WSLINK, default=self.user_data.get(WSLINK, False)): bool,
vol.Optional(DEV_DBG, default=self.user_data[DEV_DBG]): bool, vol.Optional(DEV_DBG, default=self.user_data.get(DEV_DBG, False)): bool,
} }
self.sensors: dict[str, Any] = { self.sensors: dict[str, Any] = {
@ -72,34 +89,64 @@ class ConfigOptionsFlowHandler(OptionsFlow):
self.windy_data: dict[str, Any] = { self.windy_data: dict[str, Any] = {
WINDY_API_KEY: self.config_entry.options.get(WINDY_API_KEY), WINDY_API_KEY: self.config_entry.options.get(WINDY_API_KEY),
WINDY_ENABLED: self.config_entry.options.get(WINDY_ENABLED) WINDY_ENABLED: self.config_entry.options.get(WINDY_ENABLED, False),
if isinstance(self.config_entry.options.get(WINDY_ENABLED), bool) WINDY_LOGGER_ENABLED: self.config_entry.options.get(
else False, WINDY_LOGGER_ENABLED, False
WINDY_LOGGER_ENABLED: self.config_entry.options.get(WINDY_LOGGER_ENABLED) ),
if isinstance(self.config_entry.options.get(WINDY_LOGGER_ENABLED), bool)
else False,
} }
self.windy_data_schema = { self.windy_data_schema = {
vol.Optional( 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, ): str,
vol.Optional(WINDY_ENABLED, default=self.windy_data[WINDY_ENABLED]): bool, vol.Optional(WINDY_ENABLED, default=self.windy_data[WINDY_ENABLED]): bool
or False,
vol.Optional( vol.Optional(
WINDY_LOGGER_ENABLED, WINDY_LOGGER_ENABLED,
default=self.windy_data[WINDY_LOGGER_ENABLED], default=self.windy_data[WINDY_LOGGER_ENABLED],
): bool, ): bool or False,
}
self.migrate_sensor_select = {
vol.Required(SENSOR_TO_MIGRATE): vol.In(
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): async def async_step_init(self, user_input=None):
"""Manage the options - show menu first.""" """Manage the options - show menu first."""
return self.async_show_menu(step_id="init", menu_options=["basic", "windy"]) return self.async_show_menu(
step_id="init", menu_options=["basic", "windy", "migration"]
)
async def async_step_basic(self, user_input=None): async def async_step_basic(self, user_input=None):
"""Manage basic options - credentials.""" """Manage basic options - credentials."""
errors = {} errors = {}
self._get_entry_data() await self._get_entry_data()
if user_input is None: if user_input is None:
return self.async_show_form( return self.async_show_form(
@ -136,7 +183,7 @@ class ConfigOptionsFlowHandler(OptionsFlow):
"""Manage windy options.""" """Manage windy options."""
errors = {} errors = {}
self._get_entry_data() await self._get_entry_data()
if user_input is None: if user_input is None:
return self.async_show_form( return self.async_show_form(
@ -149,11 +196,7 @@ class ConfigOptionsFlowHandler(OptionsFlow):
errors[WINDY_API_KEY] = "windy_key_required" errors[WINDY_API_KEY] = "windy_key_required"
return self.async_show_form( return self.async_show_form(
step_id="windy", step_id="windy",
data_schema=self.windy_data_schema, data_schema=vol.Schema(self.windy_data_schema),
description_placeholders={
WINDY_ENABLED: True,
WINDY_LOGGER_ENABLED: user_input[WINDY_LOGGER_ENABLED],
},
errors=errors, errors=errors,
) )
@ -165,6 +208,151 @@ class ConfigOptionsFlowHandler(OptionsFlow):
return self.async_create_entry(title=DOMAIN, data=user_input) return self.async_create_entry(title=DOMAIN, data=user_input)
async def async_step_migration(self, user_input=None):
"""Migrate sensors."""
errors = {}
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_sensor_select),
errors=errors,
description_placeholders={
"migration_status": "-",
"migration_count": "-",
},
)
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_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_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)
class ConfigFlow(ConfigFlow, domain=DOMAIN): class ConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Sencor SWS 12500 Weather Station.""" """Handle a config flow for Sencor SWS 12500 Weather Station."""

View File

@ -7,6 +7,7 @@ DOMAIN = "sws12500"
DEFAULT_URL = "/weatherstation/updateweatherstation.php" DEFAULT_URL = "/weatherstation/updateweatherstation.php"
WSLINK_URL = "/data/upload.php" WSLINK_URL = "/data/upload.php"
WINDY_URL = "https://stations.windy.com/pws/update/" WINDY_URL = "https://stations.windy.com/pws/update/"
DATABASE_PATH = "/config/home-assistant_v2.db"
ICON = "mdi:weather" ICON = "mdi:weather"
@ -14,6 +15,7 @@ API_KEY = "API_KEY"
API_ID = "API_ID" API_ID = "API_ID"
SENSORS_TO_LOAD: Final = "sensors_to_load" SENSORS_TO_LOAD: Final = "sensors_to_load"
SENSOR_TO_MIGRATE: Final = "sensor_to_migrate"
DEV_DBG: Final = "dev_debug_checkbox" DEV_DBG: Final = "dev_debug_checkbox"
WSLINK: Final = "wslink" WSLINK: Final = "wslink"
@ -60,11 +62,16 @@ BARO_PRESSURE: Final = "baro_pressure"
OUTSIDE_TEMP: Final = "outside_temp" OUTSIDE_TEMP: Final = "outside_temp"
DEW_POINT: Final = "dew_point" DEW_POINT: Final = "dew_point"
OUTSIDE_HUMIDITY: Final = "outside_humidity" OUTSIDE_HUMIDITY: Final = "outside_humidity"
OUTSIDE_CONNECTION: Final = "outside_connection"
WIND_SPEED: Final = "wind_speed" WIND_SPEED: Final = "wind_speed"
WIND_GUST: Final = "wind_gust" WIND_GUST: Final = "wind_gust"
WIND_DIR: Final = "wind_dir" WIND_DIR: Final = "wind_dir"
WIND_AZIMUT: Final = "wind_azimut" WIND_AZIMUT: Final = "wind_azimut"
RAIN: Final = "rain" 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" DAILY_RAIN: Final = "daily_rain"
SOLAR_RADIATION: Final = "solar_radiation" SOLAR_RADIATION: Final = "solar_radiation"
INDOOR_TEMP: Final = "indoor_temp" INDOOR_TEMP: Final = "indoor_temp"
@ -72,10 +79,13 @@ INDOOR_HUMIDITY: Final = "indoor_humidity"
UV: Final = "uv" UV: Final = "uv"
CH2_TEMP: Final = "ch2_temp" CH2_TEMP: Final = "ch2_temp"
CH2_HUMIDITY: Final = "ch2_humidity" CH2_HUMIDITY: Final = "ch2_humidity"
CH2_CONNECTION: Final = "ch2_connection"
CH3_TEMP: Final = "ch3_temp" CH3_TEMP: Final = "ch3_temp"
CH3_HUMIDITY: Final = "ch3_humidity" CH3_HUMIDITY: Final = "ch3_humidity"
CH3_CONNECTION: Final = "ch3_connection"
CH4_TEMP: Final = "ch4_temp" CH4_TEMP: Final = "ch4_temp"
CH4_HUMIDITY: Final = "ch4_humidity" CH4_HUMIDITY: Final = "ch4_humidity"
CH4_CONNECTION: Final = "ch4_connection"
HEAT_INDEX: Final = "heat_index" HEAT_INDEX: Final = "heat_index"
CHILL_INDEX: Final = "chill_index" CHILL_INDEX: Final = "chill_index"
@ -105,19 +115,37 @@ REMAP_ITEMS: dict = {
REMAP_WSLINK_ITEMS: dict = { REMAP_WSLINK_ITEMS: dict = {
"intem": INDOOR_TEMP, "intem": INDOOR_TEMP,
"inhum": INDOOR_HUMIDITY, "inhum": INDOOR_HUMIDITY,
"t1temp": OUTSIDE_TEMP, "t1tem": OUTSIDE_TEMP,
"t1hum": OUTSIDE_HUMIDITY, "t1hum": OUTSIDE_HUMIDITY,
"t1dew": DEW_POINT, "t1dew": DEW_POINT,
"t1wdir": WIND_DIR, "t1wdir": WIND_DIR,
"t1ws": WIND_SPEED, "t1ws": WIND_SPEED,
"t1wg": WIND_GUST, "t1wgust": WIND_GUST,
"t1rainra": RAIN, "t1rainra": RAIN,
"t1raindy": DAILY_RAIN, "t1raindy": DAILY_RAIN,
"t1solrad": SOLAR_RADIATION, "t1solrad": SOLAR_RADIATION,
"rbar": BARO_PRESSURE, "rbar": BARO_PRESSURE,
"uvi": UV "t1uvi": UV,
"t234c1tem": CH2_TEMP,
"t234c1hum": CH2_HUMIDITY,
"t1cn": OUTSIDE_CONNECTION,
"t234c1cn": CH2_CONNECTION,
"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)
# 't1bat': outdoor battery level (1 normal, 0 low)
# 't234c1bat': CH2 battery level (1 normal, 0 low) CH2 in integration is CH1 in WSLink
DISABLED_BY_DEFAULT: Final = [ DISABLED_BY_DEFAULT: Final = [
CH2_TEMP, CH2_TEMP,
CH2_HUMIDITY, CH2_HUMIDITY,
@ -167,4 +195,3 @@ AZIMUT: list[UnitOfDir] = [
UnitOfDir.NNW, UnitOfDir.NNW,
UnitOfDir.N, UnitOfDir.N,
] ]

View File

@ -10,6 +10,6 @@
"issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues", "issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues",
"requirements": [], "requirements": [],
"ssdp": [], "ssdp": [],
"version": "1.5.0", "version": "1.6.2",
"zeroconf": [] "zeroconf": []
} }

View File

@ -111,16 +111,26 @@ class WeatherSensor(
def native_value(self) -> str | int | float | None: def native_value(self) -> str | int | float | None:
"""Return value of entity.""" """Return value of entity."""
_wslink = self.coordinator.config.options.get(WSLINK)
if self.coordinator.data and (WIND_AZIMUT in self.entity_description.key): if self.coordinator.data and (WIND_AZIMUT in self.entity_description.key):
return self.entity_description.value_fn(self.coordinator.data.get(WIND_DIR)) return self.entity_description.value_fn(self.coordinator.data.get(WIND_DIR))
if self.coordinator.data and (HEAT_INDEX in self.entity_description.key): if (
self.coordinator.data
and (HEAT_INDEX in self.entity_description.key)
and not _wslink
):
return self.entity_description.value_fn(heat_index(self.coordinator.data)) return self.entity_description.value_fn(heat_index(self.coordinator.data))
if self.coordinator.data and (CHILL_INDEX in self.entity_description.key): if (
self.coordinator.data
and (CHILL_INDEX in self.entity_description.key)
and not _wslink
):
return self.entity_description.value_fn(chill_index(self.coordinator.data)) return self.entity_description.value_fn(chill_index(self.coordinator.data))
return self.entity_description.value_fn(self._data) return None if self._data == "" else self.entity_description.value_fn(self._data)
@property @property
def suggested_entity_id(self) -> str: def suggested_entity_id(self) -> str:

View File

@ -51,7 +51,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=INDOOR_TEMP, translation_key=INDOOR_TEMP,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=INDOOR_HUMIDITY, key=INDOOR_HUMIDITY,
@ -69,7 +69,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=OUTSIDE_TEMP, translation_key=OUTSIDE_TEMP,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=OUTSIDE_HUMIDITY, key=OUTSIDE_HUMIDITY,
@ -78,7 +78,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
translation_key=OUTSIDE_HUMIDITY, translation_key=OUTSIDE_HUMIDITY,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=DEW_POINT, key=DEW_POINT,
@ -87,7 +87,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer-lines", icon="mdi:thermometer-lines",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=DEW_POINT, translation_key=DEW_POINT,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=BARO_PRESSURE, key=BARO_PRESSURE,
@ -97,7 +97,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
suggested_unit_of_measurement=UnitOfPressure.HPA, suggested_unit_of_measurement=UnitOfPressure.HPA,
translation_key=BARO_PRESSURE, translation_key=BARO_PRESSURE,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_SPEED, key=WIND_SPEED,
@ -107,7 +107,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
icon="mdi:weather-windy", icon="mdi:weather-windy",
translation_key=WIND_SPEED, translation_key=WIND_SPEED,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_GUST, key=WIND_GUST,
@ -117,7 +117,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
icon="mdi:windsock", icon="mdi:windsock",
translation_key=WIND_GUST, translation_key=WIND_GUST,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_DIR, key=WIND_DIR,
@ -126,12 +126,12 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=None, suggested_display_precision=None,
icon="mdi:sign-direction", icon="mdi:sign-direction",
translation_key=WIND_DIR, translation_key=WIND_DIR,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_AZIMUT, key=WIND_AZIMUT,
icon="mdi:sign-direction", icon="mdi:sign-direction",
value_fn=lambda data: cast(str, wind_dir_to_text(data)), value_fn=lambda data: cast("str", wind_dir_to_text(data)),
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
options=list(UnitOfDir), options=list(UnitOfDir),
translation_key=WIND_AZIMUT, translation_key=WIND_AZIMUT,
@ -145,7 +145,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=RAIN, translation_key=RAIN,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=DAILY_RAIN, key=DAILY_RAIN,
@ -156,7 +156,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=DAILY_RAIN, translation_key=DAILY_RAIN,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=SOLAR_RADIATION, key=SOLAR_RADIATION,
@ -165,7 +165,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.IRRADIANCE, device_class=SensorDeviceClass.IRRADIANCE,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=SOLAR_RADIATION, translation_key=SOLAR_RADIATION,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=UV, key=UV,
@ -174,7 +174,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
native_unit_of_measurement=UV_INDEX, native_unit_of_measurement=UV_INDEX,
icon="mdi:sunglasses", icon="mdi:sunglasses",
translation_key=UV, translation_key=UV,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH2_TEMP, key=CH2_TEMP,
@ -184,7 +184,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH2_TEMP, translation_key=CH2_TEMP,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH2_HUMIDITY, key=CH2_HUMIDITY,
@ -193,7 +193,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH2_HUMIDITY, translation_key=CH2_HUMIDITY,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH3_TEMP, key=CH3_TEMP,
@ -203,7 +203,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH3_TEMP, translation_key=CH3_TEMP,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH3_HUMIDITY, key=CH3_HUMIDITY,
@ -212,7 +212,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH3_HUMIDITY, translation_key=CH3_HUMIDITY,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH4_TEMP, key=CH4_TEMP,
@ -222,7 +222,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH4_TEMP, translation_key=CH4_TEMP,
value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH4_HUMIDITY, key=CH4_HUMIDITY,
@ -231,7 +231,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH4_HUMIDITY, translation_key=CH4_HUMIDITY,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=HEAT_INDEX, key=HEAT_INDEX,
@ -239,9 +239,10 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=HEAT_INDEX, translation_key=HEAT_INDEX,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CHILL_INDEX, key=CHILL_INDEX,
@ -252,6 +253,6 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CHILL_INDEX, translation_key=CHILL_INDEX,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
), ),
) )

View File

@ -39,6 +39,10 @@ from .const import (
WIND_GUST, WIND_GUST,
WIND_SPEED, WIND_SPEED,
UnitOfDir, UnitOfDir,
MONTHLY_RAIN,
YEARLY_RAIN,
HOURLY_RAIN,
WEEKLY_RAIN,
) )
from .sensors_common import WeatherSensorEntityDescription from .sensors_common import WeatherSensorEntityDescription
from .utils import wind_dir_to_text from .utils import wind_dir_to_text
@ -138,10 +142,10 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=RAIN, key=RAIN,
native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
device_class=SensorDeviceClass.PRECIPITATION, device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL, state_class=SensorStateClass.TOTAL,
suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, suggested_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=RAIN, translation_key=RAIN,
@ -149,15 +153,59 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=DAILY_RAIN, key=DAILY_RAIN,
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, suggested_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS,
suggested_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=DAILY_RAIN, translation_key=DAILY_RAIN,
value_fn=lambda data: cast("float", data), 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=HOURLY_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=WEEKLY_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=MONTHLY_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=YEARLY_RAIN,
value_fn=lambda data: cast("float", data),
),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=SOLAR_RADIATION, key=SOLAR_RADIATION,
native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
@ -176,25 +224,25 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
translation_key=UV, translation_key=UV,
value_fn=lambda data: cast("float", data), value_fn=lambda data: cast("float", data),
), ),
# WeatherSensorEntityDescription( WeatherSensorEntityDescription(
# key=CH2_TEMP, key=CH2_TEMP,
# native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
# state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
# device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
# suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
# icon="mdi:weather-sunny", icon="mdi:weather-sunny",
# translation_key=CH2_TEMP, translation_key=CH2_TEMP,
# value_fn=lambda data: cast(float, data), value_fn=lambda data: cast("float", data),
# ), ),
# WeatherSensorEntityDescription( WeatherSensorEntityDescription(
# key=CH2_HUMIDITY, key=CH2_HUMIDITY,
# native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
# state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
# device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
# icon="mdi:weather-sunny", icon="mdi:weather-sunny",
# translation_key=CH2_HUMIDITY, translation_key=CH2_HUMIDITY,
# value_fn=lambda data: cast(int, data), value_fn=lambda data: cast("int", data),
# ), ),
# WeatherSensorEntityDescription( # WeatherSensorEntityDescription(
# key=CH3_TEMP, # key=CH3_TEMP,
# native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, # native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
@ -235,17 +283,18 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
# ), # ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=HEAT_INDEX, key=HEAT_INDEX,
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=HEAT_INDEX, translation_key=HEAT_INDEX,
value_fn=lambda data: cast("int", data), value_fn=lambda data: cast("int", data),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CHILL_INDEX, key=CHILL_INDEX,
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,

View File

@ -5,21 +5,27 @@
"valid_credentials_key": "Provide valid API KEY.", "valid_credentials_key": "Provide valid API KEY.",
"valid_credentials_match": "API ID and API KEY should not be the same." "valid_credentials_match": "API ID and API KEY should not be the same."
}, },
"step": { "step": {
"user": { "user": {
"description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant",
"title": "Configure access for Weather Station",
"data": { "data": {
"API_ID": "API ID / Station ID", "API_ID": "API ID / Station ID",
"API_KEY": "API KEY / Password", "API_KEY": "API KEY / Password",
"WSLINK": "WSLink API",
"dev_debug_checkbox": "Developer log" "dev_debug_checkbox": "Developer log"
}, },
"description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant",
"title": "Configure access for Weather Station",
"data_description": { "data_description": {
"dev_debug_checkbox": " Enable only if you want to send debuging data to the developer." "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."
} }
} }
} }
}, },
"options": { "options": {
"error": { "error": {
"valid_credentials_api": "Provide valid API ID.", "valid_credentials_api": "Provide valid API ID.",
@ -27,35 +33,58 @@
"valid_credentials_match": "API ID and API KEY should not be the same.", "valid_credentials_match": "API ID and API KEY should not be the same.",
"windy_key_required": "Windy API key is required if you want to enable this function." "windy_key_required": "Windy API key is required if you want to enable this function."
}, },
"step": { "step": {
"basic": {
"data": {
"API_ID": "API ID / Station ID",
"API_KEY": "API KEY / Password",
"dev_debug_checkbox": "Developer log"
},
"description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant",
"title": "Configure credentials",
"data_description": {
"dev_debug_checkbox": " Enable only if you want to send debuging data to the developer."
}
},
"init": { "init": {
"title": "Configure SWS12500 Integration",
"description": "Choose what do you want to configure. If basic access or resending data for Windy site", "description": "Choose what do you want to configure. If basic access or resending data for Windy site",
"menu_options": { "menu_options": {
"basic": "Basic - configure credentials for Weather Station", "basic": "Basic - configure credentials for Weather Station",
"windy": "Windy configuration" "windy": "Windy configuration"
}, }
"title": "Configure SWS12500 Integration"
}, },
"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"
},
"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."
}
},
"windy": { "windy": {
"description": "Resend weather data to your Windy stations.",
"title": "Configure Windy",
"data": { "data": {
"WINDY_API_KEY": "API KEY provided by Windy", "WINDY_API_KEY": "API KEY provided by Windy",
"windy_enabled_checkbox": "Enable resending data to Windy", "windy_enabled_checkbox": "Enable resending data to Windy",
"windy_logger_checkbox": "Log Windy data and responses" "windy_logger_checkbox": "Log Windy data and responses"
}, },
"description": "Resend weather data to your Windy stations.", "data_description": {
"title": "Configure Windy" "WINDY_API_KEY": "Windy API KEY obtained from https://https://api.windy.com/keys",
"windy_logger_checkbox": "Enable only if you want to send debuging data to the developer."
}
},
"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."
}
} }
} }
}, },
@ -77,6 +106,12 @@
"solar_radiation": { "name": "Solar irradiance" }, "solar_radiation": { "name": "Solar irradiance" },
"ch2_temp": { "name": "Channel 2 temperature" }, "ch2_temp": { "name": "Channel 2 temperature" },
"ch2_humidity": { "name": "Channel 2 humidity" }, "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" },
"heat_index": { "name": "Apparent temperature" },
"chill_index": { "name": "Wind chill" },
"wind_azimut": { "wind_azimut": {
"name": "Bearing", "name": "Bearing",
"state": { "state": {

View File

@ -7,20 +7,24 @@
}, },
"step": { "step": {
"user": { "user": {
"description": "Zadejte API ID a API KEY, aby meteostanice mohla komunikovat s HomeAssistantem",
"title": "Nastavení přihlášení",
"data": { "data": {
"API_ID": "API ID / ID stanice", "API_ID": "API ID / ID Stanice",
"API_KEY": "API KEY / Heslo", "API_KEY": "API KEY / Heslo",
"wslink": "WSLink API", "wslink": "WSLink API",
"dev_debug_checkbox": "Developer log" "dev_debug_checkbox": "Developer log"
}, },
"description": "Zadejte API ID a API KEY, aby meteostanice mohla komunikovat s HomeAssistantem",
"title": "Nastavení přístpu pro metostanici",
"data_description": { "data_description": {
"dev_debug_checkbox": " Zapnout pouze v případě, že chcete poslat ladící informace vývojáři." "dev_debug_checkbox": "Zapnout pouze v případě, že chcete poslat ladící informace vývojáři.",
"API_ID": "API ID je ID stanice, které jste nastavili v meteostanici.",
"API_KEY": "API KEY je heslo, které jste nastavili v meteostanici.",
"wslink": "WSLink API zapněte, pokud je stanice nastavena na zasílání dat přes WSLink."
} }
} }
} }
}, },
"options": { "options": {
"error": { "error": {
"valid_credentials_api": "Vyplňte platné API ID", "valid_credentials_api": "Vyplňte platné API ID",
@ -28,39 +32,63 @@
"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é!",
"windy_key_required": "Je vyžadován Windy API key, pokud chcete aktivovat přeposílání dat na Windy" "windy_key_required": "Je vyžadován Windy API key, pokud chcete aktivovat přeposílání dat na Windy"
}, },
"step": { "step": {
"init": {
"title": "Nastavení integrace SWS12500",
"description": "Vyberte, co chcete konfigurovat. Zda přihlašovací údaje nebo nastavení pro přeposílání dat na Windy.",
"menu_options": {
"basic": "Základní - přístupové údaje (přihlášení)",
"windy": "Nastavení pro přeposílání dat na Windy",
"migration": "Migrace statistiky senzoru"
}
},
"basic": { "basic": {
"description": "Zadejte API ID a API KEY, aby meteostanice mohla komunikovat s HomeAssistantem",
"title": "Nastavení přihlášení",
"data": { "data": {
"API_ID": "API ID / ID Stanice", "API_ID": "API ID / ID Stanice",
"API_KEY": "API KEY / Heslo", "API_KEY": "API KEY / Heslo",
"wslink": "WSLink API", "wslink": "WSLink API",
"dev_debug_checkbox": "Developer log" "dev_debug_checkbox": "Developer log"
}, },
"description": "Zadejte API ID a API KEY, aby meteostanice mohla komunikovat s HomeAssistantem",
"title": "Nastavení přihlášení",
"data_description": { "data_description": {
"dev_debug_checkbox": " Zapnout pouze v případě, že chcete poslat ladící informace vývojáři." "dev_debug_checkbox": "Zapnout pouze v případě, že chcete poslat ladící informace vývojáři.",
"API_ID": "API ID je ID stanice, které jste nastavili v meteostanici.",
"API_KEY": "API KEY je heslo, které jste nastavili v meteostanici.",
"wslink": "WSLink API zapněte, pokud je stanice nastavena na zasílání dat přes WSLink."
} }
}, },
"init": {
"description": "Vyberte, co chcete konfigurovat. Zda přihlašovací údaje nebo nastavení pro přeposílání dat na Windy.",
"menu_options": {
"basic": "Základní - přístupové údaje (přihlášení)",
"windy": "Nastavení pro přeposílání dat na Windy"
},
"title": "Nastavení integrace SWS12500"
},
"windy": { "windy": {
"description": "Přeposílání dat z metostanice na Windy",
"title": "Konfigurace Windy",
"data": { "data": {
"WINDY_API_KEY": "Klíč API KEY získaný z Windy", "WINDY_API_KEY": "Klíč API KEY získaný z Windy",
"windy_enabled_checkbox": "Povolit přeposílání dat na Windy", "windy_enabled_checkbox": "Povolit přeposílání dat na Windy",
"windy_logger_checkbox": "Logovat data a odpovědi z Windy" "windy_logger_checkbox": "Logovat data a odpovědi z Windy"
}, },
"description": "Přeposílání dat z metostanice na Windy", "data_description": {
"title": "Konfigurace Windy" "WINDY_API_KEY": "Klíč API KEY získaný z https://https://api.windy.com/keys",
"windy_logger_checkbox": "Zapnout pouze v případě, že chcete poslat ladící informace vývojáři."
}
},
"migration": {
"title": "Migrace statistiky senzoru.",
"description": "Pro správnou funkci dlouhodobé statistiky je nutné provést migraci jednotky senzoru v dlouhodobé statistice. Původní jednotka dlouhodobé statistiky pro denní úhrn srážek byla v mm/d, nicméně stanice zasílá pouze data v mm bez časového rozlišení.\n\n Senzor, který má být migrován je pro denní úhrn srážek. Pokud je v seznamu již správná hodnota u senzoru pro denní úhrn (mm), pak je již migrace hotová.\n\n Výsledek migrace pro senzor: {migration_status}, přepvedeno celkem {migration_count} řádků.",
"data": {
"sensor_to_migrate": "Senzor pro migraci",
"trigger_action": "Spustit migraci"
},
"data_description": {
"sensor_to_migrate": "Vyberte správný senzor pri migraci statistiky. \n Hodnoty senzoru budou zachovány, nepřepočítají se, pouze se změní jednotka v dlouhodobé statistice. ",
"trigger_action": "Po zaškrtnutí se spustí migrace statistiky senzoru."
}
} }
} }
}, },
"entity": { "entity": {
"sensor": { "sensor": {
"indoor_temp": { "name": "Vnitřní teplota" }, "indoor_temp": { "name": "Vnitřní teplota" },
@ -84,6 +112,10 @@
"ch4_humidity": { "name": "Vlhkost sensoru 4" }, "ch4_humidity": { "name": "Vlhkost sensoru 4" },
"heat_index": { "name": "Tepelný index" }, "heat_index": { "name": "Tepelný index" },
"chill_index": { "name": "Pocitová teplota" }, "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": { "wind_azimut": {
"name": "Azimut", "name": "Azimut",
"state": { "state": {

View File

@ -5,22 +5,27 @@
"valid_credentials_key": "Provide valid API KEY.", "valid_credentials_key": "Provide valid API KEY.",
"valid_credentials_match": "API ID and API KEY should not be the same." "valid_credentials_match": "API ID and API KEY should not be the same."
}, },
"step": { "step": {
"user": { "user": {
"description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant",
"title": "Configure access for Weather Station",
"data": { "data": {
"API_ID": "API ID / Station ID", "API_ID": "API ID / Station ID",
"API_KEY": "API KEY / Password", "API_KEY": "API KEY / Password",
"WSLINK": "WSLink API", "WSLINK": "WSLink API",
"dev_debug_checkbox": "Developer log" "dev_debug_checkbox": "Developer log"
}, },
"description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant",
"title": "Configure access for Weather Station",
"data_description": { "data_description": {
"dev_debug_checkbox": " Enable only if you want to send debuging data to the developer." "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."
} }
} }
} }
}, },
"options": { "options": {
"error": { "error": {
"valid_credentials_api": "Provide valid API ID.", "valid_credentials_api": "Provide valid API ID.",
@ -28,36 +33,58 @@
"valid_credentials_match": "API ID and API KEY should not be the same.", "valid_credentials_match": "API ID and API KEY should not be the same.",
"windy_key_required": "Windy API key is required if you want to enable this function." "windy_key_required": "Windy API key is required if you want to enable this function."
}, },
"step": { "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": { "basic": {
"description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant",
"title": "Configure credentials",
"data": { "data": {
"API_ID": "API ID / Station ID", "API_ID": "API ID / Station ID",
"API_KEY": "API KEY / Password", "API_KEY": "API KEY / Password",
"WSLINK": "WSLink API", "WSLINK": "WSLink API",
"dev_debug_checkbox": "Developer log" "dev_debug_checkbox": "Developer log"
}, },
"description": "Provide API ID and API KEY so the Weather Station can access HomeAssistant",
"title": "Configure credentials",
"data_description": { "data_description": {
"dev_debug_checkbox": " Enable only if you want to send debuging data to the developer." "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."
} }
}, },
"init": {
"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"
},
"title": "Configure SWS12500 Integration"
},
"windy": { "windy": {
"description": "Resend weather data to your Windy stations.",
"title": "Configure Windy",
"data": { "data": {
"WINDY_API_KEY": "API KEY provided by Windy", "WINDY_API_KEY": "API KEY provided by Windy",
"windy_enabled_checkbox": "Enable resending data to Windy", "windy_enabled_checkbox": "Enable resending data to Windy",
"windy_logger_checkbox": "Log Windy data and responses" "windy_logger_checkbox": "Log Windy data and responses"
}, },
"description": "Resend weather data to your Windy stations.", "data_description": {
"title": "Configure Windy" "WINDY_API_KEY": "Windy API KEY obtained from https://https://api.windy.com/keys",
"windy_logger_checkbox": "Enable only if you want to send debuging data to the developer."
}
},
"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."
}
} }
} }
}, },
@ -85,6 +112,10 @@
"ch4_humidity": { "name": "Channel 4 humidity" }, "ch4_humidity": { "name": "Channel 4 humidity" },
"heat_index": { "name": "Apparent temperature" }, "heat_index": { "name": "Apparent temperature" },
"chill_index": { "name": "Wind chill" }, "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": { "wind_azimut": {
"name": "Bearing", "name": "Bearing",
"state": { "state": {

View File

@ -2,18 +2,25 @@
import logging import logging
import math import math
from pathlib import Path
import sqlite3
from typing import Any from typing import Any
import numpy as np import numpy as np
from homeassistant.components import persistent_notification from homeassistant.components import persistent_notification
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTemperature from homeassistant.const import (
UnitOfPrecipitationDepth,
UnitOfTemperature,
UnitOfVolumetricFlux,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.translation import async_get_translations from homeassistant.helpers.translation import async_get_translations
from .const import ( from .const import (
AZIMUT, AZIMUT,
DATABASE_PATH,
DEV_DBG, DEV_DBG,
OUTSIDE_HUMIDITY, OUTSIDE_HUMIDITY,
OUTSIDE_TEMP, OUTSIDE_TEMP,
@ -131,9 +138,7 @@ def remap_wslink_items(entities):
def loaded_sensors(config_entry: ConfigEntry) -> list | None: def loaded_sensors(config_entry: ConfigEntry) -> list | None:
"""Get loaded sensors.""" """Get loaded sensors."""
return ( return config_entry.options.get(SENSORS_TO_LOAD) or []
config_entry.options.get(SENSORS_TO_LOAD) or []
)
def check_disabled( def check_disabled(
@ -176,13 +181,30 @@ def wind_dir_to_text(deg: float) -> UnitOfDir | None:
return None return None
def heat_index(data: Any) -> UnitOfTemperature: def fahrenheit_to_celsius(fahrenheit: float) -> float:
"""Calculate heat index from temperature.""" """Convert Fahrenheit to Celsius."""
return (fahrenheit - 32) * 5.0 / 9.0
def celsius_to_fahrenheit(celsius: float) -> float:
"""Convert Celsius to Fahrenheit."""
return celsius * 9.0 / 5.0 + 32
def heat_index(data: Any, convert: bool = False) -> UnitOfTemperature:
"""Calculate heat index from temperature.
data: dict with temperature and humidity
convert: bool, convert recieved data from Celsius to Fahrenheit
"""
temp = float(data[OUTSIDE_TEMP]) temp = float(data[OUTSIDE_TEMP])
rh = float(data[OUTSIDE_HUMIDITY]) rh = float(data[OUTSIDE_HUMIDITY])
adjustment = None adjustment = None
if convert:
temp = celsius_to_fahrenheit(temp)
simple = 0.5 * (temp + 61.0 + ((temp - 68.0) * 1.2) + (rh * 0.094)) simple = 0.5 * (temp + 61.0 + ((temp - 68.0) * 1.2) + (rh * 0.094))
if ((simple + temp) / 2) > 80: if ((simple + temp) / 2) > 80:
full_index = ( full_index = (
@ -207,12 +229,19 @@ def heat_index(data: Any) -> UnitOfTemperature:
return simple return simple
def chill_index(data: Any) -> UnitOfTemperature: def chill_index(data: Any, convert: bool = False) -> UnitOfTemperature:
"""Calculate wind chill index.""" """Calculate wind chill index from temperature and wind speed.
data: dict with temperature and wind speed
convert: bool, convert recieved data from Celsius to Fahrenheit
"""
temp = float(data[OUTSIDE_TEMP]) temp = float(data[OUTSIDE_TEMP])
wind = float(data[WIND_SPEED]) wind = float(data[WIND_SPEED])
if convert:
temp = celsius_to_fahrenheit(temp)
return ( return (
round( round(
( (
@ -225,3 +254,107 @@ def chill_index(data: Any) -> UnitOfTemperature:
if temp < 50 and wind > 3 if temp < 50 and wind > 3
else temp else temp
) )
def long_term_units_in_statistics_meta():
"""Get units in long term statitstics."""
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:
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
}
except sqlite3.Error as e:
_LOGGER.error("Error during data migration: %s", e)
finally:
conn.close()
return sensor_units
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
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

View File

@ -0,0 +1,17 @@
"""Shared keys and helpers for storing integration runtime state in hass.data.
This integration uses `hass.data[DOMAIN][entry_id]` as a per-entry dictionary.
Keeping keys in one place prevents subtle bugs where different modules store
different value types under the same key.
"""
from __future__ import annotations
from typing import Final
# Per-entry dict keys stored under hass.data[DOMAIN][entry_id]
ENTRY_COORDINATOR: Final[str] = "coordinator"
ENTRY_ADD_ENTITIES: Final[str] = "async_add_entities"
ENTRY_DESCRIPTIONS: Final[str] = "sensor_descriptions"
ENTRY_LAST_OPTIONS: Final[str] = "last_options"