From c3f40b7369a7111e983d693bef404982bfc8dbfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E5=A2=A8?= Date: Fri, 13 Feb 2026 14:02:07 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E6=9A=B4=E9=9C=B2=E5=87=BA?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E4=BE=BF=E4=BA=8ELLM=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E5=A4=A9=E6=B0=94=E9=A2=84=E6=8A=A5=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hass_weather_baidu/sensor.py | 155 +++++++++++++++++- .../hass_weather_baidu/strings.json | 15 ++ .../hass_weather_baidu/translations/en.json | 15 ++ .../translations/zh-Hans.json | 15 ++ 4 files changed, 196 insertions(+), 4 deletions(-) diff --git a/custom_components/hass_weather_baidu/sensor.py b/custom_components/hass_weather_baidu/sensor.py index 210de4e..910ae3a 100644 --- a/custom_components/hass_weather_baidu/sensor.py +++ b/custom_components/hass_weather_baidu/sensor.py @@ -1,4 +1,4 @@ -"""Sensor entities for Baidu Weather integration - Weather Alerts.""" +"""Sensor entities for Baidu Weather integration - Weather Alerts & Forecasts.""" from __future__ import annotations @@ -6,11 +6,10 @@ import logging from typing import Any from homeassistant.components.sensor import ( - SensorDeviceClass, SensorEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -20,12 +19,18 @@ from .const import ( CONF_LOCATION_NAME, DOMAIN, KEY_ALERTS, - KEY_INDEXES, + KEY_FORECASTS, + KEY_NOW, ) from .coordinator import BaiduWeatherCoordinator _LOGGER = logging.getLogger(__name__) +# Day labels for forecast sensors (index 0 = today) +_DAY_LABELS_CN = ["今天", "明天", "后天", "大后天", "第五天"] +_DAY_LABELS_EN = ["Today", "Tomorrow", "Day After Tomorrow", "In 3 Days", "In 4 Days"] +_DAY_KEYS = ["forecast_today", "forecast_tomorrow", "forecast_day2", "forecast_day3", "forecast_day4"] + async def async_setup_entry( hass: HomeAssistant, @@ -41,6 +46,14 @@ async def async_setup_entry( BaiduWeatherAqiSensor(coordinator, entry, location_name), ] + # Create daily forecast sensors (today + next 4 days = 5 sensors) + for day_index in range(5): + entities.append( + BaiduWeatherDailyForecastSensor( + coordinator, entry, location_name, day_index + ) + ) + async_add_entities(entities) @@ -187,3 +200,137 @@ class BaiduWeatherAqiSensor( attrs["aqi_level"] = "严重污染" return attrs + + +class BaiduWeatherDailyForecastSensor( + CoordinatorEntity[BaiduWeatherCoordinator], SensorEntity +): + """Sensor entity for a single day's weather forecast. + + Each instance represents one day (today, tomorrow, day after tomorrow, etc.). + The sensor state is a human-readable summary like "多云 12~20°C". + Extra attributes contain detailed forecast fields so that voice assistants + and LLM integrations can read the forecast directly from entity state. + """ + + _attr_has_entity_name = True + _attr_icon = "mdi:weather-partly-cloudy" + _attr_attribution = ATTRIBUTION + + def __init__( + self, + coordinator: BaiduWeatherCoordinator, + entry: ConfigEntry, + location_name: str, + day_index: int, + ) -> None: + """Initialize the daily forecast sensor. + + Args: + day_index: 0 = today, 1 = tomorrow, 2 = day after tomorrow, etc. + """ + super().__init__(coordinator) + self._entry = entry + self._day_index = day_index + self._attr_translation_key = _DAY_KEYS[day_index] + self._attr_unique_id = f"{DOMAIN}_{entry.entry_id}_{_DAY_KEYS[day_index]}" + self._attr_device_info = DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, entry.entry_id)}, + manufacturer="百度地图", + name=f"百度天气 - {location_name}", + configuration_url="https://lbsyun.baidu.com/", + ) + + def _get_forecast_data(self) -> dict[str, Any] | None: + """Get the forecast data for this day by index.""" + if self.coordinator.data is None: + return None + forecasts = self.coordinator.data.get(KEY_FORECASTS, []) + if self._day_index < len(forecasts): + return forecasts[self._day_index] + return None + + @property + def native_value(self) -> str | None: + """Return a human-readable weather summary for this day. + + Format: "多云 12~20°C" or "晴转多云 8~18°C" + This makes it easy for voice assistants to read aloud. + """ + fc = self._get_forecast_data() + if fc is None: + return None + + text_day = fc.get("text_day", "") + text_night = fc.get("text_night", "") + high = fc.get("high") + low = fc.get("low") + + # Build condition text + if text_day and text_night and text_day != text_night: + condition_text = f"{text_day}转{text_night}" + elif text_day: + condition_text = text_day + elif text_night: + condition_text = text_night + else: + condition_text = "未知" + + # Build temperature range + if low is not None and high is not None: + temp_text = f"{low}~{high}°C" + elif high is not None: + temp_text = f"最高{high}°C" + elif low is not None: + temp_text = f"最低{low}°C" + else: + temp_text = "" + + if temp_text: + return f"{condition_text} {temp_text}" + return condition_text + + @property + def extra_state_attributes(self) -> dict[str, Any]: + """Return detailed forecast attributes for this day. + + These attributes are designed to be easily readable by LLM integrations. + """ + attrs: dict[str, Any] = {} + fc = self._get_forecast_data() + if fc is None: + return attrs + + # Date + if fc.get("date"): + attrs["date"] = fc["date"] + + # Day label (今天/明天/后天...) + attrs["day_label"] = _DAY_LABELS_CN[self._day_index] + + # Conditions + if fc.get("text_day"): + attrs["condition_day"] = fc["text_day"] + if fc.get("text_night"): + attrs["condition_night"] = fc["text_night"] + + # Temperatures + if fc.get("high") is not None: + attrs["temperature_high"] = fc["high"] + if fc.get("low") is not None: + attrs["temperature_low"] = fc["low"] + + # Wind - Day + if fc.get("wc_day"): + attrs["wind_class_day"] = fc["wc_day"] + if fc.get("wd_day"): + attrs["wind_direction_day"] = fc["wd_day"] + + # Wind - Night + if fc.get("wc_night"): + attrs["wind_class_night"] = fc["wc_night"] + if fc.get("wd_night"): + attrs["wind_direction_night"] = fc["wd_night"] + + return attrs diff --git a/custom_components/hass_weather_baidu/strings.json b/custom_components/hass_weather_baidu/strings.json index 8890957..250abd8 100644 --- a/custom_components/hass_weather_baidu/strings.json +++ b/custom_components/hass_weather_baidu/strings.json @@ -69,6 +69,21 @@ }, "aqi": { "name": "空气质量指数" + }, + "forecast_today": { + "name": "今天天气预报" + }, + "forecast_tomorrow": { + "name": "明天天气预报" + }, + "forecast_day2": { + "name": "后天天气预报" + }, + "forecast_day3": { + "name": "大后天天气预报" + }, + "forecast_day4": { + "name": "第五天天气预报" } } } diff --git a/custom_components/hass_weather_baidu/translations/en.json b/custom_components/hass_weather_baidu/translations/en.json index c0f4b40..056f50d 100644 --- a/custom_components/hass_weather_baidu/translations/en.json +++ b/custom_components/hass_weather_baidu/translations/en.json @@ -69,6 +69,21 @@ }, "aqi": { "name": "Air Quality Index" + }, + "forecast_today": { + "name": "Today's Weather Forecast" + }, + "forecast_tomorrow": { + "name": "Tomorrow's Weather Forecast" + }, + "forecast_day2": { + "name": "Day After Tomorrow Forecast" + }, + "forecast_day3": { + "name": "3-Day Forecast" + }, + "forecast_day4": { + "name": "4-Day Forecast" } } } diff --git a/custom_components/hass_weather_baidu/translations/zh-Hans.json b/custom_components/hass_weather_baidu/translations/zh-Hans.json index 8890957..250abd8 100644 --- a/custom_components/hass_weather_baidu/translations/zh-Hans.json +++ b/custom_components/hass_weather_baidu/translations/zh-Hans.json @@ -69,6 +69,21 @@ }, "aqi": { "name": "空气质量指数" + }, + "forecast_today": { + "name": "今天天气预报" + }, + "forecast_tomorrow": { + "name": "明天天气预报" + }, + "forecast_day2": { + "name": "后天天气预报" + }, + "forecast_day3": { + "name": "大后天天气预报" + }, + "forecast_day4": { + "name": "第五天天气预报" } } }