From 04e0fd1d469b8384ccb22dd24155180d5c3ad0d8 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 29 Oct 2018 19:09:54 +0100 Subject: [PATCH 1/5] Fix controller not being stored when setup fails and sequentially fails the retry functionality (#17927) --- homeassistant/components/unifi/__init__.py | 9 +++++---- tests/components/unifi/test_init.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/unifi/__init__.py b/homeassistant/components/unifi/__init__.py index 26b60aecf42..1e07d8cb83d 100644 --- a/homeassistant/components/unifi/__init__.py +++ b/homeassistant/components/unifi/__init__.py @@ -32,20 +32,21 @@ async def async_setup(hass, config): async def async_setup_entry(hass, config_entry): """Set up the UniFi component.""" - controller = UniFiController(hass, config_entry) - if DOMAIN not in hass.data: hass.data[DOMAIN] = {} + + controller = UniFiController(hass, config_entry) + controller_id = CONTROLLER_ID.format( host=config_entry.data[CONF_CONTROLLER][CONF_HOST], site=config_entry.data[CONF_CONTROLLER][CONF_SITE_ID] ) + hass.data[DOMAIN][controller_id] = controller + if not await controller.async_setup(): return False - hass.data[DOMAIN][controller_id] = controller - if controller.mac is None: return True diff --git a/tests/components/unifi/test_init.py b/tests/components/unifi/test_init.py index 400dd3fd93e..0115801eec6 100644 --- a/tests/components/unifi/test_init.py +++ b/tests/components/unifi/test_init.py @@ -54,7 +54,7 @@ async def test_successful_config_entry(hass): async def test_controller_fail_setup(hass): - """Test that configured options for a host are loaded via config entry.""" + """Test that a failed setup still stores controller.""" entry = MockConfigEntry(domain=unifi.DOMAIN, data={ 'controller': { 'host': '0.0.0.0', @@ -75,7 +75,7 @@ async def test_controller_fail_setup(hass): controller_id = unifi.CONTROLLER_ID.format( host='0.0.0.0', site='default' ) - assert controller_id not in hass.data[unifi.DOMAIN] + assert controller_id in hass.data[unifi.DOMAIN] async def test_controller_no_mac(hass): From 3a8891d9ac59cd0cbd54ab92afc66b067868609c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 29 Oct 2018 19:21:21 +0100 Subject: [PATCH 2/5] Pass hass_config to load_platform (#17952) * Pass hass_config to load_platform * Fix tests * Lint --- homeassistant/components/apple_tv.py | 10 ++++----- homeassistant/components/elkm1/__init__.py | 3 ++- homeassistant/components/evohome.py | 2 +- homeassistant/components/google.py | 22 +++++++++++-------- homeassistant/components/maxcube.py | 4 ++-- homeassistant/components/melissa.py | 4 ++-- .../components/mysensors/__init__.py | 2 +- homeassistant/components/mysensors/gateway.py | 21 ++++++++++-------- homeassistant/components/octoprint.py | 5 +++-- homeassistant/components/opentherm_gw.py | 11 +++++----- homeassistant/components/smappee.py | 4 ++-- homeassistant/components/spc.py | 4 ++-- homeassistant/components/spider.py | 2 +- homeassistant/helpers/discovery.py | 9 ++++---- tests/components/device_tracker/test_init.py | 2 +- tests/components/light/test_mqtt_json.py | 2 +- tests/components/lock/test_mqtt.py | 2 +- tests/components/notify/test_demo.py | 3 ++- tests/components/test_google.py | 4 ++-- tests/helpers/test_discovery.py | 9 ++++---- tests/helpers/test_entity_component.py | 2 +- 21 files changed, 70 insertions(+), 57 deletions(-) diff --git a/homeassistant/components/apple_tv.py b/homeassistant/components/apple_tv.py index 012e71a08a7..b8774d76873 100644 --- a/homeassistant/components/apple_tv.py +++ b/homeassistant/components/apple_tv.py @@ -163,7 +163,7 @@ async def async_setup(hass, config): async def atv_discovered(service, info): """Set up an Apple TV that was auto discovered.""" - await _setup_atv(hass, { + await _setup_atv(hass, config, { CONF_NAME: info['name'], CONF_HOST: info['host'], CONF_LOGIN_ID: info['properties']['hG'], @@ -172,7 +172,7 @@ async def async_setup(hass, config): discovery.async_listen(hass, SERVICE_APPLE_TV, atv_discovered) - tasks = [_setup_atv(hass, conf) for conf in config.get(DOMAIN, [])] + tasks = [_setup_atv(hass, config, conf) for conf in config.get(DOMAIN, [])] if tasks: await asyncio.wait(tasks, loop=hass.loop) @@ -187,7 +187,7 @@ async def async_setup(hass, config): return True -async def _setup_atv(hass, atv_config): +async def _setup_atv(hass, hass_config, atv_config): """Set up an Apple TV.""" import pyatv name = atv_config.get(CONF_NAME) @@ -212,10 +212,10 @@ async def _setup_atv(hass, atv_config): } hass.async_create_task(discovery.async_load_platform( - hass, 'media_player', DOMAIN, atv_config)) + hass, 'media_player', DOMAIN, atv_config, hass_config)) hass.async_create_task(discovery.async_load_platform( - hass, 'remote', DOMAIN, atv_config)) + hass, 'remote', DOMAIN, atv_config, hass_config)) class AppleTVPowerManager: diff --git a/homeassistant/components/elkm1/__init__.py b/homeassistant/components/elkm1/__init__.py index 76594e16736..aa7b9973c8e 100644 --- a/homeassistant/components/elkm1/__init__.py +++ b/homeassistant/components/elkm1/__init__.py @@ -146,7 +146,8 @@ async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool: hass.data[DOMAIN] = {'elk': elk, 'config': config, 'keypads': {}} for component in SUPPORTED_DOMAINS: hass.async_create_task( - discovery.async_load_platform(hass, component, DOMAIN, {})) + discovery.async_load_platform(hass, component, DOMAIN, {}, + hass_config)) return True diff --git a/homeassistant/components/evohome.py b/homeassistant/components/evohome.py index ceeec407b05..397d3b9f6c0 100644 --- a/homeassistant/components/evohome.py +++ b/homeassistant/components/evohome.py @@ -140,6 +140,6 @@ def setup(hass, config): _LOGGER.debug("setup(), location = %s", tmp_loc) - load_platform(hass, 'climate', DOMAIN) + load_platform(hass, 'climate', DOMAIN, {}, config) return True diff --git a/homeassistant/components/google.py b/homeassistant/components/google.py index e37b3ba7ff7..49cb195d6c9 100644 --- a/homeassistant/components/google.py +++ b/homeassistant/components/google.py @@ -88,7 +88,7 @@ DEVICE_SCHEMA = vol.Schema({ }, extra=vol.ALLOW_EXTRA) -def do_authentication(hass, config): +def do_authentication(hass, hass_config, config): """Notify user of actions and authenticate. Notify user of user_code and verification_url then poll @@ -145,7 +145,7 @@ def do_authentication(hass, config): storage = Storage(hass.config.path(TOKEN_FILE)) storage.put(credentials) - do_setup(hass, config) + do_setup(hass, hass_config, config) listener() hass.components.persistent_notification.create( 'We are all setup now. Check {} for calendars that have ' @@ -167,14 +167,15 @@ def setup(hass, config): token_file = hass.config.path(TOKEN_FILE) if not os.path.isfile(token_file): - do_authentication(hass, conf) + do_authentication(hass, config, conf) else: - do_setup(hass, conf) + do_setup(hass, config, conf) return True -def setup_services(hass, track_new_found_calendars, calendar_service): +def setup_services(hass, hass_config, track_new_found_calendars, + calendar_service): """Set up the service listeners.""" def _found_calendar(call): """Check if we know about a calendar and generate PLATFORM_DISCOVER.""" @@ -190,7 +191,8 @@ def setup_services(hass, track_new_found_calendars, calendar_service): ) discovery.load_platform(hass, 'calendar', DOMAIN, - hass.data[DATA_INDEX][calendar[CONF_CAL_ID]]) + hass.data[DATA_INDEX][calendar[CONF_CAL_ID]], + hass_config) hass.services.register( DOMAIN, SERVICE_FOUND_CALENDARS, _found_calendar) @@ -210,7 +212,7 @@ def setup_services(hass, track_new_found_calendars, calendar_service): return True -def do_setup(hass, config): +def do_setup(hass, hass_config, config): """Run the setup after we have everything configured.""" # Load calendars the user has configured hass.data[DATA_INDEX] = load_config(hass.config.path(YAML_DEVICES)) @@ -218,13 +220,15 @@ def do_setup(hass, config): calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE)) track_new_found_calendars = convert(config.get(CONF_TRACK_NEW), bool, DEFAULT_CONF_TRACK_NEW) - setup_services(hass, track_new_found_calendars, calendar_service) + setup_services(hass, hass_config, track_new_found_calendars, + calendar_service) # Ensure component is loaded setup_component(hass, 'calendar', config) for calendar in hass.data[DATA_INDEX].values(): - discovery.load_platform(hass, 'calendar', DOMAIN, calendar) + discovery.load_platform(hass, 'calendar', DOMAIN, calendar, + hass_config) # Look for any new calendars hass.services.call(DOMAIN, SERVICE_SCAN_CALENDARS, None) diff --git a/homeassistant/components/maxcube.py b/homeassistant/components/maxcube.py index b574f0bcb15..9980d554232 100644 --- a/homeassistant/components/maxcube.py +++ b/homeassistant/components/maxcube.py @@ -73,8 +73,8 @@ def setup(hass, config): if connection_failed >= len(gateways): return False - load_platform(hass, 'climate', DOMAIN) - load_platform(hass, 'binary_sensor', DOMAIN) + load_platform(hass, 'climate', DOMAIN, {}, config) + load_platform(hass, 'binary_sensor', DOMAIN, {}, config) return True diff --git a/homeassistant/components/melissa.py b/homeassistant/components/melissa.py index f5a757dbcf3..49e7d62f5cb 100644 --- a/homeassistant/components/melissa.py +++ b/homeassistant/components/melissa.py @@ -39,6 +39,6 @@ def setup(hass, config): api = melissa.Melissa(username=username, password=password) hass.data[DATA_MELISSA] = api - load_platform(hass, 'sensor', DOMAIN, {}) - load_platform(hass, 'climate', DOMAIN, {}) + load_platform(hass, 'sensor', DOMAIN, {}, config) + load_platform(hass, 'climate', DOMAIN, {}, config) return True diff --git a/homeassistant/components/mysensors/__init__.py b/homeassistant/components/mysensors/__init__.py index 4f00247495a..883175340ce 100644 --- a/homeassistant/components/mysensors/__init__.py +++ b/homeassistant/components/mysensors/__init__.py @@ -111,7 +111,7 @@ async def async_setup(hass, config): hass.data[MYSENSORS_GATEWAYS] = gateways - hass.async_create_task(finish_setup(hass, gateways)) + hass.async_create_task(finish_setup(hass, config, gateways)) return True diff --git a/homeassistant/components/mysensors/gateway.py b/homeassistant/components/mysensors/gateway.py index 558e944f727..cb1dad922f8 100644 --- a/homeassistant/components/mysensors/gateway.py +++ b/homeassistant/components/mysensors/gateway.py @@ -137,7 +137,7 @@ async def _get_gateway(hass, config, gateway_conf, persistence_file): gateway.metric = hass.config.units.is_metric gateway.optimistic = conf[CONF_OPTIMISTIC] gateway.device = device - gateway.event_callback = _gw_callback_factory(hass) + gateway.event_callback = _gw_callback_factory(hass, config) gateway.nodes_config = gateway_conf[CONF_NODES] if persistence: await gateway.start_persistence() @@ -145,12 +145,13 @@ async def _get_gateway(hass, config, gateway_conf, persistence_file): return gateway -async def finish_setup(hass, gateways): +async def finish_setup(hass, hass_config, gateways): """Load any persistent devices and platforms and start gateway.""" discover_tasks = [] start_tasks = [] for gateway in gateways.values(): - discover_tasks.append(_discover_persistent_devices(hass, gateway)) + discover_tasks.append(_discover_persistent_devices( + hass, hass_config, gateway)) start_tasks.append(_gw_start(hass, gateway)) if discover_tasks: # Make sure all devices and platforms are loaded before gateway start. @@ -159,7 +160,7 @@ async def finish_setup(hass, gateways): await asyncio.wait(start_tasks, loop=hass.loop) -async def _discover_persistent_devices(hass, gateway): +async def _discover_persistent_devices(hass, hass_config, gateway): """Discover platforms for devices loaded via persistence file.""" tasks = [] new_devices = defaultdict(list) @@ -170,17 +171,18 @@ async def _discover_persistent_devices(hass, gateway): for platform, dev_ids in validated.items(): new_devices[platform].extend(dev_ids) for platform, dev_ids in new_devices.items(): - tasks.append(_discover_mysensors_platform(hass, platform, dev_ids)) + tasks.append(_discover_mysensors_platform( + hass, hass_config, platform, dev_ids)) if tasks: await asyncio.wait(tasks, loop=hass.loop) @callback -def _discover_mysensors_platform(hass, platform, new_devices): +def _discover_mysensors_platform(hass, hass_config, platform, new_devices): """Discover a MySensors platform.""" task = hass.async_create_task(discovery.async_load_platform( hass, platform, DOMAIN, - {ATTR_DEVICES: new_devices, CONF_NAME: DOMAIN})) + {ATTR_DEVICES: new_devices, CONF_NAME: DOMAIN}, hass_config)) return task @@ -215,7 +217,7 @@ async def _gw_start(hass, gateway): hass.data.pop(gateway_ready_key, None) -def _gw_callback_factory(hass): +def _gw_callback_factory(hass, hass_config): """Return a new callback for the gateway.""" @callback def mysensors_callback(msg): @@ -246,7 +248,8 @@ def _gw_callback_factory(hass): else: new_dev_ids.append(dev_id) if new_dev_ids: - _discover_mysensors_platform(hass, platform, new_dev_ids) + _discover_mysensors_platform( + hass, hass_config, platform, new_dev_ids) for signal in set(signals): # Only one signal per device is needed. # A device can have multiple platforms, ie multiple schemas. diff --git a/homeassistant/components/octoprint.py b/homeassistant/components/octoprint.py index 2a39ac2c44a..b626e9a93b5 100644 --- a/homeassistant/components/octoprint.py +++ b/homeassistant/components/octoprint.py @@ -114,11 +114,12 @@ def setup(hass, config): sensors = printer[CONF_SENSORS][CONF_MONITORED_CONDITIONS] load_platform(hass, 'sensor', DOMAIN, {'name': name, 'base_url': base_url, - 'sensors': sensors}) + 'sensors': sensors}, config) b_sensors = printer[CONF_BINARY_SENSORS][CONF_MONITORED_CONDITIONS] load_platform(hass, 'binary_sensor', DOMAIN, {'name': name, 'base_url': base_url, - 'sensors': b_sensors}) + 'sensors': b_sensors}, + config) success = True return success diff --git a/homeassistant/components/opentherm_gw.py b/homeassistant/components/opentherm_gw.py index 08807a2d2a6..7152a58afcc 100644 --- a/homeassistant/components/opentherm_gw.py +++ b/homeassistant/components/opentherm_gw.py @@ -64,9 +64,10 @@ async def async_setup(hass, config): hass.async_create_task(connect_and_subscribe( hass, conf[CONF_DEVICE], gateway)) hass.async_create_task(async_load_platform( - hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE))) + hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE), config)) if monitored_vars: - hass.async_create_task(setup_monitored_vars(hass, monitored_vars)) + hass.async_create_task(setup_monitored_vars( + hass, config, monitored_vars)) return True @@ -82,7 +83,7 @@ async def connect_and_subscribe(hass, device_path, gateway): gateway.subscribe(handle_report) -async def setup_monitored_vars(hass, monitored_vars): +async def setup_monitored_vars(hass, config, monitored_vars): """Set up requested sensors.""" gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS] sensor_type_map = { @@ -200,6 +201,6 @@ async def setup_monitored_vars(hass, monitored_vars): _LOGGER.error("Monitored variable not supported: %s", var) if binary_sensors: hass.async_create_task(async_load_platform( - hass, COMP_BINARY_SENSOR, DOMAIN, binary_sensors)) + hass, COMP_BINARY_SENSOR, DOMAIN, binary_sensors, config)) if sensors: - await async_load_platform(hass, COMP_SENSOR, DOMAIN, sensors) + await async_load_platform(hass, COMP_SENSOR, DOMAIN, sensors, config) diff --git a/homeassistant/components/smappee.py b/homeassistant/components/smappee.py index 7904f0a6cce..d8b7a68a506 100644 --- a/homeassistant/components/smappee.py +++ b/homeassistant/components/smappee.py @@ -66,8 +66,8 @@ def setup(hass, config): return False hass.data[DATA_SMAPPEE] = smappee - load_platform(hass, 'switch', DOMAIN) - load_platform(hass, 'sensor', DOMAIN) + load_platform(hass, 'switch', DOMAIN, {}, config) + load_platform(hass, 'sensor', DOMAIN, {}, config) return True diff --git a/homeassistant/components/spc.py b/homeassistant/components/spc.py index 5aa987bd0a8..dd1931e27f1 100644 --- a/homeassistant/components/spc.py +++ b/homeassistant/components/spc.py @@ -63,11 +63,11 @@ async def async_setup(hass, config): # add sensor devices for each zone (typically motion/fire/door sensors) hass.async_create_task(discovery.async_load_platform( - hass, 'binary_sensor', DOMAIN, {})) + hass, 'binary_sensor', DOMAIN, {}, config)) # create a separate alarm panel for each area hass.async_create_task(discovery.async_load_platform( - hass, 'alarm_control_panel', DOMAIN, {})) + hass, 'alarm_control_panel', DOMAIN, {}, config)) # start listening for incoming events over websocket spc.start() diff --git a/homeassistant/components/spider.py b/homeassistant/components/spider.py index 48632be6bad..a10fa129fe5 100644 --- a/homeassistant/components/spider.py +++ b/homeassistant/components/spider.py @@ -56,7 +56,7 @@ def setup(hass, config): } for component in SPIDER_COMPONENTS: - load_platform(hass, component, DOMAIN, {}) + load_platform(hass, component, DOMAIN, {}, config) _LOGGER.debug("Connection with Spider API succeeded") return True diff --git a/homeassistant/helpers/discovery.py b/homeassistant/helpers/discovery.py index 698cee0fcc2..405861eeb75 100644 --- a/homeassistant/helpers/discovery.py +++ b/homeassistant/helpers/discovery.py @@ -114,8 +114,7 @@ def async_listen_platform(hass, component, callback): @bind_hass -def load_platform(hass, component, platform, discovered=None, - hass_config=None): +def load_platform(hass, component, platform, discovered, hass_config): """Load a component and platform dynamically. Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be @@ -132,8 +131,8 @@ def load_platform(hass, component, platform, discovered=None, @bind_hass -async def async_load_platform(hass, component, platform, discovered=None, - hass_config=None): +async def async_load_platform(hass, component, platform, discovered, + hass_config): """Load a component and platform dynamically. Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be @@ -149,6 +148,8 @@ async def async_load_platform(hass, component, platform, discovered=None, This method is a coroutine. """ + assert hass_config, 'You need to pass in the real hass config' + if component in DEPENDENCY_BLACKLIST: raise HomeAssistantError( 'Cannot discover the {} component.'.format(component)) diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index b1b68ff92df..c2c39e17f64 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -180,7 +180,7 @@ class TestComponentsDeviceTracker(unittest.TestCase): assert device_tracker.DOMAIN not in self.hass.config.components discovery.load_platform( self.hass, device_tracker.DOMAIN, 'demo', {'test_key': 'test_val'}, - {}) + {'demo': {}}) self.hass.block_till_done() assert device_tracker.DOMAIN in self.hass.config.components assert mock_demo_setup_scanner.called diff --git a/tests/components/light/test_mqtt_json.py b/tests/components/light/test_mqtt_json.py index 46db2f61fb3..f388c638b0d 100644 --- a/tests/components/light/test_mqtt_json.py +++ b/tests/components/light/test_mqtt_json.py @@ -674,7 +674,7 @@ class TestLightMQTTJSON(unittest.TestCase): async def test_discovery_removal(hass, mqtt_mock, caplog): """Test removal of discovered mqtt_json lights.""" - await async_start(hass, 'homeassistant', {}) + await async_start(hass, 'homeassistant', {'mqtt': {}}) data = ( '{ "name": "Beer",' ' "platform": "mqtt_json",' diff --git a/tests/components/lock/test_mqtt.py b/tests/components/lock/test_mqtt.py index 4d2378ff9fa..ac0a83d4e2d 100644 --- a/tests/components/lock/test_mqtt.py +++ b/tests/components/lock/test_mqtt.py @@ -180,7 +180,7 @@ class TestLockMQTT(unittest.TestCase): async def test_discovery_removal_lock(hass, mqtt_mock, caplog): """Test removal of discovered lock.""" - await async_start(hass, 'homeassistant', {}) + await async_start(hass, 'homeassistant', {'mqtt': {}}) data = ( '{ "name": "Beer",' ' "command_topic": "test_topic" }' diff --git a/tests/components/notify/test_demo.py b/tests/components/notify/test_demo.py index 3be2ddcb86b..a18af7ba684 100644 --- a/tests/components/notify/test_demo.py +++ b/tests/components/notify/test_demo.py @@ -66,7 +66,8 @@ class TestNotifyDemo(unittest.TestCase): """Test discovery of notify demo platform.""" assert notify.DOMAIN not in self.hass.config.components discovery.load_platform( - self.hass, 'notify', 'demo', {'test_key': 'test_val'}, {}) + self.hass, 'notify', 'demo', {'test_key': 'test_val'}, + {'notify': {}}) self.hass.block_till_done() assert notify.DOMAIN in self.hass.config.components assert mock_demo_get_service.called diff --git a/tests/components/test_google.py b/tests/components/test_google.py index b8dc29b5dea..2be58c7e9d4 100644 --- a/tests/components/test_google.py +++ b/tests/components/test_google.py @@ -85,8 +85,8 @@ class TestGoogle(unittest.TestCase): calendar_service = google.GoogleCalendarService( self.hass.config.path(google.TOKEN_FILE)) - self.assertTrue(google.setup_services(self.hass, True, - calendar_service)) + assert google.setup_services( + self.hass, {'google': {}}, True, calendar_service) # self.hass.services.call('google', 'found_calendar', calendar, # blocking=True) diff --git a/tests/helpers/test_discovery.py b/tests/helpers/test_discovery.py index 64f90ee7452..ffafd3ca146 100644 --- a/tests/helpers/test_discovery.py +++ b/tests/helpers/test_discovery.py @@ -79,15 +79,15 @@ class TestHelpersDiscovery: platform_callback) discovery.load_platform(self.hass, 'test_component', 'test_platform', - 'discovery info') + 'discovery info', {'test_component': {}}) self.hass.block_till_done() assert mock_setup_component.called assert mock_setup_component.call_args[0] == \ - (self.hass, 'test_component', None) + (self.hass, 'test_component', {'test_component': {}}) self.hass.block_till_done() discovery.load_platform(self.hass, 'test_component_2', 'test_platform', - 'discovery info') + 'discovery info', {'test_component': {}}) self.hass.block_till_done() assert len(calls) == 1 @@ -202,7 +202,8 @@ class TestHelpersDiscovery: async def test_load_platform_forbids_config(): """Test you cannot setup config component with load_platform.""" with pytest.raises(HomeAssistantError): - await discovery.async_load_platform(None, 'config', 'zwave') + await discovery.async_load_platform(None, 'config', 'zwave', {}, + {'config': {}}) async def test_discover_forbids_config(): diff --git a/tests/helpers/test_entity_component.py b/tests/helpers/test_entity_component.py index c853d0b3447..2bef8c0b53e 100644 --- a/tests/helpers/test_entity_component.py +++ b/tests/helpers/test_entity_component.py @@ -131,7 +131,7 @@ class TestHelpersEntityComponent(unittest.TestCase): component.setup({}) discovery.load_platform(self.hass, DOMAIN, 'platform_test', - {'msg': 'discovery_info'}) + {'msg': 'discovery_info'}, {DOMAIN: {}}) self.hass.block_till_done() From 66d0fb7dbf9ed49ba17a5b2ebebdbe08d205f7af Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 29 Oct 2018 19:22:59 +0100 Subject: [PATCH 3/5] Bumped version to 0.81.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index effdec4db39..70d6eb18f1f 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 81 -PATCH_VERSION = '1' +PATCH_VERSION = '2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From a91d89413228496acc2ed27b8dd4c7dde096fbc5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 29 Oct 2018 21:16:05 +0100 Subject: [PATCH 4/5] Update requests to 2.20.0 (#17978) --- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index fa0d675f4b1..193bb42dba0 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ cryptography==2.3.1 pip>=8.0.3 pytz>=2018.04 pyyaml>=3.13,<4 -requests==2.19.1 +requests==2.20.0 voluptuous==0.11.5 voluptuous-serialize==2.0.0 diff --git a/requirements_all.txt b/requirements_all.txt index 5ad0e9aae8f..caaadd4c6f0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -11,7 +11,7 @@ cryptography==2.3.1 pip>=8.0.3 pytz>=2018.04 pyyaml>=3.13,<4 -requests==2.19.1 +requests==2.20.0 voluptuous==0.11.5 voluptuous-serialize==2.0.0 diff --git a/setup.py b/setup.py index 90f2e8357fd..5bca2cc43db 100755 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ REQUIRES = [ 'pip>=8.0.3', 'pytz>=2018.04', 'pyyaml>=3.13,<4', - 'requests==2.19.1', + 'requests==2.20.0', 'voluptuous==0.11.5', 'voluptuous-serialize==2.0.0', ] From 1e03f945b57933af1cf61023c1b6f359e9cc4ccf Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 29 Oct 2018 21:25:17 +0100 Subject: [PATCH 5/5] Don't use keyset (#17984) --- homeassistant/components/cloud/__init__.py | 68 +--------------------- tests/components/cloud/test_init.py | 25 ++++---- 2 files changed, 13 insertions(+), 80 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 3bfc5909b0b..ba2d41a9feb 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -4,20 +4,16 @@ Component to integrate the Home Assistant cloud. For more details about this component, please refer to the documentation at https://home-assistant.io/components/cloud/ """ -import asyncio from datetime import datetime, timedelta import json import logging import os -import aiohttp -import async_timeout import voluptuous as vol from homeassistant.const import ( EVENT_HOMEASSISTANT_START, CONF_REGION, CONF_MODE, CONF_NAME) from homeassistant.helpers import entityfilter, config_validation as cv -from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util import dt as dt_util from homeassistant.components.alexa import smart_home as alexa_sh from homeassistant.components.google_assistant import helpers as ga_h @@ -129,7 +125,6 @@ class Cloud: self._google_actions = google_actions self._gactions_config = None self._prefs = None - self.jwt_keyset = None self.id_token = None self.access_token = None self.refresh_token = None @@ -262,13 +257,6 @@ class Cloud: } self._prefs = prefs - success = await self._fetch_jwt_keyset() - - # Fetching keyset can fail if internet is not up yet. - if not success: - self.hass.helpers.event.async_call_later(5, self.async_start) - return - def load_config(): """Load config.""" # Ensure config dir exists @@ -288,14 +276,6 @@ class Cloud: if info is None: return - # Validate tokens - try: - for token in 'id_token', 'access_token': - self._decode_claims(info[token]) - except ValueError as err: # Raised when token is invalid - _LOGGER.warning("Found invalid token %s: %s", token, err) - return - self.id_token = info['id_token'] self.access_token = info['access_token'] self.refresh_token = info['refresh_token'] @@ -311,49 +291,7 @@ class Cloud: self._prefs[STORAGE_ENABLE_ALEXA] = alexa_enabled await self._store.async_save(self._prefs) - async def _fetch_jwt_keyset(self): - """Fetch the JWT keyset for the Cognito instance.""" - session = async_get_clientsession(self.hass) - url = ("https://cognito-idp.us-east-1.amazonaws.com/" - "{}/.well-known/jwks.json".format(self.user_pool_id)) - - try: - with async_timeout.timeout(10, loop=self.hass.loop): - req = await session.get(url) - self.jwt_keyset = await req.json() - - return True - - except (asyncio.TimeoutError, aiohttp.ClientError) as err: - _LOGGER.error("Error fetching Cognito keyset: %s", err) - return False - - def _decode_claims(self, token): + def _decode_claims(self, token): # pylint: disable=no-self-use """Decode the claims in a token.""" - from jose import jwt, exceptions as jose_exceptions - try: - header = jwt.get_unverified_header(token) - except jose_exceptions.JWTError as err: - raise ValueError(str(err)) from None - kid = header.get('kid') - - if kid is None: - raise ValueError("No kid in header") - - # Locate the key for this kid - key = None - for key_dict in self.jwt_keyset['keys']: - if key_dict['kid'] == kid: - key = key_dict - break - if not key: - raise ValueError( - "Unable to locate kid ({}) in keyset".format(kid)) - - try: - return jwt.decode( - token, key, audience=self.cognito_client_id, options={ - 'verify_exp': False, - }) - except jose_exceptions.JWTError as err: - raise ValueError(str(err)) from None + from jose import jwt + return jwt.get_unverified_claims(token) diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index 61518f0f0e8..44d56566f75 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -32,8 +32,7 @@ def test_constructor_loads_info_from_constant(): 'google_actions_sync_url': 'test-google_actions_sync_url', 'subscription_info_url': 'test-subscription-info-url' } - }), patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset', - return_value=mock_coro(True)): + }): result = yield from cloud.async_setup(hass, { 'cloud': {cloud.CONF_MODE: 'beer'} }) @@ -54,17 +53,15 @@ def test_constructor_loads_info_from_config(): """Test non-dev mode loads info from SERVERS constant.""" hass = MagicMock(data={}) - with patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset', - return_value=mock_coro(True)): - result = yield from cloud.async_setup(hass, { - 'cloud': { - cloud.CONF_MODE: cloud.MODE_DEV, - 'cognito_client_id': 'test-cognito_client_id', - 'user_pool_id': 'test-user_pool_id', - 'region': 'test-region', - 'relayer': 'test-relayer', - } - }) + result = yield from cloud.async_setup(hass, { + 'cloud': { + cloud.CONF_MODE: cloud.MODE_DEV, + 'cognito_client_id': 'test-cognito_client_id', + 'user_pool_id': 'test-user_pool_id', + 'region': 'test-region', + 'relayer': 'test-relayer', + } + }) assert result cl = hass.data['cloud'] @@ -89,8 +86,6 @@ async def test_initialize_loads_info(mock_os, hass): cl.iot.connect.return_value = mock_coro() with patch('homeassistant.components.cloud.open', mopen, create=True), \ - patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset', - return_value=mock_coro(True)), \ patch('homeassistant.components.cloud.Cloud._decode_claims'): await cl.async_start(None)