From 9f14a981580de4ebbd97e6231760dffc622a899b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:30:32 +0000 Subject: [PATCH 1/3] Use BLOOD_GLUCOSE_CONCENTRATION device class in dexcom --- homeassistant/components/dexcom/__init__.py | 16 +----- .../components/dexcom/config_flow.py | 43 ++------------- homeassistant/components/dexcom/const.py | 3 -- homeassistant/components/dexcom/sensor.py | 24 ++++++--- tests/components/dexcom/__init__.py | 7 ++- tests/components/dexcom/test_config_flow.py | 54 +------------------ tests/components/dexcom/test_sensor.py | 44 ++++++--------- 7 files changed, 46 insertions(+), 145 deletions(-) diff --git a/homeassistant/components/dexcom/__init__.py b/homeassistant/components/dexcom/__init__.py index b9a3bdba12d..e93e8e66358 100644 --- a/homeassistant/components/dexcom/__init__.py +++ b/homeassistant/components/dexcom/__init__.py @@ -6,12 +6,12 @@ import logging from pydexcom import AccountError, Dexcom, GlucoseReading, SessionError from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_PASSWORD, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import CONF_SERVER, DOMAIN, MG_DL, PLATFORMS, SERVER_OUS +from .const import CONF_SERVER, DOMAIN, PLATFORMS, SERVER_OUS _LOGGER = logging.getLogger(__name__) @@ -32,11 +32,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except SessionError as error: raise ConfigEntryNotReady from error - if not entry.options: - hass.config_entries.async_update_entry( - entry, options={CONF_UNIT_OF_MEASUREMENT: MG_DL} - ) - async def async_update_data(): try: return await hass.async_add_executor_job(dexcom.get_current_glucose_reading) @@ -55,8 +50,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator - entry.async_on_unload(entry.add_update_listener(update_listener)) - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True @@ -67,8 +60,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): hass.data[DOMAIN].pop(entry.entry_id) return unload_ok - - -async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Handle options update.""" - await hass.config_entries.async_reload(entry.entry_id) diff --git a/homeassistant/components/dexcom/config_flow.py b/homeassistant/components/dexcom/config_flow.py index c5c830dedf6..90917e0ce2c 100644 --- a/homeassistant/components/dexcom/config_flow.py +++ b/homeassistant/components/dexcom/config_flow.py @@ -7,16 +7,10 @@ from typing import Any from pydexcom import AccountError, Dexcom, SessionError import voluptuous as vol -from homeassistant.config_entries import ( - ConfigEntry, - ConfigFlow, - ConfigFlowResult, - OptionsFlow, -) -from homeassistant.const import CONF_PASSWORD, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME -from homeassistant.core import callback +from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -from .const import CONF_SERVER, DOMAIN, MG_DL, MMOL_L, SERVER_OUS, SERVER_US +from .const import CONF_SERVER, DOMAIN, SERVER_OUS, SERVER_US DATA_SCHEMA = vol.Schema( { @@ -62,34 +56,3 @@ class DexcomConfigFlow(ConfigFlow, domain=DOMAIN): return self.async_show_form( step_id="user", data_schema=DATA_SCHEMA, errors=errors ) - - @staticmethod - @callback - def async_get_options_flow( - config_entry: ConfigEntry, - ) -> DexcomOptionsFlowHandler: - """Get the options flow for this handler.""" - return DexcomOptionsFlowHandler() - - -class DexcomOptionsFlowHandler(OptionsFlow): - """Handle a option flow for Dexcom.""" - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> ConfigFlowResult: - """Handle options flow.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - data_schema = vol.Schema( - { - vol.Optional( - CONF_UNIT_OF_MEASUREMENT, - default=self.config_entry.options.get( - CONF_UNIT_OF_MEASUREMENT, MG_DL - ), - ): vol.In({MG_DL, MMOL_L}), - } - ) - return self.async_show_form(step_id="init", data_schema=data_schema) diff --git a/homeassistant/components/dexcom/const.py b/homeassistant/components/dexcom/const.py index 487a844eb2b..66999e51e4b 100644 --- a/homeassistant/components/dexcom/const.py +++ b/homeassistant/components/dexcom/const.py @@ -5,9 +5,6 @@ from homeassistant.const import Platform DOMAIN = "dexcom" PLATFORMS = [Platform.SENSOR] -MMOL_L = "mmol/L" -MG_DL = "mg/dL" - CONF_SERVER = "server" SERVER_OUS = "EU" diff --git a/homeassistant/components/dexcom/sensor.py b/homeassistant/components/dexcom/sensor.py index 10b30f39fcb..239b47dc5b1 100644 --- a/homeassistant/components/dexcom/sensor.py +++ b/homeassistant/components/dexcom/sensor.py @@ -6,7 +6,11 @@ from pydexcom import GlucoseReading from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME +from homeassistant.const import ( + CONF_UNIT_OF_MEASUREMENT, + CONF_USERNAME, + UnitOfBloodGlucoseConcentration, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -15,7 +19,7 @@ from homeassistant.helpers.update_coordinator import ( DataUpdateCoordinator, ) -from .const import DOMAIN, MG_DL +from .const import DOMAIN TRENDS = { 1: "rising_quickly", @@ -36,7 +40,7 @@ async def async_setup_entry( """Set up the Dexcom sensors.""" coordinator = hass.data[DOMAIN][config_entry.entry_id] username = config_entry.data[CONF_USERNAME] - unit_of_measurement = config_entry.options[CONF_UNIT_OF_MEASUREMENT] + unit_of_measurement = config_entry.options.get(CONF_UNIT_OF_MEASUREMENT) async_add_entities( [ DexcomGlucoseTrendSensor(coordinator, username, config_entry.entry_id), @@ -73,6 +77,7 @@ class DexcomSensorEntity( class DexcomGlucoseValueSensor(DexcomSensorEntity): """Representation of a Dexcom glucose value sensor.""" + _attr_device_class = SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION _attr_translation_key = "glucose_value" def __init__( @@ -80,12 +85,19 @@ class DexcomGlucoseValueSensor(DexcomSensorEntity): coordinator: DataUpdateCoordinator, username: str, entry_id: str, - unit_of_measurement: str, + unit_of_measurement: str | None, ) -> None: """Initialize the sensor.""" super().__init__(coordinator, username, entry_id, "value") - self._attr_native_unit_of_measurement = unit_of_measurement - self._key = "mg_dl" if unit_of_measurement == MG_DL else "mmol_l" + self._key = "mg_dl" + self._attr_native_unit_of_measurement = ( + UnitOfBloodGlucoseConcentration.MILLIGRAMS_PER_DECILITER + ) + if unit_of_measurement == "mmol/L": + # Deprecated - for compatibility + self._attr_suggested_unit_of_measurement = ( + UnitOfBloodGlucoseConcentration.MILLIMOLE_PER_LITER + ) @property def native_value(self): diff --git a/tests/components/dexcom/__init__.py b/tests/components/dexcom/__init__.py index adc9c56049a..10a742070d6 100644 --- a/tests/components/dexcom/__init__.py +++ b/tests/components/dexcom/__init__.py @@ -1,6 +1,7 @@ """Tests for the Dexcom integration.""" import json +from typing import Any from unittest.mock import patch from pydexcom import GlucoseReading @@ -20,14 +21,16 @@ CONFIG = { GLUCOSE_READING = GlucoseReading(json.loads(load_fixture("data.json", "dexcom"))) -async def init_integration(hass: HomeAssistant) -> MockConfigEntry: +async def init_integration( + hass: HomeAssistant, options: dict[str, Any] | None = None +) -> MockConfigEntry: """Set up the Dexcom integration in Home Assistant.""" entry = MockConfigEntry( domain=DOMAIN, title="test_username", unique_id="test_username", data=CONFIG, - options=None, + options=options, ) with ( patch( diff --git a/tests/components/dexcom/test_config_flow.py b/tests/components/dexcom/test_config_flow.py index e8893e21d0e..0a7338c13da 100644 --- a/tests/components/dexcom/test_config_flow.py +++ b/tests/components/dexcom/test_config_flow.py @@ -5,15 +5,13 @@ from unittest.mock import patch from pydexcom import AccountError, SessionError from homeassistant import config_entries -from homeassistant.components.dexcom.const import DOMAIN, MG_DL, MMOL_L -from homeassistant.const import CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME +from homeassistant.components.dexcom.const import DOMAIN +from homeassistant.const import CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from . import CONFIG -from tests.common import MockConfigEntry - async def test_form(hass: HomeAssistant) -> None: """Test we get the form.""" @@ -101,51 +99,3 @@ async def test_form_unknown_error(hass: HomeAssistant) -> None: assert result2["type"] is FlowResultType.FORM assert result2["errors"] == {"base": "unknown"} - - -async def test_option_flow_default(hass: HomeAssistant) -> None: - """Test config flow options.""" - entry = MockConfigEntry( - domain=DOMAIN, - data=CONFIG, - options=None, - ) - entry.add_to_hass(hass) - - result = await hass.config_entries.options.async_init(entry.entry_id) - - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "init" - - result2 = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={}, - ) - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["data"] == { - CONF_UNIT_OF_MEASUREMENT: MG_DL, - } - - -async def test_option_flow(hass: HomeAssistant) -> None: - """Test config flow options.""" - entry = MockConfigEntry( - domain=DOMAIN, - data=CONFIG, - options={CONF_UNIT_OF_MEASUREMENT: MG_DL}, - ) - entry.add_to_hass(hass) - - result = await hass.config_entries.options.async_init(entry.entry_id) - - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "init" - - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={CONF_UNIT_OF_MEASUREMENT: MMOL_L}, - ) - assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["data"] == { - CONF_UNIT_OF_MEASUREMENT: MMOL_L, - } diff --git a/tests/components/dexcom/test_sensor.py b/tests/components/dexcom/test_sensor.py index 1b7f0b026ab..8947a5c3393 100644 --- a/tests/components/dexcom/test_sensor.py +++ b/tests/components/dexcom/test_sensor.py @@ -1,10 +1,11 @@ """The sensor tests for the griddy platform.""" +from typing import Any from unittest.mock import patch from pydexcom import SessionError +import pytest -from homeassistant.components.dexcom.const import MMOL_L from homeassistant.const import ( CONF_UNIT_OF_MEASUREMENT, STATE_UNAVAILABLE, @@ -60,34 +61,21 @@ async def test_sensors_update_failed(hass: HomeAssistant) -> None: assert test_username_glucose_trend.state == STATE_UNAVAILABLE -async def test_sensors_options_changed(hass: HomeAssistant) -> None: - """Test we handle sensor unavailable.""" - entry = await init_integration(hass) +@pytest.mark.parametrize( + ("options", "state"), + [ + (None, "110"), + ({CONF_UNIT_OF_MEASUREMENT: "mg/dL"}, "110"), + ({CONF_UNIT_OF_MEASUREMENT: "mmol/L"}, "6.1"), + ], +) +async def test_config_entry_options( + hass: HomeAssistant, options: dict[str, Any], state: str +) -> None: + """Test we handle deprecated config_entry options.""" + await init_integration(hass, options) test_username_glucose_value = hass.states.get("sensor.test_username_glucose_value") - assert test_username_glucose_value.state == str(GLUCOSE_READING.value) - test_username_glucose_trend = hass.states.get("sensor.test_username_glucose_trend") - assert test_username_glucose_trend.state == GLUCOSE_READING.trend_description - - with ( - patch( - "homeassistant.components.dexcom.Dexcom.get_current_glucose_reading", - return_value=GLUCOSE_READING, - ), - patch( - "homeassistant.components.dexcom.Dexcom.create_session", - return_value="test_session_id", - ), - ): - hass.config_entries.async_update_entry( - entry=entry, - options={CONF_UNIT_OF_MEASUREMENT: MMOL_L}, - ) - await hass.async_block_till_done() - - assert entry.options == {CONF_UNIT_OF_MEASUREMENT: MMOL_L} - - test_username_glucose_value = hass.states.get("sensor.test_username_glucose_value") - assert test_username_glucose_value.state == str(GLUCOSE_READING.mmol_l) + assert test_username_glucose_value.state == state test_username_glucose_trend = hass.states.get("sensor.test_username_glucose_trend") assert test_username_glucose_trend.state == GLUCOSE_READING.trend_description From a0e96258bfdcb28c6fac8e50b752ad927bc641fb Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:43:56 +0000 Subject: [PATCH 2/3] Improve --- homeassistant/components/dexcom/sensor.py | 17 ++------------ tests/components/dexcom/test_sensor.py | 28 +---------------------- 2 files changed, 3 insertions(+), 42 deletions(-) diff --git a/homeassistant/components/dexcom/sensor.py b/homeassistant/components/dexcom/sensor.py index 239b47dc5b1..3602e39cda6 100644 --- a/homeassistant/components/dexcom/sensor.py +++ b/homeassistant/components/dexcom/sensor.py @@ -6,11 +6,7 @@ from pydexcom import GlucoseReading from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_UNIT_OF_MEASUREMENT, - CONF_USERNAME, - UnitOfBloodGlucoseConcentration, -) +from homeassistant.const import CONF_USERNAME, UnitOfBloodGlucoseConcentration from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -40,13 +36,10 @@ async def async_setup_entry( """Set up the Dexcom sensors.""" coordinator = hass.data[DOMAIN][config_entry.entry_id] username = config_entry.data[CONF_USERNAME] - unit_of_measurement = config_entry.options.get(CONF_UNIT_OF_MEASUREMENT) async_add_entities( [ DexcomGlucoseTrendSensor(coordinator, username, config_entry.entry_id), - DexcomGlucoseValueSensor( - coordinator, username, config_entry.entry_id, unit_of_measurement - ), + DexcomGlucoseValueSensor(coordinator, username, config_entry.entry_id), ], ) @@ -85,7 +78,6 @@ class DexcomGlucoseValueSensor(DexcomSensorEntity): coordinator: DataUpdateCoordinator, username: str, entry_id: str, - unit_of_measurement: str | None, ) -> None: """Initialize the sensor.""" super().__init__(coordinator, username, entry_id, "value") @@ -93,11 +85,6 @@ class DexcomGlucoseValueSensor(DexcomSensorEntity): self._attr_native_unit_of_measurement = ( UnitOfBloodGlucoseConcentration.MILLIGRAMS_PER_DECILITER ) - if unit_of_measurement == "mmol/L": - # Deprecated - for compatibility - self._attr_suggested_unit_of_measurement = ( - UnitOfBloodGlucoseConcentration.MILLIMOLE_PER_LITER - ) @property def native_value(self): diff --git a/tests/components/dexcom/test_sensor.py b/tests/components/dexcom/test_sensor.py index 8947a5c3393..5c0a5280ad6 100644 --- a/tests/components/dexcom/test_sensor.py +++ b/tests/components/dexcom/test_sensor.py @@ -1,16 +1,10 @@ """The sensor tests for the griddy platform.""" -from typing import Any from unittest.mock import patch from pydexcom import SessionError -import pytest -from homeassistant.const import ( - CONF_UNIT_OF_MEASUREMENT, - STATE_UNAVAILABLE, - STATE_UNKNOWN, -) +from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_component import async_update_entity @@ -59,23 +53,3 @@ async def test_sensors_update_failed(hass: HomeAssistant) -> None: assert test_username_glucose_value.state == STATE_UNAVAILABLE test_username_glucose_trend = hass.states.get("sensor.test_username_glucose_trend") assert test_username_glucose_trend.state == STATE_UNAVAILABLE - - -@pytest.mark.parametrize( - ("options", "state"), - [ - (None, "110"), - ({CONF_UNIT_OF_MEASUREMENT: "mg/dL"}, "110"), - ({CONF_UNIT_OF_MEASUREMENT: "mmol/L"}, "6.1"), - ], -) -async def test_config_entry_options( - hass: HomeAssistant, options: dict[str, Any], state: str -) -> None: - """Test we handle deprecated config_entry options.""" - await init_integration(hass, options) - - test_username_glucose_value = hass.states.get("sensor.test_username_glucose_value") - assert test_username_glucose_value.state == state - test_username_glucose_trend = hass.states.get("sensor.test_username_glucose_trend") - assert test_username_glucose_trend.state == GLUCOSE_READING.trend_description From e6a0407da8437d8f5ce3b7e55dc4c3b38e660aeb Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:00:01 +0000 Subject: [PATCH 3/3] Drop self._key, and move to class attribute --- homeassistant/components/dexcom/sensor.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/dexcom/sensor.py b/homeassistant/components/dexcom/sensor.py index 3602e39cda6..850678e7ac9 100644 --- a/homeassistant/components/dexcom/sensor.py +++ b/homeassistant/components/dexcom/sensor.py @@ -71,6 +71,9 @@ class DexcomGlucoseValueSensor(DexcomSensorEntity): """Representation of a Dexcom glucose value sensor.""" _attr_device_class = SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION + _attr_native_unit_of_measurement = ( + UnitOfBloodGlucoseConcentration.MILLIGRAMS_PER_DECILITER + ) _attr_translation_key = "glucose_value" def __init__( @@ -81,16 +84,12 @@ class DexcomGlucoseValueSensor(DexcomSensorEntity): ) -> None: """Initialize the sensor.""" super().__init__(coordinator, username, entry_id, "value") - self._key = "mg_dl" - self._attr_native_unit_of_measurement = ( - UnitOfBloodGlucoseConcentration.MILLIGRAMS_PER_DECILITER - ) @property def native_value(self): """Return the state of the sensor.""" if self.coordinator.data: - return getattr(self.coordinator.data, self._key) + return self.coordinator.data.mg_dl return None