diff --git a/homeassistant/components/group/sensor.py b/homeassistant/components/group/sensor.py index b67e10dd960c26..078b6a29739357 100644 --- a/homeassistant/components/group/sensor.py +++ b/homeassistant/components/group/sensor.py @@ -346,7 +346,6 @@ def __init__( self._attr_name = name if name == DEFAULT_NAME: self._attr_name = f"{DEFAULT_NAME} {sensor_type}".capitalize() - self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids} self._attr_unique_id = unique_id self._ignore_non_numeric = ignore_non_numeric self.mode = all if ignore_non_numeric is False else any @@ -374,7 +373,7 @@ def calculate_state_attributes(self, valid_state_entities: list[str]) -> None: def async_update_group_state(self) -> None: """Query all members and determine the sensor group state.""" self.calculate_state_attributes(self._get_valid_entities()) - states: list[str] = [] + states: list[str | None] = [] valid_units = self._valid_units valid_states: list[bool] = [] sensor_values: list[tuple[str, float, State]] = [] @@ -435,9 +434,12 @@ def async_update_group_state(self) -> None: state.attributes.get("unit_of_measurement"), self.entity_id, ) + else: + states.append(None) + valid_states.append(False) - # Set group as unavailable if all members do not have numeric values - self._attr_available = any(numeric_state for numeric_state in valid_states) + # Set group as unavailable if all members are unavailable or missing + self._attr_available = not all(s in (STATE_UNAVAILABLE, None) for s in states) valid_state = self.mode( state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) for state in states @@ -446,6 +448,7 @@ def async_update_group_state(self) -> None: if not valid_state or not valid_state_numeric: self._attr_native_value = None + self._extra_state_attribute = {} return # Calculate values diff --git a/homeassistant/components/matter/binary_sensor.py b/homeassistant/components/matter/binary_sensor.py index a3148f3719fdfb..71e43d6dfffc13 100644 --- a/homeassistant/components/matter/binary_sensor.py +++ b/homeassistant/components/matter/binary_sensor.py @@ -528,7 +528,10 @@ def _update_from_device(self) -> None: ), ), entity_class=MatterBinarySensor, - required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,), + required_attributes=( + clusters.Thermostat.Attributes.RemoteSensing, + clusters.Thermostat.Attributes.OutdoorTemperature, + ), allow_multi=True, ), MatterDiscoverySchema( diff --git a/homeassistant/components/portainer/manifest.json b/homeassistant/components/portainer/manifest.json index fa15be5a5a1010..0f35f2b68f836b 100644 --- a/homeassistant/components/portainer/manifest.json +++ b/homeassistant/components/portainer/manifest.json @@ -7,5 +7,5 @@ "integration_type": "hub", "iot_class": "local_polling", "quality_scale": "bronze", - "requirements": ["pyportainer==1.0.22"] + "requirements": ["pyportainer==1.0.23"] } diff --git a/requirements_all.txt b/requirements_all.txt index e614dc70d4b268..0ed594afa6f27e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2320,7 +2320,7 @@ pyplaato==0.0.19 pypoint==3.0.0 # homeassistant.components.portainer -pyportainer==1.0.22 +pyportainer==1.0.23 # homeassistant.components.probe_plus pyprobeplus==1.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c30a5015ceffb8..37a6adc73ad21f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1964,7 +1964,7 @@ pyplaato==0.0.19 pypoint==3.0.0 # homeassistant.components.portainer -pyportainer==1.0.22 +pyportainer==1.0.23 # homeassistant.components.probe_plus pyprobeplus==1.1.2 diff --git a/tests/components/group/test_sensor.py b/tests/components/group/test_sensor.py index fe0f47014b8146..dafdbbb9c3df88 100644 --- a/tests/components/group/test_sensor.py +++ b/tests/components/group/test_sensor.py @@ -206,7 +206,7 @@ async def test_not_enough_sensor_value(hass: HomeAssistant) -> None: await hass.async_block_till_done() state = hass.states.get("sensor.test_max") - assert state.state == STATE_UNAVAILABLE + assert state.state == STATE_UNKNOWN assert state.attributes.get("min_entity_id") is None assert state.attributes.get("max_entity_id") is None @@ -215,20 +215,20 @@ async def test_not_enough_sensor_value(hass: HomeAssistant) -> None: state = hass.states.get("sensor.test_max") assert state.state not in [STATE_UNAVAILABLE, STATE_UNKNOWN] - assert entity_ids[1] == state.attributes.get("max_entity_id") + assert state.attributes.get("max_entity_id") == entity_ids[1] hass.states.async_set(entity_ids[2], STATE_UNKNOWN) await hass.async_block_till_done() state = hass.states.get("sensor.test_max") assert state.state not in [STATE_UNAVAILABLE, STATE_UNKNOWN] - assert entity_ids[1] == state.attributes.get("max_entity_id") + assert state.attributes.get("max_entity_id") == entity_ids[1] hass.states.async_set(entity_ids[1], STATE_UNAVAILABLE) await hass.async_block_till_done() state = hass.states.get("sensor.test_max") - assert state.state == STATE_UNAVAILABLE + assert state.state == STATE_UNKNOWN assert state.attributes.get("min_entity_id") is None assert state.attributes.get("max_entity_id") is None @@ -281,14 +281,14 @@ async def test_reload(hass: HomeAssistant) -> None: (STATES_ONE_MISSING, "17.0"), (STATES_ONE_UNKNOWN, "17.0"), (STATES_ONE_UNAVAILABLE, "17.0"), - (STATES_ALL_ERROR, STATE_UNAVAILABLE), + (STATES_ALL_ERROR, STATE_UNKNOWN), (STATES_ALL_MISSING, STATE_UNAVAILABLE), - (STATES_ALL_UNKNOWN, STATE_UNAVAILABLE), + (STATES_ALL_UNKNOWN, STATE_UNKNOWN), (STATES_ALL_UNAVAILABLE, STATE_UNAVAILABLE), (STATES_MIX_MISSING_UNAVAILABLE, STATE_UNAVAILABLE), - (STATES_MIX_MISSING_UNKNOWN, STATE_UNAVAILABLE), - (STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE), - (STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE), + (STATES_MIX_MISSING_UNKNOWN, STATE_UNKNOWN), + (STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNKNOWN), + (STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNKNOWN), ], ) async def test_sensor_incorrect_state_with_ignore_non_numeric( @@ -339,17 +339,17 @@ async def test_sensor_incorrect_state_with_ignore_non_numeric( ("states_list", "expected_group_state", "error_count"), [ (STATES_ONE_ERROR, STATE_UNKNOWN, 1), - (STATES_ONE_MISSING, "17.0", 0), + (STATES_ONE_MISSING, STATE_UNKNOWN, 0), (STATES_ONE_UNKNOWN, STATE_UNKNOWN, 1), (STATES_ONE_UNAVAILABLE, STATE_UNKNOWN, 1), - (STATES_ALL_ERROR, STATE_UNAVAILABLE, 3), + (STATES_ALL_ERROR, STATE_UNKNOWN, 3), (STATES_ALL_MISSING, STATE_UNAVAILABLE, 0), - (STATES_ALL_UNKNOWN, STATE_UNAVAILABLE, 3), + (STATES_ALL_UNKNOWN, STATE_UNKNOWN, 3), (STATES_ALL_UNAVAILABLE, STATE_UNAVAILABLE, 3), - (STATES_MIX_MISSING_UNKNOWN, STATE_UNAVAILABLE, 2), - (STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE, 3), (STATES_MIX_MISSING_UNAVAILABLE, STATE_UNAVAILABLE, 2), - (STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE, 2), + (STATES_MIX_MISSING_UNKNOWN, STATE_UNKNOWN, 2), + (STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNKNOWN, 3), + (STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNKNOWN, 2), ], ) async def test_sensor_incorrect_state_with_not_ignore_non_numeric( @@ -402,17 +402,17 @@ async def test_sensor_incorrect_state_with_not_ignore_non_numeric( ("states_list", "expected_group_state"), [ (STATES_ONE_ERROR, STATE_UNKNOWN), - (STATES_ONE_MISSING, "32.3"), + (STATES_ONE_MISSING, STATE_UNKNOWN), (STATES_ONE_UNKNOWN, STATE_UNKNOWN), (STATES_ONE_UNAVAILABLE, STATE_UNKNOWN), - (STATES_ALL_ERROR, STATE_UNAVAILABLE), + (STATES_ALL_ERROR, STATE_UNKNOWN), (STATES_ALL_MISSING, STATE_UNAVAILABLE), - (STATES_ALL_UNKNOWN, STATE_UNAVAILABLE), + (STATES_ALL_UNKNOWN, STATE_UNKNOWN), (STATES_ALL_UNAVAILABLE, STATE_UNAVAILABLE), (STATES_MIX_MISSING_UNAVAILABLE, STATE_UNAVAILABLE), - (STATES_MIX_MISSING_UNKNOWN, STATE_UNAVAILABLE), - (STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE), - (STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE), + (STATES_MIX_MISSING_UNKNOWN, STATE_UNKNOWN), + (STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNKNOWN), + (STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNKNOWN), ], ) async def test_sensor_require_all_states( @@ -740,7 +740,7 @@ async def test_sensor_calculated_result_fails_on_uom(hass: HomeAssistant) -> Non await hass.async_block_till_done() state = hass.states.get("sensor.test_sum") - assert state.state == STATE_UNAVAILABLE + assert state.state == STATE_UNKNOWN assert state.attributes.get("device_class") == "energy" assert state.attributes.get("state_class") == "total" assert state.attributes.get("unit_of_measurement") is None @@ -847,12 +847,18 @@ async def test_last_sensor(hass: HomeAssistant) -> None: entity_ids = config["sensor"]["entities"] + for entity_id in entity_ids[1:]: + hass.states.async_set(entity_id, "0.0") + await hass.async_block_till_done() + state = hass.states.get("sensor.test_last") + assert state.state == STATE_UNKNOWN + for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items(): hass.states.async_set(entity_id, str(value)) await hass.async_block_till_done() state = hass.states.get("sensor.test_last") - assert str(float(value)) == state.state - assert entity_id == state.attributes.get("last_entity_id") + assert state.state == str(float(value)) + assert state.attributes.get("last_entity_id") == entity_id async def test_sensors_attributes_added_when_entity_info_available( diff --git a/tests/components/matter/snapshots/test_binary_sensor.ambr b/tests/components/matter/snapshots/test_binary_sensor.ambr index 9b2254e7fd3d5f..b483fff7dd78b8 100644 --- a/tests/components/matter/snapshots/test_binary_sensor.ambr +++ b/tests/components/matter/snapshots/test_binary_sensor.ambr @@ -439,54 +439,6 @@ 'state': 'off', }) # --- -# name: test_binary_sensors[eve_thermo_v4][binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'binary_sensor', - 'entity_category': , - 'entity_id': 'binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': 'Outdoor temperature remote sensing', - 'platform': 'matter', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'thermostat_remote_sensing_outdoor_temperature', - 'unique_id': '00000000000004D2-0000000000000021-MatterNodeDevice-1-ThermostatRemoteSensing_OutdoorTemperature-513-26', - 'unit_of_measurement': None, - }) -# --- -# name: test_binary_sensors[eve_thermo_v4][binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Eve Thermo 20EBP1701 Outdoor temperature remote sensing', - }), - 'context': , - 'entity_id': 'binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- # name: test_binary_sensors[eve_thermo_v5][binary_sensor.eve_thermo_20ecd1701_local_temperature_remote_sensing-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -535,54 +487,6 @@ 'state': 'off', }) # --- -# name: test_binary_sensors[eve_thermo_v5][binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'binary_sensor', - 'entity_category': , - 'entity_id': 'binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': 'Outdoor temperature remote sensing', - 'platform': 'matter', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'thermostat_remote_sensing_outdoor_temperature', - 'unique_id': '00000000000004D2-000000000000000C-MatterNodeDevice-1-ThermostatRemoteSensing_OutdoorTemperature-513-26', - 'unit_of_measurement': None, - }) -# --- -# name: test_binary_sensors[eve_thermo_v5][binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Eve Thermo 20ECD1701 Outdoor temperature remote sensing', - }), - 'context': , - 'entity_id': 'binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- # name: test_binary_sensors[heiman_motion_sensor_m1][binary_sensor.smart_motion_sensor_occupancy-entry] EntityRegistryEntrySnapshot({ 'aliases': set({