Compare commits

...
Sign in to create a new pull request.

3 commits

Author SHA1 Message Date
epenet
e6a0407da8 Drop self._key, and move to class attribute 2024-11-13 14:00:01 +00:00
epenet
a0e96258bf Improve 2024-11-13 13:43:56 +00:00
epenet
9f14a98158 Use BLOOD_GLUCOSE_CONCENTRATION device class in dexcom 2024-11-13 13:30:32 +00:00
7 changed files with 21 additions and 160 deletions

View file

@ -6,12 +6,12 @@ import logging
from pydexcom import AccountError, Dexcom, GlucoseReading, SessionError from pydexcom import AccountError, Dexcom, GlucoseReading, SessionError
from homeassistant.config_entries import ConfigEntry 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.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed 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__) _LOGGER = logging.getLogger(__name__)
@ -32,11 +32,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except SessionError as error: except SessionError as error:
raise ConfigEntryNotReady from 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(): async def async_update_data():
try: try:
return await hass.async_add_executor_job(dexcom.get_current_glucose_reading) 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 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) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True 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): if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id) hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok return unload_ok
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)

View file

@ -7,16 +7,10 @@ from typing import Any
from pydexcom import AccountError, Dexcom, SessionError from pydexcom import AccountError, Dexcom, SessionError
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ( from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
ConfigEntry, from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import CONF_PASSWORD, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME
from homeassistant.core import callback
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( DATA_SCHEMA = vol.Schema(
{ {
@ -62,34 +56,3 @@ class DexcomConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_show_form( return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors 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)

View file

@ -5,9 +5,6 @@ from homeassistant.const import Platform
DOMAIN = "dexcom" DOMAIN = "dexcom"
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
MMOL_L = "mmol/L"
MG_DL = "mg/dL"
CONF_SERVER = "server" CONF_SERVER = "server"
SERVER_OUS = "EU" SERVER_OUS = "EU"

View file

@ -6,7 +6,7 @@ from pydexcom import GlucoseReading
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME from homeassistant.const import CONF_USERNAME, UnitOfBloodGlucoseConcentration
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -15,7 +15,7 @@ from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator, DataUpdateCoordinator,
) )
from .const import DOMAIN, MG_DL from .const import DOMAIN
TRENDS = { TRENDS = {
1: "rising_quickly", 1: "rising_quickly",
@ -36,13 +36,10 @@ async def async_setup_entry(
"""Set up the Dexcom sensors.""" """Set up the Dexcom sensors."""
coordinator = hass.data[DOMAIN][config_entry.entry_id] coordinator = hass.data[DOMAIN][config_entry.entry_id]
username = config_entry.data[CONF_USERNAME] username = config_entry.data[CONF_USERNAME]
unit_of_measurement = config_entry.options[CONF_UNIT_OF_MEASUREMENT]
async_add_entities( async_add_entities(
[ [
DexcomGlucoseTrendSensor(coordinator, username, config_entry.entry_id), DexcomGlucoseTrendSensor(coordinator, username, config_entry.entry_id),
DexcomGlucoseValueSensor( DexcomGlucoseValueSensor(coordinator, username, config_entry.entry_id),
coordinator, username, config_entry.entry_id, unit_of_measurement
),
], ],
) )
@ -73,6 +70,10 @@ class DexcomSensorEntity(
class DexcomGlucoseValueSensor(DexcomSensorEntity): class DexcomGlucoseValueSensor(DexcomSensorEntity):
"""Representation of a Dexcom glucose value sensor.""" """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" _attr_translation_key = "glucose_value"
def __init__( def __init__(
@ -80,18 +81,15 @@ class DexcomGlucoseValueSensor(DexcomSensorEntity):
coordinator: DataUpdateCoordinator, coordinator: DataUpdateCoordinator,
username: str, username: str,
entry_id: str, entry_id: str,
unit_of_measurement: str,
) -> None: ) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(coordinator, username, entry_id, "value") 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"
@property @property
def native_value(self): def native_value(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
if self.coordinator.data: if self.coordinator.data:
return getattr(self.coordinator.data, self._key) return self.coordinator.data.mg_dl
return None return None

View file

@ -1,6 +1,7 @@
"""Tests for the Dexcom integration.""" """Tests for the Dexcom integration."""
import json import json
from typing import Any
from unittest.mock import patch from unittest.mock import patch
from pydexcom import GlucoseReading from pydexcom import GlucoseReading
@ -20,14 +21,16 @@ CONFIG = {
GLUCOSE_READING = GlucoseReading(json.loads(load_fixture("data.json", "dexcom"))) 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.""" """Set up the Dexcom integration in Home Assistant."""
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
title="test_username", title="test_username",
unique_id="test_username", unique_id="test_username",
data=CONFIG, data=CONFIG,
options=None, options=options,
) )
with ( with (
patch( patch(

View file

@ -5,15 +5,13 @@ from unittest.mock import patch
from pydexcom import AccountError, SessionError from pydexcom import AccountError, SessionError
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.dexcom.const import DOMAIN, MG_DL, MMOL_L from homeassistant.components.dexcom.const import DOMAIN
from homeassistant.const import CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME from homeassistant.const import CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
from . import CONFIG from . import CONFIG
from tests.common import MockConfigEntry
async def test_form(hass: HomeAssistant) -> None: async def test_form(hass: HomeAssistant) -> None:
"""Test we get the form.""" """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["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "unknown"} 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,
}

View file

@ -4,12 +4,7 @@ from unittest.mock import patch
from pydexcom import SessionError from pydexcom import SessionError
from homeassistant.components.dexcom.const import MMOL_L from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.const import (
CONF_UNIT_OF_MEASUREMENT,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_component import async_update_entity from homeassistant.helpers.entity_component import async_update_entity
@ -58,36 +53,3 @@ async def test_sensors_update_failed(hass: HomeAssistant) -> None:
assert test_username_glucose_value.state == STATE_UNAVAILABLE assert test_username_glucose_value.state == STATE_UNAVAILABLE
test_username_glucose_trend = hass.states.get("sensor.test_username_glucose_trend") test_username_glucose_trend = hass.states.get("sensor.test_username_glucose_trend")
assert test_username_glucose_trend.state == STATE_UNAVAILABLE 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)
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)
test_username_glucose_trend = hass.states.get("sensor.test_username_glucose_trend")
assert test_username_glucose_trend.state == GLUCOSE_READING.trend_description