Skip to content

Commit

Permalink
Add config flow for Tile (home-assistant#36173)
Browse files Browse the repository at this point in the history
* Overhaul Tile

* Adjust coverage

* Fix tests

* Code review

* Code review

* Remove unused config flow step

* Revert "Remove unused config flow step"

This reverts commit cb206e0.

* Fix tests
  • Loading branch information
bachya authored Jun 4, 2020
1 parent fae8062 commit 7a3c2e1
Show file tree
Hide file tree
Showing 13 changed files with 468 additions and 116 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@ omit =
homeassistant/components/thomson/device_tracker.py
homeassistant/components/tibber/*
homeassistant/components/tikteck/light.py
homeassistant/components/tile/__init__.py
homeassistant/components/tile/device_tracker.py
homeassistant/components/time_date/sensor.py
homeassistant/components/tmb/sensor.py
Expand Down
144 changes: 143 additions & 1 deletion homeassistant/components/tile/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,143 @@
"""The tile component."""
"""The Tile component."""
import asyncio
from datetime import timedelta

from pytile import async_login
from pytile.errors import TileError

from homeassistant.const import ATTR_ATTRIBUTION, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DATA_COORDINATOR, DOMAIN, LOGGER

PLATFORMS = ["device_tracker"]
DEVICE_TYPES = ["PHONE", "TILE"]

DEFAULT_ATTRIBUTION = "Data provided by Tile"
DEFAULT_ICON = "mdi:view-grid"
DEFAULT_UPDATE_INTERVAL = timedelta(minutes=2)

CONF_SHOW_INACTIVE = "show_inactive"


async def async_setup(hass, config):
"""Set up the Tile component."""
hass.data[DOMAIN] = {DATA_COORDINATOR: {}}

return True


async def async_setup_entry(hass, config_entry):
"""Set up Tile as config entry."""
websession = aiohttp_client.async_get_clientsession(hass)

client = await async_login(
config_entry.data[CONF_USERNAME],
config_entry.data[CONF_PASSWORD],
session=websession,
)

async def async_update_data():
"""Get new data from the API."""
try:
return await client.tiles.all()
except TileError as err:
raise UpdateFailed(f"Error while retrieving data: {err}")

coordinator = DataUpdateCoordinator(
hass,
LOGGER,
name=config_entry.title,
update_interval=DEFAULT_UPDATE_INTERVAL,
update_method=async_update_data,
)

await coordinator.async_refresh()
hass.data[DOMAIN][DATA_COORDINATOR][config_entry.entry_id] = coordinator

for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, component)
)

return True


async def async_unload_entry(hass, config_entry):
"""Unload a Tile config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in PLATFORMS
]
)
)
if unload_ok:
hass.data[DOMAIN][DATA_COORDINATOR].pop(config_entry.entry_id)

return unload_ok


class TileEntity(Entity):
"""Define a generic Tile entity."""

def __init__(self, coordinator):
"""Initialize."""
self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
self._name = None
self._unique_id = None
self.coordinator = coordinator

@property
def device_state_attributes(self):
"""Return the device state attributes."""
return self._attrs

@property
def icon(self):
"""Return the icon."""
return DEFAULT_ICON

@property
def name(self):
"""Return the name."""
return self._name

@property
def should_poll(self):
"""Disable polling."""
return False

@property
def unique_id(self):
"""Return the unique ID of the entity."""
return self._unique_id

@callback
def _update_from_latest_data(self):
"""Update the entity from the latest data."""
raise NotImplementedError

async def async_added_to_hass(self):
"""Register callbacks."""

@callback
def update():
"""Update the state."""
self._update_from_latest_data()
self.async_write_ha_state()

self.async_on_remove(self.coordinator.async_add_listener(update))

self._update_from_latest_data()

async def async_update(self):
"""Update the entity.
Only used by the generic entity update service.
"""
await self.coordinator.async_request_refresh()
52 changes: 52 additions & 0 deletions homeassistant/components/tile/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Config flow to configure the Tile integration."""
from pytile import async_login
from pytile.errors import TileError
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import aiohttp_client

from .const import DOMAIN # pylint: disable=unused-import


class TileFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a Tile config flow."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL

def __init__(self):
"""Initialize the config flow."""
self.data_schema = vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
)

async def _show_form(self, errors=None):
"""Show the form to the user."""
return self.async_show_form(
step_id="user", data_schema=self.data_schema, errors=errors or {}
)

async def async_step_import(self, import_config):
"""Import a config entry from configuration.yaml."""
return await self.async_step_user(import_config)

async def async_step_user(self, user_input=None):
"""Handle the start of the config flow."""
if not user_input:
return await self._show_form()

await self.async_set_unique_id(user_input[CONF_USERNAME])
self._abort_if_unique_id_configured()

session = aiohttp_client.async_get_clientsession(self.hass)

try:
await async_login(
user_input[CONF_USERNAME], user_input[CONF_PASSWORD], session=session
)
except TileError:
return await self._show_form({"base": "invalid_credentials"})

return self.async_create_entry(title=user_input[CONF_USERNAME], data=user_input)
8 changes: 8 additions & 0 deletions homeassistant/components/tile/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Define Tile constants."""
import logging

DOMAIN = "tile"

DATA_COORDINATOR = "coordinator"

LOGGER = logging.getLogger(__package__)
Loading

0 comments on commit 7a3c2e1

Please sign in to comment.