mirror of
https://github.com/home-assistant/core.git
synced 2026-08-28 02:24:46 -05:00
Add translation keys for well-known mobile_app sensors
Mobile app sensors are dynamically registered by the iOS and Android Companion apps with whatever name/icon/state values the app supplies. For a curated set of sensor unique IDs that the apps emit consistently, let HA own the name (via translation_key + has_entity_name), the icon (via icons.json) and the state (via state translations). For known sensors the app-supplied name and icon are ignored. All other sensors keep the existing behaviour. The initial set covers battery_state, charger_type, ringer_mode and audio_mode.
This commit is contained in:
@@ -26,11 +26,13 @@ from .const import (
|
||||
ATTR_SENSOR_STATE,
|
||||
ATTR_SENSOR_STATE_CLASS,
|
||||
ATTR_SENSOR_TYPE,
|
||||
ATTR_SENSOR_UNIQUE_ID,
|
||||
DATA_PENDING_UPDATES,
|
||||
DOMAIN,
|
||||
SIGNAL_SENSOR_UPDATE,
|
||||
)
|
||||
from .helpers import device_info
|
||||
from .known_sensors import get_translation_key
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -49,7 +51,14 @@ class MobileAppEntity(RestoreEntity):
|
||||
self._attr_entity_registry_enabled_default = not config.get(
|
||||
ATTR_SENSOR_DISABLED
|
||||
)
|
||||
self._attr_name = config[CONF_NAME]
|
||||
translation_key = get_translation_key(
|
||||
config[ATTR_SENSOR_TYPE], config[ATTR_SENSOR_UNIQUE_ID]
|
||||
)
|
||||
if translation_key is not None:
|
||||
self._attr_has_entity_name = True
|
||||
self._attr_translation_key = translation_key
|
||||
else:
|
||||
self._attr_name = config[CONF_NAME]
|
||||
self._async_update_attr_from_config()
|
||||
|
||||
@callback
|
||||
@@ -59,7 +68,8 @@ class MobileAppEntity(RestoreEntity):
|
||||
self._attr_device_class = config.get(ATTR_SENSOR_DEVICE_CLASS)
|
||||
self._attr_state_class = config.get(ATTR_SENSOR_STATE_CLASS)
|
||||
self._attr_extra_state_attributes = config[ATTR_SENSOR_ATTRIBUTES]
|
||||
self._attr_icon = config[ATTR_SENSOR_ICON]
|
||||
if self._attr_translation_key is None:
|
||||
self._attr_icon = config[ATTR_SENSOR_ICON]
|
||||
self._attr_entity_category = config.get(ATTR_SENSOR_ENTITY_CATEGORY)
|
||||
self._attr_available = config.get(ATTR_SENSOR_STATE) != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@@ -4,6 +4,20 @@
|
||||
"notify": {
|
||||
"default": "mdi:cellphone-message"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"audio_mode": {
|
||||
"default": "mdi:volume-high"
|
||||
},
|
||||
"battery_state": {
|
||||
"default": "mdi:battery"
|
||||
},
|
||||
"charger_type": {
|
||||
"default": "mdi:power-plug"
|
||||
},
|
||||
"ringer_mode": {
|
||||
"default": "mdi:bell-ring"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Known sensor unique IDs that have translation support.
|
||||
|
||||
Mobile app sensors are dynamically registered by the companion apps. For a
|
||||
curated set of sensor IDs that the iOS and Android Companion apps use
|
||||
consistently we provide HA-side translations for the entity name, icon and
|
||||
(when applicable) state. For all other sensor IDs the integration keeps using
|
||||
the name/icon supplied by the app.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .const import ATTR_SENSOR_TYPE_SENSOR
|
||||
|
||||
# Mapping of (entity_type, sensor_unique_id) to translation_key.
|
||||
# The translation_key must have a matching entry in strings.json under
|
||||
# entity.<entity_type>.<translation_key>.
|
||||
KNOWN_SENSORS: dict[tuple[str, str], str] = {
|
||||
(ATTR_SENSOR_TYPE_SENSOR, "battery_state"): "battery_state",
|
||||
(ATTR_SENSOR_TYPE_SENSOR, "charger_type"): "charger_type",
|
||||
(ATTR_SENSOR_TYPE_SENSOR, "ringer_mode"): "ringer_mode",
|
||||
(ATTR_SENSOR_TYPE_SENSOR, "audio_mode"): "audio_mode",
|
||||
}
|
||||
|
||||
|
||||
def get_translation_key(entity_type: str, sensor_unique_id: str) -> str | None:
|
||||
"""Return the translation key for a known sensor, or None."""
|
||||
return KNOWN_SENSORS.get((entity_type, sensor_unique_id))
|
||||
@@ -18,6 +18,47 @@
|
||||
"title": "Title"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"audio_mode": {
|
||||
"name": "Audio mode",
|
||||
"state": {
|
||||
"normal": "Normal",
|
||||
"ringing": "Ringing",
|
||||
"in_call": "In call",
|
||||
"in_communication": "In communication",
|
||||
"call_screening": "Call screening"
|
||||
}
|
||||
},
|
||||
"battery_state": {
|
||||
"name": "Battery state",
|
||||
"state": {
|
||||
"charging": "Charging",
|
||||
"discharging": "Discharging",
|
||||
"full": "Full",
|
||||
"not_charging": "Not charging",
|
||||
"unknown": "Unknown"
|
||||
}
|
||||
},
|
||||
"charger_type": {
|
||||
"name": "Charger type",
|
||||
"state": {
|
||||
"ac": "AC",
|
||||
"usb": "USB",
|
||||
"wireless": "Wireless",
|
||||
"none": "None"
|
||||
}
|
||||
},
|
||||
"ringer_mode": {
|
||||
"name": "Ringer mode",
|
||||
"state": {
|
||||
"normal": "Normal",
|
||||
"silent": "Silent",
|
||||
"vibrate": "Vibrate"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"device_not_connected_for_local_push_notifications": {
|
||||
"message": "Device {device_name} is not connected for local push notifications"
|
||||
|
||||
@@ -120,6 +120,7 @@ from .helpers import (
|
||||
safe_registration,
|
||||
webhook_response,
|
||||
)
|
||||
from .known_sensors import get_translation_key
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -567,6 +568,8 @@ async def webhook_register_sensor(
|
||||
|
||||
data[CONF_WEBHOOK_ID] = config_entry.data[CONF_WEBHOOK_ID]
|
||||
|
||||
translation_key = get_translation_key(entity_type, unique_id)
|
||||
|
||||
# If sensor already is registered, update current state instead
|
||||
if existing_sensor:
|
||||
_LOGGER.debug(
|
||||
@@ -577,10 +580,25 @@ async def webhook_register_sensor(
|
||||
assert entry is not None
|
||||
changes: dict[str, Any] = {}
|
||||
|
||||
if (
|
||||
new_name := f"{device_name} {data[ATTR_SENSOR_NAME]}"
|
||||
) != entry.original_name:
|
||||
changes["original_name"] = new_name
|
||||
if translation_key is not None:
|
||||
# For known sensors HA owns the name and icon via translations,
|
||||
# so we ignore the values supplied by the mobile app.
|
||||
if entry.translation_key != translation_key:
|
||||
changes["translation_key"] = translation_key
|
||||
if entry.original_name is not None:
|
||||
changes["original_name"] = None
|
||||
if entry.original_icon is not None:
|
||||
changes["original_icon"] = None
|
||||
else:
|
||||
if (
|
||||
new_name := f"{device_name} {data[ATTR_SENSOR_NAME]}"
|
||||
) != entry.original_name:
|
||||
changes["original_name"] = new_name
|
||||
if (
|
||||
ATTR_SENSOR_ICON in data
|
||||
and entry.original_icon != data[ATTR_SENSOR_ICON]
|
||||
):
|
||||
changes["original_icon"] = data[ATTR_SENSOR_ICON]
|
||||
|
||||
if (
|
||||
should_be_disabled := data.get(ATTR_SENSOR_DISABLED)
|
||||
@@ -595,7 +613,6 @@ async def webhook_register_sensor(
|
||||
("device_class", ATTR_SENSOR_DEVICE_CLASS),
|
||||
("unit_of_measurement", ATTR_SENSOR_UOM),
|
||||
("entity_category", ATTR_SENSOR_ENTITY_CATEGORY),
|
||||
("original_icon", ATTR_SENSOR_ICON),
|
||||
):
|
||||
if data_key in data and getattr(entry, ent_reg_key) != data[data_key]:
|
||||
changes[ent_reg_key] = data[data_key]
|
||||
|
||||
Reference in New Issue
Block a user