Add Dexcom Integration (#33852)

* Initial commit for Dexcom integration

* Dexcom config flow testing

* Clarify errors during setup

* Resolve minor test issues

* Update sensor availability, resolve linting issues

* Add sensor tests

* Remove title due to 0.109, add abort

* >94.97% codecov/patch

* Move .translations/ to translations/

* Add constants for servers and unit of measurements

* Bump pydexcom version

* Updated domain schema, Dexcom creation

* Support for different units of measurement

* Update tests

* Remove empty items from manifest

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Raise UpdateFailed if fetching new session fails

* Switch everything over to required

* Simplify state information

* Simplify async_on_remove

* Pydexcom package now handles fetching new session

* Only allow config flow

* Remove ternary operator

* Bump version, pydexcom handling session refresh

* Using common strings

* Import from test.async_mock

* Shorten variable names

* Resolve tests after removing yaml support

* Return false if credentials are invalid

* Available seems to handle if data is empty

* Now using option flow, remove handling import

* Add fixture for JSON returned from API

* Overhaul testing

* Revise update options

* Bump pydexcom version

* Combat listener repetition

* Undo update listener using callback

* Change sensor availability to use last_update_success

* Update sensor availability and tests

* Rename test

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Gage Benne
2020-07-01 20:14:54 -04:00
committed by GitHub
parent 3498882fe1
commit bcabf6da91
16 changed files with 792 additions and 0 deletions

View File

@ -94,6 +94,7 @@ homeassistant/components/denonavr/* @scarface-4711 @starkillerOG
homeassistant/components/derivative/* @afaucogney
homeassistant/components/device_automation/* @home-assistant/core
homeassistant/components/devolo_home_control/* @2Fake @Shutgun
homeassistant/components/dexcom/* @gagebenne
homeassistant/components/digital_ocean/* @fabaff
homeassistant/components/directv/* @ctalkington
homeassistant/components/discogs/* @thibmaek

View File

@ -0,0 +1,100 @@
"""The Dexcom integration."""
import asyncio
from datetime import timedelta
import logging
from pydexcom import AccountError, Dexcom, SessionError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_UNIT_OF_MEASUREMENT, 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,
COORDINATOR,
DOMAIN,
MG_DL,
PLATFORMS,
SERVER_OUS,
UNDO_UPDATE_LISTENER,
)
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=180)
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up configured Dexcom."""
hass.data[DOMAIN] = {}
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Dexcom from a config entry."""
try:
dexcom = await hass.async_add_executor_job(
Dexcom,
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
entry.data[CONF_SERVER] == SERVER_OUS,
)
except AccountError:
return False
except SessionError:
raise ConfigEntryNotReady
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)
except SessionError as error:
raise UpdateFailed(error)
hass.data[DOMAIN][entry.entry_id] = {
COORDINATOR: DataUpdateCoordinator(
hass,
_LOGGER,
name=DOMAIN,
update_method=async_update_data,
update_interval=SCAN_INTERVAL,
),
UNDO_UPDATE_LISTENER: entry.add_update_listener(update_listener),
}
await hass.data[DOMAIN][entry.entry_id][COORDINATOR].async_refresh()
for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, component)
for component in PLATFORMS
]
)
)
hass.data[DOMAIN][entry.entry_id][UNDO_UPDATE_LISTENER]()
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
async def update_listener(hass, entry):
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)

View File

@ -0,0 +1,95 @@
"""Config flow for Dexcom integration."""
import logging
from pydexcom import AccountError, Dexcom, SessionError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME
from homeassistant.core import callback
from .const import ( # pylint:disable=unused-import
CONF_SERVER,
DOMAIN,
MG_DL,
MMOL_L,
SERVER_OUS,
SERVER_US,
)
_LOGGER = logging.getLogger(__name__)
DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_SERVER): vol.In({SERVER_US, SERVER_OUS}),
}
)
class DexcomConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Dexcom."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
try:
await self.hass.async_add_executor_job(
Dexcom,
user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
user_input[CONF_SERVER] == SERVER_OUS,
)
except SessionError:
errors["base"] = "session_error"
except AccountError:
errors["base"] = "account_error"
except Exception: # pylint: disable=broad-except
errors["base"] = "unknown"
if "base" not in errors:
await self.async_set_unique_id(user_input[CONF_USERNAME])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_input[CONF_USERNAME], data=user_input
)
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
)
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return DexcomOptionsFlowHandler(config_entry)
class DexcomOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle a option flow for Dexcom."""
def __init__(self, config_entry: config_entries.ConfigEntry):
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(self, user_input=None):
"""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

@ -0,0 +1,30 @@
"""Constants for the Dexcom integration."""
DOMAIN = "dexcom"
PLATFORMS = ["sensor"]
GLUCOSE_VALUE_ICON = "mdi:diabetes"
GLUCOSE_TREND_ICON = [
"mdi:help",
"mdi:arrow-up-thick",
"mdi:arrow-up",
"mdi:arrow-top-right",
"mdi:arrow-right",
"mdi:arrow-bottom-right",
"mdi:arrow-down",
"mdi:arrow-down-thick",
"mdi:help",
"mdi:alert-circle-outline",
]
MMOL_L = "mmol/L"
MG_DL = "mg/dL"
CONF_SERVER = "server"
SERVER_OUS = "EU"
SERVER_US = "US"
COORDINATOR = "coordinator"
UNDO_UPDATE_LISTENER = "undo_update_listener"

View File

@ -0,0 +1,10 @@
{
"domain": "dexcom",
"name": "Dexcom",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/dexcom",
"requirements": ["pydexcom==0.2.0"],
"codeowners": [
"@gagebenne"
]
}

View File

@ -0,0 +1,133 @@
"""Support for Dexcom sensors."""
from homeassistant.const import CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME
from homeassistant.helpers.entity import Entity
from .const import COORDINATOR, DOMAIN, GLUCOSE_TREND_ICON, GLUCOSE_VALUE_ICON, MG_DL
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Dexcom sensors."""
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
username = config_entry.data[CONF_USERNAME]
unit_of_measurement = config_entry.options[CONF_UNIT_OF_MEASUREMENT]
sensors = []
sensors.append(DexcomGlucoseTrendSensor(coordinator, username))
sensors.append(DexcomGlucoseValueSensor(coordinator, username, unit_of_measurement))
async_add_entities(sensors, False)
class DexcomGlucoseValueSensor(Entity):
"""Representation of a Dexcom glucose value sensor."""
def __init__(self, coordinator, username, unit_of_measurement):
"""Initialize the sensor."""
self._state = None
self._unit_of_measurement = unit_of_measurement
self._attribute_unit_of_measurement = (
"mg_dl" if unit_of_measurement == MG_DL else "mmol_l"
)
self._coordinator = coordinator
self._name = f"{DOMAIN}_{username}_glucose_value"
self._unique_id = f"{username}-value"
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def icon(self):
"""Return the icon for the frontend."""
return GLUCOSE_VALUE_ICON
@property
def unit_of_measurement(self):
"""Return the unit of measurement of the device."""
return self._unit_of_measurement
@property
def state(self):
"""Return the state of the sensor."""
if self._coordinator.data:
return getattr(self._coordinator.data, self._attribute_unit_of_measurement)
return None
@property
def available(self):
"""Return True if entity is available."""
return self._coordinator.last_update_success
@property
def should_poll(self):
"""Return False, updates are controlled via coordinator."""
return False
@property
def unique_id(self):
"""Device unique id."""
return self._unique_id
async def async_update(self):
"""Get the latest state of the sensor."""
await self._coordinator.async_request_refresh()
async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(
self._coordinator.async_add_listener(self.async_write_ha_state)
)
class DexcomGlucoseTrendSensor(Entity):
"""Representation of a Dexcom glucose trend sensor."""
def __init__(self, coordinator, username):
"""Initialize the sensor."""
self._state = None
self._coordinator = coordinator
self._name = f"{DOMAIN}_{username}_glucose_trend"
self._unique_id = f"{username}-trend"
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def icon(self):
"""Return the icon for the frontend."""
if self._coordinator.data:
return GLUCOSE_TREND_ICON[self._coordinator.data.trend]
return GLUCOSE_TREND_ICON[0]
@property
def state(self):
"""Return the state of the sensor."""
if self._coordinator.data:
return self._coordinator.data.trend_description
return None
@property
def available(self):
"""Return True if entity is available."""
return self._coordinator.last_update_success
@property
def should_poll(self):
"""Return False, updates are controlled via coordinator."""
return False
@property
def unique_id(self):
"""Device unique id."""
return self._unique_id
async def async_update(self):
"""Get the latest state of the sensor."""
await self._coordinator.async_request_refresh()
async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(
self._coordinator.async_add_listener(self.async_write_ha_state)
)

View File

@ -0,0 +1,30 @@
{
"config": {
"step": {
"user": {
"title": "Setup Dexcom integration",
"description": "Enter Dexcom Share credentials",
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"server": "Server"
}
}
},
"error": {
"session_error": "[%key:common::config_flow::error::cannot_connect%]",
"account_error": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": { "already_configured_account": "[%key:common::config_flow::abort::already_configured_account%]" }
},
"options": {
"step": {
"init": {
"data": {
"unit_of_measurement": "Unit of measurement"
}
}
}
}
}

View File

@ -0,0 +1,32 @@
{
"config": {
"abort": {
"already_configured_account": "[%key:common::config_flow::abort::already_configured_account%]"
},
"error": {
"account_error": "[%key:common::config_flow::error::invalid_auth%]",
"session_error": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"user": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
"server": "Server",
"username": "[%key:common::config_flow::data::username%]"
},
"description": "Enter Dexcom Share credentials",
"title": "Setup Dexcom integration"
}
}
},
"options": {
"step": {
"init": {
"data": {
"unit_of_measurement": "Unit of measurement"
}
}
}
}
}

View File

@ -34,6 +34,7 @@ FLOWS = [
"deconz",
"denonavr",
"devolo_home_control",
"dexcom",
"dialogflow",
"directv",
"doorbird",

View File

@ -1290,6 +1290,9 @@ pydeconz==71
# homeassistant.components.delijn
pydelijn==0.6.0
# homeassistant.components.dexcom
pydexcom==0.2.0
# homeassistant.components.zwave
pydispatcher==2.0.5

View File

@ -584,6 +584,9 @@ pydaikin==2.2.0
# homeassistant.components.deconz
pydeconz==71
# homeassistant.components.dexcom
pydexcom==0.2.0
# homeassistant.components.zwave
pydispatcher==2.0.5

View File

@ -0,0 +1,42 @@
"""Tests for the Dexcom integration."""
import json
from pydexcom import GlucoseReading
from homeassistant.components.dexcom.const import CONF_SERVER, DOMAIN, SERVER_US
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from tests.async_mock import patch
from tests.common import MockConfigEntry, load_fixture
CONFIG = {
CONF_USERNAME: "test_username",
CONF_PASSWORD: "test_password",
CONF_SERVER: SERVER_US,
}
GLUCOSE_READING = GlucoseReading(json.loads(load_fixture("dexcom_data.json")))
async def init_integration(hass) -> MockConfigEntry:
"""Set up the Dexcom integration in Home Assistant."""
entry = MockConfigEntry(
domain=DOMAIN,
title="test_username",
unique_id="test_username",
data=CONFIG,
options=None,
)
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",
):
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry

View File

@ -0,0 +1,130 @@
"""Test the Dexcom config flow."""
from pydexcom import AccountError, SessionError
from homeassistant import config_entries, data_entry_flow, setup
from homeassistant.components.dexcom.const import DOMAIN, MG_DL, MMOL_L
from homeassistant.const import CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME
from tests.async_mock import patch
from tests.common import MockConfigEntry
from tests.components.dexcom import CONFIG
async def test_form(hass):
"""Test we get the form."""
await setup.async_setup_component(hass, "persistent_notification", {})
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {}
with patch(
"homeassistant.components.dexcom.config_flow.Dexcom.create_session",
return_value="test_session_id",
), patch(
"homeassistant.components.dexcom.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.dexcom.async_setup_entry", return_value=True,
) as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG,
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result2["title"] == CONFIG[CONF_USERNAME]
assert result2["data"] == CONFIG
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_account_error(hass):
"""Test we handle account error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.dexcom.config_flow.Dexcom", side_effect=AccountError,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG,
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["errors"] == {"base": "account_error"}
async def test_form_session_error(hass):
"""Test we handle session error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.dexcom.config_flow.Dexcom", side_effect=SessionError,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG,
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["errors"] == {"base": "session_error"}
async def test_form_unknown_error(hass):
"""Test we handle unknown error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.dexcom.config_flow.Dexcom", side_effect=Exception,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG,
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["errors"] == {"base": "unknown"}
async def test_option_flow_default(hass):
"""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"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "init"
result2 = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={},
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result2["data"] == {
CONF_UNIT_OF_MEASUREMENT: MG_DL,
}
async def test_option_flow(hass):
"""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"] == data_entry_flow.RESULT_TYPE_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"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["data"] == {
CONF_UNIT_OF_MEASUREMENT: MMOL_L,
}

View File

@ -0,0 +1,61 @@
"""Test the Dexcom config flow."""
from pydexcom import AccountError, SessionError
from homeassistant.components.dexcom.const import DOMAIN
from homeassistant.config_entries import ENTRY_STATE_LOADED, ENTRY_STATE_NOT_LOADED
from tests.async_mock import patch
from tests.common import MockConfigEntry
from tests.components.dexcom import CONFIG, init_integration
async def test_setup_entry_account_error(hass):
"""Test entry setup failed due to account error."""
entry = MockConfigEntry(
domain=DOMAIN,
title="test_username",
unique_id="test_username",
data=CONFIG,
options=None,
)
with patch(
"homeassistant.components.dexcom.Dexcom", side_effect=AccountError,
):
entry.add_to_hass(hass)
result = await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert result is False
async def test_setup_entry_session_error(hass):
"""Test entry setup failed due to session error."""
entry = MockConfigEntry(
domain=DOMAIN,
title="test_username",
unique_id="test_username",
data=CONFIG,
options=None,
)
with patch(
"homeassistant.components.dexcom.Dexcom", side_effect=SessionError,
):
entry.add_to_hass(hass)
result = await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert result is False
async def test_unload_entry(hass):
"""Test successful unload of entry."""
entry = await init_integration(hass)
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert entry.state == ENTRY_STATE_LOADED
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
assert entry.state == ENTRY_STATE_NOT_LOADED
assert not hass.data.get(DOMAIN)

View File

@ -0,0 +1,114 @@
"""The sensor tests for the griddy platform."""
from pydexcom import SessionError
from homeassistant.components.dexcom.const import MMOL_L
from homeassistant.const import (
CONF_UNIT_OF_MEASUREMENT,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from tests.async_mock import patch
from tests.components.dexcom import GLUCOSE_READING, init_integration
async def test_sensors(hass):
"""Test we get sensor data."""
await init_integration(hass)
test_username_glucose_value = hass.states.get(
"sensor.dexcom_test_username_glucose_value"
)
assert test_username_glucose_value.state == str(GLUCOSE_READING.value)
test_username_glucose_trend = hass.states.get(
"sensor.dexcom_test_username_glucose_trend"
)
assert test_username_glucose_trend.state == GLUCOSE_READING.trend_description
async def test_sensors_unknown(hass):
"""Test we handle sensor state unknown."""
await init_integration(hass)
with patch(
"homeassistant.components.dexcom.Dexcom.get_current_glucose_reading",
return_value=None,
):
await hass.helpers.entity_component.async_update_entity(
"sensor.dexcom_test_username_glucose_value"
)
await hass.helpers.entity_component.async_update_entity(
"sensor.dexcom_test_username_glucose_trend"
)
test_username_glucose_value = hass.states.get(
"sensor.dexcom_test_username_glucose_value"
)
assert test_username_glucose_value.state == STATE_UNKNOWN
test_username_glucose_trend = hass.states.get(
"sensor.dexcom_test_username_glucose_trend"
)
assert test_username_glucose_trend.state == STATE_UNKNOWN
async def test_sensors_update_failed(hass):
"""Test we handle sensor update failed."""
await init_integration(hass)
with patch(
"homeassistant.components.dexcom.Dexcom.get_current_glucose_reading",
side_effect=SessionError,
):
await hass.helpers.entity_component.async_update_entity(
"sensor.dexcom_test_username_glucose_value"
)
await hass.helpers.entity_component.async_update_entity(
"sensor.dexcom_test_username_glucose_trend"
)
test_username_glucose_value = hass.states.get(
"sensor.dexcom_test_username_glucose_value"
)
assert test_username_glucose_value.state == STATE_UNAVAILABLE
test_username_glucose_trend = hass.states.get(
"sensor.dexcom_test_username_glucose_trend"
)
assert test_username_glucose_trend.state == STATE_UNAVAILABLE
async def test_sensors_options_changed(hass):
"""Test we handle sensor unavailable."""
entry = await init_integration(hass)
test_username_glucose_value = hass.states.get(
"sensor.dexcom_test_username_glucose_value"
)
assert test_username_glucose_value.state == str(GLUCOSE_READING.value)
test_username_glucose_trend = hass.states.get(
"sensor.dexcom_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.dexcom_test_username_glucose_value"
)
assert test_username_glucose_value.state == str(GLUCOSE_READING.mmol_l)
test_username_glucose_trend = hass.states.get(
"sensor.dexcom_test_username_glucose_trend"
)
assert test_username_glucose_trend.state == GLUCOSE_READING.trend_description

7
tests/fixtures/dexcom_data.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"DT": "/Date(1587165223000+0000)/",
"ST": "/Date(1587179623000)/",
"Trend": 4,
"Value": 110,
"WT": "/Date(1587179623000)/"
}