mirror of
https://github.com/home-assistant/core.git
synced 2026-09-27 01:46:11 -04:00
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:
co-authored by
Martin Hjelmare
parent
3498882fe1
commit
bcabf6da91
@@ -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
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"DT": "/Date(1587165223000+0000)/",
|
||||
"ST": "/Date(1587179623000)/",
|
||||
"Trend": 4,
|
||||
"Value": 110,
|
||||
"WT": "/Date(1587179623000)/"
|
||||
}
|
||||
Reference in New Issue
Block a user