Add ConfigFlow for seventeentrack integration (#111196)
* Add config flow to 17Track * Import config from configuration.yaml * 1. move import to async_setup_platform 2. add USERNAME (email) in title for uniqueness * Add options flow * Add tests * Add CONF_SHOW_ARCHIVED and CONF_SHOW_DELIVERED to data from options * Update homeassistant/components/seventeentrack/__init__.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/__init__.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/config_flow.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/manifest.json Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/config_flow.py Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com> * Update homeassistant/components/seventeentrack/__init__.py Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com> * Update homeassistant/components/seventeentrack/sensor.py Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com> * 1. Added repair issues 2. _async_validate_input inlined 3. added unique id 4. take default scan interval * fix * 1. move async_create_issue to async_setup_platform 2. fix tests 3. black + pylint * combine USER_SCHEMA and OPTIONS_SCHEMA * small fix * remove async_setup * fix tests and add 100% coverage * 1. remove CONFIG_SCHEMA 2. remove error log 3. add issue with more description when import issues happen 4. some linting * Update homeassistant/components/seventeentrack/config_flow.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * use freezer use AsyncMock fix tests * add test_flow_fails parametrize tests where needed test_import_flow_already_configured - where a unique id already configured (abort flow) * lint * fix rebase issues * some more fix * 17Track revert tests and put them in a different PR * adapt tests to MockConfigEntry * Update tests/components/seventeentrack/test_sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/seventeentrack/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update tests/components/seventeentrack/__init__.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * 1. create fixture for config and another with options 2. set options with default values 3. remove CONFIG_SCHEMA * Update tests/components/seventeentrack/conftest.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update tests/components/seventeentrack/conftest.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * 1. get options from import data and default if not present 2. rename mock_config_entry_no_options -> mock_config_entry_with_default_options * move ACCOUNT_ID to mock_seventeentrack_api.return_value.profile.account_id * Apply suggestions from code review * Update tests/components/seventeentrack/test_config_flow.py --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com>pull/112987/head^2
parent
564c31e846
commit
3c06fbbd82
homeassistant
components/seventeentrack
generated
tests/components/seventeentrack
|
@ -1190,6 +1190,8 @@ build.json @home-assistant/supervisor
|
|||
/tests/components/senz/ @milanmeu
|
||||
/homeassistant/components/serial/ @fabaff
|
||||
/homeassistant/components/seven_segments/ @fabaff
|
||||
/homeassistant/components/seventeentrack/ @shaiu
|
||||
/tests/components/seventeentrack/ @shaiu
|
||||
/homeassistant/components/sfr_box/ @epenet
|
||||
/tests/components/sfr_box/ @epenet
|
||||
/homeassistant/components/sharkiq/ @JeffResc @funkybunch
|
||||
|
|
|
@ -1 +1,32 @@
|
|||
"""The seventeentrack component."""
|
||||
|
||||
from py17track import Client as SeventeenTrackClient
|
||||
from py17track.errors import SeventeenTrackError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up 17Track from a config entry."""
|
||||
|
||||
session = async_get_clientsession(hass)
|
||||
client = SeventeenTrackClient(session=session)
|
||||
|
||||
try:
|
||||
await client.profile.login(entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD])
|
||||
except SeventeenTrackError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = client
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
"""Adds config flow for 17track.net."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from py17track import Client as SeventeenTrackClient
|
||||
from py17track.errors import SeventeenTrackError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.config_entries import ConfigFlowResult
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers.schema_config_entry_flow import (
|
||||
SchemaFlowFormStep,
|
||||
SchemaOptionsFlowHandler,
|
||||
)
|
||||
|
||||
from .const import (
|
||||
CONF_SHOW_ARCHIVED,
|
||||
CONF_SHOW_DELIVERED,
|
||||
DEFAULT_SHOW_ARCHIVED,
|
||||
DEFAULT_SHOW_DELIVERED,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
CONF_SHOW = {
|
||||
vol.Optional(CONF_SHOW_ARCHIVED, default=DEFAULT_SHOW_ARCHIVED): bool,
|
||||
vol.Optional(CONF_SHOW_DELIVERED, default=DEFAULT_SHOW_DELIVERED): bool,
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
OPTIONS_SCHEMA = vol.Schema(CONF_SHOW)
|
||||
OPTIONS_FLOW = {
|
||||
"init": SchemaFlowFormStep(OPTIONS_SCHEMA),
|
||||
}
|
||||
|
||||
USER_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class SeventeenTrackConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""17track config flow."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
) -> SchemaOptionsFlowHandler:
|
||||
"""Get options flow for this handler."""
|
||||
return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle a flow initialized by the user."""
|
||||
|
||||
errors = {}
|
||||
if user_input:
|
||||
client = await self._get_client()
|
||||
|
||||
try:
|
||||
if not await client.profile.login(
|
||||
user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
|
||||
):
|
||||
errors["base"] = "invalid_auth"
|
||||
except SeventeenTrackError as err:
|
||||
_LOGGER.error("There was an error while logging in: %s", err)
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
if not errors:
|
||||
account_id = client.profile.account_id
|
||||
await self.async_set_unique_id(account_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_USERNAME],
|
||||
data=user_input,
|
||||
options={
|
||||
CONF_SHOW_ARCHIVED: DEFAULT_SHOW_ARCHIVED,
|
||||
CONF_SHOW_DELIVERED: DEFAULT_SHOW_DELIVERED,
|
||||
},
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=USER_SCHEMA,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Import 17Track config from configuration.yaml."""
|
||||
|
||||
client = await self._get_client()
|
||||
|
||||
try:
|
||||
login_result = await client.profile.login(
|
||||
import_data[CONF_USERNAME], import_data[CONF_PASSWORD]
|
||||
)
|
||||
except SeventeenTrackError as err:
|
||||
_LOGGER.error("There was an error while logging in: %s", err)
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
||||
if not login_result:
|
||||
_LOGGER.error("Invalid username and password provided")
|
||||
return self.async_abort(reason="invalid_credentials")
|
||||
|
||||
account_id = client.profile.account_id
|
||||
|
||||
await self.async_set_unique_id(account_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(
|
||||
title=import_data[CONF_USERNAME],
|
||||
data=import_data,
|
||||
options={
|
||||
CONF_SHOW_ARCHIVED: import_data.get(
|
||||
CONF_SHOW_ARCHIVED, DEFAULT_SHOW_ARCHIVED
|
||||
),
|
||||
CONF_SHOW_DELIVERED: import_data.get(
|
||||
CONF_SHOW_DELIVERED, DEFAULT_SHOW_DELIVERED
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
async def _get_client(self):
|
||||
session = aiohttp_client.async_get_clientsession(self.hass)
|
||||
return SeventeenTrackClient(session=session)
|
|
@ -0,0 +1,39 @@
|
|||
"""Constants for the 17track.net component."""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
ATTR_DESTINATION_COUNTRY = "destination_country"
|
||||
ATTR_INFO_TEXT = "info_text"
|
||||
ATTR_TIMESTAMP = "timestamp"
|
||||
ATTR_ORIGIN_COUNTRY = "origin_country"
|
||||
ATTR_PACKAGES = "packages"
|
||||
ATTR_PACKAGE_TYPE = "package_type"
|
||||
ATTR_STATUS = "status"
|
||||
ATTR_TRACKING_INFO_LANGUAGE = "tracking_info_language"
|
||||
ATTR_TRACKING_NUMBER = "tracking_number"
|
||||
|
||||
CONF_SHOW_ARCHIVED = "show_archived"
|
||||
CONF_SHOW_DELIVERED = "show_delivered"
|
||||
|
||||
DEFAULT_SHOW_ARCHIVED = False
|
||||
DEFAULT_SHOW_DELIVERED = False
|
||||
|
||||
DOMAIN = "seventeentrack"
|
||||
|
||||
DATA_PACKAGES = "package_data"
|
||||
DATA_SUMMARY = "summary_data"
|
||||
|
||||
ATTRIBUTION = "Data provided by 17track.net"
|
||||
DEFAULT_SCAN_INTERVAL = timedelta(minutes=10)
|
||||
|
||||
UNIQUE_ID_TEMPLATE = "package_{0}_{1}"
|
||||
ENTITY_ID_TEMPLATE = "sensor.seventeentrack_package_{0}"
|
||||
|
||||
NOTIFICATION_DELIVERED_ID = "package_delivered_{0}"
|
||||
NOTIFICATION_DELIVERED_TITLE = "Package {0} delivered"
|
||||
NOTIFICATION_DELIVERED_MESSAGE = (
|
||||
"Package Delivered: {0}<br />Visit 17.track for more information: "
|
||||
"https://t.17track.net/track#nums={1}"
|
||||
)
|
||||
|
||||
VALUE_DELIVERED = "Delivered"
|
|
@ -1,8 +1,10 @@
|
|||
{
|
||||
"domain": "seventeentrack",
|
||||
"name": "17TRACK",
|
||||
"codeowners": [],
|
||||
"codeowners": ["@shaiu"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/seventeentrack",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["py17track"],
|
||||
"requirements": ["py17track==2021.12.2"]
|
||||
|
|
|
@ -2,66 +2,53 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from py17track import Client as SeventeenTrackClient
|
||||
from py17track.errors import SeventeenTrackError
|
||||
from py17track.package import Package
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_LOCATION,
|
||||
CONF_PASSWORD,
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import (
|
||||
aiohttp_client,
|
||||
config_validation as cv,
|
||||
entity,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers import config_validation as cv, entity, entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
|
||||
from homeassistant.util import Throttle, slugify
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_DESTINATION_COUNTRY = "destination_country"
|
||||
ATTR_INFO_TEXT = "info_text"
|
||||
ATTR_TIMESTAMP = "timestamp"
|
||||
ATTR_ORIGIN_COUNTRY = "origin_country"
|
||||
ATTR_PACKAGES = "packages"
|
||||
ATTR_PACKAGE_TYPE = "package_type"
|
||||
ATTR_STATUS = "status"
|
||||
ATTR_TRACKING_INFO_LANGUAGE = "tracking_info_language"
|
||||
ATTR_TRACKING_NUMBER = "tracking_number"
|
||||
|
||||
CONF_SHOW_ARCHIVED = "show_archived"
|
||||
CONF_SHOW_DELIVERED = "show_delivered"
|
||||
|
||||
DATA_PACKAGES = "package_data"
|
||||
DATA_SUMMARY = "summary_data"
|
||||
|
||||
ATTRIBUTION = "Data provided by 17track.net"
|
||||
DEFAULT_SCAN_INTERVAL = timedelta(minutes=10)
|
||||
|
||||
UNIQUE_ID_TEMPLATE = "package_{0}_{1}"
|
||||
ENTITY_ID_TEMPLATE = "sensor.seventeentrack_package_{0}"
|
||||
|
||||
NOTIFICATION_DELIVERED_ID = "package_delivered_{0}"
|
||||
NOTIFICATION_DELIVERED_TITLE = "Package {0} delivered"
|
||||
NOTIFICATION_DELIVERED_MESSAGE = (
|
||||
"Package Delivered: {0}<br />Visit 17.track for more information: "
|
||||
"https://t.17track.net/track#nums={1}"
|
||||
from .const import (
|
||||
ATTR_DESTINATION_COUNTRY,
|
||||
ATTR_INFO_TEXT,
|
||||
ATTR_ORIGIN_COUNTRY,
|
||||
ATTR_PACKAGE_TYPE,
|
||||
ATTR_PACKAGES,
|
||||
ATTR_STATUS,
|
||||
ATTR_TIMESTAMP,
|
||||
ATTR_TRACKING_INFO_LANGUAGE,
|
||||
ATTR_TRACKING_NUMBER,
|
||||
ATTRIBUTION,
|
||||
CONF_SHOW_ARCHIVED,
|
||||
CONF_SHOW_DELIVERED,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DOMAIN,
|
||||
ENTITY_ID_TEMPLATE,
|
||||
NOTIFICATION_DELIVERED_MESSAGE,
|
||||
NOTIFICATION_DELIVERED_TITLE,
|
||||
UNIQUE_ID_TEMPLATE,
|
||||
VALUE_DELIVERED,
|
||||
)
|
||||
|
||||
VALUE_DELIVERED = "Delivered"
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
|
@ -72,6 +59,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
}
|
||||
)
|
||||
|
||||
ISSUE_PLACEHOLDER = {"url": "/config/integrations/dashboard/add?domain=seventeentrack"}
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
|
@ -79,32 +68,57 @@ async def async_setup_platform(
|
|||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Configure the platform and add the sensors."""
|
||||
"""Initialize 17Track import from config."""
|
||||
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
|
||||
client = SeventeenTrackClient(session=session)
|
||||
|
||||
try:
|
||||
login_result = await client.profile.login(
|
||||
config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=config
|
||||
)
|
||||
if (
|
||||
result["type"] == FlowResultType.CREATE_ENTRY
|
||||
or result["reason"] == "already_configured"
|
||||
):
|
||||
async_create_issue(
|
||||
hass,
|
||||
HOMEASSISTANT_DOMAIN,
|
||||
f"deprecated_yaml_{DOMAIN}",
|
||||
is_fixable=False,
|
||||
breaks_in_ha_version="2024.10.0",
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
translation_placeholders={
|
||||
"domain": DOMAIN,
|
||||
"integration_title": "17Track",
|
||||
},
|
||||
)
|
||||
else:
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"deprecated_yaml_import_issue_${result['reason']}",
|
||||
breaks_in_ha_version="2024.10.0",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key=f"deprecated_yaml_import_issue_${result['reason']}",
|
||||
translation_placeholders=ISSUE_PLACEHOLDER,
|
||||
)
|
||||
|
||||
if not login_result:
|
||||
_LOGGER.error("Invalid username and password provided")
|
||||
return
|
||||
except SeventeenTrackError as err:
|
||||
_LOGGER.error("There was an error while logging in: %s", err)
|
||||
return
|
||||
|
||||
scan_interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up a 17Track sensor entry."""
|
||||
|
||||
client = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
data = SeventeenTrackData(
|
||||
client,
|
||||
async_add_entities,
|
||||
scan_interval,
|
||||
config[CONF_SHOW_ARCHIVED],
|
||||
config[CONF_SHOW_DELIVERED],
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
config_entry.options[CONF_SHOW_ARCHIVED],
|
||||
config_entry.options[CONF_SHOW_DELIVERED],
|
||||
str(hass.config.time_zone),
|
||||
)
|
||||
await data.async_update()
|
||||
|
@ -117,7 +131,7 @@ class SeventeenTrackSummarySensor(SensorEntity):
|
|||
_attr_icon = "mdi:package"
|
||||
_attr_native_unit_of_measurement = "packages"
|
||||
|
||||
def __init__(self, data, status, initial_state):
|
||||
def __init__(self, data, status, initial_state) -> None:
|
||||
"""Initialize."""
|
||||
self._attr_extra_state_attributes = {}
|
||||
self._data = data
|
||||
|
@ -127,12 +141,12 @@ class SeventeenTrackSummarySensor(SensorEntity):
|
|||
self._attr_unique_id = f"summary_{data.account_id}_{slugify(status)}"
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
def available(self) -> bool:
|
||||
"""Return whether the entity is available."""
|
||||
return self._state is not None
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state."""
|
||||
return self._state
|
||||
|
||||
|
@ -169,7 +183,7 @@ class SeventeenTrackPackageSensor(SensorEntity):
|
|||
_attr_attribution = ATTRIBUTION
|
||||
_attr_icon = "mdi:package"
|
||||
|
||||
def __init__(self, data, package):
|
||||
def __init__(self, data, package) -> None:
|
||||
"""Initialize."""
|
||||
self._attr_extra_state_attributes = {
|
||||
ATTR_DESTINATION_COUNTRY: package.destination_country,
|
||||
|
@ -196,14 +210,14 @@ class SeventeenTrackPackageSensor(SensorEntity):
|
|||
return self._data.packages.get(self._tracking_number) is not None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
if not (name := self._friendly_name):
|
||||
name = self._tracking_number
|
||||
return f"Seventeentrack Package: {name}"
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state."""
|
||||
return self._state
|
||||
|
||||
|
@ -278,18 +292,17 @@ class SeventeenTrackData:
|
|||
show_archived,
|
||||
show_delivered,
|
||||
timezone,
|
||||
):
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self._async_add_entities = async_add_entities
|
||||
self._client = client
|
||||
self._scan_interval = scan_interval
|
||||
self._show_archived = show_archived
|
||||
self.account_id = client.profile.account_id
|
||||
self.packages = {}
|
||||
self.packages: dict[str, Package] = {}
|
||||
self.show_delivered = show_delivered
|
||||
self.timezone = timezone
|
||||
self.summary = {}
|
||||
|
||||
self.summary: dict[str, int] = {}
|
||||
self.async_update = Throttle(self._scan_interval)(self._async_update)
|
||||
self.first_update = True
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "Configure general settings",
|
||||
"data": {
|
||||
"show_archived": "Whether sensors should be created for archived packages",
|
||||
"show_delivered": "Whether sensors should be created for delivered packages"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecated_yaml_import_issue_cannot_connect": {
|
||||
"title": "The 17Track YAML configuration import cannot connect to server",
|
||||
"description": "Configuring 17Track using YAML is being removed but there was a connection error importing your YAML configuration.\n\nThings you can try:\nMake sure your home assistant can reach the web.\n\nThen restart Home Assistant to try importing this integration again.\n\nAlternatively, you may remove the 17Track configuration from your YAML configuration entirely, restart Home Assistant, and add the 17Track integration manually."
|
||||
},
|
||||
"deprecated_yaml_import_issue_invalid_credentials": {
|
||||
"title": "The 17Track YAML configuration import request failed due to invalid credentials",
|
||||
"description": "Configuring 17Track using YAML is being removed but there were invalid credentials provided while importing your existing configuration.\nSetup will not proceed.\n\nVerify that your 17Track credentials are correct and restart Home Assistant to attempt the import again.\n\nAlternatively, you may remove the 17Track configuration from your YAML configuration entirely, restart Home Assistant, and add the 17Track integration manually."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -458,6 +458,7 @@ FLOWS = {
|
|||
"sensorpush",
|
||||
"sentry",
|
||||
"senz",
|
||||
"seventeentrack",
|
||||
"sfr_box",
|
||||
"sharkiq",
|
||||
"shelly",
|
||||
|
|
|
@ -5271,8 +5271,8 @@
|
|||
},
|
||||
"seventeentrack": {
|
||||
"name": "17TRACK",
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"integration_type": "service",
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
},
|
||||
"sfr_box": {
|
||||
|
|
|
@ -6,15 +6,18 @@ from freezegun.api import FrozenDateTimeFactory
|
|||
|
||||
from homeassistant.components.seventeentrack.sensor import DEFAULT_SCAN_INTERVAL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def init_integration(hass: HomeAssistant, config: ConfigType):
|
||||
"""Set up the seventeentrack integration in Home Assistant."""
|
||||
assert await async_setup_component(hass, "sensor", config)
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Set up the 17Track integration in Home Assistant."""
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
|
|
|
@ -1,46 +1,23 @@
|
|||
"""Configuration for 17Track tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Optional
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from py17track.package import Package
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.seventeentrack.const import (
|
||||
DEFAULT_SHOW_ARCHIVED,
|
||||
DEFAULT_SHOW_DELIVERED,
|
||||
)
|
||||
from homeassistant.components.seventeentrack.sensor import (
|
||||
CONF_SHOW_ARCHIVED,
|
||||
CONF_SHOW_DELIVERED,
|
||||
)
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
|
||||
VALID_CONFIG_MINIMAL = {
|
||||
"sensor": {
|
||||
"platform": "seventeentrack",
|
||||
CONF_USERNAME: "test",
|
||||
CONF_PASSWORD: "test",
|
||||
}
|
||||
}
|
||||
|
||||
INVALID_CONFIG = {"sensor": {"platform": "seventeentrack", "boom": "test"}}
|
||||
|
||||
VALID_CONFIG_FULL = {
|
||||
"sensor": {
|
||||
"platform": "seventeentrack",
|
||||
CONF_USERNAME: "test",
|
||||
CONF_PASSWORD: "test",
|
||||
CONF_SHOW_ARCHIVED: True,
|
||||
CONF_SHOW_DELIVERED: True,
|
||||
}
|
||||
}
|
||||
|
||||
VALID_CONFIG_FULL_NO_DELIVERED = {
|
||||
"sensor": {
|
||||
"platform": "seventeentrack",
|
||||
CONF_USERNAME: "test",
|
||||
CONF_PASSWORD: "test",
|
||||
CONF_SHOW_ARCHIVED: False,
|
||||
CONF_SHOW_DELIVERED: False,
|
||||
}
|
||||
}
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
DEFAULT_SUMMARY = {
|
||||
"Not Found": 0,
|
||||
|
@ -52,6 +29,8 @@ DEFAULT_SUMMARY = {
|
|||
"Returned": 0,
|
||||
}
|
||||
|
||||
ACCOUNT_ID = "1234"
|
||||
|
||||
NEW_SUMMARY_DATA = {
|
||||
"Not Found": 1,
|
||||
"In Transit": 1,
|
||||
|
@ -62,6 +41,67 @@ NEW_SUMMARY_DATA = {
|
|||
"Returned": 1,
|
||||
}
|
||||
|
||||
VALID_CONFIG = {
|
||||
CONF_USERNAME: "test",
|
||||
CONF_PASSWORD: "test",
|
||||
}
|
||||
|
||||
INVALID_CONFIG = {"notusername": "seventeentrack", "notpassword": "test"}
|
||||
|
||||
VALID_OPTIONS = {
|
||||
CONF_SHOW_ARCHIVED: True,
|
||||
CONF_SHOW_DELIVERED: True,
|
||||
}
|
||||
|
||||
NO_DELIVERED_OPTIONS = {
|
||||
CONF_SHOW_ARCHIVED: False,
|
||||
CONF_SHOW_DELIVERED: False,
|
||||
}
|
||||
|
||||
VALID_PLATFORM_CONFIG_FULL = {
|
||||
"sensor": {
|
||||
"platform": "seventeentrack",
|
||||
CONF_USERNAME: "test",
|
||||
CONF_PASSWORD: "test",
|
||||
CONF_SHOW_ARCHIVED: True,
|
||||
CONF_SHOW_DELIVERED: True,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.seventeentrack.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
domain="seventeentrack",
|
||||
data=VALID_CONFIG,
|
||||
options=VALID_OPTIONS,
|
||||
unique_id=ACCOUNT_ID,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry_with_default_options() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
domain="seventeentrack",
|
||||
data=VALID_CONFIG,
|
||||
options={
|
||||
CONF_SHOW_ARCHIVED: DEFAULT_SHOW_ARCHIVED,
|
||||
CONF_SHOW_DELIVERED: DEFAULT_SHOW_DELIVERED,
|
||||
},
|
||||
unique_id=ACCOUNT_ID,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_seventeentrack():
|
||||
|
@ -69,10 +109,15 @@ def mock_seventeentrack():
|
|||
mock_seventeentrack_api = AsyncMock()
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.seventeentrack.sensor.SeventeenTrackClient",
|
||||
"homeassistant.components.seventeentrack.SeventeenTrackClient",
|
||||
return_value=mock_seventeentrack_api,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.seventeentrack.config_flow.SeventeenTrackClient",
|
||||
return_value=mock_seventeentrack_api,
|
||||
) as mock_seventeentrack_api,
|
||||
):
|
||||
mock_seventeentrack_api.return_value.profile.account_id = ACCOUNT_ID
|
||||
mock_seventeentrack_api.return_value.profile.login.return_value = True
|
||||
mock_seventeentrack_api.return_value.profile.packages.return_value = []
|
||||
mock_seventeentrack_api.return_value.profile.summary.return_value = (
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
"""Define tests for the 17Track config flow."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from py17track.errors import SeventeenTrackError
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.seventeentrack import DOMAIN
|
||||
from homeassistant.components.seventeentrack.const import (
|
||||
CONF_SHOW_ARCHIVED,
|
||||
CONF_SHOW_DELIVERED,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
ACCOUNT_ID = "1234"
|
||||
|
||||
VALID_CONFIG = {
|
||||
CONF_USERNAME: "someemail@gmail.com",
|
||||
CONF_PASSWORD: "edc3eee7330e4fdda04489e3fbc283d0",
|
||||
}
|
||||
|
||||
VALID_CONFIG_OLD = {
|
||||
CONF_USERNAME: "someemail@gmail.com",
|
||||
CONF_PASSWORD: "edc3eee7330e4fdda04489e3fbc283d0",
|
||||
}
|
||||
|
||||
|
||||
async def test_create_entry(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_seventeentrack: AsyncMock
|
||||
) -> None:
|
||||
"""Test that the user step works."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
VALID_CONFIG,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "someemail@gmail.com"
|
||||
assert result2["data"] == {
|
||||
CONF_PASSWORD: "edc3eee7330e4fdda04489e3fbc283d0",
|
||||
CONF_USERNAME: "someemail@gmail.com",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("return_value", "side_effect", "error"),
|
||||
[
|
||||
(
|
||||
False,
|
||||
None,
|
||||
"invalid_auth",
|
||||
),
|
||||
(
|
||||
True,
|
||||
SeventeenTrackError(),
|
||||
"cannot_connect",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_flow_fails(
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
return_value,
|
||||
side_effect,
|
||||
error,
|
||||
) -> None:
|
||||
"""Test that the user step fails."""
|
||||
mock_seventeentrack.return_value.profile.login.return_value = return_value
|
||||
mock_seventeentrack.return_value.profile.login.side_effect = side_effect
|
||||
failed_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert failed_result["errors"] == {"base": error}
|
||||
|
||||
mock_seventeentrack.return_value.profile.login.return_value = True
|
||||
mock_seventeentrack.return_value.profile.login.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
failed_result["flow_id"],
|
||||
VALID_CONFIG,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "someemail@gmail.com"
|
||||
assert result["data"] == {
|
||||
CONF_PASSWORD: "edc3eee7330e4fdda04489e3fbc283d0",
|
||||
CONF_USERNAME: "someemail@gmail.com",
|
||||
}
|
||||
|
||||
|
||||
async def test_import_flow(hass: HomeAssistant, mock_seventeentrack: AsyncMock) -> None:
|
||||
"""Test the import configuration flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=VALID_CONFIG_OLD,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "someemail@gmail.com"
|
||||
assert result["data"][CONF_USERNAME] == "someemail@gmail.com"
|
||||
assert result["data"][CONF_PASSWORD] == "edc3eee7330e4fdda04489e3fbc283d0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("return_value", "side_effect", "error"),
|
||||
[
|
||||
(
|
||||
False,
|
||||
None,
|
||||
"invalid_credentials",
|
||||
),
|
||||
(
|
||||
True,
|
||||
SeventeenTrackError(),
|
||||
"cannot_connect",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_import_flow_cannot_connect_error(
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
return_value,
|
||||
side_effect,
|
||||
error,
|
||||
) -> None:
|
||||
"""Test the import configuration flow with error."""
|
||||
mock_seventeentrack.return_value.profile.login.return_value = return_value
|
||||
mock_seventeentrack.return_value.profile.login.side_effect = side_effect
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=VALID_CONFIG_OLD,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
||||
assert result["reason"] == error
|
||||
|
||||
|
||||
async def test_option_flow(hass: HomeAssistant, mock_seventeentrack: AsyncMock) -> None:
|
||||
"""Test option flow."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=VALID_CONFIG,
|
||||
options={
|
||||
CONF_SHOW_ARCHIVED: False,
|
||||
CONF_SHOW_DELIVERED: False,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_SHOW_ARCHIVED: True, CONF_SHOW_DELIVERED: False},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["data"][CONF_SHOW_ARCHIVED]
|
||||
assert not result["data"][CONF_SHOW_DELIVERED]
|
||||
|
||||
|
||||
async def test_import_flow_already_configured(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
) -> None:
|
||||
"""Test the import configuration flow with error."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=VALID_CONFIG,
|
||||
unique_id=ACCOUNT_ID,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
result_aborted = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
VALID_CONFIG,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result_aborted["type"] == data_entry_flow.FlowResultType.ABORT
|
||||
assert result_aborted["reason"] == "already_configured"
|
|
@ -8,61 +8,73 @@ from freezegun.api import FrozenDateTimeFactory
|
|||
from py17track.errors import SeventeenTrackError
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.issue_registry import IssueRegistry
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import goto_future, init_integration
|
||||
from .conftest import (
|
||||
DEFAULT_SUMMARY,
|
||||
INVALID_CONFIG,
|
||||
NEW_SUMMARY_DATA,
|
||||
VALID_CONFIG_FULL,
|
||||
VALID_CONFIG_FULL_NO_DELIVERED,
|
||||
VALID_CONFIG_MINIMAL,
|
||||
VALID_PLATFORM_CONFIG_FULL,
|
||||
get_package,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_full_valid_config(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure everything starts correctly."""
|
||||
await init_integration(hass, VALID_CONFIG_MINIMAL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == len(DEFAULT_SUMMARY.keys())
|
||||
|
||||
|
||||
async def test_valid_config(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure everything starts correctly."""
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == len(DEFAULT_SUMMARY.keys())
|
||||
|
||||
|
||||
async def test_invalid_config(hass: HomeAssistant) -> None:
|
||||
async def test_invalid_config(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Ensure nothing is created when config is wrong."""
|
||||
await init_integration(hass, INVALID_CONFIG)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert not hass.states.async_entity_ids("sensor")
|
||||
|
||||
|
||||
async def test_login_exception(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure everything starts correctly."""
|
||||
mock_seventeentrack.return_value.profile.login.side_effect = SeventeenTrackError(
|
||||
"Error"
|
||||
)
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert not hass.states.async_entity_ids("sensor")
|
||||
|
||||
|
||||
async def test_add_package(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure package is added correctly when user add a new package."""
|
||||
package = get_package()
|
||||
mock_seventeentrack.return_value.profile.packages.return_value = [package]
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = {}
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert hass.states.get("sensor.seventeentrack_package_456") is not None
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
||||
|
||||
|
@ -82,14 +94,16 @@ async def test_add_package(
|
|||
|
||||
|
||||
async def test_add_package_default_friendly_name(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure package is added correctly with default friendly name when user add a new package without his own friendly name."""
|
||||
package = get_package(friendly_name=None)
|
||||
mock_seventeentrack.return_value.profile.packages.return_value = [package]
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = {}
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
state_456 = hass.states.get("sensor.seventeentrack_package_456")
|
||||
assert state_456 is not None
|
||||
assert state_456.attributes["friendly_name"] == "Seventeentrack Package: 456"
|
||||
|
@ -97,7 +111,10 @@ async def test_add_package_default_friendly_name(
|
|||
|
||||
|
||||
async def test_remove_package(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure entity is not there anymore if package is not there."""
|
||||
package1 = get_package()
|
||||
|
@ -115,7 +132,7 @@ async def test_remove_package(
|
|||
]
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = {}
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.seventeentrack_package_456") is not None
|
||||
assert hass.states.get("sensor.seventeentrack_package_789") is not None
|
||||
|
@ -136,7 +153,9 @@ async def test_remove_package(
|
|||
|
||||
|
||||
async def test_package_error(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure package is added correctly when user add a new package."""
|
||||
mock_seventeentrack.return_value.profile.packages.side_effect = SeventeenTrackError(
|
||||
|
@ -144,19 +163,22 @@ async def test_package_error(
|
|||
)
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = {}
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert hass.states.get("sensor.seventeentrack_package_456") is None
|
||||
|
||||
|
||||
async def test_friendly_name_changed(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test friendly name change."""
|
||||
package = get_package()
|
||||
mock_seventeentrack.return_value.profile.packages.return_value = [package]
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = {}
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.seventeentrack_package_456") is not None
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
||||
|
@ -175,7 +197,10 @@ async def test_friendly_name_changed(
|
|||
|
||||
|
||||
async def test_delivered_not_shown(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry_with_default_options: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure delivered packages are not shown."""
|
||||
package = get_package(status=40)
|
||||
|
@ -185,7 +210,7 @@ async def test_delivered_not_shown(
|
|||
with patch(
|
||||
"homeassistant.components.seventeentrack.sensor.persistent_notification"
|
||||
) as persistent_notification_mock:
|
||||
await init_integration(hass, VALID_CONFIG_FULL_NO_DELIVERED)
|
||||
await init_integration(hass, mock_config_entry_with_default_options)
|
||||
await goto_future(hass, freezer)
|
||||
|
||||
assert not hass.states.async_entity_ids()
|
||||
|
@ -193,7 +218,9 @@ async def test_delivered_not_shown(
|
|||
|
||||
|
||||
async def test_delivered_shown(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure delivered packages are show when user choose to show them."""
|
||||
package = get_package(status=40)
|
||||
|
@ -203,7 +230,7 @@ async def test_delivered_shown(
|
|||
with patch(
|
||||
"homeassistant.components.seventeentrack.sensor.persistent_notification"
|
||||
) as persistent_notification_mock:
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.seventeentrack_package_456") is not None
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
||||
|
@ -211,14 +238,17 @@ async def test_delivered_shown(
|
|||
|
||||
|
||||
async def test_becomes_delivered_not_shown_notification(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry_with_default_options: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure notification is triggered when package becomes delivered."""
|
||||
package = get_package()
|
||||
mock_seventeentrack.return_value.profile.packages.return_value = [package]
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = {}
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL_NO_DELIVERED)
|
||||
await init_integration(hass, mock_config_entry_with_default_options)
|
||||
|
||||
assert hass.states.get("sensor.seventeentrack_package_456") is not None
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
||||
|
@ -237,14 +267,17 @@ async def test_becomes_delivered_not_shown_notification(
|
|||
|
||||
|
||||
async def test_summary_correctly_updated(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure summary entities are not duplicated."""
|
||||
package = get_package(status=30)
|
||||
mock_seventeentrack.return_value.profile.packages.return_value = [package]
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = DEFAULT_SUMMARY
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert len(hass.states.async_entity_ids()) == 8
|
||||
|
||||
|
@ -272,7 +305,9 @@ async def test_summary_correctly_updated(
|
|||
|
||||
|
||||
async def test_summary_error(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test summary empty if error."""
|
||||
package = get_package(status=30)
|
||||
|
@ -281,7 +316,7 @@ async def test_summary_error(
|
|||
"Error"
|
||||
)
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
||||
|
||||
|
@ -291,7 +326,9 @@ async def test_summary_error(
|
|||
|
||||
|
||||
async def test_utc_timestamp(
|
||||
hass: HomeAssistant, mock_seventeentrack: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure package timestamp is converted correctly from HA-defined time zone to UTC."""
|
||||
|
||||
|
@ -299,7 +336,7 @@ async def test_utc_timestamp(
|
|||
mock_seventeentrack.return_value.profile.packages.return_value = [package]
|
||||
mock_seventeentrack.return_value.profile.summary.return_value = {}
|
||||
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.seventeentrack_package_456") is not None
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
||||
|
@ -313,5 +350,18 @@ async def test_non_valid_platform_config(
|
|||
) -> None:
|
||||
"""Test if login fails."""
|
||||
mock_seventeentrack.return_value.profile.login.return_value = False
|
||||
await init_integration(hass, VALID_CONFIG_FULL)
|
||||
assert await async_setup_component(hass, "sensor", VALID_PLATFORM_CONFIG_FULL)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids()) == 0
|
||||
|
||||
|
||||
async def test_full_valid_platform_config(
|
||||
hass: HomeAssistant,
|
||||
mock_seventeentrack: AsyncMock,
|
||||
issue_registry: IssueRegistry,
|
||||
) -> None:
|
||||
"""Ensure everything starts correctly."""
|
||||
assert await async_setup_component(hass, "sensor", VALID_PLATFORM_CONFIG_FULL)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids()) == len(DEFAULT_SUMMARY.keys())
|
||||
assert len(issue_registry.issues) == 1
|
||||
|
|
Loading…
Reference in New Issue