Compare commits
20 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
ee7be37ba6 | |
|
|
8034ada12f | |
|
|
99d25bfd56 | |
|
|
af87fd0719 | |
|
|
0ab5321170 | |
|
|
fdcd28f96a | |
|
|
093a7915f0 | |
|
|
ea0a5e34e3 | |
|
|
fdd4cacddf | |
|
|
601d1f3984 | |
|
|
287e74673e | |
|
|
397e44e5f2 | |
|
|
f9d80d7a00 | |
|
|
2b57eeb4fa | |
|
|
dc5c45483e | |
|
|
3d112757d0 | |
|
|
189777fbdb | |
|
|
8e97fc7404 | |
|
|
908a60ade4 | |
|
|
47143d9ac2 |
|
|
@ -0,0 +1,5 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: schizza
|
||||
ko_fi: schizza
|
||||
buy_me_a_coffee: schizza
|
||||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -1,12 +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
|
||||
import homeassistant.helpers.entity_registry as er
|
||||
|
||||
from .const import (
|
||||
API_ID,
|
||||
|
|
@ -14,12 +17,18 @@ from .const import (
|
|||
DEV_DBG,
|
||||
DOMAIN,
|
||||
INVALID_CREDENTIALS,
|
||||
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):
|
||||
|
|
@ -39,15 +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] = {
|
||||
|
|
@ -58,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] = {
|
||||
|
|
@ -80,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,
|
||||
|
|
@ -90,15 +107,46 @@ class ConfigOptionsFlowHandler(OptionsFlow):
|
|||
): 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):
|
||||
"""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):
|
||||
"""Manage basic options - credentials."""
|
||||
errors = {}
|
||||
|
||||
self._get_entry_data()
|
||||
await self._get_entry_data()
|
||||
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
|
|
@ -135,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(
|
||||
|
|
@ -148,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,
|
||||
)
|
||||
|
||||
|
|
@ -160,6 +208,151 @@ class ConfigOptionsFlowHandler(OptionsFlow):
|
|||
|
||||
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):
|
||||
"""Handle a config flow for Sencor SWS 12500 Weather Station."""
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ DOMAIN = "sws12500"
|
|||
DEFAULT_URL = "/weatherstation/updateweatherstation.php"
|
||||
WSLINK_URL = "/data/upload.php"
|
||||
WINDY_URL = "https://stations.windy.com/pws/update/"
|
||||
DATABASE_PATH = "/config/home-assistant_v2.db"
|
||||
|
||||
ICON = "mdi:weather"
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ API_KEY = "API_KEY"
|
|||
API_ID = "API_ID"
|
||||
|
||||
SENSORS_TO_LOAD: Final = "sensors_to_load"
|
||||
SENSOR_TO_MIGRATE: Final = "sensor_to_migrate"
|
||||
|
||||
DEV_DBG: Final = "dev_debug_checkbox"
|
||||
WSLINK: Final = "wslink"
|
||||
|
|
@ -66,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"
|
||||
|
|
@ -127,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
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@
|
|||
"issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues",
|
||||
"requirements": [],
|
||||
"ssdp": [],
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.2",
|
||||
"zeroconf": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
|
|||
),
|
||||
WeatherSensorEntityDescription(
|
||||
key=OUTSIDE_TEMP,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
icon="mdi:thermometer",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ from .const import (
|
|||
WIND_GUST,
|
||||
WIND_SPEED,
|
||||
UnitOfDir,
|
||||
MONTHLY_RAIN,
|
||||
YEARLY_RAIN,
|
||||
HOURLY_RAIN,
|
||||
WEEKLY_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=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
suggested_display_precision=2,
|
||||
icon="mdi:weather-pouring",
|
||||
translation_key=RAIN,
|
||||
|
|
@ -149,15 +153,59 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
|
|||
),
|
||||
WeatherSensorEntityDescription(
|
||||
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,
|
||||
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
||||
suggested_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
|
||||
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=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(
|
||||
key=SOLAR_RADIATION,
|
||||
native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
@ -74,6 +73,18 @@
|
|||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@
|
|||
"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"
|
||||
"windy": "Nastavení pro přeposílání dat na Windy",
|
||||
"migration": "Migrace statistiky senzoru"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -72,6 +73,18 @@
|
|||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -99,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": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
@ -74,6 +73,18 @@
|
|||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -101,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": {
|
||||
|
|
|
|||
|
|
@ -2,18 +2,25 @@
|
|||
|
||||
import logging
|
||||
import math
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
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.helpers.translation import async_get_translations
|
||||
|
||||
from .const import (
|
||||
AZIMUT,
|
||||
DATABASE_PATH,
|
||||
DEV_DBG,
|
||||
OUTSIDE_HUMIDITY,
|
||||
OUTSIDE_TEMP,
|
||||
|
|
@ -247,3 +254,107 @@ def chill_index(data: Any, convert: bool = False) -> UnitOfTemperature:
|
|||
if temp < 50 and wind > 3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
Loading…
Reference in New Issue