Compare commits
3 commits
dev
...
epenet-202
Author | SHA1 | Date | |
---|---|---|---|
|
e6a0407da8 | ||
|
a0e96258bf | ||
|
9f14a98158 |
7 changed files with 21 additions and 160 deletions
|
@ -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)
|
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue