chore(quality): manifest keys, ConfigEntryNotReady, remove dead sqlite code
- manifest: add single_config_entry, integration_type "device", loggers ["aioecowitt"]; drop empty homekit/ssdp/zeroconf placeholders. - Q10: raise ConfigEntryNotReady (not PlatformNotReady) when route registration fails in async_setup_entry; drop the now-unused import. - Q11: delete the ~105-line commented-out sqlite3 statistics-migration block in utils.py (would have been blocking I/O in the event loop) and its dead import. Deferred (with reason): quality_scale (needs a rule audit + quality_scale.yaml), device-identifier 2-tuple and suggested_entity_id (entity-naming/identity changes, to land with the planned integration rename), CoordinatorEntity generics in sensor.py (would introduce a circular import - intentionally untyped). Note: the health-probe protocol gate was reverted - the proxy add-on can front WU/WSLink/Ecowitt, so polling must stay unconditional. 308 passing, 100% coverage; ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent
418d0c7546
commit
df8e503d2c
|
|
@ -36,7 +36,7 @@ from py_typecheck import checked, checked_or
|
||||||
|
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady, PlatformNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
|
@ -178,7 +178,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: SWSConfigEntry) -> bool:
|
||||||
else:
|
else:
|
||||||
if not register_path(hass, coordinator, coordinator_health, entry):
|
if not register_path(hass, coordinator, coordinator_health, entry):
|
||||||
_LOGGER.error("Fatal: path not registered!")
|
_LOGGER.error("Fatal: path not registered!")
|
||||||
raise PlatformNotReady
|
raise ConfigEntryNotReady("Webhook routes could not be registered")
|
||||||
|
|
||||||
routes = hass.data[DOMAIN].get("routes")
|
routes = hass.data[DOMAIN].get("routes")
|
||||||
if routes is not None:
|
if routes is not None:
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,11 @@ class HealthCoordinator(DataUpdateCoordinator):
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_update_data(self) -> dict[str, Any]:
|
async def _async_update_data(self) -> dict[str, Any]:
|
||||||
"""Refresh add-on health metadata from the WSLink proxy."""
|
"""Refresh add-on health metadata from the WSLink proxy.
|
||||||
|
|
||||||
|
The proxy add-on can front any protocol (WU / WSLink / Ecowitt), so the probe
|
||||||
|
is not gated on a specific protocol option - it always runs.
|
||||||
|
"""
|
||||||
session = async_get_clientsession(self.hass, False)
|
session = async_get_clientsession(self.hass, False)
|
||||||
url = get_url(self.hass)
|
url = get_url(self.hass)
|
||||||
ip = await async_get_source_ip(self.hass)
|
ip = await async_get_source_ip(self.hass)
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,16 @@
|
||||||
"http"
|
"http"
|
||||||
],
|
],
|
||||||
"documentation": "https://github.com/schizza/SWS-12500-custom-component",
|
"documentation": "https://github.com/schizza/SWS-12500-custom-component",
|
||||||
"homekit": {},
|
"integration_type": "device",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues",
|
"issue_tracker": "https://github.com/schizza/SWS-12500-custom-component/issues",
|
||||||
|
"loggers": [
|
||||||
|
"aioecowitt"
|
||||||
|
],
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"typecheck-runtime==0.2.0",
|
"typecheck-runtime==0.2.0",
|
||||||
"aioecowitt==2025.9.2"
|
"aioecowitt==2025.9.2"
|
||||||
],
|
],
|
||||||
"ssdp": [],
|
"single_config_entry": true,
|
||||||
"version": "2.0.0-pre1",
|
"version": "2.0.0-pre1"
|
||||||
"zeroconf": []
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ from homeassistant.helpers.translation import async_get_translations
|
||||||
from .const import (
|
from .const import (
|
||||||
AZIMUT,
|
AZIMUT,
|
||||||
CONNECTION_GATED_SENSORS,
|
CONNECTION_GATED_SENSORS,
|
||||||
# DATABASE_PATH,
|
|
||||||
DEV_DBG,
|
DEV_DBG,
|
||||||
OUTSIDE_HUMIDITY,
|
OUTSIDE_HUMIDITY,
|
||||||
OUTSIDE_TEMP,
|
OUTSIDE_TEMP,
|
||||||
|
|
@ -397,110 +396,3 @@ def battery_5step_to_pct(value: str) -> int | None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return round(int(value) / 5 * 100)
|
return round(int(value) / 5 * 100)
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# def long_term_units_in_statistics_meta():
|
|
||||||
# """Get units in long term statitstics."""
|
|
||||||
# sensor_units = []
|
|
||||||
# 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) -> int | 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
|
|
||||||
|
|
|
||||||
|
|
@ -199,8 +199,8 @@ async def test_async_setup_entry_creates_runtime_data_and_forwards_platforms(
|
||||||
async def test_async_setup_entry_fatal_when_register_path_returns_false(
|
async def test_async_setup_entry_fatal_when_register_path_returns_false(
|
||||||
hass_with_http, monkeypatch
|
hass_with_http, monkeypatch
|
||||||
):
|
):
|
||||||
"""Cover the fatal branch when `register_path` returns False -> PlatformNotReady."""
|
"""Cover the fatal branch when `register_path` returns False -> ConfigEntryNotReady."""
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
|
|
@ -223,7 +223,7 @@ async def test_async_setup_entry_fatal_when_register_path_returns_false(
|
||||||
AsyncMock(return_value=True),
|
AsyncMock(return_value=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(PlatformNotReady):
|
with pytest.raises(ConfigEntryNotReady):
|
||||||
await async_setup_entry(hass_with_http, entry)
|
await async_setup_entry(hass_with_http, entry)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue