From d2f8c527a5f968fbe3c1fc0ba9d5028e73a238c9 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Wed, 25 Oct 2023 12:15:09 +0200 Subject: [PATCH] Add entity translations to Tomorrow.io (#99632) --- .../components/tomorrowio/__init__.py | 2 +- homeassistant/components/tomorrowio/sensor.py | 67 ++++------ .../components/tomorrowio/strings.json | 119 +++++++++++++++--- .../components/tomorrowio/weather.py | 3 +- tests/components/tomorrowio/test_sensor.py | 9 +- tests/components/tomorrowio/test_weather.py | 5 +- 6 files changed, 131 insertions(+), 74 deletions(-) diff --git a/homeassistant/components/tomorrowio/__init__.py b/homeassistant/components/tomorrowio/__init__.py index 626049276f5..25b814c106a 100644 --- a/homeassistant/components/tomorrowio/__init__.py +++ b/homeassistant/components/tomorrowio/__init__.py @@ -327,6 +327,7 @@ class TomorrowioEntity(CoordinatorEntity[TomorrowioDataUpdateCoordinator]): """Base Tomorrow.io Entity.""" _attr_attribution = ATTRIBUTION + _attr_has_entity_name = True def __init__( self, @@ -340,7 +341,6 @@ class TomorrowioEntity(CoordinatorEntity[TomorrowioDataUpdateCoordinator]): self._config_entry = config_entry self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, self._config_entry.data[CONF_API_KEY])}, - name=INTEGRATION_NAME, manufacturer=INTEGRATION_NAME, sw_version=f"v{self.api_version}", entry_type=DeviceEntryType.SERVICE, diff --git a/homeassistant/components/tomorrowio/sensor.py b/homeassistant/components/tomorrowio/sensor.py index 4aa2748ad30..947bbf6fd2f 100644 --- a/homeassistant/components/tomorrowio/sensor.py +++ b/homeassistant/components/tomorrowio/sensor.py @@ -25,7 +25,6 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, CONF_API_KEY, - CONF_NAME, PERCENTAGE, UnitOfIrradiance, UnitOfLength, @@ -75,10 +74,6 @@ from .const import ( class TomorrowioSensorEntityDescription(SensorEntityDescription): """Describes a Tomorrow.io sensor entity.""" - # TomorrowioSensor does not support UNDEFINED or None, - # restrict the type to str. - name: str = "" - attribute: str = "" unit_imperial: str | None = None unit_metric: str | None = None @@ -111,16 +106,16 @@ def convert_ppb_to_ugm3(molecular_weight: int | float) -> Callable[[float], floa SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="feels_like", + translation_key="feels_like", attribute=TMRW_ATTR_FEELS_LIKE, - name="Feels Like", native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), TomorrowioSensorEntityDescription( key="dew_point", + translation_key="dew_point", attribute=TMRW_ATTR_DEW_POINT, - name="Dew Point", icon="mdi:thermometer-water", native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, @@ -130,7 +125,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="pressure_surface_level", attribute=TMRW_ATTR_PRESSURE_SURFACE_LEVEL, - name="Pressure (Surface Level)", native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, @@ -140,7 +134,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="global_horizontal_irradiance", attribute=TMRW_ATTR_SOLAR_GHI, - name="Global Horizontal Irradiance", unit_imperial=UnitOfIrradiance.BTUS_PER_HOUR_SQUARE_FOOT, unit_metric=UnitOfIrradiance.WATTS_PER_SQUARE_METER, imperial_conversion=(1 / 3.15459), @@ -150,8 +143,8 @@ SENSOR_TYPES = ( # Data comes in as km, convert to miles for imperial TomorrowioSensorEntityDescription( key="cloud_base", + translation_key="cloud_base", attribute=TMRW_ATTR_CLOUD_BASE, - name="Cloud Base", icon="mdi:cloud-arrow-down", unit_imperial=UnitOfLength.MILES, unit_metric=UnitOfLength.KILOMETERS, @@ -166,8 +159,8 @@ SENSOR_TYPES = ( # Data comes in as km, convert to miles for imperial TomorrowioSensorEntityDescription( key="cloud_ceiling", + translation_key="cloud_ceiling", attribute=TMRW_ATTR_CLOUD_CEILING, - name="Cloud Ceiling", icon="mdi:cloud-arrow-up", unit_imperial=UnitOfLength.MILES, unit_metric=UnitOfLength.KILOMETERS, @@ -181,16 +174,16 @@ SENSOR_TYPES = ( ), TomorrowioSensorEntityDescription( key="cloud_cover", + translation_key="cloud_cover", attribute=TMRW_ATTR_CLOUD_COVER, - name="Cloud Cover", icon="mdi:cloud-percent", native_unit_of_measurement=PERCENTAGE, ), # Data comes in as m/s, convert to mi/h for imperial TomorrowioSensorEntityDescription( key="wind_gust", + translation_key="wind_gust", attribute=TMRW_ATTR_WIND_GUST, - name="Wind Gust", icon="mdi:weather-windy", unit_imperial=UnitOfSpeed.MILES_PER_HOUR, unit_metric=UnitOfSpeed.METERS_PER_SECOND, @@ -202,10 +195,9 @@ SENSOR_TYPES = ( ), TomorrowioSensorEntityDescription( key="precipitation_type", - attribute=TMRW_ATTR_PRECIPITATION_TYPE, - name="Precipitation Type", - value_map=PrecipitationType, translation_key="precipitation_type", + attribute=TMRW_ATTR_PRECIPITATION_TYPE, + value_map=PrecipitationType, icon="mdi:weather-snowy-rainy", ), # Data comes in as ppb, convert to µg/m^3 @@ -213,7 +205,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="ozone", attribute=TMRW_ATTR_OZONE, - name="Ozone", native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, multiplication_factor=convert_ppb_to_ugm3(48), device_class=SensorDeviceClass.OZONE, @@ -222,7 +213,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="particulate_matter_2_5_mm", attribute=TMRW_ATTR_PARTICULATE_MATTER_25, - name="Particulate Matter < 2.5 μm", native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, device_class=SensorDeviceClass.PM25, state_class=SensorStateClass.MEASUREMENT, @@ -230,7 +220,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="particulate_matter_10_mm", attribute=TMRW_ATTR_PARTICULATE_MATTER_10, - name="Particulate Matter < 10 μm", native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, device_class=SensorDeviceClass.PM10, state_class=SensorStateClass.MEASUREMENT, @@ -240,7 +229,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="nitrogen_dioxide", attribute=TMRW_ATTR_NITROGEN_DIOXIDE, - name="Nitrogen Dioxide", native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, multiplication_factor=convert_ppb_to_ugm3(46.01), device_class=SensorDeviceClass.NITROGEN_DIOXIDE, @@ -250,7 +238,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="carbon_monoxide", attribute=TMRW_ATTR_CARBON_MONOXIDE, - name="Carbon Monoxide", native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, multiplication_factor=1 / 1000, device_class=SensorDeviceClass.CO, @@ -261,7 +248,6 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key="sulphur_dioxide", attribute=TMRW_ATTR_SULPHUR_DIOXIDE, - name="Sulphur Dioxide", native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, multiplication_factor=convert_ppb_to_ugm3(64.07), device_class=SensorDeviceClass.SULPHUR_DIOXIDE, @@ -269,90 +255,82 @@ SENSOR_TYPES = ( ), TomorrowioSensorEntityDescription( key="us_epa_air_quality_index", + translation_key="us_epa_air_quality_index", attribute=TMRW_ATTR_EPA_AQI, - name="US EPA Air Quality Index", device_class=SensorDeviceClass.AQI, state_class=SensorStateClass.MEASUREMENT, ), TomorrowioSensorEntityDescription( key="us_epa_primary_pollutant", - attribute=TMRW_ATTR_EPA_PRIMARY_POLLUTANT, - name="US EPA Primary Pollutant", - value_map=PrimaryPollutantType, translation_key="primary_pollutant", + attribute=TMRW_ATTR_EPA_PRIMARY_POLLUTANT, + value_map=PrimaryPollutantType, ), TomorrowioSensorEntityDescription( key="us_epa_health_concern", - attribute=TMRW_ATTR_EPA_HEALTH_CONCERN, - name="US EPA Health Concern", - value_map=HealthConcernType, translation_key="health_concern", + attribute=TMRW_ATTR_EPA_HEALTH_CONCERN, + value_map=HealthConcernType, icon="mdi:hospital", ), TomorrowioSensorEntityDescription( key="china_mep_air_quality_index", + translation_key="china_mep_air_quality_index", attribute=TMRW_ATTR_CHINA_AQI, - name="China MEP Air Quality Index", device_class=SensorDeviceClass.AQI, ), TomorrowioSensorEntityDescription( key="china_mep_primary_pollutant", + translation_key="china_mep_primary_pollutant", attribute=TMRW_ATTR_CHINA_PRIMARY_POLLUTANT, - name="China MEP Primary Pollutant", value_map=PrimaryPollutantType, - translation_key="primary_pollutant", ), TomorrowioSensorEntityDescription( key="china_mep_health_concern", + translation_key="china_mep_health_concern", attribute=TMRW_ATTR_CHINA_HEALTH_CONCERN, - name="China MEP Health Concern", value_map=HealthConcernType, - translation_key="health_concern", icon="mdi:hospital", ), TomorrowioSensorEntityDescription( key="tree_pollen_index", + translation_key="pollen_index", attribute=TMRW_ATTR_POLLEN_TREE, - name="Tree Pollen Index", icon="mdi:tree", value_map=PollenIndex, - translation_key="pollen_index", ), TomorrowioSensorEntityDescription( key="weed_pollen_index", + translation_key="weed_pollen_index", attribute=TMRW_ATTR_POLLEN_WEED, - name="Weed Pollen Index", value_map=PollenIndex, - translation_key="pollen_index", icon="mdi:flower-pollen", ), TomorrowioSensorEntityDescription( key="grass_pollen_index", + translation_key="grass_pollen_index", attribute=TMRW_ATTR_POLLEN_GRASS, - name="Grass Pollen Index", icon="mdi:grass", value_map=PollenIndex, - translation_key="pollen_index", ), TomorrowioSensorEntityDescription( key="fire_index", + translation_key="fire_index", attribute=TMRW_ATTR_FIRE_INDEX, - name="Fire Index", icon="mdi:fire", ), TomorrowioSensorEntityDescription( key="uv_index", + translation_key="uv_index", attribute=TMRW_ATTR_UV_INDEX, - name="UV Index", state_class=SensorStateClass.MEASUREMENT, icon="mdi:sun-wireless", ), TomorrowioSensorEntityDescription( key="uv_radiation_health_concern", + translation_key="uv_radiation_health_concern", attribute=TMRW_ATTR_UV_HEALTH_CONCERN, - name="UV Radiation Health Concern", value_map=UVDescription, - translation_key="uv_index", icon="mdi:weather-sunny-alert", ), ) @@ -399,7 +377,6 @@ class BaseTomorrowioSensorEntity(TomorrowioEntity, SensorEntity): """Initialize Tomorrow.io Sensor Entity.""" super().__init__(config_entry, coordinator, api_version) self.entity_description = description - self._attr_name = f"{self._config_entry.data[CONF_NAME]} - {description.name}" self._attr_unique_id = f"{self._config_entry.unique_id}_{description.key}" if self.entity_description.native_unit_of_measurement is None: self._attr_native_unit_of_measurement = description.unit_metric diff --git a/homeassistant/components/tomorrowio/strings.json b/homeassistant/components/tomorrowio/strings.json index a104570f5c8..03a8a169920 100644 --- a/homeassistant/components/tomorrowio/strings.json +++ b/homeassistant/components/tomorrowio/strings.json @@ -33,36 +33,39 @@ }, "entity": { "sensor": { - "health_concern": { - "state": { - "good": "Good", - "moderate": "Moderate", - "unhealthy_for_sensitive_groups": "Unhealthy for Sensitive Groups", - "unhealthy": "Unhealthy", - "very_unhealthy": "Very Unhealthy", - "hazardous": "Hazardous" - } + "feels_like": { + "name": "Feels like" }, - "pollen_index": { - "state": { - "none": "None", - "very_low": "Very Low", - "low": "Low", - "medium": "Medium", - "high": "High", - "very_high": "Very High" - } + "dew_point": { + "name": "Dew point" + }, + "cloud_base": { + "name": "Cloud base" + }, + "cloud_ceiling": { + "name": "Cloud ceiling" + }, + "cloud_cover": { + "name": "Cloud cover" + }, + "wind_gust": { + "name": "Wind gust" }, "precipitation_type": { + "name": "Precipitation type", "state": { "none": "None", "rain": "Rain", "snow": "Snow", - "freezing_rain": "Freezing Rain", - "ice_pellets": "Ice Pellets" + "freezing_rain": "Freezing rain", + "ice_pellets": "Ice pellets" } }, + "us_epa_air_quality_index": { + "name": "US EPA air quality index" + }, "primary_pollutant": { + "name": "US EPA primary pollutant", "state": { "pm25": "[%key:component::sensor::entity_component::pm25::name%]", "pm10": "[%key:component::sensor::entity_component::pm10::name%]", @@ -72,7 +75,83 @@ "so2": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]" } }, + "health_concern": { + "name": "US EPA health concern", + "state": { + "good": "Good", + "moderate": "Moderate", + "unhealthy_for_sensitive_groups": "Unhealthy for sensitive groups", + "unhealthy": "Unhealthy", + "very_unhealthy": "Very unhealthy", + "hazardous": "Hazardous" + } + }, + "china_mep_air_quality_index": { + "name": "China MEP air quality index" + }, + "china_mep_primary_pollutant": { + "name": "China MEP primary pollutant", + "state": { + "pm25": "[%key:component::sensor::entity_component::pm25::name%]", + "pm10": "[%key:component::sensor::entity_component::pm10::name%]", + "o3": "[%key:component::sensor::entity_component::ozone::name%]", + "no2": "[%key:component::sensor::entity_component::nitrogen_dioxide::name%]", + "co": "[%key:component::sensor::entity_component::carbon_monoxide::name%]", + "so2": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]" + } + }, + "china_mep_health_concern": { + "name": "China MEP health concern", + "state": { + "good": "[%key:component::tomorrowio::entity::sensor::health_concern::state::good%]", + "moderate": "[%key:component::tomorrowio::entity::sensor::health_concern::state::moderate%]", + "unhealthy_for_sensitive_groups": "[%key:component::tomorrowio::entity::sensor::health_concern::state::unhealthy_for_sensitive_groups%]", + "unhealthy": "[%key:component::tomorrowio::entity::sensor::health_concern::state::unhealthy%]", + "very_unhealthy": "[%key:component::tomorrowio::entity::sensor::health_concern::state::very_unhealthy%]", + "hazardous": "[%key:component::tomorrowio::entity::sensor::health_concern::state::hazardous%]" + } + }, + "pollen_index": { + "name": "Tree pollen index", + "state": { + "none": "None", + "very_low": "Very low", + "low": "Low", + "medium": "Medium", + "high": "High", + "very_high": "Very high" + } + }, + "weed_pollen_index": { + "name": "Weed pollen index", + "state": { + "none": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::none%]", + "very_low": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::very_low%]", + "low": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::low%]", + "medium": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::medium%]", + "high": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::high%]", + "very_high": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::very_high%]" + } + }, + "grass_pollen_index": { + "name": "Grass pollen index", + "state": { + "none": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::none%]", + "very_low": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::very_low%]", + "low": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::low%]", + "medium": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::medium%]", + "high": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::high%]", + "very_high": "[%key:component::tomorrowio::entity::sensor::pollen_index::state::very_high%]" + } + }, + "fire_index": { + "name": "Fire index" + }, "uv_index": { + "name": "UV index" + }, + "uv_radiation_health_concern": { + "name": "UV radiation health concern", "state": { "low": "Low", "moderate": "Moderate", diff --git a/homeassistant/components/tomorrowio/weather.py b/homeassistant/components/tomorrowio/weather.py index b0b82d81463..06a147366e8 100644 --- a/homeassistant/components/tomorrowio/weather.py +++ b/homeassistant/components/tomorrowio/weather.py @@ -24,7 +24,6 @@ from homeassistant.components.weather import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_API_KEY, - CONF_NAME, UnitOfLength, UnitOfPrecipitationDepth, UnitOfPressure, @@ -118,7 +117,7 @@ class TomorrowioWeatherEntity(TomorrowioEntity, SingleCoordinatorWeatherEntity): self._attr_entity_registry_enabled_default = ( forecast_type == DEFAULT_FORECAST_TYPE ) - self._attr_name = f"{config_entry.data[CONF_NAME]} - {forecast_type.title()}" + self._attr_name = forecast_type.title() self._attr_unique_id = _calculate_unique_id( config_entry.unique_id, forecast_type ) diff --git a/tests/components/tomorrowio/test_sensor.py b/tests/components/tomorrowio/test_sensor.py index 77335769383..53e455ffb8d 100644 --- a/tests/components/tomorrowio/test_sensor.py +++ b/tests/components/tomorrowio/test_sensor.py @@ -37,8 +37,8 @@ O3 = "ozone" CO = "carbon_monoxide" NO2 = "nitrogen_dioxide" SO2 = "sulphur_dioxide" -PM25 = "particulate_matter_2_5_mm" -PM10 = "particulate_matter_10_mm" +PM25 = "pm2_5" +PM10 = "pm10" MEP_AQI = "china_mep_air_quality_index" MEP_HEALTH_CONCERN = "china_mep_health_concern" MEP_PRIMARY_POLLUTANT = "china_mep_primary_pollutant" @@ -51,10 +51,10 @@ WEED_POLLEN = "weed_pollen_index" TREE_POLLEN = "tree_pollen_index" FEELS_LIKE = "feels_like" DEW_POINT = "dew_point" -PRESSURE_SURFACE_LEVEL = "pressure_surface_level" +PRESSURE_SURFACE_LEVEL = "pressure" SNOW_ACCUMULATION = "snow_accumulation" ICE_ACCUMULATION = "ice_accumulation" -GHI = "global_horizontal_irradiance" +GHI = "irradiance" CLOUD_BASE = "cloud_base" CLOUD_COVER = "cloud_cover" CLOUD_CEILING = "cloud_ceiling" @@ -121,6 +121,7 @@ async def _setup( data = _get_config_schema(hass, SOURCE_USER)(config) data[CONF_NAME] = DEFAULT_NAME config_entry = MockConfigEntry( + title=DEFAULT_NAME, domain=DOMAIN, data=data, options={CONF_TIMESTEP: DEFAULT_TIMESTEP}, diff --git a/tests/components/tomorrowio/test_weather.py b/tests/components/tomorrowio/test_weather.py index 229e62065a6..863623ee524 100644 --- a/tests/components/tomorrowio/test_weather.py +++ b/tests/components/tomorrowio/test_weather.py @@ -78,6 +78,7 @@ async def _setup_config_entry(hass: HomeAssistant, config: dict[str, Any]) -> St data = _get_config_schema(hass, SOURCE_USER)(config) data[CONF_NAME] = DEFAULT_NAME config_entry = MockConfigEntry( + title=DEFAULT_NAME, domain=DOMAIN, data=data, options={CONF_TIMESTEP: DEFAULT_TIMESTEP}, @@ -228,7 +229,7 @@ async def test_v4_weather(hass: HomeAssistant, tomorrowio_config_entry_update) - ATTR_FORECAST_WIND_BEARING: 239.6, ATTR_FORECAST_WIND_SPEED: 34.16, # 9.49 m/s -> km/h } - assert weather_state.attributes[ATTR_FRIENDLY_NAME] == "Tomorrow.io - Daily" + assert weather_state.attributes[ATTR_FRIENDLY_NAME] == "Tomorrow.io Daily" assert weather_state.attributes[ATTR_WEATHER_HUMIDITY] == 23 assert weather_state.attributes[ATTR_WEATHER_OZONE] == 46.53 assert weather_state.attributes[ATTR_WEATHER_PRECIPITATION_UNIT] == "mm" @@ -261,7 +262,7 @@ async def test_v4_weather_legacy_entities(hass: HomeAssistant) -> None: ATTR_FORECAST_WIND_BEARING: 239.6, ATTR_FORECAST_WIND_SPEED: 34.16, # 9.49 m/s -> km/h } - assert weather_state.attributes[ATTR_FRIENDLY_NAME] == "Tomorrow.io - Daily" + assert weather_state.attributes[ATTR_FRIENDLY_NAME] == "Tomorrow.io Daily" assert weather_state.attributes[ATTR_WEATHER_HUMIDITY] == 23 assert weather_state.attributes[ATTR_WEATHER_OZONE] == 46.53 assert weather_state.attributes[ATTR_WEATHER_PRECIPITATION_UNIT] == "mm"