Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 13 additions & 19 deletions homeassistant/components/hue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@
import ipaddress
import logging

from aiohue.util import normalize_bridge_id
import voluptuous as vol

from homeassistant import config_entries, core
from homeassistant.const import CONF_FILENAME, CONF_HOST
from homeassistant.const import CONF_HOST
from homeassistant.helpers import config_validation as cv, device_registry as dr

from .bridge import HueBridge, normalize_bridge_id
from .config_flow import ( # Loading the config flow file will register the flow
configured_hosts,
)
from .bridge import HueBridge
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)
Expand All @@ -32,8 +30,6 @@
{
# Validate as IP address and then convert back to a string.
vol.Required(CONF_HOST): vol.All(ipaddress.ip_address, cv.string),
# This is for legacy reasons and is only used for importing auth.
vol.Optional(CONF_FILENAME, default=PHUE_CONFIG_FILE): cv.string,
vol.Optional(
CONF_ALLOW_UNREACHABLE, default=DEFAULT_ALLOW_UNREACHABLE
): cv.boolean,
Expand Down Expand Up @@ -65,37 +61,35 @@ async def async_setup(hass, config):

hass.data[DOMAIN] = {}
hass.data[DATA_CONFIGS] = {}
configured = configured_hosts(hass)

# User has configured bridges
if CONF_BRIDGES not in conf:
return True

bridges = conf[CONF_BRIDGES]

configured_hosts = set(
entry.data["host"] for entry in hass.config_entries.async_entries(DOMAIN)
)

for bridge_conf in bridges:
host = bridge_conf[CONF_HOST]

# Store config in hass.data so the config entry can find it
hass.data[DATA_CONFIGS][host] = bridge_conf

# If configured, the bridge will be set up during config entry phase
if host in configured:
if host in configured_hosts:
continue

# No existing config entry found, try importing it or trigger link
# config flow if no existing auth. Because we're inside the setup of
# this component we'll have to use hass.async_add_job to avoid a
# deadlock: creating a config entry will set up the component but the
# setup would block till the entry is created!
# No existing config entry found, trigger link config flow. Because we're
# inside the setup of this component we'll have to use hass.async_add_job
# to avoid a deadlock: creating a config entry will set up the component
# but the setup would block till the entry is created!
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={
"host": bridge_conf[CONF_HOST],
"path": bridge_conf[CONF_FILENAME],
},
data={"host": bridge_conf[CONF_HOST]},
)
)

Expand Down
43 changes: 13 additions & 30 deletions homeassistant/components/hue/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import slugify as unicode_slug
import voluptuous as vol

from homeassistant import core
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client, config_validation as cv

Expand Down Expand Up @@ -45,8 +46,15 @@ async def async_setup(self, tries=0):
host = self.host
hass = self.hass

bridge = aiohue.Bridge(
host,
username=self.config_entry.data["username"],
websession=aiohttp_client.async_get_clientsession(hass),
)

try:
self.api = await get_bridge(hass, host, self.config_entry.data["username"])
await authenticate_bridge(hass, bridge)

except AuthenticationRequired:
# Usernames can become invalid if hub is reset or user removed.
# We are going to fail the config entry setup and initiate a new
Expand All @@ -63,6 +71,8 @@ async def async_setup(self, tries=0):
LOGGER.exception("Unknown error connecting with Hue bridge at %s", host)
return False

self.api = bridge

hass.async_create_task(
hass.config_entries.async_forward_entry_setup(self.config_entry, "light")
)
Expand Down Expand Up @@ -175,16 +185,12 @@ async def handle_unauthorized_error(self):
create_config_flow(self.hass, self.host)


async def get_bridge(hass, host, username=None):
async def authenticate_bridge(hass: core.HomeAssistant, bridge: aiohue.Bridge):
"""Create a bridge object and verify authentication."""
bridge = aiohue.Bridge(
host, username=username, websession=aiohttp_client.async_get_clientsession(hass)
)

try:
with async_timeout.timeout(10):
# Create username if we don't have one
if not username:
if not bridge.username:
device_name = unicode_slug.slugify(
hass.config.location_name, max_length=19
)
Expand All @@ -193,33 +199,10 @@ async def get_bridge(hass, host, username=None):
# Initialize bridge (and validate our username)
await bridge.initialize()

return bridge
except (aiohue.LinkButtonNotPressed, aiohue.Unauthorized):
raise AuthenticationRequired
except (asyncio.TimeoutError, aiohue.RequestError):
raise CannotConnect
except aiohue.AiohueException:
LOGGER.exception("Unknown Hue linking error occurred")
raise AuthenticationRequired


def normalize_bridge_id(bridge_id: str):
"""Normalize a bridge identifier.

There are three sources where we receive bridge ID from:
- ssdp/upnp: <host>/description.xml, field root/device/serialNumber
- nupnp: "id" field
- Hue Bridge API: config.bridgeid

The SSDP/UPNP source does not contain the middle 4 characters compared
to the other sources. In all our tests the middle 4 characters are "fffe".
"""
if len(bridge_id) == 16:
return bridge_id[0:6] + bridge_id[-6:]

if len(bridge_id) == 12:
return bridge_id

LOGGER.warning("Unexpected bridge id number found: %s", bridge_id)

return bridge_id
Loading