2019-02-14 15:01:46 +00:00
""" Support for the Philips Hue system. """
2018-03-17 03:27:05 +00:00
import ipaddress
2017-12-10 18:15:01 +00:00
import logging
2019-12-16 18:45:09 +00:00
from aiohue . util import normalize_bridge_id
2017-12-10 18:15:01 +00:00
import voluptuous as vol
2019-12-16 11:27:43 +00:00
from homeassistant import config_entries , core
2020-02-05 21:57:17 +00:00
from homeassistant . components import persistent_notification
2019-12-16 18:45:09 +00:00
from homeassistant . const import CONF_HOST
2019-07-31 19:25:30 +00:00
from homeassistant . helpers import config_validation as cv , device_registry as dr
2018-03-30 03:15:40 +00:00
2019-12-16 18:45:09 +00:00
from . bridge import HueBridge
2020-02-11 16:50:07 +00:00
from . const import (
CONF_ALLOW_HUE_GROUPS ,
CONF_ALLOW_UNREACHABLE ,
DEFAULT_ALLOW_HUE_GROUPS ,
DEFAULT_ALLOW_UNREACHABLE ,
DOMAIN ,
)
2017-12-10 18:15:01 +00:00
_LOGGER = logging . getLogger ( __name__ )
CONF_BRIDGES = " bridges "
2019-07-31 19:25:30 +00:00
DATA_CONFIGS = " hue_configs "
2019-02-15 16:43:30 +00:00
2019-07-31 19:25:30 +00:00
PHUE_CONFIG_FILE = " phue.conf "
2017-12-10 18:15:01 +00:00
2019-07-31 19:25:30 +00:00
BRIDGE_CONFIG_SCHEMA = vol . Schema (
{
# Validate as IP address and then convert back to a string.
vol . Required ( CONF_HOST ) : vol . All ( ipaddress . ip_address , cv . string ) ,
2020-07-02 12:12:24 +00:00
vol . Optional ( CONF_ALLOW_UNREACHABLE ) : cv . boolean ,
vol . Optional ( CONF_ALLOW_HUE_GROUPS ) : cv . boolean ,
2020-01-17 23:38:38 +00:00
vol . Optional ( " filename " ) : str ,
2019-07-31 19:25:30 +00:00
}
)
CONFIG_SCHEMA = vol . Schema (
2020-07-02 12:12:24 +00:00
vol . All (
cv . deprecated ( DOMAIN , invalidation_version = " 0.115.0 " ) ,
{
DOMAIN : vol . Schema (
{
vol . Optional ( CONF_BRIDGES ) : vol . All (
cv . ensure_list , [ BRIDGE_CONFIG_SCHEMA ] ,
)
}
)
} ,
) ,
2019-07-31 19:25:30 +00:00
extra = vol . ALLOW_EXTRA ,
)
2017-12-10 18:15:01 +00:00
2018-03-17 03:27:05 +00:00
async def async_setup ( hass , config ) :
2017-12-10 18:15:01 +00:00
""" Set up the Hue platform. """
2018-01-25 13:55:14 +00:00
conf = config . get ( DOMAIN )
if conf is None :
conf = { }
2017-12-10 18:15:01 +00:00
2018-03-30 03:15:40 +00:00
hass . data [ DOMAIN ] = { }
2019-02-15 16:43:30 +00:00
hass . data [ DATA_CONFIGS ] = { }
2017-12-10 18:15:01 +00:00
2020-07-02 12:12:24 +00:00
# User has not configured bridges
2018-10-04 14:04:44 +00:00
if CONF_BRIDGES not in conf :
2018-03-17 03:27:05 +00:00
return True
2017-12-10 18:15:01 +00:00
2018-10-04 14:04:44 +00:00
bridges = conf [ CONF_BRIDGES ]
2020-04-04 18:05:15 +00:00
configured_hosts = {
2020-01-16 09:19:38 +00:00
entry . data . get ( " host " ) for entry in hass . config_entries . async_entries ( DOMAIN )
2020-04-04 18:05:15 +00:00
}
2019-12-16 18:45:09 +00:00
2018-03-30 03:15:40 +00:00
for bridge_conf in bridges :
host = bridge_conf [ CONF_HOST ]
2017-12-10 18:15:01 +00:00
2018-03-30 03:15:40 +00:00
# Store config in hass.data so the config entry can find it
2019-02-15 16:43:30 +00:00
hass . data [ DATA_CONFIGS ] [ host ] = bridge_conf
2017-12-10 18:15:01 +00:00
2019-12-16 18:45:09 +00:00
if host in configured_hosts :
2018-03-30 03:15:40 +00:00
continue
2018-03-17 03:27:05 +00:00
2019-12-16 18:45:09 +00:00
# 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!
2019-07-31 19:25:30 +00:00
hass . async_create_task (
hass . config_entries . flow . async_init (
DOMAIN ,
context = { " source " : config_entries . SOURCE_IMPORT } ,
2019-12-16 18:45:09 +00:00
data = { " host " : bridge_conf [ CONF_HOST ] } ,
2019-07-31 19:25:30 +00:00
)
)
2018-03-17 03:27:05 +00:00
2018-03-30 03:15:40 +00:00
return True
2018-03-04 05:28:04 +00:00
2019-12-16 11:27:43 +00:00
async def async_setup_entry (
hass : core . HomeAssistant , entry : config_entries . ConfigEntry
) :
2018-03-30 03:15:40 +00:00
""" Set up a bridge from a config entry. """
2019-07-31 19:25:30 +00:00
host = entry . data [ " host " ]
2019-02-15 16:43:30 +00:00
config = hass . data [ DATA_CONFIGS ] . get ( host )
2018-03-04 05:28:04 +00:00
2020-07-02 12:12:24 +00:00
# Migrate allow_unreachable from config entry data to config entry options
if (
CONF_ALLOW_UNREACHABLE not in entry . options
and CONF_ALLOW_UNREACHABLE in entry . data
and entry . data [ CONF_ALLOW_UNREACHABLE ] != DEFAULT_ALLOW_UNREACHABLE
) :
options = {
* * entry . options ,
CONF_ALLOW_UNREACHABLE : entry . data [ CONF_ALLOW_UNREACHABLE ] ,
}
data = entry . data . copy ( )
data . pop ( CONF_ALLOW_UNREACHABLE )
hass . config_entries . async_update_entry ( entry , data = data , options = options )
# Migrate allow_hue_groups from config entry data to config entry options
if (
CONF_ALLOW_HUE_GROUPS not in entry . options
and CONF_ALLOW_HUE_GROUPS in entry . data
and entry . data [ CONF_ALLOW_HUE_GROUPS ] != DEFAULT_ALLOW_HUE_GROUPS
) :
options = {
* * entry . options ,
CONF_ALLOW_HUE_GROUPS : entry . data [ CONF_ALLOW_HUE_GROUPS ] ,
}
data = entry . data . copy ( )
data . pop ( CONF_ALLOW_HUE_GROUPS )
hass . config_entries . async_update_entry ( entry , data = data , options = options )
# Overwrite from YAML configuration
if config is not None :
options = { }
if CONF_ALLOW_HUE_GROUPS in config and (
CONF_ALLOW_HUE_GROUPS not in entry . options
or config [ CONF_ALLOW_HUE_GROUPS ] != entry . options [ CONF_ALLOW_HUE_GROUPS ]
) :
options [ CONF_ALLOW_HUE_GROUPS ] = config [ CONF_ALLOW_HUE_GROUPS ]
if CONF_ALLOW_UNREACHABLE in config and (
CONF_ALLOW_UNREACHABLE not in entry . options
or config [ CONF_ALLOW_UNREACHABLE ] != entry . options [ CONF_ALLOW_UNREACHABLE ]
) :
options [ CONF_ALLOW_UNREACHABLE ] = config [ CONF_ALLOW_UNREACHABLE ]
if options :
hass . config_entries . async_update_entry (
entry , options = { * * entry . options , * * options } ,
)
2018-03-04 05:28:04 +00:00
2020-07-02 12:12:24 +00:00
bridge = HueBridge ( hass , entry )
2018-08-29 15:04:04 +00:00
if not await bridge . async_setup ( ) :
return False
2020-01-31 22:47:40 +00:00
hass . data [ DOMAIN ] [ entry . entry_id ] = bridge
2018-08-29 15:04:04 +00:00
config = bridge . api . config
2019-12-16 11:27:43 +00:00
# For backwards compat
2020-07-10 16:37:36 +00:00
unique_id = normalize_bridge_id ( config . bridgeid )
2019-12-16 11:27:43 +00:00
if entry . unique_id is None :
2020-07-10 16:37:36 +00:00
hass . config_entries . async_update_entry ( entry , unique_id = unique_id )
# For recovering from bug where we incorrectly assumed homekit ID = bridge ID
elif entry . unique_id != unique_id :
# Find entries with this unique ID
other_entry = next (
(
entry
for entry in hass . config_entries . async_entries ( DOMAIN )
if entry . unique_id == unique_id
) ,
None ,
2019-12-16 11:27:43 +00:00
)
2020-07-10 16:37:36 +00:00
if other_entry is None :
# If no other entry, update unique ID of this entry ID.
hass . config_entries . async_update_entry ( entry , unique_id = unique_id )
elif other_entry . source == config_entries . SOURCE_IGNORE :
# There is another entry but it is ignored, delete that one and update this one
hass . async_create_task (
hass . config_entries . async_remove ( other_entry . entry_id )
)
hass . config_entries . async_update_entry ( entry , unique_id = unique_id )
else :
# There is another entry that already has the right unique ID. Delete this entry
hass . async_create_task ( hass . config_entries . async_remove ( entry . entry_id ) )
return False
2018-08-29 15:04:04 +00:00
device_registry = await dr . async_get_registry ( hass )
device_registry . async_get_or_create (
2018-09-17 11:39:30 +00:00
config_entry_id = entry . entry_id ,
2019-07-31 19:25:30 +00:00
connections = { ( dr . CONNECTION_NETWORK_MAC , config . mac ) } ,
identifiers = { ( DOMAIN , config . bridgeid ) } ,
manufacturer = " Signify " ,
2018-08-29 15:04:04 +00:00
name = config . name ,
2019-02-19 05:31:42 +00:00
model = config . modelid ,
sw_version = config . swversion ,
2018-08-29 15:04:04 +00:00
)
2020-02-05 21:57:17 +00:00
if config . modelid == " BSB002 " and config . swversion < " 1935144040 " :
persistent_notification . async_create (
hass ,
" Your Hue hub has a known security vulnerability ([CVE-2020-6007](https://cve.circl.lu/cve/CVE-2020-6007)). Go to the Hue app and check for software updates. " ,
" Signify Hue " ,
" hue_hub_firmware " ,
)
elif config . swupdate2_bridge_state == " readytoinstall " :
err = (
" Please check for software updates of the bridge in the Philips Hue App. " ,
" Signify Hue " ,
" hue_hub_firmware " ,
)
2019-02-19 05:31:42 +00:00
_LOGGER . warning ( err )
2018-08-29 15:04:04 +00:00
return True
2018-04-12 12:28:54 +00:00
async def async_unload_entry ( hass , entry ) :
""" Unload a config entry. """
2020-01-31 22:47:40 +00:00
bridge = hass . data [ DOMAIN ] . pop ( entry . entry_id )
2018-04-12 12:28:54 +00:00
return await bridge . async_reset ( )