From 1c27931716c4ac7e07ee3b7204afce44fc80f6c0 Mon Sep 17 00:00:00 2001 From: ReneNulschDE Date: Tue, 16 Dec 2025 09:21:10 +0100 Subject: [PATCH 1/5] Add zero-guard for inverse unit conversions in unit_conversion --- homeassistant/util/unit_conversion.py | 2 +- tests/util/test_unit_conversion.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index d1c77a89170608..cff272a2c82b9a 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -129,7 +129,7 @@ def converter_factory( return lambda value: value from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit) if cls._are_unit_inverses(from_unit, to_unit): - return lambda val: to_ratio / (val / from_ratio) + return lambda val: 0 if val == 0 else to_ratio / (val / from_ratio) return lambda val: (val / from_ratio) * to_ratio @classmethod diff --git a/tests/util/test_unit_conversion.py b/tests/util/test_unit_conversion.py index 8a3ce0939358d8..45d952b94af47e 100644 --- a/tests/util/test_unit_conversion.py +++ b/tests/util/test_unit_conversion.py @@ -592,6 +592,12 @@ 4, UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, ), + ( + 0, + UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM, + 0, + UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, + ), ( 20, UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR, From 1a733978b6131112285dea125d811946c202b68d Mon Sep 17 00:00:00 2001 From: ReneNulschDE Date: Tue, 16 Dec 2025 09:52:19 +0100 Subject: [PATCH 2/5] Add zero guard to converter_factory_allow_none --- homeassistant/util/unit_conversion.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index cff272a2c82b9a..61b7821239df93 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -155,7 +155,11 @@ def converter_factory_allow_none( return lambda value: value from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit) if cls._are_unit_inverses(from_unit, to_unit): - return lambda val: None if val is None else to_ratio / (val / from_ratio) + return ( + lambda val: None + if val is None + else (0 if val == 0 else to_ratio / (val / from_ratio)) + ) return lambda val: None if val is None else (val / from_ratio) * to_ratio @classmethod From ad22657f0accb77eaef084bc6cd8a321c4fa2bdd Mon Sep 17 00:00:00 2001 From: ReneNulschDE Date: Wed, 17 Dec 2025 14:37:36 +0100 Subject: [PATCH 3/5] Change conversion result to None --- homeassistant/util/unit_conversion.py | 4 ++-- tests/util/test_unit_conversion.py | 30 +++++++++++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index 61b7821239df93..54ac75eb66c064 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -157,8 +157,8 @@ def converter_factory_allow_none( if cls._are_unit_inverses(from_unit, to_unit): return ( lambda val: None - if val is None - else (0 if val == 0 else to_ratio / (val / from_ratio)) + if val is None or val == 0 + else to_ratio / (val / from_ratio) ) return lambda val: None if val is None else (val / from_ratio) * to_ratio diff --git a/tests/util/test_unit_conversion.py b/tests/util/test_unit_conversion.py index 45d952b94af47e..dd9e97d9c26a52 100644 --- a/tests/util/test_unit_conversion.py +++ b/tests/util/test_unit_conversion.py @@ -592,12 +592,6 @@ 4, UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, ), - ( - 0, - UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM, - 0, - UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, - ), ( 20, UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR, @@ -1272,6 +1266,30 @@ def test_unit_conversion_factory_allow_none_with_none() -> None: ) +def test_unit_conversion_factory_allow_none_with_zero_for_inverse_units() -> None: + """Test converter_factory_allow_none returns None for zero with inverse units.""" + # Test EnergyDistanceConverter with inverse units (kWh/100km <-> km/kWh) + assert ( + EnergyDistanceConverter.converter_factory_allow_none( + UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM, + UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, + )(0) + is None + ) + assert ( + EnergyDistanceConverter.converter_factory_allow_none( + UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, + UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM, + )(0) + is None + ) + # Test with non-zero value to ensure normal conversion still works + assert EnergyDistanceConverter.converter_factory_allow_none( + UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM, + UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, + )(25) == pytest.approx(4) + + @pytest.mark.parametrize( ("converter", "value", "from_unit", "expected", "to_unit"), chain( From d17c72d84dff861b0c14946b6db49eb1eb0bb5be Mon Sep 17 00:00:00 2001 From: ReneNulschDE Date: Wed, 17 Dec 2025 14:42:35 +0100 Subject: [PATCH 4/5] revert zero guard as this is not used in the statistics job --- homeassistant/util/unit_conversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index 54ac75eb66c064..68b27b98cd8d30 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -129,7 +129,7 @@ def converter_factory( return lambda value: value from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit) if cls._are_unit_inverses(from_unit, to_unit): - return lambda val: 0 if val == 0 else to_ratio / (val / from_ratio) + return lambda val: to_ratio / (val / from_ratio) return lambda val: (val / from_ratio) * to_ratio @classmethod From fc6d41ffafc4f555b13817da420e564bc611ca41 Mon Sep 17 00:00:00 2001 From: ReneNulschDE Date: Wed, 17 Dec 2025 17:32:29 +0100 Subject: [PATCH 5/5] Add more tests --- tests/util/test_unit_conversion.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/util/test_unit_conversion.py b/tests/util/test_unit_conversion.py index dd9e97d9c26a52..7351ef60d3ac0b 100644 --- a/tests/util/test_unit_conversion.py +++ b/tests/util/test_unit_conversion.py @@ -1264,6 +1264,34 @@ def test_unit_conversion_factory_allow_none_with_none() -> None: )(None) is None ) + assert ( + EnergyDistanceConverter.converter_factory_allow_none( + UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR, + UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM, + )(0) + is None + ) + assert ( + EnergyDistanceConverter.converter_factory_allow_none( + UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM, + UnitOfEnergyDistance.WATT_HOUR_PER_KM, + )(0) + == 0 + ) + assert ( + EnergyDistanceConverter.converter_factory_allow_none( + UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, + UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR, + )(0.0) + == 0.0 + ) + assert ( + EnergyDistanceConverter.converter_factory_allow_none( + UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR, + UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR, + )(0) + == 0.0 + ) def test_unit_conversion_factory_allow_none_with_zero_for_inverse_units() -> None: