diff --git a/homeassistant/components/fitbit/sensor.py b/homeassistant/components/fitbit/sensor.py index 2218454bd61..d58dad4ca67 100644 --- a/homeassistant/components/fitbit/sensor.py +++ b/homeassistant/components/fitbit/sensor.py @@ -24,7 +24,7 @@ from homeassistant.const import ( UnitOfVolume, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -41,6 +41,8 @@ _CONFIGURING: dict[str, str] = {} SCAN_INTERVAL: Final = datetime.timedelta(minutes=30) +FITBIT_TRACKER_SUBSTRING = "/tracker/" + def _default_value_fn(result: dict[str, Any]) -> str: """Parse a Fitbit timeseries API responses.""" @@ -122,11 +124,34 @@ class FitbitSensorEntityDescription(SensorEntityDescription): unit_fn: Callable[[FitbitUnitSystem], str | None] = lambda x: None scope: FitbitScope | None = None + @property + def is_tracker(self) -> bool: + """Return if the entity is a tracker.""" + return FITBIT_TRACKER_SUBSTRING in self.key + + +def _build_device_info( + config_entry: ConfigEntry, entity_description: FitbitSensorEntityDescription +) -> DeviceInfo: + """Build device info for sensor entities info across devices.""" + unique_id = cast(str, config_entry.unique_id) + if entity_description.is_tracker: + return DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, f"{unique_id}_tracker")}, + translation_key="tracker", + translation_placeholders={"display_name": config_entry.title}, + ) + return DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, unique_id)}, + ) + FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( FitbitSensorEntityDescription( key="activities/activityCalories", - name="Activity Calories", + translation_key="activity_calories", native_unit_of_measurement="cal", icon="mdi:fire", scope=FitbitScope.ACTIVITY, @@ -135,7 +160,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/calories", - name="Calories", + translation_key="calories", native_unit_of_measurement="cal", icon="mdi:fire", scope=FitbitScope.ACTIVITY, @@ -143,7 +168,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/caloriesBMR", - name="Calories BMR", + translation_key="calories_bmr", native_unit_of_measurement="cal", icon="mdi:fire", scope=FitbitScope.ACTIVITY, @@ -153,7 +178,6 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/distance", - name="Distance", icon="mdi:map-marker", device_class=SensorDeviceClass.DISTANCE, value_fn=_distance_value_fn, @@ -163,7 +187,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/elevation", - name="Elevation", + translation_key="elevation", icon="mdi:walk", device_class=SensorDeviceClass.DISTANCE, unit_fn=_elevation_unit, @@ -173,7 +197,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/floors", - name="Floors", + translation_key="floors", native_unit_of_measurement="floors", icon="mdi:walk", scope=FitbitScope.ACTIVITY, @@ -182,7 +206,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/heart", - name="Resting Heart Rate", + translation_key="resting_heart_rate", native_unit_of_measurement="bpm", icon="mdi:heart-pulse", value_fn=_int_value_or_none("restingHeartRate"), @@ -191,7 +215,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/minutesFairlyActive", - name="Minutes Fairly Active", + translation_key="minutes_fairly_active", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", device_class=SensorDeviceClass.DURATION, @@ -201,7 +225,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/minutesLightlyActive", - name="Minutes Lightly Active", + translation_key="minutes_lightly_active", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", device_class=SensorDeviceClass.DURATION, @@ -211,7 +235,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/minutesSedentary", - name="Minutes Sedentary", + translation_key="minutes_sedentary", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:seat-recline-normal", device_class=SensorDeviceClass.DURATION, @@ -221,7 +245,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/minutesVeryActive", - name="Minutes Very Active", + translation_key="minutes_very_active", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:run", device_class=SensorDeviceClass.DURATION, @@ -231,7 +255,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/steps", - name="Steps", + translation_key="steps", native_unit_of_measurement="steps", icon="mdi:walk", scope=FitbitScope.ACTIVITY, @@ -239,7 +263,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/activityCalories", - name="Tracker Activity Calories", + translation_key="activity_calories", native_unit_of_measurement="cal", icon="mdi:fire", scope=FitbitScope.ACTIVITY, @@ -249,7 +273,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/calories", - name="Tracker Calories", + translation_key="calories", native_unit_of_measurement="cal", icon="mdi:fire", scope=FitbitScope.ACTIVITY, @@ -259,7 +283,6 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/distance", - name="Tracker Distance", icon="mdi:map-marker", device_class=SensorDeviceClass.DISTANCE, value_fn=_distance_value_fn, @@ -271,7 +294,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/elevation", - name="Tracker Elevation", + translation_key="elevation", icon="mdi:walk", device_class=SensorDeviceClass.DISTANCE, unit_fn=_elevation_unit, @@ -282,7 +305,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/floors", - name="Tracker Floors", + translation_key="floors", native_unit_of_measurement="floors", icon="mdi:walk", scope=FitbitScope.ACTIVITY, @@ -292,7 +315,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/minutesFairlyActive", - name="Tracker Minutes Fairly Active", + translation_key="minutes_fairly_active", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", device_class=SensorDeviceClass.DURATION, @@ -303,7 +326,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/minutesLightlyActive", - name="Tracker Minutes Lightly Active", + translation_key="minutes_lightly_active", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", device_class=SensorDeviceClass.DURATION, @@ -314,7 +337,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/minutesSedentary", - name="Tracker Minutes Sedentary", + translation_key="minutes_sedentary", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:seat-recline-normal", device_class=SensorDeviceClass.DURATION, @@ -325,7 +348,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/minutesVeryActive", - name="Tracker Minutes Very Active", + translation_key="minutes_very_active", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:run", device_class=SensorDeviceClass.DURATION, @@ -336,7 +359,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="activities/tracker/steps", - name="Tracker Steps", + translation_key="steps", native_unit_of_measurement="steps", icon="mdi:walk", scope=FitbitScope.ACTIVITY, @@ -346,7 +369,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="body/bmi", - name="BMI", + translation_key="bmi", native_unit_of_measurement="BMI", icon="mdi:human", state_class=SensorStateClass.MEASUREMENT, @@ -357,7 +380,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="body/fat", - name="Body Fat", + translation_key="body_fat", native_unit_of_measurement=PERCENTAGE, icon="mdi:human", state_class=SensorStateClass.MEASUREMENT, @@ -368,7 +391,6 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="body/weight", - name="Weight", icon="mdi:human", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.WEIGHT, @@ -378,7 +400,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="sleep/awakeningsCount", - name="Awakenings Count", + translation_key="awakenings_count", native_unit_of_measurement="times awaken", icon="mdi:sleep", scope=FitbitScope.SLEEP, @@ -387,7 +409,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="sleep/efficiency", - name="Sleep Efficiency", + translation_key="sleep_efficiency", native_unit_of_measurement=PERCENTAGE, icon="mdi:sleep", state_class=SensorStateClass.MEASUREMENT, @@ -396,7 +418,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="sleep/minutesAfterWakeup", - name="Minutes After Wakeup", + translation_key="minutes_after_wakeup", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", device_class=SensorDeviceClass.DURATION, @@ -406,7 +428,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="sleep/minutesAsleep", - name="Sleep Minutes Asleep", + translation_key="sleep_minutes_asleep", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", device_class=SensorDeviceClass.DURATION, @@ -416,7 +438,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="sleep/minutesAwake", - name="Sleep Minutes Awake", + translation_key="sleep_minutes_awake", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", device_class=SensorDeviceClass.DURATION, @@ -426,7 +448,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="sleep/minutesToFallAsleep", - name="Sleep Minutes to Fall Asleep", + translation_key="sleep_minutes_to_fall_asleep", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", device_class=SensorDeviceClass.DURATION, @@ -436,7 +458,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="sleep/timeInBed", - name="Sleep Time in Bed", + translation_key="sleep_time_in_bed", native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:hotel", device_class=SensorDeviceClass.DURATION, @@ -446,7 +468,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="foods/log/caloriesIn", - name="Calories In", + translation_key="calories_in", native_unit_of_measurement="cal", icon="mdi:food-apple", state_class=SensorStateClass.TOTAL_INCREASING, @@ -455,7 +477,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( ), FitbitSensorEntityDescription( key="foods/log/water", - name="Water", + translation_key="water", icon="mdi:cup-water", unit_fn=_water_unit, state_class=SensorStateClass.TOTAL_INCREASING, @@ -467,14 +489,14 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( # Different description depending on clock format SLEEP_START_TIME = FitbitSensorEntityDescription( key="sleep/startTime", - name="Sleep Start Time", + translation_key="sleep_start_time", icon="mdi:clock", scope=FitbitScope.SLEEP, entity_category=EntityCategory.DIAGNOSTIC, ) SLEEP_START_TIME_12HR = FitbitSensorEntityDescription( key="sleep/startTime", - name="Sleep Start Time", + translation_key="sleep_start_time", icon="mdi:clock", value_fn=_clock_format_12h, scope=FitbitScope.SLEEP, @@ -540,6 +562,7 @@ async def async_setup_entry( description, units=description.unit_fn(unit_system), enable_default_override=is_explicit_enable(description), + device_info=_build_device_info(entry, description), ) for description in resource_list if is_allowed_resource(description) @@ -574,6 +597,7 @@ class FitbitSensor(SensorEntity): entity_description: FitbitSensorEntityDescription _attr_attribution = ATTRIBUTION + _attr_has_entity_name = True def __init__( self, @@ -583,6 +607,7 @@ class FitbitSensor(SensorEntity): description: FitbitSensorEntityDescription, units: str | None, enable_default_override: bool, + device_info: DeviceInfo, ) -> None: """Initialize the Fitbit sensor.""" self.config_entry = config_entry @@ -590,6 +615,7 @@ class FitbitSensor(SensorEntity): self.api = api self._attr_unique_id = f"{user_profile_id}_{description.key}" + self._attr_device_info = device_info if units is not None: self._attr_native_unit_of_measurement = units diff --git a/homeassistant/components/fitbit/strings.json b/homeassistant/components/fitbit/strings.json index 2df6fa14b07..9029a8265bb 100644 --- a/homeassistant/components/fitbit/strings.json +++ b/homeassistant/components/fitbit/strings.json @@ -38,7 +38,82 @@ }, "battery_level": { "name": "Battery level" + }, + "activity_calories": { + "name": "Activity calories" + }, + "calories": { + "name": "Calories" + }, + "calories_bmr": { + "name": "Calories BMR" + }, + "elevation": { + "name": "Elevation" + }, + "floors": { + "name": "Floors" + }, + "resting_heart_rate": { + "name": "Resting heart rate" + }, + "minutes_fairly_active": { + "name": "Minutes fairly active" + }, + "minutes_lightly_active": { + "name": "Minutes lightly active" + }, + "minutes_sedentary": { + "name": "Minutes sedentary" + }, + "minutes_very_active": { + "name": "Minutes very active" + }, + "sleep_start_time": { + "name": "Sleep start time" + }, + "steps": { + "name": "Steps" + }, + "bmi": { + "name": "BMI" + }, + "body_fat": { + "name": "Body fat" + }, + "awakenings_count": { + "name": "Awakenings count" + }, + "sleep_efficiency": { + "name": "Sleep efficiency" + }, + "minutes_after_wakeup": { + "name": "Minutes after wakeup" + }, + "sleep_minutes_asleep": { + "name": "Sleep minutes asleep" + }, + "sleep_minutes_awake": { + "name": "Sleep minutes awake" + }, + "sleep_minutes_to_fall_asleep": { + "name": "Sleep minutes to fall asleep" + }, + "sleep_time_in_bed": { + "name": "Sleep time in bed" + }, + "calories_in": { + "name": "Calories in" + }, + "water": { + "name": "Water" } } + }, + + "device": { + "tracker": { + "name": "{display_name} tracker" + } } } diff --git a/tests/components/fitbit/conftest.py b/tests/components/fitbit/conftest.py index 48ceca02d0e..8a408748f16 100644 --- a/tests/components/fitbit/conftest.py +++ b/tests/components/fitbit/conftest.py @@ -90,6 +90,7 @@ def mock_config_entry( **imported_config_data, }, unique_id=PROFILE_USER_ID, + title=DISPLAY_NAME, ) diff --git a/tests/components/fitbit/snapshots/test_sensor.ambr b/tests/components/fitbit/snapshots/test_sensor.ambr index 55b2639a56d..068df25454d 100644 --- a/tests/components/fitbit/snapshots/test_sensor.ambr +++ b/tests/components/fitbit/snapshots/test_sensor.ambr @@ -4,7 +4,7 @@ '99', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Water', + 'friendly_name': 'First L. Water', 'icon': 'mdi:cup-water', 'state_class': , 'unit_of_measurement': , @@ -16,7 +16,7 @@ '1600', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Calories In', + 'friendly_name': 'First L. Calories in', 'icon': 'mdi:food-apple', 'state_class': , 'unit_of_measurement': 'cal', @@ -28,7 +28,7 @@ '99', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Water', + 'friendly_name': 'First L. Water', 'icon': 'mdi:cup-water', 'state_class': , 'unit_of_measurement': , @@ -40,19 +40,19 @@ '1600', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Calories In', + 'friendly_name': 'First L. Calories in', 'icon': 'mdi:food-apple', 'state_class': , 'unit_of_measurement': 'cal', }), ) # --- -# name: test_sensors[monitored_resources0-sensor.activity_calories-activities/activityCalories-135] +# name: test_sensors[monitored_resources0-sensor.first_l_activity_calories-activities/activityCalories-135] tuple( '135', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Activity Calories', + 'friendly_name': 'First L. Activity calories', 'icon': 'mdi:fire', 'state_class': , 'unit_of_measurement': 'cal', @@ -60,254 +60,26 @@ 'fitbit-api-user-id-1_activities/activityCalories', ) # --- -# name: test_sensors[monitored_resources1-sensor.calories-activities/calories-139] +# name: test_sensors[monitored_resources1-sensor.first_l_tracker_activity_calories-activities/tracker/activityCalories-135] tuple( - '139', + '135', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Calories', + 'friendly_name': 'First L. tracker Activity calories', 'icon': 'mdi:fire', 'state_class': , 'unit_of_measurement': 'cal', }), - 'fitbit-api-user-id-1_activities/calories', + 'fitbit-api-user-id-1_activities/tracker/activityCalories', ) # --- -# name: test_sensors[monitored_resources10-sensor.steps-activities/steps-5600] - tuple( - '5600', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Steps', - 'icon': 'mdi:walk', - 'state_class': , - 'unit_of_measurement': 'steps', - }), - 'fitbit-api-user-id-1_activities/steps', - ) -# --- -# name: test_sensors[monitored_resources11-sensor.weight-body/weight-175] - tuple( - '175.0', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'weight', - 'friendly_name': 'Weight', - 'icon': 'mdi:human', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_body/weight', - ) -# --- -# name: test_sensors[monitored_resources12-sensor.body_fat-body/fat-18] - tuple( - '18.0', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Body Fat', - 'icon': 'mdi:human', - 'state_class': , - 'unit_of_measurement': '%', - }), - 'fitbit-api-user-id-1_body/fat', - ) -# --- -# name: test_sensors[monitored_resources13-sensor.bmi-body/bmi-23.7] - tuple( - '23.7', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'BMI', - 'icon': 'mdi:human', - 'state_class': , - 'unit_of_measurement': 'BMI', - }), - 'fitbit-api-user-id-1_body/bmi', - ) -# --- -# name: test_sensors[monitored_resources14-sensor.awakenings_count-sleep/awakeningsCount-7] - tuple( - '7', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Awakenings Count', - 'icon': 'mdi:sleep', - 'state_class': , - 'unit_of_measurement': 'times awaken', - }), - 'fitbit-api-user-id-1_sleep/awakeningsCount', - ) -# --- -# name: test_sensors[monitored_resources15-sensor.sleep_efficiency-sleep/efficiency-80] - tuple( - '80', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Sleep Efficiency', - 'icon': 'mdi:sleep', - 'state_class': , - 'unit_of_measurement': '%', - }), - 'fitbit-api-user-id-1_sleep/efficiency', - ) -# --- -# name: test_sensors[monitored_resources16-sensor.minutes_after_wakeup-sleep/minutesAfterWakeup-17] - tuple( - '17', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'duration', - 'friendly_name': 'Minutes After Wakeup', - 'icon': 'mdi:sleep', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_sleep/minutesAfterWakeup', - ) -# --- -# name: test_sensors[monitored_resources17-sensor.sleep_minutes_asleep-sleep/minutesAsleep-360] - tuple( - '360', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'duration', - 'friendly_name': 'Sleep Minutes Asleep', - 'icon': 'mdi:sleep', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_sleep/minutesAsleep', - ) -# --- -# name: test_sensors[monitored_resources18-sensor.sleep_minutes_awake-sleep/minutesAwake-35] - tuple( - '35', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'duration', - 'friendly_name': 'Sleep Minutes Awake', - 'icon': 'mdi:sleep', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_sleep/minutesAwake', - ) -# --- -# name: test_sensors[monitored_resources19-sensor.sleep_minutes_to_fall_asleep-sleep/minutesToFallAsleep-35] - tuple( - '35', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'duration', - 'friendly_name': 'Sleep Minutes to Fall Asleep', - 'icon': 'mdi:sleep', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_sleep/minutesToFallAsleep', - ) -# --- -# name: test_sensors[monitored_resources2-sensor.distance-activities/distance-12.7] - tuple( - '12.70', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'distance', - 'friendly_name': 'Distance', - 'icon': 'mdi:map-marker', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_activities/distance', - ) -# --- -# name: test_sensors[monitored_resources20-sensor.sleep_start_time-sleep/startTime-2020-01-27T00:17:30.000] - tuple( - '2020-01-27T00:17:30.000', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Sleep Start Time', - 'icon': 'mdi:clock', - }), - 'fitbit-api-user-id-1_sleep/startTime', - ) -# --- -# name: test_sensors[monitored_resources21-sensor.sleep_time_in_bed-sleep/timeInBed-462] - tuple( - '462', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'duration', - 'friendly_name': 'Sleep Time in Bed', - 'icon': 'mdi:hotel', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_sleep/timeInBed', - ) -# --- -# name: test_sensors[monitored_resources3-sensor.elevation-activities/elevation-7600.24] - tuple( - '7600.24', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'distance', - 'friendly_name': 'Elevation', - 'icon': 'mdi:walk', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_activities/elevation', - ) -# --- -# name: test_sensors[monitored_resources4-sensor.floors-activities/floors-8] - tuple( - '8', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Floors', - 'icon': 'mdi:walk', - 'state_class': , - 'unit_of_measurement': 'floors', - }), - 'fitbit-api-user-id-1_activities/floors', - ) -# --- -# name: test_sensors[monitored_resources5-sensor.resting_heart_rate-activities/heart-api_value5] - tuple( - '76', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'friendly_name': 'Resting Heart Rate', - 'icon': 'mdi:heart-pulse', - 'state_class': , - 'unit_of_measurement': 'bpm', - }), - 'fitbit-api-user-id-1_activities/heart', - ) -# --- -# name: test_sensors[monitored_resources6-sensor.minutes_fairly_active-activities/minutesFairlyActive-35] - tuple( - '35', - ReadOnlyDict({ - 'attribution': 'Data provided by Fitbit.com', - 'device_class': 'duration', - 'friendly_name': 'Minutes Fairly Active', - 'icon': 'mdi:walk', - 'state_class': , - 'unit_of_measurement': , - }), - 'fitbit-api-user-id-1_activities/minutesFairlyActive', - ) -# --- -# name: test_sensors[monitored_resources7-sensor.minutes_lightly_active-activities/minutesLightlyActive-95] +# name: test_sensors[monitored_resources10-sensor.first_l_minutes_lightly_active-activities/minutesLightlyActive-95] tuple( '95', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', 'device_class': 'duration', - 'friendly_name': 'Minutes Lightly Active', + 'friendly_name': 'First L. Minutes lightly active', 'icon': 'mdi:walk', 'state_class': , 'unit_of_measurement': , @@ -315,13 +87,13 @@ 'fitbit-api-user-id-1_activities/minutesLightlyActive', ) # --- -# name: test_sensors[monitored_resources8-sensor.minutes_sedentary-activities/minutesSedentary-18] +# name: test_sensors[monitored_resources11-sensor.first_l_minutes_sedentary-activities/minutesSedentary-18] tuple( '18', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', 'device_class': 'duration', - 'friendly_name': 'Minutes Sedentary', + 'friendly_name': 'First L. Minutes sedentary', 'icon': 'mdi:seat-recline-normal', 'state_class': , 'unit_of_measurement': , @@ -329,13 +101,13 @@ 'fitbit-api-user-id-1_activities/minutesSedentary', ) # --- -# name: test_sensors[monitored_resources9-sensor.minutes_very_active-activities/minutesVeryActive-20] +# name: test_sensors[monitored_resources12-sensor.first_l_minutes_very_active-activities/minutesVeryActive-20] tuple( '20', ReadOnlyDict({ 'attribution': 'Data provided by Fitbit.com', 'device_class': 'duration', - 'friendly_name': 'Minutes Very Active', + 'friendly_name': 'First L. Minutes very active', 'icon': 'mdi:run', 'state_class': , 'unit_of_measurement': , @@ -343,3 +115,271 @@ 'fitbit-api-user-id-1_activities/minutesVeryActive', ) # --- +# name: test_sensors[monitored_resources13-sensor.first_l_steps-activities/steps-5600] + tuple( + '5600', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Steps', + 'icon': 'mdi:walk', + 'state_class': , + 'unit_of_measurement': 'steps', + }), + 'fitbit-api-user-id-1_activities/steps', + ) +# --- +# name: test_sensors[monitored_resources14-sensor.first_l_weight-body/weight-175] + tuple( + '175.0', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'weight', + 'friendly_name': 'First L. Weight', + 'icon': 'mdi:human', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_body/weight', + ) +# --- +# name: test_sensors[monitored_resources15-sensor.first_l_body_fat-body/fat-18] + tuple( + '18.0', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Body fat', + 'icon': 'mdi:human', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'fitbit-api-user-id-1_body/fat', + ) +# --- +# name: test_sensors[monitored_resources16-sensor.first_l_bmi-body/bmi-23.7] + tuple( + '23.7', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. BMI', + 'icon': 'mdi:human', + 'state_class': , + 'unit_of_measurement': 'BMI', + }), + 'fitbit-api-user-id-1_body/bmi', + ) +# --- +# name: test_sensors[monitored_resources17-sensor.first_l_awakenings_count-sleep/awakeningsCount-7] + tuple( + '7', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Awakenings count', + 'icon': 'mdi:sleep', + 'state_class': , + 'unit_of_measurement': 'times awaken', + }), + 'fitbit-api-user-id-1_sleep/awakeningsCount', + ) +# --- +# name: test_sensors[monitored_resources18-sensor.first_l_sleep_efficiency-sleep/efficiency-80] + tuple( + '80', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Sleep efficiency', + 'icon': 'mdi:sleep', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'fitbit-api-user-id-1_sleep/efficiency', + ) +# --- +# name: test_sensors[monitored_resources19-sensor.first_l_minutes_after_wakeup-sleep/minutesAfterWakeup-17] + tuple( + '17', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'duration', + 'friendly_name': 'First L. Minutes after wakeup', + 'icon': 'mdi:sleep', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_sleep/minutesAfterWakeup', + ) +# --- +# name: test_sensors[monitored_resources2-sensor.first_l_calories-activities/calories-139] + tuple( + '139', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Calories', + 'icon': 'mdi:fire', + 'state_class': , + 'unit_of_measurement': 'cal', + }), + 'fitbit-api-user-id-1_activities/calories', + ) +# --- +# name: test_sensors[monitored_resources20-sensor.first_l_sleep_minutes_asleep-sleep/minutesAsleep-360] + tuple( + '360', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'duration', + 'friendly_name': 'First L. Sleep minutes asleep', + 'icon': 'mdi:sleep', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_sleep/minutesAsleep', + ) +# --- +# name: test_sensors[monitored_resources21-sensor.first_l_sleep_minutes_awake-sleep/minutesAwake-35] + tuple( + '35', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'duration', + 'friendly_name': 'First L. Sleep minutes awake', + 'icon': 'mdi:sleep', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_sleep/minutesAwake', + ) +# --- +# name: test_sensors[monitored_resources22-sensor.first_l_sleep_minutes_to_fall_asleep-sleep/minutesToFallAsleep-35] + tuple( + '35', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'duration', + 'friendly_name': 'First L. Sleep minutes to fall asleep', + 'icon': 'mdi:sleep', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_sleep/minutesToFallAsleep', + ) +# --- +# name: test_sensors[monitored_resources23-sensor.first_l_sleep_start_time-sleep/startTime-2020-01-27T00:17:30.000] + tuple( + '2020-01-27T00:17:30.000', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Sleep start time', + 'icon': 'mdi:clock', + }), + 'fitbit-api-user-id-1_sleep/startTime', + ) +# --- +# name: test_sensors[monitored_resources24-sensor.first_l_sleep_time_in_bed-sleep/timeInBed-462] + tuple( + '462', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'duration', + 'friendly_name': 'First L. Sleep time in bed', + 'icon': 'mdi:hotel', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_sleep/timeInBed', + ) +# --- +# name: test_sensors[monitored_resources3-sensor.first_l_tracker_calories-activities/tracker/calories-139] + tuple( + '139', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. tracker Calories', + 'icon': 'mdi:fire', + 'state_class': , + 'unit_of_measurement': 'cal', + }), + 'fitbit-api-user-id-1_activities/tracker/calories', + ) +# --- +# name: test_sensors[monitored_resources4-sensor.first_l_distance-activities/distance-12.7] + tuple( + '12.70', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'distance', + 'friendly_name': 'First L. Distance', + 'icon': 'mdi:map-marker', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_activities/distance', + ) +# --- +# name: test_sensors[monitored_resources5-sensor.first_l_tracker_distance-activities/distance-12.7] + tuple( + 'unknown', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'distance', + 'friendly_name': 'First L. tracker Distance', + 'icon': 'mdi:map-marker', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_activities/tracker/distance', + ) +# --- +# name: test_sensors[monitored_resources6-sensor.first_l_elevation-activities/elevation-7600.24] + tuple( + '7600.24', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'distance', + 'friendly_name': 'First L. Elevation', + 'icon': 'mdi:walk', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_activities/elevation', + ) +# --- +# name: test_sensors[monitored_resources7-sensor.first_l_floors-activities/floors-8] + tuple( + '8', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Floors', + 'icon': 'mdi:walk', + 'state_class': , + 'unit_of_measurement': 'floors', + }), + 'fitbit-api-user-id-1_activities/floors', + ) +# --- +# name: test_sensors[monitored_resources8-sensor.first_l_resting_heart_rate-activities/heart-api_value8] + tuple( + '76', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'friendly_name': 'First L. Resting heart rate', + 'icon': 'mdi:heart-pulse', + 'state_class': , + 'unit_of_measurement': 'bpm', + }), + 'fitbit-api-user-id-1_activities/heart', + ) +# --- +# name: test_sensors[monitored_resources9-sensor.first_l_minutes_fairly_active-activities/minutesFairlyActive-35] + tuple( + '35', + ReadOnlyDict({ + 'attribution': 'Data provided by Fitbit.com', + 'device_class': 'duration', + 'friendly_name': 'First L. Minutes fairly active', + 'icon': 'mdi:walk', + 'state_class': , + 'unit_of_measurement': , + }), + 'fitbit-api-user-id-1_activities/minutesFairlyActive', + ) +# --- diff --git a/tests/components/fitbit/test_sensor.py b/tests/components/fitbit/test_sensor.py index d67bd75396f..cee9835f89f 100644 --- a/tests/components/fitbit/test_sensor.py +++ b/tests/components/fitbit/test_sensor.py @@ -78,133 +78,151 @@ def mock_token_refresh(requests_mock: Mocker) -> None: [ ( ["activities/activityCalories"], - "sensor.activity_calories", + "sensor.first_l_activity_calories", "activities/activityCalories", "135", ), + ( + ["activities/tracker/activityCalories"], + "sensor.first_l_tracker_activity_calories", + "activities/tracker/activityCalories", + "135", + ), ( ["activities/calories"], - "sensor.calories", + "sensor.first_l_calories", "activities/calories", "139", ), + ( + ["activities/tracker/calories"], + "sensor.first_l_tracker_calories", + "activities/tracker/calories", + "139", + ), ( ["activities/distance"], - "sensor.distance", + "sensor.first_l_distance", + "activities/distance", + "12.7", + ), + ( + ["activities/tracker/distance"], + "sensor.first_l_tracker_distance", "activities/distance", "12.7", ), ( ["activities/elevation"], - "sensor.elevation", + "sensor.first_l_elevation", "activities/elevation", "7600.24", ), ( ["activities/floors"], - "sensor.floors", + "sensor.first_l_floors", "activities/floors", "8", ), ( ["activities/heart"], - "sensor.resting_heart_rate", + "sensor.first_l_resting_heart_rate", "activities/heart", {"restingHeartRate": 76}, ), ( ["activities/minutesFairlyActive"], - "sensor.minutes_fairly_active", + "sensor.first_l_minutes_fairly_active", "activities/minutesFairlyActive", 35, ), ( ["activities/minutesLightlyActive"], - "sensor.minutes_lightly_active", + "sensor.first_l_minutes_lightly_active", "activities/minutesLightlyActive", 95, ), ( ["activities/minutesSedentary"], - "sensor.minutes_sedentary", + "sensor.first_l_minutes_sedentary", "activities/minutesSedentary", 18, ), ( ["activities/minutesVeryActive"], - "sensor.minutes_very_active", + "sensor.first_l_minutes_very_active", "activities/minutesVeryActive", 20, ), ( ["activities/steps"], - "sensor.steps", + "sensor.first_l_steps", "activities/steps", "5600", ), ( ["body/weight"], - "sensor.weight", + "sensor.first_l_weight", "body/weight", "175", ), ( ["body/fat"], - "sensor.body_fat", + "sensor.first_l_body_fat", "body/fat", "18", ), ( ["body/bmi"], - "sensor.bmi", + "sensor.first_l_bmi", "body/bmi", "23.7", ), ( ["sleep/awakeningsCount"], - "sensor.awakenings_count", + "sensor.first_l_awakenings_count", "sleep/awakeningsCount", "7", ), ( ["sleep/efficiency"], - "sensor.sleep_efficiency", + "sensor.first_l_sleep_efficiency", "sleep/efficiency", "80", ), ( ["sleep/minutesAfterWakeup"], - "sensor.minutes_after_wakeup", + "sensor.first_l_minutes_after_wakeup", "sleep/minutesAfterWakeup", "17", ), ( ["sleep/minutesAsleep"], - "sensor.sleep_minutes_asleep", + "sensor.first_l_sleep_minutes_asleep", "sleep/minutesAsleep", "360", ), ( ["sleep/minutesAwake"], - "sensor.sleep_minutes_awake", + "sensor.first_l_sleep_minutes_awake", "sleep/minutesAwake", "35", ), ( ["sleep/minutesToFallAsleep"], - "sensor.sleep_minutes_to_fall_asleep", + "sensor.first_l_sleep_minutes_to_fall_asleep", "sleep/minutesToFallAsleep", "35", ), ( ["sleep/startTime"], - "sensor.sleep_start_time", + "sensor.first_l_sleep_start_time", "sleep/startTime", "2020-01-27T00:17:30.000", ), ( ["sleep/timeInBed"], - "sensor.sleep_time_in_bed", + "sensor.first_l_sleep_time_in_bed", "sleep/timeInBed", "462", ), @@ -359,7 +377,7 @@ async def test_profile_local( entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 - state = hass.states.get("sensor.weight") + state = hass.states.get("sensor.first_l_weight") assert state assert state.attributes.get("unit_of_measurement") == expected_unit @@ -409,7 +427,7 @@ async def test_sleep_time_clock_format( ) assert await integration_setup() - state = hass.states.get("sensor.sleep_start_time") + state = hass.states.get("sensor.first_l_sleep_start_time") assert state assert state.state == expected_state @@ -445,16 +463,16 @@ async def test_activity_scope_config_entry( states = hass.states.async_all() assert {s.entity_id for s in states} == { - "sensor.activity_calories", - "sensor.calories", - "sensor.distance", - "sensor.elevation", - "sensor.floors", - "sensor.minutes_fairly_active", - "sensor.minutes_lightly_active", - "sensor.minutes_sedentary", - "sensor.minutes_very_active", - "sensor.steps", + "sensor.first_l_activity_calories", + "sensor.first_l_calories", + "sensor.first_l_distance", + "sensor.first_l_elevation", + "sensor.first_l_floors", + "sensor.first_l_minutes_fairly_active", + "sensor.first_l_minutes_lightly_active", + "sensor.first_l_minutes_sedentary", + "sensor.first_l_minutes_very_active", + "sensor.first_l_steps", } @@ -478,7 +496,7 @@ async def test_heartrate_scope_config_entry( states = hass.states.async_all() assert {s.entity_id for s in states} == { - "sensor.resting_heart_rate", + "sensor.first_l_resting_heart_rate", } @@ -506,11 +524,11 @@ async def test_nutrition_scope_config_entry( ) assert await integration_setup() - state = hass.states.get("sensor.water") + state = hass.states.get("sensor.first_l_water") assert state assert (state.state, state.attributes) == snapshot - state = hass.states.get("sensor.calories_in") + state = hass.states.get("sensor.first_l_calories_in") assert state assert (state.state, state.attributes) == snapshot @@ -545,14 +563,14 @@ async def test_sleep_scope_config_entry( states = hass.states.async_all() assert {s.entity_id for s in states} == { - "sensor.awakenings_count", - "sensor.sleep_efficiency", - "sensor.minutes_after_wakeup", - "sensor.sleep_minutes_asleep", - "sensor.sleep_minutes_awake", - "sensor.sleep_minutes_to_fall_asleep", - "sensor.sleep_time_in_bed", - "sensor.sleep_start_time", + "sensor.first_l_awakenings_count", + "sensor.first_l_sleep_efficiency", + "sensor.first_l_minutes_after_wakeup", + "sensor.first_l_sleep_minutes_asleep", + "sensor.first_l_sleep_minutes_awake", + "sensor.first_l_sleep_minutes_to_fall_asleep", + "sensor.first_l_sleep_time_in_bed", + "sensor.first_l_sleep_start_time", } @@ -573,7 +591,7 @@ async def test_weight_scope_config_entry( states = hass.states.async_all() assert [s.entity_id for s in states] == [ - "sensor.weight", + "sensor.first_l_weight", ] @@ -623,7 +641,7 @@ async def test_sensor_update_failed( assert await integration_setup() - state = hass.states.get("sensor.resting_heart_rate") + state = hass.states.get("sensor.first_l_resting_heart_rate") assert state assert state.state == "unavailable" @@ -655,7 +673,7 @@ async def test_sensor_update_failed_requires_reauth( assert await integration_setup() - state = hass.states.get("sensor.resting_heart_rate") + state = hass.states.get("sensor.first_l_resting_heart_rate") assert state assert state.state == "unavailable" @@ -698,14 +716,14 @@ async def test_sensor_update_success( assert await integration_setup() - state = hass.states.get("sensor.resting_heart_rate") + state = hass.states.get("sensor.first_l_resting_heart_rate") assert state assert state.state == "60" - await async_update_entity(hass, "sensor.resting_heart_rate") + await async_update_entity(hass, "sensor.first_l_resting_heart_rate") await hass.async_block_till_done() - state = hass.states.get("sensor.resting_heart_rate") + state = hass.states.get("sensor.first_l_resting_heart_rate") assert state assert state.state == "70" @@ -867,6 +885,6 @@ async def test_resting_heart_rate_responses( ) assert await integration_setup() - state = hass.states.get("sensor.resting_heart_rate") + state = hass.states.get("sensor.first_l_resting_heart_rate") assert state assert state.state == expected_state