Return group unit of measurement when device_class is None (#110973)

* Groups: Return units when device_class is None

* Fixes

* Mods

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
This commit is contained in:
Steve HOLWEG 2024-02-26 04:44:05 +01:00 committed by GitHub
parent 34e9c29ef2
commit 4a128f1225
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 123 additions and 16 deletions

View file

@ -396,7 +396,7 @@ class SensorGroup(GroupEntity, SensorEntity):
self._state_incorrect.add(entity_id) self._state_incorrect.add(entity_id)
_LOGGER.warning( _LOGGER.warning(
"Unable to use state. Only entities with correct unit of measurement" "Unable to use state. Only entities with correct unit of measurement"
" is supported when having a device class," " is supported,"
" entity %s, value %s with device class %s" " entity %s, value %s with device class %s"
" and unit of measurement %s excluded from calculation in %s", " and unit of measurement %s excluded from calculation in %s",
entity_id, entity_id,
@ -548,19 +548,28 @@ class SensorGroup(GroupEntity, SensorEntity):
# Ensure only valid unit of measurements for the specific device class can be used # Ensure only valid unit of measurements for the specific device class can be used
if ( if (
# Test if uom's in device class is convertible (
(device_class := self.device_class) in UNIT_CONVERTERS # Test if uom's in device class is convertible
and all( (device_class := self.device_class) in UNIT_CONVERTERS
uom in UNIT_CONVERTERS[device_class].VALID_UNITS and all(
for uom in unit_of_measurements uom in UNIT_CONVERTERS[device_class].VALID_UNITS
for uom in unit_of_measurements
)
) )
) or ( or (
# Test if uom's in device class is not convertible # Test if uom's in device class is not convertible
device_class device_class
and device_class not in UNIT_CONVERTERS and device_class not in UNIT_CONVERTERS
and device_class in DEVICE_CLASS_UNITS and device_class in DEVICE_CLASS_UNITS
and all( and all(
uom in DEVICE_CLASS_UNITS[device_class] for uom in unit_of_measurements uom in DEVICE_CLASS_UNITS[device_class]
for uom in unit_of_measurements
)
)
or (
# Test no device class and all uom's are same
device_class is None
and all(x == unit_of_measurements[0] for x in unit_of_measurements)
) )
): ):
async_delete_issue( async_delete_issue(
@ -608,6 +617,7 @@ class SensorGroup(GroupEntity, SensorEntity):
"""Return valid units. """Return valid units.
If device class is set and compatible unit of measurements. If device class is set and compatible unit of measurements.
If device class is not set, use one unit of measurement.
""" """
if ( if (
device_class := self.device_class device_class := self.device_class
@ -621,4 +631,6 @@ class SensorGroup(GroupEntity, SensorEntity):
): ):
valid_uoms: set = DEVICE_CLASS_UNITS[device_class] valid_uoms: set = DEVICE_CLASS_UNITS[device_class]
return valid_uoms return valid_uoms
if device_class is None and self.native_unit_of_measurement:
return {self.native_unit_of_measurement}
return set() return set()

View file

@ -257,7 +257,7 @@
}, },
"uoms_not_matching_no_device_class": { "uoms_not_matching_no_device_class": {
"title": "Unit of measurements is not correct", "title": "Unit of measurements is not correct",
"description": "Unit of measurements `{uoms}` of input sensors `{source_entities}` are not compatible using no device class of sensor group `{entity_id}`.\n\nPlease correct the unit of measurements on the source entities or set a proper device class on the sensor group and reload the group sensor to fix this issue." "description": "Unit of measurements `{uoms}` of input sensors `{source_entities}` are not compatible when not using a device class on sensor group `{entity_id}`.\n\nPlease correct the unit of measurements on the source entities or set a proper device class on the sensor group and reload the group sensor to fix this issue."
}, },
"device_classes_not_matching": { "device_classes_not_matching": {
"title": "Device classes is not correct", "title": "Device classes is not correct",

View file

@ -424,6 +424,101 @@ async def test_sensor_calculated_properties(hass: HomeAssistant) -> None:
assert state.state == str(float(sum(VALUES))) assert state.state == str(float(sum(VALUES)))
async def test_sensor_with_uoms_but_no_device_class(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test the sensor works with same uom when there is no device class."""
config = {
SENSOR_DOMAIN: {
"platform": GROUP_DOMAIN,
"name": "test_sum",
"type": "sum",
"entities": ["sensor.test_1", "sensor.test_2", "sensor.test_3"],
"unique_id": "very_unique_id_last_sensor",
}
}
entity_ids = config["sensor"]["entities"]
hass.states.async_set(
entity_ids[0],
VALUES[0],
{
"device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT,
"unit_of_measurement": "W",
},
)
hass.states.async_set(
entity_ids[1],
VALUES[1],
{
"device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT,
"unit_of_measurement": "W",
},
)
hass.states.async_set(
entity_ids[2],
VALUES[2],
{
"unit_of_measurement": "W",
},
)
await hass.async_block_till_done()
assert await async_setup_component(hass, "sensor", config)
await hass.async_block_till_done()
state = hass.states.get("sensor.test_sum")
assert state.attributes.get("device_class") is None
assert state.attributes.get("state_class") is None
assert state.attributes.get("unit_of_measurement") == "W"
assert state.state == str(float(sum(VALUES)))
assert not issue_registry.issues
hass.states.async_set(
entity_ids[0],
VALUES[0],
{
"device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT,
"unit_of_measurement": "kW",
},
)
await hass.async_block_till_done()
state = hass.states.get("sensor.test_sum")
assert state.attributes.get("device_class") is None
assert state.attributes.get("state_class") is None
assert state.attributes.get("unit_of_measurement") == "W"
assert state.state == STATE_UNKNOWN
assert (
"Unable to use state. Only entities with correct unit of measurement is supported"
in caplog.text
)
hass.states.async_set(
entity_ids[0],
VALUES[0],
{
"device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT,
"unit_of_measurement": "W",
},
)
await hass.async_block_till_done()
state = hass.states.get("sensor.test_sum")
assert state.attributes.get("device_class") is None
assert state.attributes.get("state_class") is None
assert state.attributes.get("unit_of_measurement") == "W"
assert state.state == str(float(sum(VALUES)))
async def test_sensor_calculated_properties_not_same( async def test_sensor_calculated_properties_not_same(
hass: HomeAssistant, issue_registry: ir.IssueRegistry hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None: ) -> None:
@ -616,7 +711,7 @@ async def test_sensor_calculated_properties_not_convertible_device_class(
assert ( assert (
"Unable to use state. Only entities with correct unit of measurement is" "Unable to use state. Only entities with correct unit of measurement is"
" supported when having a device class" " supported"
) not in caplog.text ) not in caplog.text
hass.states.async_set( hass.states.async_set(
@ -637,7 +732,7 @@ async def test_sensor_calculated_properties_not_convertible_device_class(
assert ( assert (
"Unable to use state. Only entities with correct unit of measurement is" "Unable to use state. Only entities with correct unit of measurement is"
" supported when having a device class, entity sensor.test_3, value 15.3 with" " supported, entity sensor.test_3, value 15.3 with"
" device class humidity and unit of measurement None excluded from calculation" " device class humidity and unit of measurement None excluded from calculation"
" in sensor.test_sum" " in sensor.test_sum"
) in caplog.text ) in caplog.text