Spring cleaning ☀️ (#23)

* Spring cleaning

* Actions

* Fix branches

* Changes for config_flow
This commit is contained in:
Joakim Sørensen
2020-04-17 19:42:59 +02:00
committed by GitHub
parent d408fbec4a
commit a53b0f75a1
28 changed files with 349 additions and 731 deletions

View File

@@ -1,244 +1,107 @@
"""
Component to integrate with blueprint.
Custom integration to integrate blueprint with Home Assistant.
For more details about this component, please refer to
For more details about this integration, please refer to
https://github.com/custom-components/blueprint
"""
import os
from datetime import timedelta
import asyncio
import logging
import voluptuous as vol
from homeassistant import config_entries
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.util import Throttle
from datetime import timedelta
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from sampleclient.client import Client
from integrationhelper.const import CC_STARTUP_VERSION
from .const import (
CONF_BINARY_SENSOR,
CONF_ENABLED,
CONF_NAME,
CONF_PASSWORD,
CONF_SENSOR,
CONF_SWITCH,
CONF_USERNAME,
DEFAULT_NAME,
DOMAIN_DATA,
DOMAIN,
ISSUE_URL,
PLATFORMS,
REQUIRED_FILES,
VERSION,
STARTUP_MESSAGE,
)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
SCAN_INTERVAL = timedelta(seconds=30)
_LOGGER = logging.getLogger(__name__)
BINARY_SENSOR_SCHEMA = vol.Schema(
{
vol.Optional(CONF_ENABLED, default=True): cv.boolean,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
SENSOR_SCHEMA = vol.Schema(
{
vol.Optional(CONF_ENABLED, default=True): cv.boolean,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
SWITCH_SCHEMA = vol.Schema(
{
vol.Optional(CONF_ENABLED, default=True): cv.boolean,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_BINARY_SENSOR): vol.All(
cv.ensure_list, [BINARY_SENSOR_SCHEMA]
),
vol.Optional(CONF_SENSOR): vol.All(cv.ensure_list, [SENSOR_SCHEMA]),
vol.Optional(CONF_SWITCH): vol.All(cv.ensure_list, [SWITCH_SCHEMA]),
}
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass, config):
"""Set up this component using YAML."""
if config.get(DOMAIN) is None:
# We get here if the integration is set up using config flow
return True
# Print startup message
_LOGGER.info(
CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL)
)
# Check that all required files are present
file_check = await check_files(hass)
if not file_check:
return False
# Create DATA dict
hass.data[DOMAIN_DATA] = {}
# Get "global" configuration.
username = config[DOMAIN].get(CONF_USERNAME)
password = config[DOMAIN].get(CONF_PASSWORD)
# Configure the client.
client = Client(username, password)
hass.data[DOMAIN_DATA]["client"] = BlueprintData(hass, client)
# Load platforms
for platform in PLATFORMS:
# Get platform specific configuration
platform_config = config[DOMAIN].get(platform, {})
# If platform is not enabled, skip.
if not platform_config:
continue
for entry in platform_config:
entry_config = entry
# If entry is not enabled, skip.
if not entry_config[CONF_ENABLED]:
continue
hass.async_create_task(
discovery.async_load_platform(
hass, platform, DOMAIN, entry_config, config
)
)
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={}
)
)
async def async_setup(hass: HomeAssistant, config: Config):
"""Set up this integration using YAML is not supported."""
return True
async def async_setup_entry(hass, config_entry):
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up this integration using UI."""
conf = hass.data.get(DOMAIN_DATA)
if config_entry.source == config_entries.SOURCE_IMPORT:
if conf is None:
hass.async_create_task(
hass.config_entries.async_remove(config_entry.entry_id)
if hass.data.get(DOMAIN) is None:
hass.data.setdefault(DOMAIN, {})
_LOGGER.info(STARTUP_MESSAGE)
username = entry.data.get(CONF_USERNAME)
password = entry.data.get(CONF_PASSWORD)
coordinator = BlueprintDataUpdateCoordinator(
hass, username=username, password=password
)
await coordinator.async_refresh()
if not coordinator.last_update_success:
raise ConfigEntryNotReady
hass.data[DOMAIN][entry.entry_id] = coordinator
for platform in PLATFORMS:
if entry.options.get(platform, True):
coordinator.platforms.append(platform)
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
return False
# Print startup message
_LOGGER.info(
CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL)
)
# Check that all required files are present
file_check = await check_files(hass)
if not file_check:
return False
# Create DATA dict
hass.data[DOMAIN_DATA] = {}
# Get "global" configuration.
username = config_entry.data.get(CONF_USERNAME)
password = config_entry.data.get(CONF_PASSWORD)
# Configure the client.
client = Client(username, password)
hass.data[DOMAIN_DATA]["client"] = BlueprintData(hass, client)
# Add binary_sensor
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(config_entry, "binary_sensor")
)
# Add sensor
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(config_entry, "sensor")
)
# Add switch
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(config_entry, "switch")
)
entry.add_update_listener(async_reload_entry)
return True
class BlueprintData:
"""This class handle communication and stores the data."""
class BlueprintDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the API."""
def __init__(self, hass, client):
"""Initialize the class."""
self.hass = hass
self.client = client
def __init__(self, hass, username, password):
"""Initialize."""
self.api = Client(username, password)
self.platforms = []
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def update_data(self):
"""Update data."""
# This is where the main logic to update platform data goes.
super().__init__(
hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL,
)
async def _async_update_data(self):
"""Update data via library."""
try:
data = self.client.get_data()
self.hass.data[DOMAIN_DATA]["data"] = data
except Exception as error: # pylint: disable=broad-except
_LOGGER.error("Could not update data - %s", error)
data = await self.api.async_get_data()
return data.get("data", {})
except Exception as exception:
raise UpdateFailed(exception)
async def check_files(hass):
"""Return bool that indicates if all files are present."""
# Verify that the user downloaded all files.
base = f"{hass.config.path()}/custom_components/{DOMAIN}/"
missing = []
for file in REQUIRED_FILES:
fullpath = "{}{}".format(base, file)
if not os.path.exists(fullpath):
missing.append(file)
if missing:
_LOGGER.critical("The following files are missing: %s", str(missing))
returnvalue = False
else:
returnvalue = True
return returnvalue
async def async_remove_entry(hass, config_entry):
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Handle removal of an entry."""
try:
await hass.config_entries.async_forward_entry_unload(
config_entry, "binary_sensor"
coordinator = hass.data[DOMAIN][entry.entry_id]
unloaded = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
if platform in coordinator.platforms
]
)
_LOGGER.info(
"Successfully removed binary_sensor from the blueprint integration"
)
except ValueError:
pass
)
if unloaded:
hass.data[DOMAIN].pop(entry.entry_id)
try:
await hass.config_entries.async_forward_entry_unload(config_entry, "sensor")
_LOGGER.info("Successfully removed sensor from the blueprint integration")
except ValueError:
pass
return unloaded
try:
await hass.config_entries.async_forward_entry_unload(config_entry, "switch")
_LOGGER.info("Successfully removed switch from the blueprint integration")
except ValueError:
pass
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Reload config entry."""
await async_unload_entry(hass, entry)
await async_setup_entry(hass, entry)