From 6644b9e4a83d0407c3ad01fd4b1ead8780f12f14 Mon Sep 17 00:00:00 2001 From: Home Assistant Version Control Date: Thu, 20 Aug 2026 15:12:43 +0000 Subject: [PATCH] 19 files --- .../__pycache__/__init__.cpython-314.pyc | Bin 9672 -> 0 bytes .../__pycache__/config_flow.cpython-314.pyc | Bin 15130 -> 0 bytes .../__pycache__/const.cpython-314.pyc | Bin 10065 -> 0 bytes .../forecast_models.cpython-314.pyc | Bin 11110 -> 0 bytes .../__pycache__/sensor.cpython-314.pyc | Bin 43051 -> 0 bytes ...weather.cpython-314.opt-beartype0v22v9.pyc | Bin 24449 -> 0 bytes .../__pycache__/weather.cpython-314.pyc | Bin 24449 -> 0 bytes ...weather_update_coordinator.cpython-314.pyc | Bin 5752 -> 0 bytes .../pirateweather/config_flow.py | 14 +- .../pirateweather/forecast_models.py | 16 +-- custom_components/pirateweather/manifest.json | 2 +- custom_components/pirateweather/sensor.py | 124 +++++++++--------- custom_components/pirateweather/weather.py | 28 ++-- .../weather_update_coordinator.py | 28 ++-- .../versatile_thermostat/base_thermostat.py | 108 ++++++++++++++- .../versatile_thermostat/climate.py | 2 +- .../versatile_thermostat/manifest.json | 4 +- .../versatile_thermostat/services.yaml | 2 +- .../thermostat_climate.py | 67 +++++++++- 19 files changed, 285 insertions(+), 110 deletions(-) delete mode 100644 custom_components/pirateweather/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/pirateweather/__pycache__/config_flow.cpython-314.pyc delete mode 100644 custom_components/pirateweather/__pycache__/const.cpython-314.pyc delete mode 100644 custom_components/pirateweather/__pycache__/forecast_models.cpython-314.pyc delete mode 100644 custom_components/pirateweather/__pycache__/sensor.cpython-314.pyc delete mode 100644 custom_components/pirateweather/__pycache__/weather.cpython-314.opt-beartype0v22v9.pyc delete mode 100644 custom_components/pirateweather/__pycache__/weather.cpython-314.pyc delete mode 100644 custom_components/pirateweather/__pycache__/weather_update_coordinator.cpython-314.pyc diff --git a/custom_components/pirateweather/__pycache__/__init__.cpython-314.pyc b/custom_components/pirateweather/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index cbf42d254798be1d3903ca965322febdf9188c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9672 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 diff --git a/custom_components/pirateweather/__pycache__/config_flow.cpython-314.pyc b/custom_components/pirateweather/__pycache__/config_flow.cpython-314.pyc deleted file mode 100644 index de71e49ad5a9d2f50083e3e1863edbf54ae59606..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15130 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 sOq<_Ttu&}gz@E7K+ZGweRXrb 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 Wjmns^?@RSR_s~B6K7~QD-u@5enSU1m diff --git a/custom_components/pirateweather/__pycache__/weather.cpython-314.pyc b/custom_components/pirateweather/__pycache__/weather.cpython-314.pyc deleted file mode 100644 index e7488745dae43771af08bd05696d9b13c86827a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24449 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 Wjmns^?@RSR_s~B6K7~QD-u@5enSU1m diff --git a/custom_components/pirateweather/__pycache__/weather_update_coordinator.cpython-314.pyc b/custom_components/pirateweather/__pycache__/weather_update_coordinator.cpython-314.pyc deleted file mode 100644 index 56c3dbb3a90278c0457440db523477ab32aec0a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5752 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 diff --git a/custom_components/pirateweather/config_flow.py b/custom_components/pirateweather/config_flow.py index 52d45bb5..399886f7 100644 --- a/custom_components/pirateweather/config_flow.py +++ b/custom_components/pirateweather/config_flow.py @@ -105,9 +105,9 @@ class PirateWeatherConfigFlow(ConfigFlow, domain=DOMAIN): if user_input is not None: latitude = user_input[CONF_LATITUDE] longitude = user_input[CONF_LONGITUDE] - forecastMode = "daily" - forecastPlatform = user_input[PW_PLATFORM] - entityNamee = user_input[CONF_NAME] + forecast_mode = "daily" + forecast_platform = user_input[PW_PLATFORM] + entity_name = user_input[CONF_NAME] endpoint = user_input[CONF_ENDPOINT] # Convert scan interval to timedelta @@ -125,7 +125,7 @@ class PirateWeatherConfigFlow(ConfigFlow, domain=DOMAIN): # Unique value includes the location and forcastHours/ forecastDays to seperate WeatherEntity/ Sensor # await self.async_set_unique_id(f"pw-{latitude}-{longitude}-{forecastDays}-{forecastHours}-{forecastMode}-{entityNamee}") await self.async_set_unique_id( - f"pw-{latitude}-{longitude}-{forecastPlatform}-{forecastMode}-{entityNamee}" + f"pw-{latitude}-{longitude}-{forecast_platform}-{forecast_mode}-{entity_name}" ) self._abort_if_unique_id_configured() @@ -328,8 +328,10 @@ class PirateWeatherOptionsFlow(OptionsFlow): async def _is_pw_api_online(hass, api_key, lat, lon, endpoint): - forecastString = endpoint + "/forecast/" + api_key + "/" + str(lat) + "," + str(lon) + forecast_string = ( + endpoint + "/forecast/" + api_key + "/" + str(lat) + "," + str(lon) + ) session = async_get_clientsession(hass) - async with session.get(forecastString) as resp: + async with session.get(forecast_string) as resp: return resp.status diff --git a/custom_components/pirateweather/forecast_models.py b/custom_components/pirateweather/forecast_models.py index 496f2659..74bd0033 100644 --- a/custom_components/pirateweather/forecast_models.py +++ b/custom_components/pirateweather/forecast_models.py @@ -26,8 +26,8 @@ class Forecast(UnicodeMixin): self.json = data self._alerts = [] - for alertJSON in self.json.get("alerts", []): - self._alerts.append(Alert(alertJSON)) + for alert_json in self.json.get("alerts", []): + self._alerts.append(Alert(alert_json)) def update(self): """Update the forecast data by making a new request to the same URL.""" @@ -135,24 +135,24 @@ class PirateWeatherFlagsBlock(UnicodeMixin): class PirateWeatherDataPoint(UnicodeMixin): """Represent a single data point in a weather forecast, such as an hourly or daily data point.""" - def __init__(self, d={}): + def __init__(self, d=None): """Initialize the data point with timestamp and weather information.""" - self.d = d + self.d = d or {} try: - self.time = datetime.datetime.fromtimestamp(int(d["time"])) - self.utime = d["time"] + self.time = datetime.datetime.fromtimestamp(int(self.d["time"])) + self.utime = self.d["time"] except KeyError: pass try: - sr_time = int(d["sunriseTime"]) + sr_time = int(self.d["sunriseTime"]) self.sunriseTime = datetime.datetime.fromtimestamp(sr_time) except KeyError: self.sunriseTime = None try: - ss_time = int(d["sunsetTime"]) + ss_time = int(self.d["sunsetTime"]) self.sunsetTime = datetime.datetime.fromtimestamp(ss_time) except KeyError: self.sunsetTime = None diff --git a/custom_components/pirateweather/manifest.json b/custom_components/pirateweather/manifest.json index 901bb02e..34a06aa1 100644 --- a/custom_components/pirateweather/manifest.json +++ b/custom_components/pirateweather/manifest.json @@ -10,5 +10,5 @@ "integration_type": "service", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/alexander0042/pirate-weather-ha/issues", - "version": "1.9.0" + "version": "1.9.1" } diff --git a/custom_components/pirateweather/sensor.py b/custom_components/pirateweather/sensor.py index d4c5bc09..445566a6 100644 --- a/custom_components/pirateweather/sensor.py +++ b/custom_components/pirateweather/sensor.py @@ -17,7 +17,6 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import ( ATTR_ATTRIBUTION, - CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, @@ -27,6 +26,7 @@ from homeassistant.const import ( DEGREE, PERCENTAGE, UV_INDEX, + UnitOfDensity, UnitOfIrradiance, UnitOfLength, UnitOfPressure, @@ -451,11 +451,11 @@ SENSOR_TYPES: dict[str, PirateWeatherSensorEntityDescription] = { name="Smoke", device_class=SensorDeviceClass.PM25, state_class=SensorStateClass.MEASUREMENT, - si_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - us_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ca_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - uk_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - uk2_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + si_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + us_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + ca_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + uk_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + uk2_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, suggested_display_precision=2, icon="mdi:smoke", forecast_mode=["currently", "hourly"], @@ -465,11 +465,11 @@ SENSOR_TYPES: dict[str, PirateWeatherSensorEntityDescription] = { name="Smoke Max", device_class=SensorDeviceClass.PM25, state_class=SensorStateClass.MEASUREMENT, - si_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - us_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ca_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - uk_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - uk2_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + si_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + us_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + ca_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + uk_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, + uk2_unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER, suggested_display_precision=2, icon="mdi:smoke", forecast_mode=["daily"], @@ -1034,22 +1034,22 @@ async def async_setup_entry( service_id = config_entry.unique_id or config_entry.entry_id # Round Output - outputRound = domain_data[PW_ROUND] + output_round = domain_data[PW_ROUND] sensors: list[PirateWeatherSensor] = [] for condition in conditions: # Save units for conversion later - requestUnits = domain_data[CONF_UNITS] + request_units = domain_data[CONF_UNITS] - sensorDescription = SENSOR_TYPES[condition] + sensor_description = SENSOR_TYPES[condition] if condition in DEPRECATED_SENSOR_TYPES: _LOGGER.warning("Monitored condition %s is deprecated", condition) if ( - not sensorDescription.forecast_mode - or "currently" in sensorDescription.forecast_mode + not sensor_description.forecast_mode + or "currently" in sensor_description.forecast_mode ): unique_id = f"{config_entry.unique_id}-sensor-{condition}" sensors.append( @@ -1060,14 +1060,14 @@ async def async_setup_entry( unique_id, forecast_day=None, forecast_hour=None, - description=sensorDescription, - requestUnits=requestUnits, - outputRound=outputRound, + description=sensor_description, + request_units=request_units, + output_round=output_round, service_id=service_id, ) ) - if forecast_days is not None and "daily" in sensorDescription.forecast_mode: + if forecast_days is not None and "daily" in sensor_description.forecast_mode: for forecast_day in forecast_days: unique_id = ( f"{config_entry.unique_id}-sensor-{condition}-daily-{forecast_day}" @@ -1080,14 +1080,14 @@ async def async_setup_entry( unique_id, forecast_day=int(forecast_day), forecast_hour=None, - description=sensorDescription, - requestUnits=requestUnits, - outputRound=outputRound, + description=sensor_description, + request_units=request_units, + output_round=output_round, service_id=service_id, ) ) - if forecast_hours is not None and "hourly" in sensorDescription.forecast_mode: + if forecast_hours is not None and "hourly" in sensor_description.forecast_mode: for forecast_h in forecast_hours: unique_id = ( f"{config_entry.unique_id}-sensor-{condition}-hourly-{forecast_h}" @@ -1100,9 +1100,9 @@ async def async_setup_entry( unique_id, forecast_day=None, forecast_hour=int(forecast_h), - description=sensorDescription, - requestUnits=requestUnits, - outputRound=outputRound, + description=sensor_description, + request_units=request_units, + output_round=output_round, service_id=service_id, ) ) @@ -1117,7 +1117,7 @@ class PirateWeatherSensor(SensorEntity): _attr_attribution = ATTRIBUTION entity_description: PirateWeatherSensorEntityDescription - def __init__( + def __init__( # noqa: PLR0917 self, weather_coordinator: WeatherUpdateCoordinator, condition: str, @@ -1126,8 +1126,8 @@ class PirateWeatherSensor(SensorEntity): forecast_day: int, forecast_hour: int, description: PirateWeatherSensorEntityDescription, - requestUnits: str, - outputRound: str, + request_units: str, + output_round: str, service_id: str, ) -> None: """Initialize the sensor.""" @@ -1149,8 +1149,8 @@ class PirateWeatherSensor(SensorEntity): self.forecast_day = forecast_day self.forecast_hour = forecast_hour - self.requestUnits = requestUnits - self.outputRound = outputRound + self.request_units = request_units + self.output_round = output_round self.type = condition self._icon = None self._alerts = None @@ -1188,7 +1188,7 @@ class PirateWeatherSensor(SensorEntity): @property def unit_system(self): """Return the unit system of this entity.""" - return self.requestUnits + return self.request_units @property def entity_picture(self) -> str | None: @@ -1223,11 +1223,11 @@ class PirateWeatherSensor(SensorEntity): def extra_state_attributes(self): """Return the state attributes.""" if self.type == "alerts": - extraATTR = self._alerts - extraATTR[ATTR_ATTRIBUTION] = ATTRIBUTION + extra_attr = self._alerts + extra_attr[ATTR_ATTRIBUTION] = ATTRIBUTION else: - extraATTR = {ATTR_ATTRIBUTION: ATTRIBUTION} - return extraATTR + extra_attr = {ATTR_ATTRIBUTION: ATTRIBUTION} + return extra_attr @property def native_value(self) -> StateType: @@ -1249,15 +1249,15 @@ class PirateWeatherSensor(SensorEntity): dkey = f"{attr}_{i!s}" else: dkey = attr - alertsAttr = getattr(alert, attr) + alerts_attr = getattr(alert, attr) # Convert time to string using dt_util - if isinstance(alertsAttr, int): - alertsAttr = dt_util.as_local( - dt_util.utc_from_timestamp(alertsAttr) + if isinstance(alerts_attr, int): + alerts_attr = dt_util.as_local( + dt_util.utc_from_timestamp(alerts_attr) ).isoformat() - alerts[dkey] = alertsAttr + alerts[dkey] = alerts_attr self._alerts = alerts native_val = len(data) @@ -1342,12 +1342,12 @@ class PirateWeatherSensor(SensorEntity): self._icon = getattr(data, "icon", "") # If output rounding is requested, round to nearest integer - if self.outputRound == "Yes": - roundingVal = 0 - roundingPrecip = 2 + if self.output_round == "Yes": + rounding_val = 0 + rounding_precip = 2 else: - roundingVal = 2 - roundingPrecip = 4 + rounding_val = 2 + rounding_precip = 4 # Some state data needs to be rounded to whole values or converted to # percentages @@ -1364,10 +1364,10 @@ class PirateWeatherSensor(SensorEntity): "sunset_time", "time", ]: - outState = datetime.datetime.fromtimestamp(state, datetime.UTC) + out_state = datetime.datetime.fromtimestamp(state, datetime.UTC) elif self.type == "fire_risk_level": - outState = fire_index(state) + out_state = fire_index(state) elif self.type in [ "dew_point", "temperature", @@ -1395,10 +1395,10 @@ class PirateWeatherSensor(SensorEntity): "solar", "solar_max", ]: - if roundingVal == 0: - outState = int(round(state, roundingVal)) + if rounding_val == 0: + out_state = int(round(state, rounding_val)) else: - outState = round(state, roundingVal) + out_state = round(state, rounding_val) elif self.type in [ "precip_accumulation", @@ -1423,12 +1423,12 @@ class PirateWeatherSensor(SensorEntity): and self.unit_system != "us" ): state = state * 10 - outState = round(state, roundingPrecip) + out_state = round(state, rounding_precip) else: - outState = state + out_state = state - return outState + return out_state async def async_added_to_hass(self) -> None: """Connect to dispatcher listening for entity data notifications.""" @@ -1455,16 +1455,16 @@ def fire_index(fire_index): """Convert numeric fire index to a textual value.""" if fire_index == -999: - outState = "N/A" + out_state = "N/A" elif fire_index >= 30: - outState = "Extreme" + out_state = "Extreme" elif fire_index >= 20: - outState = "Very High" + out_state = "Very High" elif fire_index >= 10: - outState = "High" + out_state = "High" elif fire_index >= 5: - outState = "Moderate" + out_state = "Moderate" else: - outState = "Low" + out_state = "Low" - return outState + return out_state diff --git a/custom_components/pirateweather/weather.py b/custom_components/pirateweather/weather.py index e6ac0f28..6ae5c55d 100644 --- a/custom_components/pirateweather/weather.py +++ b/custom_components/pirateweather/weather.py @@ -267,10 +267,10 @@ async def async_setup_entry( service_id = config_entry.unique_id or config_entry.entry_id # Round Output - outputRound = domain_data[PW_ROUND] + output_round = domain_data[PW_ROUND] pw_weather = PirateWeather( - name, unique_id, forecast_mode, weather_coordinator, outputRound, service_id + name, unique_id, forecast_mode, weather_coordinator, output_round, service_id ) async_add_entities([pw_weather], False) @@ -288,13 +288,13 @@ class PirateWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordinator]): | WeatherEntityFeature.FORECAST_HOURLY ) - def __init__( + def __init__( # noqa: PLR0917 self, name: str, unique_id, forecast_mode: str, weather_coordinator: WeatherUpdateCoordinator, - outputRound: str, + output_round: str, service_id: str, ) -> None: """Initialize the sensor.""" @@ -315,7 +315,7 @@ class PirateWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordinator]): self._ds_hourly = self._weather_coordinator.data.hourly() self._ds_daily = self._weather_coordinator.data.daily() - self.outputRound = outputRound + self.output_round = output_round units = WEATHER_UNITS.get( self._weather_coordinator.requested_units, WEATHER_UNITS["si"] @@ -369,11 +369,13 @@ class PirateWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordinator]): @property def cloud_coverage(self): """Return the cloud coverage.""" - cloudCover = ( - self._weather_coordinator.data.currently().d.get("cloudCover") * 100.0 - ) + cloud_cover = self._weather_coordinator.data.currently().d.get("cloudCover") - return round(cloudCover, 2) if cloudCover != -999 else None + return ( + round(cloud_cover * 100, 2) + if cloud_cover is not None and cloud_cover != -999 + else None + ) @property def humidity(self): @@ -399,16 +401,16 @@ class PirateWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordinator]): @property def native_wind_gust_speed(self): """Return the wind gust speed.""" - windGust = self._weather_coordinator.data.currently().d.get("windGust") + wind_gust = self._weather_coordinator.data.currently().d.get("windGust") - return round(windGust, 2) if windGust != -999 else None + return round(wind_gust, 2) if wind_gust != -999 else None @property def wind_bearing(self): """Return the wind bearing.""" - windBearing = self._weather_coordinator.data.currently().d.get("windBearing") + wind_bearing = self._weather_coordinator.data.currently().d.get("windBearing") - return windBearing if windBearing != -999 else None + return wind_bearing if wind_bearing != -999 else None @property def ozone(self): diff --git a/custom_components/pirateweather/weather_update_coordinator.py b/custom_components/pirateweather/weather_update_coordinator.py index bac5b0e9..e7aaf20a 100644 --- a/custom_components/pirateweather/weather_update_coordinator.py +++ b/custom_components/pirateweather/weather_update_coordinator.py @@ -21,7 +21,7 @@ ATTRIBUTION = "Powered by Pirate Weather" class WeatherUpdateCoordinator(DataUpdateCoordinator): """Weather data update coordinator.""" - def __init__( + def __init__( # noqa: PLR0917 self, api_key, latitude, @@ -72,25 +72,27 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): """Poll weather data from PW.""" if self.latitude == 0.0: - requestLatitude = self.hass.config.latitude + request_latitude = self.hass.config.latitude else: - requestLatitude = self.latitude + request_latitude = self.latitude if self.longitude == 0.0: - requestLongitude = self.hass.config.longitude + request_longitude = self.hass.config.longitude else: - requestLongitude = self.longitude + request_longitude = self.longitude - _LOGGER.debug("Request coordinates: %s, %s", requestLatitude, requestLongitude) + _LOGGER.debug( + "Request coordinates: %s, %s", request_latitude, request_longitude + ) - forecastString = ( + forecast_string = ( self.endpoint + "/forecast/" + self._api_key + "/" - + str(requestLatitude) + + str(request_latitude) + "," - + str(requestLongitude) + + str(request_longitude) + "?units=" + self.requested_units + "&extend=hourly" @@ -104,12 +106,12 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): m.strip() for m in self.models.split(",") if m.strip() ) if exclusions: - forecastString += "&exclude=" + exclusions + forecast_string += "&exclude=" + exclusions session = async_get_clientsession(self.hass) - async with session.get(forecastString) as resp: + async with session.get(forecast_string) as resp: resp.raise_for_status() - jsonText = await resp.json() + json_text = await resp.json() headers = resp.headers _LOGGER.debug("Pirate Weather data update from: %s", self.endpoint) - return Forecast(jsonText, resp, headers) + return Forecast(json_text, resp, headers) diff --git a/custom_components/versatile_thermostat/base_thermostat.py b/custom_components/versatile_thermostat/base_thermostat.py index eb0f10d8..ad4d1e1f 100644 --- a/custom_components/versatile_thermostat/base_thermostat.py +++ b/custom_components/versatile_thermostat/base_thermostat.py @@ -205,6 +205,17 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): # Instantiate all features manager self._managers: list[BaseFeatureManager] = [] + # Names of feature managers provided by external plugins that have + # already been instantiated for this thermostat. Used to avoid + # registering the same external manager twice (post_init + startup retry). + self._external_manager_names: set[str] = set() + + # Instances of feature managers provided by external plugins. They are + # also present in ``self._managers`` (for lifecycle) but are tracked + # separately so they can be refreshed on every control cycle (internal + # managers have their own dedicated per-cycle mechanisms). + self._external_managers: list[BaseFeatureManager] = [] + self._presence_manager: FeaturePresenceManager = FeaturePresenceManager( self, hass ) @@ -237,6 +248,42 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): """Register a manager""" self._managers.append(manager) + def _load_external_feature_managers(self): + """Instantiate feature managers provided by external plugins. + + Query the VTherm API registry and create one manager instance per + eligible thermostat (the factory decides eligibility through its + ``supports`` method). Managers whose plugin registers after this + thermostat has been built are picked up later during ``async_startup``, + mirroring the external proportional algorithm retry behavior. + """ + api = VersatileThermostatAPI.get_vtherm_api(self.hass) + if api is None or not hasattr(api, "get_feature_manager_factories"): + return + + for factory in api.get_feature_manager_factories(): + name = factory.name + if name in self._external_manager_names: + continue + try: + if not factory.supports(self): + continue + manager = factory.create(self) + manager.post_init(self._entry_infos) + except Exception as exc: # pylint: disable=broad-except + _LOGGER.error( + "%s - Error while creating external feature manager '%s': %s", + self, + name, + exc, + ) + continue + + self.register_manager(manager) + self._external_manager_names.add(name) + self._external_managers.append(manager) + _LOGGER.info("%s - Registered external feature manager '%s'", self, name) + def clean_central_config_doublon( self, config_entry: ConfigData, central_config: ConfigEntry | None ) -> dict[str, Any]: @@ -308,6 +355,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): for manager in self._managers: manager.post_init(entry_infos) + # Instantiate feature managers provided by external plugins that are + # already registered at this point. Late-registered plugins are handled + # by a retry in async_startup. + self._load_external_feature_managers() + self._use_central_config_temperature = entry_infos.get( CONF_USE_PRESETS_CENTRAL_CONFIG ) or ( @@ -487,6 +539,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): _LOGGER.debug("%s - Calling async_startup_internal", self) # need_write_state = False + # Retry loading external feature managers: a plugin may have registered + # its factory after this thermostat was built (load order between the + # core and the plugin is not guaranteed). Newly created managers are + # then started in the loop below. + self._load_external_feature_managers() + # start listening for all managers for manager in self._managers: await manager.start_listening() @@ -898,6 +956,30 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): """Return the temperature we try to reach.""" return self._state_manager.current_state.target_temperature + @property + def regulated_target_temperature(self) -> float | None: + """Return the regulated target temperature used to drive the underlying. + + The base implementation returns the plain target temperature. Over + climate thermostats override this to expose their regulated value. + """ + return self.target_temperature + + @property + def underlying_fan_modes(self) -> list[str] | None: + """Return the fan modes exposed by the underlying climate(s). + + The base implementation returns None because most thermostats do not + expose an underlying climate with fan control. + """ + return None + + async def async_set_underlying_fan_mode(self, fan_mode: str) -> None: + """Send a fan mode to the underlying climate(s). + + The base implementation is a no-op for thermostats without fan control. + """ + @property def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" @@ -1609,6 +1691,25 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): # Call specific control heating await self._control_heating_specific(timestamp, force) + # Refresh external feature managers (provided by plugins) on every cycle. + # This is done after the specific control heating so that a manager can + # react to the regulated target temperature (e.g. drive the underlying + # fan mode), and before the publication block below so that any custom + # attributes updated here are written to HA within the same cycle. + # Only external managers are refreshed here: internal managers have their + # own dedicated per-cycle mechanisms. A per-manager guard prevents a + # faulty plugin from breaking the control loop. + for manager in self._external_managers: + try: + await manager.refresh_state() + except Exception as exc: # pylint: disable=broad-except + _LOGGER.error( + "%s - Error while refreshing external feature manager '%s': %s", + self, + manager.name, + exc, + ) + # Check for heating/cooling failures (only for TPI VTherms) await self._heating_failure_detection_manager.refresh_state() @@ -1884,7 +1985,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): self._state_manager.add_custom_attributes(self._attr_extra_state_attributes) for manager in self._managers: - manager.add_custom_attributes(self._attr_extra_state_attributes) + # add_custom_attributes is optional for external feature managers + # (it is not part of the InterfaceFeatureManager contract), so call + # it defensively. + publish_attributes = getattr(manager, "add_custom_attributes", None) + if callable(publish_attributes): + publish_attributes(self._attr_extra_state_attributes) def send_event(self, event_type: EventType, data: dict): """Send an event""" diff --git a/custom_components/versatile_thermostat/climate.py b/custom_components/versatile_thermostat/climate.py index a650fbaf..d48284c2 100644 --- a/custom_components/versatile_thermostat/climate.py +++ b/custom_components/versatile_thermostat/climate.py @@ -125,7 +125,7 @@ async def async_setup_entry( ["None", "Low", "Medium", "High", "Turbo"] ), }, - "service_set_auto_fan_mode", + "service_set_auto_fan_mode_deprecated", ) platform.async_register_entity_service( diff --git a/custom_components/versatile_thermostat/manifest.json b/custom_components/versatile_thermostat/manifest.json index 4128684d..0588b0b1 100644 --- a/custom_components/versatile_thermostat/manifest.json +++ b/custom_components/versatile_thermostat/manifest.json @@ -19,9 +19,9 @@ "requirements": [ "numpy", "scipy", - "vtherm_api>=0.3.0" + "vtherm_api>=0.4.0" ], "ssdp": [], - "version": "10.1.0", + "version": "10.2.0", "zeroconf": [] } diff --git a/custom_components/versatile_thermostat/services.yaml b/custom_components/versatile_thermostat/services.yaml index dbde9346..16086b4c 100644 --- a/custom_components/versatile_thermostat/services.yaml +++ b/custom_components/versatile_thermostat/services.yaml @@ -113,7 +113,7 @@ set_auto_regulation_mode: set_auto_fan_mode: name: Set Auto Fan mode - description: Change the mode of auto-fan (only for VTherm over climate) + description: Deprecated when using the vtherm_auto_fan_extended plugin. Without the plugin, legacy core behavior is kept. target: entity: integration: versatile_thermostat diff --git a/custom_components/versatile_thermostat/thermostat_climate.py b/custom_components/versatile_thermostat/thermostat_climate.py index 0764ac17..4783856b 100644 --- a/custom_components/versatile_thermostat/thermostat_climate.py +++ b/custom_components/versatile_thermostat/thermostat_climate.py @@ -8,6 +8,7 @@ from datetime import timedelta, datetime from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import Event, HomeAssistant, State, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.event import async_track_state_change_event, async_track_time_interval, EventStateChangedData, async_call_later from homeassistant.components.climate import ( HVACAction, @@ -27,6 +28,9 @@ from .vtherm_hvac_mode import VThermHvacMode _LOGGER = get_vtherm_logger(__name__) +AUTO_FAN_PLUGIN_DOMAIN = "vtherm_auto_fan_extended" +AUTO_FAN_PLUGIN_TARGET_VTHERM_KEY = "target_vtherm_unique_id" + HVAC_ACTION_ON = [ # pylint: disable=invalid-name HVACAction.COOLING, HVACAction.DRYING, @@ -465,8 +469,8 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): return None def determine_fan_mode_contains_speed(fan_modes: list[str]) -> bool: - """Determine if the fan_modes contains speed modes by searching for the keywords "low"/"1"/"one"/"speed_1".""" - for val in ["low", "1", "one", "speed_1"]: + """Determine if the fan_modes contains speed modes by searching for the keywords "low"/"1"/"one"/"speed_1"/"on_low".""" + for val in ["low", "1", "one", "speed_1", "on_low"]: if find_fan_mode(fan_modes, val): return True return False @@ -482,6 +486,8 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): index = speed_modes.index("one") elif "speed_1" in speed_modes: index = speed_modes.index("speed_1") + elif "on_low" in speed_modes: + index = speed_modes.index("on_low") if index > -1 and index >= len(speed_modes) / 2: speed_modes.reverse() @@ -1008,6 +1014,20 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): """Get the regulated target temperature""" return self._regulated_target_temp + @property + def regulated_target_temperature(self) -> float | None: + """Return the regulated target temperature used to drive the underlying.""" + return self._regulated_target_temp + + @property + def underlying_fan_modes(self) -> list[str] | None: + """Return the fan modes exposed by the underlying climate(s).""" + return self.fan_modes + + async def async_set_underlying_fan_mode(self, fan_mode: str) -> None: + """Send a fan mode to the underlying climate(s).""" + await self.async_set_fan_mode(fan_mode) + @property def is_regulated(self) -> bool: """Check if the ThermostatOverClimate is regulated""" @@ -1306,6 +1326,49 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): self.update_custom_attributes() self.async_write_ha_state() + async def service_set_auto_fan_mode_deprecated(self, auto_fan_mode: str): + """Deprecated service handler for auto fan mode. + + Auto fan is now managed by the vtherm_auto_fan_extended plugin. + """ + config_entries = getattr(self._hass, "config_entries", None) + plugin_is_configured = False + if config_entries is not None: + try: + plugin_entries = config_entries.async_entries(AUTO_FAN_PLUGIN_DOMAIN) + except Exception: # pylint: disable=broad-except + plugin_entries = [] + + # If at least one entry targets this VTherm, plugin takes ownership. + plugin_is_configured = any( + entry.data.get(AUTO_FAN_PLUGIN_TARGET_VTHERM_KEY) == self.unique_id + for entry in plugin_entries + ) + + # Backward compatibility: old entries without target still mean plugin ownership. + if not plugin_is_configured and plugin_entries: + plugin_is_configured = any( + AUTO_FAN_PLUGIN_TARGET_VTHERM_KEY not in entry.data + for entry in plugin_entries + ) + + # If plugin is not configured for this VTherm, keep legacy core behavior. + if not plugin_is_configured: + await self.service_set_auto_fan_mode(auto_fan_mode) + return + + _LOGGER.warning( + "%s - set_auto_fan_mode is deprecated in core and managed by the " + "vtherm_auto_fan_extended plugin (requested: %s)", + self, + auto_fan_mode, + ) + raise HomeAssistantError( + "The set_auto_fan_mode service is now managed by the " + "vtherm_auto_fan_extended plugin and is no longer available in " + "Versatile Thermostat core." + ) + @overrides async def async_turn_off(self) -> None: """Turn off the climate entity."""