def test_disable_component_if_invalid_return(self): """Test disabling component if invalid return.""" loader.set_component( 'disabled_component', MockModule('disabled_component', setup=lambda hass, config: None)) assert not bootstrap.setup_component(self.hass, 'disabled_component') assert loader.get_component('disabled_component') is None assert 'disabled_component' not in self.hass.config.components loader.set_component( 'disabled_component', MockModule('disabled_component', setup=lambda hass, config: False)) assert not bootstrap.setup_component(self.hass, 'disabled_component') assert loader.get_component('disabled_component') is not None assert 'disabled_component' not in self.hass.config.components loader.set_component( 'disabled_component', MockModule('disabled_component', setup=lambda hass, config: True)) assert bootstrap.setup_component(self.hass, 'disabled_component') assert loader.get_component('disabled_component') is not None assert 'disabled_component' in self.hass.config.components
def is_on(hass, entity_id=None): """Load up the module to call the is_on method. If there is no entity id given we will check all. """ if entity_id: group = get_component('group') entity_ids = group.expand_entity_ids(hass, [entity_id]) else: entity_ids = hass.states.entity_ids() for entity_id in entity_ids: domain = split_entity_id(entity_id)[0] module = get_component(domain) try: if module.is_on(hass, entity_id): return True except AttributeError: # module is None or method is_on does not exist _LOGGER.exception("Failed to call %s.is_on for %s", module, entity_id) return False
def __init__(self, hass, state_topic, command_topic, qos, retain, payload_on, payload_off, optimistic, value_template): """ @return: """ self.hass = hass self._mqtt = loader.get_component('mqtt') self._switch = loader.get_component("switch") self._hass = hass self._state_topic = state_topic self._command_topic = command_topic self._qos = qos self._retain = retain self._payload_on = payload_on self._payload_off = payload_off self._optimistic = optimistic def command_recevie(topic, payload, qos): if payload == payload_on: self.turn_on() elif payload == payload_off: self.turn_off() self._mqtt.subscribe(hass, command_topic, command_recevie)
async def test_log_warning_custom_component(hass, caplog): """Test that we log a warning when loading a custom component.""" loader.get_component(hass, 'test_standalone') assert \ 'You are using a custom component for test_standalone' in caplog.text loader.get_component(hass, 'light.test') assert 'You are using a custom component for light.test' in caplog.text
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Fitbit sensor.""" config_path = hass.config.path(FITBIT_CONFIG_FILE) if os.path.isfile(config_path): config_file = config_from_file(config_path) if config_file == DEFAULT_CONFIG: request_app_setup(hass, config, add_devices, config_path, discovery_info=None) return False else: config_file = config_from_file(config_path, DEFAULT_CONFIG) request_app_setup(hass, config, add_devices, config_path, discovery_info=None) return False if "fitbit" in _CONFIGURING: get_component("configurator").request_done(_CONFIGURING.pop("fitbit")) import fitbit access_token = config_file.get("access_token") refresh_token = config_file.get("refresh_token") if None not in (access_token, refresh_token): authd_client = fitbit.Fitbit(config_file.get("client_id"), config_file.get("client_secret"), access_token=access_token, refresh_token=refresh_token) if int(time.time()) - config_file.get("last_saved_at", 0) > 3600: authd_client.client.refresh_token() authd_client.system = authd_client.user_profile_get()["user"]["locale"] dev = [] for resource in config.get("monitored_resources", FITBIT_DEFAULT_RESOURCE_LIST): dev.append(FitbitSensor(authd_client, config_path, resource, hass.config.temperature_unit == TEMP_CELSIUS)) add_devices(dev) else: oauth = fitbit.api.FitbitOauth2Client(config_file.get("client_id"), config_file.get("client_secret")) redirect_uri = "{}{}".format(hass.config.api.base_url, FITBIT_AUTH_CALLBACK_PATH) fitbit_auth_start_url, _ = oauth.authorize_token_url( redirect_uri=redirect_uri, scope=["activity", "heartrate", "nutrition", "profile", "settings", "sleep", "weight"]) hass.wsgi.register_redirect(FITBIT_AUTH_START, fitbit_auth_start_url) hass.wsgi.register_view(FitbitAuthCallbackView(hass, config, add_devices, oauth)) request_oauth_completion(hass)
def setUp(self): # pylint: disable=invalid-name """ Init needed objects. """ self.hass = get_test_home_assistant() self.hass.states.set('light.Bowl', STATE_ON) self.hass.states.set('light.Ceiling', STATE_OFF) self.hass.states.set('light.Kitchen', STATE_OFF) loader.get_component('group').setup_group( self.hass, 'test', ['light.Ceiling', 'light.Kitchen'])
def setup_ecobee(hass, network, config): """Setup Ecobee thermostat.""" # If ecobee has a PIN then it needs to be configured. if network.pin is not None: request_configuration(network, hass, config) return if 'ecobee' in _CONFIGURING: configurator = get_component('configurator') configurator.request_done(_CONFIGURING.pop('ecobee')) # Ensure component is loaded bootstrap.setup_component(hass, 'thermostat', config) bootstrap.setup_component(hass, 'sensor', config) hold_temp = config[DOMAIN].get(HOLD_TEMP, False) # Fire thermostat discovery event hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { ATTR_SERVICE: DISCOVER_THERMOSTAT, ATTR_DISCOVERED: {'hold_temp': hold_temp} }) # Fire sensor discovery event hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { ATTR_SERVICE: DISCOVER_SENSORS, ATTR_DISCOVERED: {} })
def setup(hass, config): """ Sets up the device tracker. """ if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER): return False tracker_type = config[DOMAIN][ha.CONF_TYPE] tracker_implementation = get_component( 'device_tracker.{}'.format(tracker_type)) if tracker_implementation is None: _LOGGER.error("Unknown device_tracker type specified.") return False device_scanner = tracker_implementation.get_scanner(hass, config) if device_scanner is None: _LOGGER.error("Failed to initialize device scanner for %s", tracker_type) return False DeviceTracker(hass, device_scanner) return True
def request_configuration(nest, hass, config): """Request configuration steps from the user.""" configurator = get_component('configurator') if 'nest' in _CONFIGURING: _LOGGER.debug("configurator failed") configurator.notify_errors( _CONFIGURING['nest'], "Failed to configure, please try again.") return def nest_configuration_callback(data): """The actions to do when our configuration callback is called.""" _LOGGER.debug("configurator callback") pin = data.get('pin') setup_nest(hass, nest, config, pin=pin) _CONFIGURING['nest'] = configurator.request_config( hass, "Nest", nest_configuration_callback, description=('To configure Nest, click Request Authorization below, ' 'log into your Nest account, ' 'and then enter the resulting PIN'), link_name='Request Authorization', link_url=nest.authorize_url, submit_caption="Confirm", fields=[{'id': 'pin', 'name': 'Enter the PIN', 'type': ''}] )
def test_light_profiles(self): """ Test light profiles. """ platform = loader.get_component('light.test') platform.init() user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE) with open(user_light_file, 'w') as user_file: user_file.write('id,x,y,brightness\n') user_file.write('test,.4,.6,100\n') self.assertTrue(light.setup( self.hass, {light.DOMAIN: {CONF_PLATFORM: 'test'}} )) dev1, dev2, dev3 = platform.DEVICES light.turn_on(self.hass, dev1.entity_id, profile='test') self.hass.pool.block_till_done() method, data = dev1.last_call('turn_on') self.assertEqual( {light.ATTR_XY_COLOR: [.4, .6], light.ATTR_BRIGHTNESS: 100}, data)
def request_configuration(device_id, insteonhub, model, hass, add_devices_callback): """Request configuration steps from the user.""" configurator = get_component('configurator') # We got an error if this method is called while we are configuring if device_id in _CONFIGURING: configurator.notify_errors( _CONFIGURING[device_id], 'Failed to register, please try again.') return def insteon_fan_config_callback(data): """The actions to do when our configuration callback is called.""" setup_fan(device_id, data.get('name'), insteonhub, hass, add_devices_callback) _CONFIGURING[device_id] = configurator.request_config( hass, 'Insteon ' + model + ' addr: ' + device_id, insteon_fan_config_callback, description=('Enter a name for ' + model + ' Fan addr: ' + device_id), entity_picture='/static/images/config_insteon.png', submit_caption='Confirm', fields=[{'id': 'name', 'name': 'Name', 'type': ''}] )
def test_update_stale(self): """Test stalled update.""" scanner = get_component('device_tracker.test').SCANNER scanner.reset() scanner.come_home('DEV1') register_time = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) scan_time = datetime(2015, 9, 15, 23, 1, tzinfo=dt_util.UTC) with patch('homeassistant.components.device_tracker.dt_util.utcnow', return_value=register_time): self.assertTrue(device_tracker.setup(self.hass, { 'device_tracker': { 'platform': 'test', 'consider_home': 59, }})) self.assertEqual(STATE_HOME, self.hass.states.get('device_tracker.dev1').state) scanner.leave_home('DEV1') with patch('homeassistant.components.device_tracker.dt_util.utcnow', return_value=scan_time): fire_time_changed(self.hass, scan_time) self.hass.pool.block_till_done() self.assertEqual(STATE_NOT_HOME, self.hass.states.get('device_tracker.dev1').state)
def test_update_stale(self): """Test stalled update.""" scanner = get_component(self.hass, 'device_tracker.test').SCANNER scanner.reset() scanner.come_home('DEV1') register_time = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) scan_time = datetime(2015, 9, 15, 23, 1, tzinfo=dt_util.UTC) with patch('homeassistant.components.device_tracker.dt_util.utcnow', return_value=register_time): with assert_setup_component(1, device_tracker.DOMAIN): assert setup_component(self.hass, device_tracker.DOMAIN, { device_tracker.DOMAIN: { CONF_PLATFORM: 'test', device_tracker.CONF_CONSIDER_HOME: 59, }}) self.hass.block_till_done() assert STATE_HOME == \ self.hass.states.get('device_tracker.dev1').state scanner.leave_home('DEV1') with patch('homeassistant.components.device_tracker.dt_util.utcnow', return_value=scan_time): fire_time_changed(self.hass, scan_time) self.hass.block_till_done() assert STATE_NOT_HOME == \ self.hass.states.get('device_tracker.dev1').state
def setup(hass, config): """ Sets up the device tracker. """ if not validate_config(config, {DOMAIN: [CONF_PLATFORM]}, _LOGGER): return False tracker_type = config[DOMAIN].get(CONF_PLATFORM) tracker_implementation = get_component( 'device_tracker.{}'.format(tracker_type)) if tracker_implementation is None: _LOGGER.error("Unknown device_tracker type specified.") return False device_scanner = tracker_implementation.get_scanner(hass, config) if device_scanner is None: _LOGGER.error("Failed to initialize device scanner for %s", tracker_type) return False seconds = util.convert(config[DOMAIN].get(CONF_SECONDS), int, DEFAULT_CONF_SECONDS) track_new_devices = config[DOMAIN].get(TRACK_NEW_DEVICES) or False _LOGGER.info("Tracking new devices: %s", track_new_devices) tracker = DeviceTracker(hass, device_scanner, seconds, track_new_devices) # We only succeeded if we got to parse the known devices file return not tracker.invalid_known_devices_file
def _setup_component(hass, domain, config): """ Setup a component for Home Assistant. """ component = loader.get_component(domain) missing_deps = [dep for dep in component.DEPENDENCIES if dep not in hass.config.components] if missing_deps: _LOGGER.error("Not initializing %s because not all dependencies loaded: %s", domain, ", ".join(missing_deps)) return False try: if component.setup(hass, config): hass.config.components.append(component.DOMAIN) # Assumption: if a component does not depend on groups # it communicates with devices if group.DOMAIN not in component.DEPENDENCIES: hass.pool.add_worker() hass.bus.fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}) return True else: _LOGGER.error("component %s failed to initialize", domain) except Exception: # pylint: disable=broad-except _LOGGER.exception("Error during setup of component %s", domain) return False
def test_heater_switch(self): """Test heater switching test switch.""" platform = loader.get_component(self.hass, 'switch.test') platform.init() self.switch_1 = platform.DEVICES[1] assert setup_component(self.hass, switch.DOMAIN, {'switch': { 'platform': 'test'}}) heater_switch = self.switch_1.entity_id assert setup_component(self.hass, climate.DOMAIN, {'climate': { 'platform': 'generic_thermostat', 'name': 'test', 'heater': heater_switch, 'target_sensor': ENT_SENSOR }}) self.assertEqual(STATE_OFF, self.hass.states.get(heater_switch).state) self._setup_sensor(18) self.hass.block_till_done() climate.set_temperature(self.hass, 23) self.hass.block_till_done() self.assertEqual(STATE_ON, self.hass.states.get(heater_switch).state)
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the mysensors platform for sensors.""" # Only act if loaded via mysensors by discovery event. # Otherwise gateway is not setup. if discovery_info is None: return mysensors = get_component('mysensors') for gateway in mysensors.GATEWAYS.values(): # Define the S_TYPES and V_TYPES that the platform should handle as # states. Map them in a dict of lists. pres = gateway.const.Presentation set_req = gateway.const.SetReq map_sv_types = { pres.S_DIMMER: [set_req.V_DIMMER], } device_class_map = { pres.S_DIMMER: MySensorsLightDimmer, } if float(gateway.version) >= 1.5: # Add V_RGBW when rgb_white is implemented in the frontend map_sv_types.update({ pres.S_RGB_LIGHT: [set_req.V_RGB], }) map_sv_types[pres.S_DIMMER].append(set_req.V_PERCENTAGE) device_class_map.update({ pres.S_RGB_LIGHT: MySensorsLightRGB, }) devices = {} gateway.platform_callbacks.append(mysensors.pf_callback_factory( map_sv_types, devices, add_devices, device_class_map))
def prepare_setup_platform(hass, config, domain, platform_name): """ Loads a platform and makes sure dependencies are setup. """ _ensure_loader_prepared(hass) platform_path = PLATFORM_FORMAT.format(domain, platform_name) platform = loader.get_component(platform_path) # Not found if platform is None: return None # Already loaded or no dependencies elif platform_path in hass.config.components or not hasattr(platform, "DEPENDENCIES"): return platform # Load dependencies for component in platform.DEPENDENCIES: if not setup_component(hass, component, config): _LOGGER.error( "Unable to prepare setup for platform %s because dependency " "%s could not be initialized", platform_path, component, ) return None return platform
def prepare_setup_platform(hass, config, domain, platform_name): """ Loads a platform and makes sure dependencies are setup. """ _ensure_loader_prepared(hass) platform_path = PLATFORM_FORMAT.format(domain, platform_name) platform = loader.get_component(platform_path) # Not found if platform is None: return None # Already loaded elif platform_path in hass.config.components: return platform # Load dependencies if hasattr(platform, 'DEPENDENCIES'): for component in platform.DEPENDENCIES: if not setup_component(hass, component, config): _LOGGER.error( 'Unable to prepare setup for platform %s because ' 'dependency %s could not be initialized', platform_path, component) return None if not _handle_requirements(platform, platform_path): return None return platform
def setup(hass, config): """ Sets up the Wink component. """ logger = logging.getLogger(__name__) if not validate_config(config, {DOMAIN: [CONF_ACCESS_TOKEN]}, logger): return False import pywink pywink.set_bearer_token(config[DOMAIN][CONF_ACCESS_TOKEN]) # Load components for the devices in the Wink that we support for component_name, func_exists, discovery_type in ( ('light', pywink.get_bulbs, DISCOVER_LIGHTS), ('switch', pywink.get_switches, DISCOVER_SWITCHES), ('sensor', pywink.get_sensors, DISCOVER_SENSORS)): if func_exists(): component = get_component(component_name) # Ensure component is loaded bootstrap.setup_component(hass, component.DOMAIN, config) # Fire discovery event hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { ATTR_SERVICE: discovery_type, ATTR_DISCOVERED: {} }) return True
def extract_entity_ids(hass, service_call, expand_group=True): """Helper method to extract a list of entity ids from a service call. Will convert group entity ids to the entity ids it represents. Async friendly. """ if not (service_call.data and ATTR_ENTITY_ID in service_call.data): return [] group = get_component('group') # Entity ID attr can be a list or a string service_ent_id = service_call.data[ATTR_ENTITY_ID] if expand_group: if isinstance(service_ent_id, str): return group.expand_entity_ids(hass, [service_ent_id]) return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)] else: if isinstance(service_ent_id, str): return [service_ent_id] return service_ent_id
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the available Netatmo weather sensors.""" netatmo = get_component('netatmo') data = NetAtmoData(netatmo.NETATMO_AUTH, config.get(CONF_STATION, None)) dev = [] import lnetatmo try: if CONF_MODULES in config: # Iterate each module for module_name, monitored_conditions in\ config[CONF_MODULES].items(): # Test if module exist """ if module_name not in data.get_module_names(): _LOGGER.error('Module name: "%s" not found', module_name) continue # Only create sensor for monitored """ for variable in monitored_conditions: dev.append(NetAtmoSensor(data, module_name, variable)) else: for module_name in data.get_module_names(): for variable in\ data.station_data.monitoredConditions(module_name): if variable in SENSOR_TYPES.keys(): dev.append(NetAtmoSensor(data, module_name, variable)) else: _LOGGER.warning("Ignoring unknown var %s for mod %s", variable, module_name) except lnetatmo.NoDevice: return None add_devices(dev, True)
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup access to Netatmo binary sensor.""" netatmo = get_component('netatmo') home = config.get(CONF_HOME, None) timeout = config.get(CONF_TIMEOUT, 15) module_name = None import lnetatmo try: data = WelcomeData(netatmo.NETATMO_AUTH, home) if data.get_camera_names() == []: return None except lnetatmo.NoDevice: return None sensors = config.get(CONF_MONITORED_CONDITIONS, SENSOR_TYPES) for camera_name in data.get_camera_names(): if CONF_CAMERAS in config: if config[CONF_CAMERAS] != [] and \ camera_name not in config[CONF_CAMERAS]: continue for variable in sensors: if variable in ('Tag Vibration', 'Tag Open'): continue add_devices([WelcomeBinarySensor(data, camera_name, module_name, home, timeout, variable)]) for module_name in data.get_module_names(camera_name): for variable in sensors: if variable in ('Tag Vibration', 'Tag Open'): add_devices([WelcomeBinarySensor(data, camera_name, module_name, home, timeout, variable)])
def request_configuration(host, hass, add_devices, filename, allow_unreachable, allow_in_emulated_hue, allow_hue_groups): """Request configuration steps from the user.""" configurator = get_component('configurator') # We got an error if this method is called while we are configuring if host in _CONFIGURING: configurator.notify_errors( _CONFIGURING[host], "Failed to register, please try again.") return # pylint: disable=unused-argument def hue_configuration_callback(data): """The actions to do when our configuration callback is called.""" setup_bridge(host, hass, add_devices, filename, allow_unreachable, allow_in_emulated_hue, allow_hue_groups) _CONFIGURING[host] = configurator.request_config( hass, "Philips Hue", hue_configuration_callback, description=("Press the button on the bridge to register Philips Hue " "with Home Assistant."), entity_picture="/static/images/logo_philips_hue.png", description_image="/static/images/config_philips_hue.jpg", submit_caption="I have pressed the button" )
def request_configuration(host, hass, config, add_devices_callback): """Request configuration steps from the user.""" configurator = get_component('configurator') # We got an error if this method is called while we are configuring if host in _CONFIGURING: configurator.notify_errors(_CONFIGURING[host], 'Failed to register, please try again.') return def plex_configuration_callback(data): """The actions to do when our configuration callback is called.""" setup_plexserver(host, data.get('token'), hass, config, add_devices_callback) _CONFIGURING[host] = configurator.request_config( hass, 'Plex Media Server', plex_configuration_callback, description=('Enter the X-Plex-Token'), entity_picture='/static/images/logo_plex_mediaserver.png', submit_caption='Confirm', fields=[{ 'id': 'token', 'name': 'X-Plex-Token', 'type': '' }])
def get_scanner(hass, config): """Setup Unifi device_tracker.""" from pyunifi.controller import Controller host = config[DOMAIN].get(CONF_HOST) username = config[DOMAIN].get(CONF_USERNAME) password = config[DOMAIN].get(CONF_PASSWORD) site_id = config[DOMAIN].get(CONF_SITE_ID) port = config[DOMAIN].get(CONF_PORT) persistent_notification = loader.get_component('persistent_notification') try: ctrl = Controller(host, username, password, port, 'v4', site_id) except urllib.error.HTTPError as ex: _LOGGER.error('Failed to connect to Unifi: %s', ex) persistent_notification.create( hass, 'Failed to connect to Unifi. ' 'Error: {}<br />' 'You will need to restart hass after fixing.' ''.format(ex), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) return False return UnifiScanner(ctrl)
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the available OctoPrint sensors.""" octoprint = get_component('octoprint') name = config.get(CONF_NAME) monitored_conditions = config.get(CONF_MONITORED_CONDITIONS) devices = [] types = ["actual", "target"] for octo_type in monitored_conditions: if octo_type == "Temperatures": for tool in octoprint.OCTOPRINT.get_tools(): for temp_type in types: new_sensor = OctoPrintSensor(octoprint.OCTOPRINT, temp_type, temp_type, name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0], SENSOR_TYPES[octo_type][1], tool) devices.append(new_sensor) else: new_sensor = OctoPrintSensor(octoprint.OCTOPRINT, octo_type, SENSOR_TYPES[octo_type][2], name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0], SENSOR_TYPES[octo_type][1]) devices.append(new_sensor) add_devices(devices)
def setup_nest(hass, nest, config, pin=None): """Setup Nest Devices.""" if pin is not None: _LOGGER.debug("pin acquired, requesting access token") nest.request_token(pin) if nest.access_token is None: _LOGGER.debug("no access_token, requesting configuration") request_configuration(nest, hass, config) return if 'nest' in _CONFIGURING: _LOGGER.debug("configuration done") configurator = get_component('configurator') configurator.request_done(_CONFIGURING.pop('nest')) _LOGGER.debug("proceeding with setup") conf = config[DOMAIN] hass.data[DATA_NEST] = NestDevice(hass, conf, nest) _LOGGER.debug("proceeding with discovery") discovery.load_platform(hass, 'climate', DOMAIN, {}, config) discovery.load_platform(hass, 'sensor', DOMAIN, {}, config) discovery.load_platform(hass, 'binary_sensor', DOMAIN, {}, config) discovery.load_platform(hass, 'camera', DOMAIN, {}, config) _LOGGER.debug("setup done") return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the SimpliSafe platform.""" from simplipy.api import SimpliSafeApiInterface, get_systems name = config.get(CONF_NAME) code = config.get(CONF_CODE) username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) persistent_notification = loader.get_component('persistent_notification') simplisafe = SimpliSafeApiInterface() status = simplisafe.set_credentials(username, password) if status: hass.data[DOMAIN] = simplisafe locations = get_systems(simplisafe) for location in locations: add_devices([SimpliSafeAlarm(location, name, code)]) else: message = 'Failed to log into SimpliSafe. Check credentials.' _LOGGER.error(message) persistent_notification.create( hass, message, title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) return False def logout(event): """Logout of the SimpliSafe API.""" hass.data[DOMAIN].logout() hass.bus.listen(EVENT_HOMEASSISTANT_STOP, logout)
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the MyQ component.""" from pymyq import MyQAPI as pymyq username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) brand = config.get(CONF_TYPE) persistent_notification = loader.get_component('persistent_notification') myq = pymyq(username, password, brand) try: if not myq.is_supported_brand(): raise ValueError("Unsupported type. See documentation") if not myq.is_login_valid(): raise ValueError("Username or Password is incorrect") add_devices(MyQDevice(myq, door) for door in myq.get_garage_doors()) return True except (TypeError, KeyError, NameError, ValueError) as ex: _LOGGER.error("%s", ex) persistent_notification.create( hass, 'Error: {}<br />' 'You will need to restart hass after fixing.' ''.format(ex), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) return False
def _async_setup_component(hass: core.HomeAssistant, domain: str, config) -> bool: """Setup a component for Home Assistant. This method is a coroutine. """ # pylint: disable=too-many-return-statements if domain in hass.config.components: return True setup_lock = hass.data.get('setup_lock') if setup_lock is None: setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop) setup_progress = hass.data.get('setup_progress') if setup_progress is None: setup_progress = hass.data['setup_progress'] = [] if domain in setup_progress: _LOGGER.error('Attempt made to setup %s during setup of %s', domain, domain) _async_persistent_notification(hass, domain, True) return False try: # Used to indicate to discovery that a setup is ongoing and allow it # to wait till it is done. did_lock = False if not setup_lock.locked(): yield from setup_lock.acquire() did_lock = True setup_progress.append(domain) config = yield from async_prepare_setup_component(hass, config, domain) if config is None: return False component = loader.get_component(domain) if component is None: _async_persistent_notification(hass, domain) return False async_comp = hasattr(component, 'async_setup') try: _LOGGER.info("Setting up %s", domain) if async_comp: result = yield from component.async_setup(hass, config) else: result = yield from hass.loop.run_in_executor( None, component.setup, hass, config) except Exception: # pylint: disable=broad-except _LOGGER.exception('Error during setup of component %s', domain) _async_persistent_notification(hass, domain, True) return False if result is False: _LOGGER.error('component %s failed to initialize', domain) _async_persistent_notification(hass, domain, True) return False elif result is not True: _LOGGER.error( 'component %s did not return boolean if setup ' 'was successful. Disabling component.', domain) _async_persistent_notification(hass, domain, True) loader.set_component(domain, None) return False hass.config.components.append(component.DOMAIN) hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}) return True finally: setup_progress.remove(domain) if did_lock: setup_lock.release()
def setup(hass, config): """ Track states and offer events for switches. """ logger = logging.getLogger(__name__) if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, logger): return False switch_type = config[DOMAIN][ha.CONF_TYPE] switch_init = get_component('switch.{}'.format(switch_type)) if switch_init is None: logger.error("Error loading switch component %s", switch_type) return False switches = switch_init.get_switches(hass, config[DOMAIN]) if len(switches) == 0: logger.error("No switches found") return False # Setup a dict mapping entity IDs to devices ent_to_switch = {} no_name_count = 1 for switch in switches: name = switch.get_name() if name is None: name = "Switch #{}".format(no_name_count) no_name_count += 1 entity_id = util.ensure_unique_string( ENTITY_ID_FORMAT.format(util.slugify(name)), list(ent_to_switch.keys())) switch.entity_id = entity_id ent_to_switch[entity_id] = switch # pylint: disable=unused-argument def update_states(time, force_reload=False): """ Update states of all switches. """ # First time this method gets called, force_reload should be True if force_reload or \ datetime.now() - update_states.last_updated > \ MIN_TIME_BETWEEN_SCANS: logger.info("Updating switch states") update_states.last_updated = datetime.now() for switch in switches: switch.update_ha_state(hass) update_states(None, True) def handle_switch_service(service): """ Handles calls to the switch services. """ devices = [ ent_to_switch[entity_id] for entity_id in extract_entity_ids(hass, service) if entity_id in ent_to_switch ] if not devices: devices = switches for switch in devices: if service.service == SERVICE_TURN_ON: switch.turn_on() else: switch.turn_off() switch.update_ha_state(hass) # Track all switches in a group group.setup_group(hass, GROUP_NAME_ALL_SWITCHES, ent_to_switch.keys(), False) # Update state every 30 seconds hass.track_time_change(update_states, second=[0, 30]) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service) hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service) return True
def test_see_passive_zone_state(self): """Test that the device tracker sets gps for passive trackers.""" register_time = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) scan_time = datetime(2015, 9, 15, 23, 1, tzinfo=dt_util.UTC) with assert_setup_component(1, zone.DOMAIN): zone_info = { 'name': 'Home', 'latitude': 1, 'longitude': 2, 'radius': 250, 'passive': False } setup_component(self.hass, zone.DOMAIN, { 'zone': zone_info }) scanner = get_component('device_tracker.test').SCANNER scanner.reset() scanner.come_home('dev1') with patch('homeassistant.components.device_tracker.dt_util.utcnow', return_value=register_time): with assert_setup_component(1, device_tracker.DOMAIN): assert setup_component(self.hass, device_tracker.DOMAIN, { device_tracker.DOMAIN: { CONF_PLATFORM: 'test', device_tracker.CONF_CONSIDER_HOME: 59, }}) state = self.hass.states.get('device_tracker.dev1') attrs = state.attributes self.assertEqual(STATE_HOME, state.state) self.assertEqual(state.object_id, 'dev1') self.assertEqual(state.name, 'dev1') self.assertEqual(attrs.get('friendly_name'), 'dev1') self.assertEqual(attrs.get('latitude'), 1) self.assertEqual(attrs.get('longitude'), 2) self.assertEqual(attrs.get('gps_accuracy'), 0) self.assertEqual(attrs.get('source_type'), device_tracker.SOURCE_TYPE_ROUTER) scanner.leave_home('dev1') with patch('homeassistant.components.device_tracker.dt_util.utcnow', return_value=scan_time): fire_time_changed(self.hass, scan_time) self.hass.block_till_done() state = self.hass.states.get('device_tracker.dev1') attrs = state.attributes self.assertEqual(STATE_NOT_HOME, state.state) self.assertEqual(state.object_id, 'dev1') self.assertEqual(state.name, 'dev1') self.assertEqual(attrs.get('friendly_name'), 'dev1') self.assertEqual(attrs.get('latitude'), None) self.assertEqual(attrs.get('longitude'), None) self.assertEqual(attrs.get('gps_accuracy'), None) self.assertEqual(attrs.get('source_type'), device_tracker.SOURCE_TYPE_ROUTER)
def setup_bridge(host, hass, add_devices, filename, allow_unreachable, allow_in_emulated_hue, allow_hue_groups): """Setup a phue bridge based on host parameter.""" import phue try: bridge = phue.Bridge(host, config_file_path=hass.config.path(filename)) except ConnectionRefusedError: # Wrong host was given _LOGGER.error("Error connecting to the Hue bridge at %s", host) return except phue.PhueRegistrationException: _LOGGER.warning("Connected to Hue at %s but not registered.", host) request_configuration(host, hass, add_devices, filename, allow_unreachable, allow_in_emulated_hue, allow_hue_groups) return # If we came here and configuring this host, mark as done if host in _CONFIGURING: request_id = _CONFIGURING.pop(host) configurator = get_component('configurator') configurator.request_done(request_id) lights = {} lightgroups = {} skip_groups = not allow_hue_groups @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_lights(): """Update the Hue light objects with latest info from the bridge.""" nonlocal skip_groups try: api = bridge.get_api() except socket.error: # socket.error when we cannot reach Hue _LOGGER.exception("Cannot reach the bridge") return api_lights = api.get('lights') if not isinstance(api_lights, dict): _LOGGER.error("Got unexpected result from Hue API") return if skip_groups: api_groups = {} else: api_groups = api.get('groups') if not isinstance(api_groups, dict): _LOGGER.error("Got unexpected result from Hue API") return new_lights = [] api_name = api.get('config').get('name') if api_name in ('RaspBee-GW', 'deCONZ-GW'): bridge_type = 'deconz' else: bridge_type = 'hue' for light_id, info in api_lights.items(): if light_id not in lights: lights[light_id] = HueLight(int(light_id), info, bridge, update_lights, bridge_type, allow_unreachable, allow_in_emulated_hue) new_lights.append(lights[light_id]) else: lights[light_id].info = info lights[light_id].schedule_update_ha_state() for lightgroup_id, info in api_groups.items(): if 'state' not in info: _LOGGER.warning('Group info does not contain state. ' 'Please update your hub.') skip_groups = True break if lightgroup_id not in lightgroups: lightgroups[lightgroup_id] = HueLight(int(lightgroup_id), info, bridge, update_lights, bridge_type, allow_unreachable, allow_in_emulated_hue, True) new_lights.append(lightgroups[lightgroup_id]) else: lightgroups[lightgroup_id].info = info lightgroups[lightgroup_id].schedule_update_ha_state() if new_lights: add_devices(new_lights) _CONFIGURED_BRIDGES[socket.gethostbyname(host)] = True # create a service for calling run_scene directly on the bridge, # used to simplify automation rules. def hue_activate_scene(call): """Service to call directly directly into bridge to set scenes.""" group_name = call.data[ATTR_GROUP_NAME] scene_name = call.data[ATTR_SCENE_NAME] bridge.run_scene(group_name, scene_name) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_HUE_SCENE, hue_activate_scene, descriptions.get(SERVICE_HUE_SCENE), schema=SCENE_SCHEMA) update_lights()
def setup(hass, config): # pylint: disable=unused-argument,too-many-locals """ Listen for chromecast events. """ logger = logging.getLogger(__name__) discovery = get_component('discovery') try: # pylint: disable=redefined-outer-name import pychromecast except ImportError: logger.exception(("Failed to import pychromecast. " "Did you maybe not install the 'pychromecast' " "dependency?")) return False casts = {} # If discovery component not loaded, scan ourselves if discovery.DOMAIN not in hass.components: logger.info("Scanning for Chromecasts") hosts = pychromecast.discover_chromecasts() for host in hosts: setup_chromecast(casts, host) def chromecast_discovered(service, info): """ Called when a Chromecast has been discovered. """ logger.info("New Chromecast discovered: %s", info[0]) setup_chromecast(casts, info[0]) discovery.listen( hass, discovery.services.GOOGLE_CAST, chromecast_discovered) def update_chromecast_state(entity_id, chromecast): """ Retrieve state of Chromecast and update statemachine. """ chromecast.refresh() status = chromecast.app state_attr = {ATTR_FRIENDLY_NAME: chromecast.device.friendly_name} if status and status.app_id != pychromecast.APP_ID['HOME']: state = status.app_id ramp = chromecast.get_protocol(pychromecast.PROTOCOL_RAMP) if ramp and ramp.state != pychromecast.RAMP_STATE_UNKNOWN: if ramp.state == pychromecast.RAMP_STATE_PLAYING: state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING else: state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED if ramp.content_id: state_attr[ATTR_MEDIA_CONTENT_ID] = ramp.content_id if ramp.title: state_attr[ATTR_MEDIA_TITLE] = ramp.title if ramp.artist: state_attr[ATTR_MEDIA_ARTIST] = ramp.artist if ramp.album: state_attr[ATTR_MEDIA_ALBUM] = ramp.album if ramp.image_url: state_attr[ATTR_MEDIA_IMAGE_URL] = ramp.image_url if ramp.duration: state_attr[ATTR_MEDIA_DURATION] = ramp.duration state_attr[ATTR_MEDIA_VOLUME] = ramp.volume else: state = STATE_NO_APP hass.states.set(entity_id, state, state_attr) def update_chromecast_states(time): """ Updates all chromecast states. """ if casts: logger.info("Updating Chromecast status") for entity_id, cast in casts.items(): update_chromecast_state(entity_id, cast) def _service_to_entities(service): """ Helper method to get entities from service. """ entity_ids = extract_entity_ids(hass, service) if entity_ids: for entity_id in entity_ids: cast = casts.get(entity_id) if cast: yield entity_id, cast else: yield from casts.items() def turn_off_service(service): """ Service to exit any running app on the specified ChromeCast and shows idle screen. Will quit all ChromeCasts if nothing specified. """ for entity_id, cast in _service_to_entities(service): cast.quit_app() update_chromecast_state(entity_id, cast) def volume_up_service(service): """ Service to send the chromecast the command for volume up. """ for _, cast in _service_to_entities(service): ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP) if ramp: ramp.volume_up() def volume_down_service(service): """ Service to send the chromecast the command for volume down. """ for _, cast in _service_to_entities(service): ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP) if ramp: ramp.volume_down() def media_play_pause_service(service): """ Service to send the chromecast the command for play/pause. """ for _, cast in _service_to_entities(service): ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP) if ramp: ramp.playpause() def media_play_service(service): """ Service to send the chromecast the command for play/pause. """ for _, cast in _service_to_entities(service): ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP) if ramp and ramp.state == pychromecast.RAMP_STATE_STOPPED: ramp.playpause() def media_pause_service(service): """ Service to send the chromecast the command for play/pause. """ for _, cast in _service_to_entities(service): ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP) if ramp and ramp.state == pychromecast.RAMP_STATE_PLAYING: ramp.playpause() def media_next_track_service(service): """ Service to send the chromecast the command for next track. """ for entity_id, cast in _service_to_entities(service): ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP) if ramp: next(ramp) update_chromecast_state(entity_id, cast) def play_youtube_video_service(service, video_id): """ Plays specified video_id on the Chromecast's YouTube channel. """ if video_id: # if service.data.get('video') returned None for entity_id, cast in _service_to_entities(service): pychromecast.play_youtube_video(video_id, cast.host) update_chromecast_state(entity_id, cast) hass.track_time_change(update_chromecast_states, second=range(0, 60, 15)) hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service) hass.services.register(DOMAIN, SERVICE_VOLUME_UP, volume_up_service) hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN, volume_down_service) hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE, media_play_pause_service) hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY, media_play_service) hass.services.register(DOMAIN, SERVICE_MEDIA_PAUSE, media_pause_service) hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK, media_next_track_service) hass.services.register(DOMAIN, "start_fireplace", lambda service: play_youtube_video_service(service, "eyU3bRy2x44")) hass.services.register(DOMAIN, "start_epic_sax", lambda service: play_youtube_video_service(service, "kxopViU98Xo")) hass.services.register(DOMAIN, SERVICE_YOUTUBE_VIDEO, lambda service: play_youtube_video_service(service, service.data.get( 'video'))) update_chromecast_states(None) return True
def setup(hass, config): """Setup a demo environment.""" group = loader.get_component('group') configurator = loader.get_component('configurator') config.setdefault(ha.DOMAIN, {}) config.setdefault(DOMAIN, {}) if config[DOMAIN].get('hide_demo_state') != 1: hass.states.set('a.Demo_Mode', 'Enabled') # Setup sun if not hass.config.latitude: hass.config.latitude = 32.87336 if not hass.config.longitude: hass.config.longitude = 117.22743 bootstrap.setup_component(hass, 'sun') # Setup demo platforms demo_config = config.copy() for component in COMPONENTS_WITH_DEMO_PLATFORM: demo_config[component] = {CONF_PLATFORM: 'demo'} bootstrap.setup_component(hass, component, demo_config) # Setup room groups lights = sorted(hass.states.entity_ids('light')) switches = sorted(hass.states.entity_ids('switch')) media_players = sorted(hass.states.entity_ids('media_player')) group.Group(hass, 'living room', [ lights[1], switches[0], 'input_select.living_room_preset', 'rollershutter.living_room_window', media_players[1], 'scene.romantic_lights']) group.Group(hass, 'bedroom', [lights[0], switches[1], media_players[0]]) group.Group(hass, 'kitchen', [ lights[2], 'rollershutter.kitchen_window', 'lock.kitchen_door']) group.Group(hass, 'doors', [ 'lock.front_door', 'lock.kitchen_door', 'garage_door.right_garage_door', 'garage_door.left_garage_door']) group.Group(hass, 'automations', [ 'input_select.who_cooks', 'input_boolean.notify', ]) group.Group(hass, 'people', [ 'device_tracker.demo_anne_therese', 'device_tracker.demo_home_boy', 'device_tracker.demo_paulus']) group.Group(hass, 'thermostats', [ 'thermostat.nest', 'thermostat.thermostat']) group.Group(hass, 'downstairs', [ 'group.living_room', 'group.kitchen', 'scene.romantic_lights', 'rollershutter.kitchen_window', 'rollershutter.living_room_window', 'group.doors', 'thermostat.nest', ], view=True) group.Group(hass, 'Upstairs', [ 'thermostat.thermostat', 'group.bedroom', ], view=True) # Setup scripts bootstrap.setup_component( hass, 'script', {'script': { 'demo': { 'alias': 'Toggle {}'.format(lights[0].split('.')[1]), 'sequence': [{ 'service': 'light.turn_off', 'data': {ATTR_ENTITY_ID: lights[0]} }, { 'delay': {'seconds': 5} }, { 'service': 'light.turn_on', 'data': {ATTR_ENTITY_ID: lights[0]} }, { 'delay': {'seconds': 5} }, { 'service': 'light.turn_off', 'data': {ATTR_ENTITY_ID: lights[0]} }] }}}) # Setup scenes bootstrap.setup_component( hass, 'scene', {'scene': [ {'name': 'Romantic lights', 'entities': { lights[0]: True, lights[1]: {'state': 'on', 'xy_color': [0.33, 0.66], 'brightness': 200}, }}, {'name': 'Switch on and off', 'entities': { switches[0]: True, switches[1]: False, }}, ]}) # Set up input select bootstrap.setup_component( hass, 'input_select', {'input_select': {'living_room_preset': {'options': ['Visitors', 'Visitors with kids', 'Home Alone']}, 'who_cooks': {'icon': 'mdi:panda', 'initial': 'Anne Therese', 'name': 'Cook today', 'options': ['Paulus', 'Anne Therese']}}}) # Set up input boolean bootstrap.setup_component( hass, 'input_boolean', {'input_boolean': {'notify': {'icon': 'mdi:car', 'initial': False, 'name': 'Notify Anne Therese is home'}}}) # Set up weblink bootstrap.setup_component( hass, 'weblink', {'weblink': {'entities': [{'name': 'Router', 'url': 'http://192.168.1.1'}]}}) # Setup configurator configurator_ids = [] def hue_configuration_callback(data): """Fake callback, mark config as done.""" time.sleep(2) # First time it is called, pretend it failed. if len(configurator_ids) == 1: configurator.notify_errors( configurator_ids[0], "Failed to register, please try again.") configurator_ids.append(0) else: configurator.request_done(configurator_ids[0]) request_id = configurator.request_config( hass, "Philips Hue", hue_configuration_callback, description=("Press the button on the bridge to register Philips Hue " "with Home Assistant."), description_image="/static/images/config_philips_hue.jpg", submit_caption="I have pressed the button" ) configurator_ids.append(request_id) return True
def setup_bridge(host, hass, add_devices_callback, filename, allow_unreachable): """Setup a phue bridge based on host parameter.""" import phue try: bridge = phue.Bridge( host, config_file_path=hass.config.path(filename)) except ConnectionRefusedError: # Wrong host was given _LOGGER.exception("Error connecting to the Hue bridge at %s", host) return except phue.PhueRegistrationException: _LOGGER.warning("Connected to Hue at %s but not registered.", host) request_configuration(host, hass, add_devices_callback, filename, allow_unreachable) return # If we came here and configuring this host, mark as done if host in _CONFIGURING: request_id = _CONFIGURING.pop(host) configurator = get_component('configurator') configurator.request_done(request_id) lights = {} @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_lights(): """Update the Hue light objects with latest info from the bridge.""" try: api = bridge.get_api() except socket.error: # socket.error when we cannot reach Hue _LOGGER.exception("Cannot reach the bridge") return api_states = api.get('lights') if not isinstance(api_states, dict): _LOGGER.error("Got unexpected result from Hue API") return new_lights = [] api_name = api.get('config').get('name') if api_name == 'RaspBee-GW': bridge_type = 'deconz' else: bridge_type = 'hue' for light_id, info in api_states.items(): if light_id not in lights: lights[light_id] = HueLight(int(light_id), info, bridge, update_lights, bridge_type, allow_unreachable) new_lights.append(lights[light_id]) else: lights[light_id].info = info if new_lights: add_devices_callback(new_lights) _CONFIGURED_BRIDGES[socket.gethostbyname(host)] = True update_lights()
def test_flux_with_multiple_lights(self): """Test the flux switch with multiple light entities.""" platform = loader.get_component('light.test') platform.init() self.assertTrue( setup_component(self.hass, light.DOMAIN, {light.DOMAIN: { CONF_PLATFORM: 'test' }})) dev1, dev2, dev3 = platform.DEVICES light.turn_on(self.hass, entity_id=dev2.entity_id) self.hass.block_till_done() light.turn_on(self.hass, entity_id=dev3.entity_id) self.hass.block_till_done() state = self.hass.states.get(dev1.entity_id) self.assertEqual(STATE_ON, state.state) self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get('brightness')) state = self.hass.states.get(dev2.entity_id) self.assertEqual(STATE_ON, state.state) self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get('brightness')) state = self.hass.states.get(dev3.entity_id) self.assertEqual(STATE_ON, state.state) self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get('brightness')) test_time = dt_util.now().replace(hour=12, minute=0, second=0) sunset_time = test_time.replace(hour=17, minute=0, second=0) sunrise_time = test_time.replace(hour=5, minute=0, second=0) + timedelta(days=1) with patch('homeassistant.util.dt.now', return_value=test_time): with patch('homeassistant.components.sun.next_rising', return_value=sunrise_time): with patch('homeassistant.components.sun.next_setting', return_value=sunset_time): assert setup_component( self.hass, switch.DOMAIN, { switch.DOMAIN: { 'platform': 'flux', 'name': 'flux', 'lights': [ dev1.entity_id, dev2.entity_id, dev3.entity_id ] } }) turn_on_calls = mock_service(self.hass, light.DOMAIN, SERVICE_TURN_ON) switch.turn_on(self.hass, 'switch.flux') self.hass.block_till_done() fire_time_changed(self.hass, test_time) self.hass.block_till_done() call = turn_on_calls[-1] self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 171) self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.452, 0.386]) call = turn_on_calls[-2] self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 171) self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.452, 0.386]) call = turn_on_calls[-3] self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 171) self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.452, 0.386])
def async_process_component_config(hass: HomeAssistant, config: Dict, domain: str) -> Optional[Dict]: """Check component configuration and return processed configuration. Returns None on error. This method must be run in the event loop. """ component = get_component(hass, domain) if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) # type: ignore except vol.Invalid as ex: async_log_exception(ex, domain, config, hass) return None elif hasattr(component, 'PLATFORM_SCHEMA'): platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: p_validated = component.PLATFORM_SCHEMA( # type: ignore p_config) except vol.Invalid as ex: async_log_exception(ex, domain, config, hass) continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue platform = get_platform(hass, domain, p_name) if platform is None: continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): # pylint: disable=no-member try: p_validated = platform.PLATFORM_SCHEMA( # type: ignore p_validated) except vol.Invalid as ex: async_log_exception(ex, '{}.{}'.format(domain, p_name), p_validated, hass) continue platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = { key: value for key, value in config.items() if key not in filter_keys } config[domain] = platforms return config
def merge_packages_config(hass: HomeAssistant, config: Dict, packages: Dict, _log_pkg_error: Callable = _log_pkg_error) -> Dict: """Merge packages into the top-level configuration. Mutate config.""" # pylint: disable=too-many-nested-blocks PACKAGES_CONFIG_SCHEMA(packages) for pack_name, pack_conf in packages.items(): for comp_name, comp_conf in pack_conf.items(): if comp_name == CONF_CORE: continue # If component name is given with a trailing description, remove it # when looking for component domain = comp_name.split(' ')[0] component = get_component(hass, domain) if component is None: _log_pkg_error(pack_name, comp_name, config, "does not exist") continue if hasattr(component, 'PLATFORM_SCHEMA'): if not comp_conf: continue # Ensure we dont add Falsy items to list config[comp_name] = cv.ensure_list(config.get(comp_name)) config[comp_name].extend(cv.ensure_list(comp_conf)) continue if hasattr(component, 'CONFIG_SCHEMA'): merge_type, _ = _identify_config_schema(component) if merge_type == 'list': if not comp_conf: continue # Ensure we dont add Falsy items to list config[comp_name] = cv.ensure_list(config.get(comp_name)) config[comp_name].extend(cv.ensure_list(comp_conf)) continue if comp_conf is None: comp_conf = OrderedDict() if not isinstance(comp_conf, dict): _log_pkg_error(pack_name, comp_name, config, "cannot be merged. Expected a dict.") continue if comp_name not in config or config[comp_name] is None: config[comp_name] = OrderedDict() if not isinstance(config[comp_name], dict): _log_pkg_error( pack_name, comp_name, config, "cannot be merged. Dict expected in main config.") continue if not isinstance(comp_conf, dict): _log_pkg_error(pack_name, comp_name, config, "cannot be merged. Dict expected in package.") continue error = _recursive_merge(conf=config[comp_name], package=comp_conf) if error: _log_pkg_error(pack_name, comp_name, config, "has duplicate key '{}'".format(error)) return config
def check_ha_config_file(hass): """Check if Home Assistant configuration file is valid.""" config_dir = hass.config.config_dir result = HomeAssistantConfig() def _pack_error(package, component, config, message): """Handle errors from packages: _log_pkg_error.""" message = "Package {} setup failed. Component {} {}".format( package, component, message) domain = 'homeassistant.packages.{}.{}'.format(package, component) pack_config = core_config[CONF_PACKAGES].get(package, config) result.add_error(message, domain, pack_config) def _comp_error(ex, domain, config): """Handle errors from components: async_log_exception.""" result.add_error(_format_config_error(ex, domain, config), domain, config) # Load configuration.yaml try: config_path = find_config_file(config_dir) if not config_path: return result.add_error("File configuration.yaml not found.") config = load_yaml_config_file(config_path) except HomeAssistantError as err: return result.add_error("Error loading {}: {}".format( config_path, err)) finally: yaml.clear_secret_cache() # Extract and validate core [homeassistant] config try: core_config = config.pop(CONF_CORE, {}) core_config = CORE_CONFIG_SCHEMA(core_config) result[CONF_CORE] = core_config except vol.Invalid as err: result.add_error(err, CONF_CORE, core_config) core_config = {} # Merge packages merge_packages_config(hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error) core_config.pop(CONF_PACKAGES, None) # Ensure we have no None values after merge for key, value in config.items(): if not value: config[key] = {} # Filter out repeating config sections components = set(key.split(' ')[0] for key in config.keys()) # Process and validate config for domain in components: component = loader.get_component(hass, domain) if not component: result.add_error("Component not found: {}".format(domain)) continue if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) result[domain] = config[domain] except vol.Invalid as ex: _comp_error(ex, domain, config) continue if not hasattr(component, 'PLATFORM_SCHEMA'): continue platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: p_validated = component.PLATFORM_SCHEMA(p_config) except vol.Invalid as ex: _comp_error(ex, domain, config) continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue platform = loader.get_platform(hass, domain, p_name) if platform is None: result.add_error("Platform not found: {}.{}".format( domain, p_name)) continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: _comp_error(ex, '{}.{}'.format(domain, p_name), p_validated) continue platforms.append(p_validated) # Remove config for current component and add validated config back in. for filter_comp in extract_domain_configs(config, domain): del config[filter_comp] result[domain] = platforms return result
def async_prepare_setup_component(hass: core.HomeAssistant, config: dict, domain: str): """Prepare setup of a component and return processed config. This method is a coroutine. """ # pylint: disable=too-many-return-statements component = loader.get_component(domain) missing_deps = [ dep for dep in getattr(component, 'DEPENDENCIES', []) if dep not in hass.config.components ] if missing_deps: _LOGGER.error( 'Not initializing %s because not all dependencies loaded: %s', domain, ", ".join(missing_deps)) return None if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) except vol.Invalid as ex: async_log_exception(ex, domain, config, hass) return None elif hasattr(component, 'PLATFORM_SCHEMA'): platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: p_validated = component.PLATFORM_SCHEMA(p_config) except vol.Invalid as ex: async_log_exception(ex, domain, config, hass) continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue platform = yield from async_prepare_setup_platform( hass, config, domain, p_name) if platform is None: continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: # pylint: disable=no-member p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: async_log_exception(ex, '{}.{}'.format(domain, p_name), p_validated, hass) continue platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = { key: value for key, value in config.items() if key not in filter_keys } config[domain] = platforms res = yield from hass.loop.run_in_executor(None, _handle_requirements, hass, component, domain) if not res: return None return config
def setup(hass, config): """ Exposes light control via statemachine and services. """ if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER): return False light_type = config[DOMAIN][ha.CONF_TYPE] light_init = get_component('light.{}'.format(light_type)) if light_init is None: _LOGGER.error("Unknown light type specified: %s", light_type) return False lights = light_init.get_lights(hass, config[DOMAIN]) if len(lights) == 0: _LOGGER.error("No lights found") return False ent_to_light = {} no_name_count = 1 for light in lights: name = light.get_name() if name is None: name = "Light #{}".format(no_name_count) no_name_count += 1 entity_id = util.ensure_unique_string( ENTITY_ID_FORMAT.format(util.slugify(name)), list(ent_to_light.keys())) light.entity_id = entity_id ent_to_light[entity_id] = light # Load built-in profiles and custom profiles profile_paths = [ os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE), hass.get_config_path(LIGHT_PROFILES_FILE) ] profiles = {} for profile_path in profile_paths: if os.path.isfile(profile_path): with open(profile_path) as inp: reader = csv.reader(inp) # Skip the header next(reader, None) try: for profile_id, color_x, color_y, brightness in reader: profiles[profile_id] = (float(color_x), float(color_y), int(brightness)) except ValueError: # ValueError if not 4 values per row # ValueError if convert to float/int failed _LOGGER.error("Error parsing light profiles from %s", profile_path) return False # pylint: disable=unused-argument def update_lights_state(now): """ Update the states of all the lights. """ for light in lights: light.update_ha_state(hass) update_lights_state(None) # Track all lights in a group group.setup_group(hass, GROUP_NAME_ALL_LIGHTS, ent_to_light.keys(), False) def handle_light_service(service): """ Hande a turn light on or off service call. """ # Get and validate data dat = service.data # Convert the entity ids to valid light ids lights = [ ent_to_light[entity_id] for entity_id in extract_entity_ids(hass, service) if entity_id in ent_to_light ] if not lights: lights = list(ent_to_light.values()) transition = util.convert(dat.get(ATTR_TRANSITION), int) if service.service == SERVICE_TURN_OFF: for light in lights: light.turn_off(transition=transition) else: # Processing extra data for turn light on request # We process the profile first so that we get the desired # behavior that extra service data attributes overwrite # profile values profile = profiles.get(dat.get(ATTR_PROFILE)) if profile: *color, bright = profile else: color, bright = None, None if ATTR_BRIGHTNESS in dat: bright = util.convert(dat.get(ATTR_BRIGHTNESS), int) if ATTR_XY_COLOR in dat: try: # xy_color should be a list containing 2 floats xy_color = dat.get(ATTR_XY_COLOR) if len(xy_color) == 2: color = [float(val) for val in xy_color] except (TypeError, ValueError): # TypeError if xy_color is not iterable # ValueError if value could not be converted to float pass if ATTR_RGB_COLOR in dat: try: # rgb_color should be a list containing 3 ints rgb_color = dat.get(ATTR_RGB_COLOR) if len(rgb_color) == 3: color = util.color_RGB_to_xy(int(rgb_color[0]), int(rgb_color[1]), int(rgb_color[2])) except (TypeError, ValueError): # TypeError if rgb_color is not iterable # ValueError if not all values can be converted to int pass for light in lights: light.turn_on(transition=transition, brightness=bright, xy_color=color) for light in lights: light.update_ha_state(hass, True) # Update light state every 30 seconds hass.track_time_change(update_lights_state, second=[0, 30]) # Listen for light on and light off service calls hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service) return True
def device_state_attributes(self): """Provide attributes for display on device card.""" insteon_plm = get_component('insteon_plm') return insteon_plm.common_attributes(self)
def __init__(self, wink): """Initialize the Wink binary sensor.""" super().__init__(wink) wink = get_component('wink') self._unit_of_measurement = self.wink.UNIT self.capability = self.wink.capability()
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Fitbit sensor.""" config_path = hass.config.path(FITBIT_CONFIG_FILE) if os.path.isfile(config_path): config_file = config_from_file(config_path) if config_file == DEFAULT_CONFIG: request_app_setup(hass, config, add_devices, config_path, discovery_info=None) return False else: config_file = config_from_file(config_path, DEFAULT_CONFIG) request_app_setup(hass, config, add_devices, config_path, discovery_info=None) return False if "fitbit" in _CONFIGURING: get_component("configurator").request_done(_CONFIGURING.pop("fitbit")) import fitbit access_token = config_file.get("access_token") refresh_token = config_file.get("refresh_token") if None not in (access_token, refresh_token): authd_client = fitbit.Fitbit(config_file.get("client_id"), config_file.get("client_secret"), access_token=access_token, refresh_token=refresh_token) if int(time.time()) - config_file.get("last_saved_at", 0) > 3600: authd_client.client.refresh_token() authd_client.system = authd_client.user_profile_get()["user"]["locale"] if authd_client.system != 'en_GB': if hass.config.units.is_metric: authd_client.system = "metric" else: authd_client.system = "en_US" dev = [] for resource in config.get("monitored_resources", FITBIT_DEFAULT_RESOURCE_LIST): dev.append(FitbitSensor(authd_client, config_path, resource, hass.config.units.is_metric)) add_devices(dev) else: oauth = fitbit.api.FitbitOauth2Client(config_file.get("client_id"), config_file.get("client_secret")) redirect_uri = "{}{}".format(hass.config.api.base_url, FITBIT_AUTH_CALLBACK_PATH) fitbit_auth_start_url, _ = oauth.authorize_token_url( redirect_uri=redirect_uri, scope=["activity", "heartrate", "nutrition", "profile", "settings", "sleep", "weight"]) hass.wsgi.register_redirect(FITBIT_AUTH_START, fitbit_auth_start_url) hass.wsgi.register_view(FitbitAuthCallbackView(hass, config, add_devices, oauth)) request_oauth_completion(hass)
def _setup_component(hass: core.HomeAssistant, domain: str, config) -> bool: """Setup a component for Home Assistant.""" # pylint: disable=too-many-return-statements,too-many-branches # pylint: disable=too-many-statements if domain in hass.config.components: return True with _SETUP_LOCK: # It might have been loaded while waiting for lock if domain in hass.config.components: return True if domain in _CURRENT_SETUP: _LOGGER.error('Attempt made to setup %s during setup of %s', domain, domain) return False component = loader.get_component(domain) missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', []) if dep not in hass.config.components] if missing_deps: _LOGGER.error( 'Not initializing %s because not all dependencies loaded: %s', domain, ", ".join(missing_deps)) return False if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) except vol.MultipleInvalid as ex: _log_exception(ex, domain, config) return False elif hasattr(component, 'PLATFORM_SCHEMA'): platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: p_validated = component.PLATFORM_SCHEMA(p_config) except vol.MultipleInvalid as ex: _log_exception(ex, domain, p_config) return False # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue platform = prepare_setup_platform(hass, config, domain, p_name) if platform is None: return False # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.MultipleInvalid as ex: _log_exception(ex, '{}.{}'.format(domain, p_name), p_validated) return False platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = {key: value for key, value in config.items() if key not in filter_keys} config[domain] = platforms if not _handle_requirements(hass, component, domain): return False _CURRENT_SETUP.append(domain) try: result = component.setup(hass, config) if result is False: _LOGGER.error('component %s failed to initialize', domain) return False elif result is not True: _LOGGER.error('component %s did not return boolean if setup ' 'was successful. Disabling component.', domain) loader.set_component(domain, None) return False except Exception: # pylint: disable=broad-except _LOGGER.exception('Error during setup of component %s', domain) return False finally: _CURRENT_SETUP.remove(domain) hass.config.components.append(component.DOMAIN) # Assumption: if a component does not depend on groups # it communicates with devices if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []): hass.pool.add_worker() hass.bus.fire( EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}) return True
def get_service(hass, config, discovery_info=None): mqtt = loader.get_component('mqtt') topic = config.get(CONF_TOPIC, DEFAULT_TOPIC) return MqttNotificationService(mqtt, hass, topic)
def setup_plexserver(host, token, hass, optional_config, add_devices_callback): """Setup a plexserver based on host parameter.""" import plexapi.server import plexapi.exceptions try: plexserver = plexapi.server.PlexServer('http://%s' % host, token) except (plexapi.exceptions.BadRequest, plexapi.exceptions.Unauthorized, plexapi.exceptions.NotFound) as error: _LOGGER.info(error) # No token or wrong token request_configuration(host, hass, optional_config, add_devices_callback) return # If we came here and configuring this host, mark as done if host in _CONFIGURING: request_id = _CONFIGURING.pop(host) configurator = get_component('configurator') configurator.request_done(request_id) _LOGGER.info('Discovery configuration done!') # Save config if not config_from_file(hass.config.path(PLEX_CONFIG_FILE), {host: { 'token': token }}): _LOGGER.error('failed to save config file') _LOGGER.info('Connected to: http://%s', host) plex_clients = {} plex_sessions = {} track_utc_time_change(hass, lambda now: update_devices(), second=30) @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_devices(): """Update the devices objects.""" try: devices = plexserver.clients() except plexapi.exceptions.BadRequest: _LOGGER.exception('Error listing plex devices') return except OSError: _LOGGER.error('Could not connect to plex server at http://%s', host) return new_plex_clients = [] for device in devices: # For now, let's allow all deviceClass types if device.deviceClass in ['badClient']: continue if device.machineIdentifier not in plex_clients: new_client = PlexClient(optional_config, device, None, plex_sessions, update_devices, update_sessions) plex_clients[device.machineIdentifier] = new_client new_plex_clients.append(new_client) else: plex_clients[device.machineIdentifier].set_device(device) # add devices with a session and no client (ex. PlexConnect Apple TV's) if optional_config[CONF_INCLUDE_NON_CLIENTS]: for machineIdentifier, session in plex_sessions.items(): if machineIdentifier not in plex_clients: new_client = PlexClient(optional_config, None, session, plex_sessions, update_devices, update_sessions) plex_clients[machineIdentifier] = new_client new_plex_clients.append(new_client) else: plex_clients[machineIdentifier].set_session(session) # force devices to idle that do not have a valid session for machineIdentifier, client in plex_clients.items(): if client.session is None: client.set_state(STATE_IDLE) # add devices to dynamic groups if optional_config[CONF_USE_DYNAMIC_GROUPS]: active_entity_id_list = [] inactive_entity_id_list = [] for machineIdentifier, client in plex_clients.items(): if client.entity_id: if client.state in [STATE_IDLE, STATE_OFF]: inactive_entity_id_list.append(client.entity_id) else: active_entity_id_list.append(client.entity_id) # set groups with updated memberships set_group_members(hass, GROUP_ACTIVE_DEVICES, active_entity_id_list) set_group_members(hass, GROUP_INACTIVE_DEVICES, inactive_entity_id_list) if new_plex_clients: add_devices_callback(new_plex_clients) @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_sessions(): """Update the sessions objects.""" try: sessions = plexserver.sessions() except plexapi.exceptions.BadRequest: _LOGGER.exception('Error listing plex sessions') return plex_sessions.clear() for session in sessions: plex_sessions[session.player.machineIdentifier] = session update_sessions() update_devices()
def closest(self, *args): """Find closest entity. Closest to home: closest(states) closest(states.device_tracker) closest('group.children') closest(states.group.children) Closest to a point: closest(23.456, 23.456, 'group.children') closest('zone.school', 'group.children') closest(states.zone.school, 'group.children') """ if len(args) == 1: latitude = self._hass.config.latitude longitude = self._hass.config.longitude entities = args[0] elif len(args) == 2: point_state = self._resolve_state(args[0]) if point_state is None: _LOGGER.warning('Closest:Unable to find state %s', args[0]) return None elif not loc_helper.has_location(point_state): _LOGGER.warning( 'Closest:State does not contain valid location: %s', point_state) return None latitude = point_state.attributes.get(ATTR_LATITUDE) longitude = point_state.attributes.get(ATTR_LONGITUDE) entities = args[1] else: latitude = convert(args[0], float) longitude = convert(args[1], float) if latitude is None or longitude is None: _LOGGER.warning('Closest:Received invalid coordinates: %s, %s', args[0], args[1]) return None entities = args[2] if isinstance(entities, (AllStates, DomainStates)): states = list(entities) else: if isinstance(entities, State): gr_entity_id = entities.entity_id else: gr_entity_id = str(entities) group = get_component('group') states = [ self._hass.states.get(entity_id) for entity_id in group.expand_entity_ids( self._hass, [gr_entity_id]) ] return loc_helper.closest(latitude, longitude, states)
def test_flux_with_multiple_lights(self): """Test the flux switch with multiple light entities.""" platform = loader.get_component(self.hass, 'light.test') platform.init() self.assertTrue( setup_component(self.hass, light.DOMAIN, {light.DOMAIN: { CONF_PLATFORM: 'test' }})) dev1, dev2, dev3 = platform.DEVICES common_light.turn_on(self.hass, entity_id=dev2.entity_id) self.hass.block_till_done() common_light.turn_on(self.hass, entity_id=dev3.entity_id) self.hass.block_till_done() state = self.hass.states.get(dev1.entity_id) self.assertEqual(STATE_ON, state.state) self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get('brightness')) state = self.hass.states.get(dev2.entity_id) self.assertEqual(STATE_ON, state.state) self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get('brightness')) state = self.hass.states.get(dev3.entity_id) self.assertEqual(STATE_ON, state.state) self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get('brightness')) test_time = dt_util.now().replace(hour=12, minute=0, second=0) sunset_time = test_time.replace(hour=17, minute=0, second=0) sunrise_time = test_time.replace(hour=5, minute=0, second=0) def event_date(hass, event, now=None): if event == 'sunrise': print('sunrise {}'.format(sunrise_time)) return sunrise_time print('sunset {}'.format(sunset_time)) return sunset_time with patch('homeassistant.util.dt.now', return_value=test_time): with patch('homeassistant.helpers.sun.get_astral_event_date', side_effect=event_date): assert setup_component( self.hass, switch.DOMAIN, { switch.DOMAIN: { 'platform': 'flux', 'name': 'flux', 'lights': [dev1.entity_id, dev2.entity_id, dev3.entity_id] } }) turn_on_calls = mock_service(self.hass, light.DOMAIN, SERVICE_TURN_ON) common.turn_on(self.hass, 'switch.flux') self.hass.block_till_done() fire_time_changed(self.hass, test_time) self.hass.block_till_done() call = turn_on_calls[-1] self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 163) self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.46, 0.376]) call = turn_on_calls[-2] self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 163) self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.46, 0.376]) call = turn_on_calls[-3] self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 163) self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.46, 0.376])
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Fitbit sensor.""" config_path = hass.config.path(FITBIT_CONFIG_FILE) if os.path.isfile(config_path): config_file = config_from_file(config_path) if config_file == DEFAULT_CONFIG: request_app_setup(hass, config, add_devices, config_path, discovery_info=None) return False else: config_file = config_from_file(config_path, DEFAULT_CONFIG) request_app_setup(hass, config, add_devices, config_path, discovery_info=None) return False if "fitbit" in _CONFIGURING: get_component("configurator").request_done(_CONFIGURING.pop("fitbit")) import fitbit access_token = config_file.get("access_token") refresh_token = config_file.get("refresh_token") if None not in (access_token, refresh_token): authd_client = fitbit.Fitbit(config_file.get("client_id"), config_file.get("client_secret"), access_token=access_token, refresh_token=refresh_token) if int(time.time()) - config_file.get("last_saved_at", 0) > 3600: authd_client.client.refresh_token() authd_client.system = authd_client.user_profile_get()["user"]["locale"] dev = [] for resource in config.get("monitored_resources", FITBIT_DEFAULT_RESOURCE_LIST): dev.append(FitbitSensor(authd_client, config_path, resource)) add_devices(dev) else: oauth = fitbit.api.FitbitOauth2Client(config_file.get("client_id"), config_file.get("client_secret")) redirect_uri = "{}{}".format(hass.config.api.base_url, FITBIT_AUTH_CALLBACK_PATH) def _start_fitbit_auth(handler, path_match, data): """Start Fitbit OAuth2 flow.""" url, _ = oauth.authorize_token_url(redirect_uri=redirect_uri, scope=["activity", "heartrate", "nutrition", "profile", "settings", "sleep", "weight"]) handler.send_response(301) handler.send_header("Location", url) handler.end_headers() def _finish_fitbit_auth(handler, path_match, data): """Finish Fitbit OAuth2 flow.""" response_message = """Fitbit has been successfully authorized! You can close this window now!""" from oauthlib.oauth2.rfc6749.errors import MismatchingStateError from oauthlib.oauth2.rfc6749.errors import MissingTokenError if data.get("code") is not None: try: oauth.fetch_access_token(data.get("code"), redirect_uri) except MissingTokenError as error: _LOGGER.error("Missing token: %s", error) response_message = """Something went wrong when attempting authenticating with Fitbit. The error encountered was {}. Please try again!""".format(error) except MismatchingStateError as error: _LOGGER.error("Mismatched state, CSRF error: %s", error) response_message = """Something went wrong when attempting authenticating with Fitbit. The error encountered was {}. Please try again!""".format(error) else: _LOGGER.error("Unknown error when authing") response_message = """Something went wrong when attempting authenticating with Fitbit. An unknown error occurred. Please try again! """ html_response = """<html><head><title>Fitbit Auth</title></head> <body><h1>{}</h1></body></html>""".format(response_message) html_response = html_response.encode("utf-8") handler.send_response(HTTP_OK) handler.write_content(html_response, content_type="text/html") config_contents = { "access_token": oauth.token["access_token"], "refresh_token": oauth.token["refresh_token"], "client_id": oauth.client_id, "client_secret": oauth.client_secret } if not config_from_file(config_path, config_contents): _LOGGER.error("failed to save config file") setup_platform(hass, config, add_devices, discovery_info=None) hass.http.register_path("GET", FITBIT_AUTH_START, _start_fitbit_auth, require_auth=False) hass.http.register_path("GET", FITBIT_AUTH_CALLBACK_PATH, _finish_fitbit_auth, require_auth=False) request_oauth_completion(hass)
def _async_setup_component(hass: core.HomeAssistant, domain: str, config) -> bool: """Setup a component for Home Assistant. This method is a coroutine. hass: Home Assistant instance. domain: Domain of component to setup. config: The Home Assistant configuration. """ def log_error(msg, link=True): """Log helper.""" _LOGGER.error('Setup failed for %s: %s', domain, msg) async_notify_setup_error(hass, domain, link) component = loader.get_component(domain) if not component: log_error('Component not found.', False) return False # Validate no circular dependencies components = loader.load_order_component(domain) # OrderedSet is empty if component or dependencies could not be resolved if not components: log_error('Unable to resolve component or dependencies.') return False processed_config = \ conf_util.async_process_component_config(hass, config, domain) if processed_config is None: log_error('Invalid config.') return False if not hass.config.skip_pip and hasattr(component, 'REQUIREMENTS'): req_success = yield from _async_process_requirements( hass, domain, component.REQUIREMENTS) if not req_success: log_error('Could not install all requirements.') return False if hasattr(component, 'DEPENDENCIES'): dep_success = yield from _async_process_dependencies( hass, config, domain, component.DEPENDENCIES) if not dep_success: log_error('Could not setup all dependencies.') return False async_comp = hasattr(component, 'async_setup') _LOGGER.info("Setting up %s", domain) warn_task = hass.loop.call_later( SLOW_SETUP_WARNING, _LOGGER.warning, 'Setup of %s is taking over %s seconds.', domain, SLOW_SETUP_WARNING) try: if async_comp: result = yield from component.async_setup(hass, processed_config) else: result = yield from hass.loop.run_in_executor( None, component.setup, hass, processed_config) except Exception: # pylint: disable=broad-except _LOGGER.exception('Error during setup of component %s', domain) async_notify_setup_error(hass, domain, True) return False finally: warn_task.cancel() if result is False: log_error('Component failed to initialize.') return False elif result is not True: log_error('Component did not return boolean if setup was successful. ' 'Disabling component.') loader.set_component(domain, None) return False hass.config.components.add(component.DOMAIN) # cleanup if domain in hass.data[DATA_SETUP]: hass.data[DATA_SETUP].pop(domain) hass.bus.async_fire( EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN} ) return True
def setup(hass, config): """ Setup a demo environment. """ group = loader.get_component('group') configurator = loader.get_component('configurator') config.setdefault(ha.DOMAIN, {}) config.setdefault(DOMAIN, {}) if config[DOMAIN].get('hide_demo_state') != '1': hass.states.set('a.Demo_Mode', 'Enabled') light_colors = [[0.861, 0.3259], [0.6389, 0.3028], [0.1684, 0.0416]] def mock_turn_on(service): """ Will fake the component has been turned on. """ if service.data and ATTR_ENTITY_ID in service.data: entity_ids = extract_entity_ids(hass, service) else: entity_ids = hass.states.entity_ids(service.domain) for entity_id in entity_ids: domain, _ = split_entity_id(entity_id) if domain == "light": rgb_color = service.data.get(ATTR_RGB_COLOR) if rgb_color: color = color_RGB_to_xy(rgb_color[0], rgb_color[1], rgb_color[2]) else: cur_state = hass.states.get(entity_id) # Use current color if available if cur_state and cur_state.attributes.get(ATTR_XY_COLOR): color = cur_state.attributes.get(ATTR_XY_COLOR) else: color = random.choice(light_colors) data = { ATTR_BRIGHTNESS: service.data.get(ATTR_BRIGHTNESS, 200), ATTR_XY_COLOR: color } else: data = None hass.states.set(entity_id, STATE_ON, data) def mock_turn_off(service): """ Will fake the component has been turned off. """ if service.data and ATTR_ENTITY_ID in service.data: entity_ids = extract_entity_ids(hass, service) else: entity_ids = hass.states.entity_ids(service.domain) for entity_id in entity_ids: hass.states.set(entity_id, STATE_OFF) # Setup sun if CONF_LATITUDE not in config[ha.DOMAIN]: config[ha.DOMAIN][CONF_LATITUDE] = '32.87336' if CONF_LONGITUDE not in config[ha.DOMAIN]: config[ha.DOMAIN][CONF_LONGITUDE] = '-117.22743' loader.get_component('sun').setup(hass, config) # Setup fake lights lights = [ 'light.Bowl', 'light.Ceiling', 'light.TV_Back_light', 'light.Bed_light' ] hass.services.register('light', SERVICE_TURN_ON, mock_turn_on) hass.services.register('light', SERVICE_TURN_OFF, mock_turn_off) mock_turn_on( ha.ServiceCall('light', SERVICE_TURN_ON, {'entity_id': lights[0:2]})) mock_turn_off( ha.ServiceCall('light', SERVICE_TURN_OFF, {'entity_id': lights[2:]})) group.setup_group(hass, GROUP_NAME_ALL_LIGHTS, lights, False) # Setup switch switches = ['switch.AC', 'switch.Christmas_Lights'] hass.services.register('switch', SERVICE_TURN_ON, mock_turn_on) hass.services.register('switch', SERVICE_TURN_OFF, mock_turn_off) mock_turn_on( ha.ServiceCall('switch', SERVICE_TURN_ON, {'entity_id': switches[0:1]})) mock_turn_off( ha.ServiceCall('switch', SERVICE_TURN_OFF, {'entity_id': switches[1:]})) # Setup room groups group.setup_group(hass, 'living_room', lights[0:3] + switches[0:1]) group.setup_group(hass, 'bedroom', [lights[3]] + switches[1:]) # Setup process hass.states.set("process.XBMC", STATE_ON) # Setup device tracker hass.states.set( "device_tracker.Paulus", "home", {ATTR_ENTITY_PICTURE: "http://graph.facebook.com/schoutsen/picture"}) hass.states.set( "device_tracker.Anne_Therese", "not_home", { ATTR_ENTITY_PICTURE: "http://graph.facebook.com/anne.t.frederiksen/picture" }) hass.states.set( "group.all_devices", "home", { "auto": True, "entity_id": ["device_tracker.Paulus", "device_tracker.Anne_Therese"] }) # Setup chromecast hass.states.set( "chromecast.Living_Rm", "Plex", { 'friendly_name': 'Living Room', ATTR_ENTITY_PICTURE: 'http://graph.facebook.com/KillBillMovie/picture' }) # Setup tellstick sensors hass.states.set("tellstick_sensor.Outside_temperature", "15.6", { 'friendly_name': 'Outside temperature', 'unit_of_measurement': '°C' }) hass.states.set("tellstick_sensor.Outside_humidity", "54", { 'friendly_name': 'Outside humidity', 'unit_of_measurement': '%' }) # Nest demo hass.states.set( "thermostat.Nest", "23", { ATTR_UNIT_OF_MEASUREMENT: TEMP_CELCIUS, ATTR_CURRENT_TEMPERATURE: '18', ATTR_AWAY_MODE: STATE_OFF }) configurator_ids = [] def hue_configuration_callback(data): """ Fake callback, mark config as done. """ time.sleep(2) # First time it is called, pretend it failed. if len(configurator_ids) == 1: configurator.notify_errors( configurator_ids[0], "Failed to register, please try again.") configurator_ids.append(0) else: configurator.request_done(configurator_ids[0]) request_id = configurator.request_config( hass, "Philips Hue", hue_configuration_callback, description=("Press the button on the bridge to register Philips Hue " "with Home Assistant."), description_image="/static/images/config_philips_hue.jpg", submit_caption="I have pressed the button") configurator_ids.append(request_id) return True
def test_get_component(self): """Test if get_component works.""" self.assertEqual(http, loader.get_component('http')) self.assertIsNotNone(loader.get_component('switch.test'))
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the smart mi fan platform.""" import miio host = config.get(CONF_HOST) name = config.get(CONF_NAME) token = config.get(CONF_TOKEN) devices = config.get(CONF_SWITCHES, {}) persistent_notification = loader.get_component('persistent_notification') @asyncio.coroutine def _learn_command(call): ir_remote = miio.Device(host, token) if not ir_remote: _LOGGER.error("Failed to connect to device.") return key = randint(1,1000000) ir_remote.send("miIO.ir_learn", {'key': str(key)}) _LOGGER.info("Press the key you want HASS to learn") start_time = utcnow() while (utcnow() - start_time) < timedelta(seconds=DEFAULT_TIMEOUT): res = ir_remote.send("miIO.ir_read", {'key': str(key)}) _LOGGER.error(type(res["code"])) _LOGGER.error(res["code"]) if res["code"]: log_msg = 'Recieved packet is: %s' % res["code"] _LOGGER.info(log_msg) persistent_notification.async_create(hass, log_msg, title='Chuangmi switch') return yield from asyncio.sleep(1, loop=hass.loop) _LOGGER.error('Did not received any signal.') persistent_notification.async_create(hass, "Did not received any signal", title='Chuangmi switch') @asyncio.coroutine def _send_packet(call): ir_remote = miio.Device(host, token) if not ir_remote: _LOGGER.error("Failed to connect to device.") return packets = call.data.get('packet', []) for packet in packets: for retry in range(DEFAULT_RETRY): try: ir_remote.send("miIO.ir_play", {'freq':38400, 'code': str(packet)}) break except (socket.timeout, ValueError): _LOGGER.error("Failed to send packet to device.") ir_remote = miio.Device(host, token) if not ir_remote: _LOGGER.error("Failed to connect to device.") hass.services.register(DOMAIN, SERVICE_LEARN + '_' + host.replace('.', '_'), _learn_command) hass.services.register(DOMAIN, SERVICE_SEND + '_' + host.replace('.', '_'), _send_packet) switches = [] for object_id, device_config in devices.items(): switches.append( ChuangmiIRSwitch( ir_remote, device_config.get(CONF_NAME, object_id), device_config.get(CONF_COMMAND_ON), device_config.get(CONF_COMMAND_OFF) ) ) add_devices(switches)
def setup(hass, config): """Set up the Wink component.""" import pywink from pubnubsubhandler import PubNubSubscriptionHandler if hass.data.get(DOMAIN) is None: hass.data[DOMAIN] = { 'unique_ids': [], 'entities': {}, 'oauth': {}, 'configuring': {}, 'pubnub': None, 'configurator': False } def _get_wink_token_from_web(): _email = hass.data[DOMAIN]["oauth"]["email"] _password = hass.data[DOMAIN]["oauth"]["password"] payload = {'username': _email, 'password': _password} token_response = requests.post(CONF_TOKEN_URL, data=payload) try: token = token_response.text.split(':')[1].split()[0].rstrip('<br') except IndexError: _LOGGER.error("Error getting token. Please check email/password.") return False pywink.set_bearer_token(token) client_id = config[DOMAIN].get(ATTR_CLIENT_ID) client_secret = config[DOMAIN].get(ATTR_CLIENT_SECRET) email = config[DOMAIN].get(CONF_EMAIL) password = config[DOMAIN].get(CONF_PASSWORD) local_control = config[DOMAIN].get(CONF_LOCAL_CONTROL) if None not in [client_id, client_secret]: _LOGGER.info("Using legacy oauth authentication") if not local_control: pywink.disable_local_control() hass.data[DOMAIN]["oauth"]["client_id"] = client_id hass.data[DOMAIN]["oauth"]["client_secret"] = client_secret hass.data[DOMAIN]["oauth"]["email"] = email hass.data[DOMAIN]["oauth"]["password"] = password pywink.legacy_set_wink_credentials(email, password, client_id, client_secret) elif None not in [email, password]: _LOGGER.info("Using web form authentication") pywink.disable_local_control() hass.data[DOMAIN]["oauth"]["email"] = email hass.data[DOMAIN]["oauth"]["password"] = password _get_wink_token_from_web() else: _LOGGER.info("Using new oauth authentication") if not local_control: pywink.disable_local_control() config_path = hass.config.path(WINK_CONFIG_FILE) if os.path.isfile(config_path): config_file = _read_config_file(config_path) if config_file == DEFAULT_CONFIG: _request_app_setup(hass, config) return True # else move on because the user modified the file else: _write_config_file(config_path, DEFAULT_CONFIG) _request_app_setup(hass, config) return True if DOMAIN in hass.data[DOMAIN]['configuring']: _configurator = hass.data[DOMAIN]['configuring'] get_component('configurator').request_done( _configurator.pop(DOMAIN)) # Using oauth access_token = config_file.get(ATTR_ACCESS_TOKEN) refresh_token = config_file.get(ATTR_REFRESH_TOKEN) # This will be called after authorizing Home-Assistant if None not in (access_token, refresh_token): pywink.set_wink_credentials(config_file.get(ATTR_CLIENT_ID), config_file.get(ATTR_CLIENT_SECRET), access_token=access_token, refresh_token=refresh_token) # This is called to create the redirect so the user can Authorize # Home-Assistant else: redirect_uri = '{}{}'.format(hass.config.api.base_url, WINK_AUTH_CALLBACK_PATH) wink_auth_start_url = pywink.get_authorization_url( config_file.get(ATTR_CLIENT_ID), redirect_uri) hass.http.register_redirect(WINK_AUTH_START, wink_auth_start_url) hass.http.register_view( WinkAuthCallbackView(config, config_file, pywink.request_token)) _request_oauth_completion(hass, config) return True pywink.set_user_agent(USER_AGENT) hass.data[DOMAIN]['pubnub'] = PubNubSubscriptionHandler( pywink.get_subscription_key()) def _subscribe(): hass.data[DOMAIN]['pubnub'].subscribe() # Call subscribe after the user sets up wink via the configurator # All other methods will complete setup before # EVENT_HOMEASSISTANT_START is called meaning they # will call subscribe via the method below. (start_subscription) if hass.data[DOMAIN]['configurator']: _subscribe() def keep_alive_call(event_time): """Call the Wink API endpoints to keep PubNub working.""" _LOGGER.info("Polling the Wink API to keep PubNub updates flowing.") pywink.set_user_agent(str(int(time.time()))) _temp_response = pywink.get_user() _LOGGER.debug(str(json.dumps(_temp_response))) time.sleep(1) pywink.set_user_agent(USER_AGENT) _temp_response = pywink.wink_api_fetch() _LOGGER.debug(str(json.dumps(_temp_response))) # Call the Wink API every hour to keep PubNub updates flowing track_time_interval(hass, keep_alive_call, timedelta(minutes=60)) def start_subscription(event): """Start the pubnub subscription.""" _subscribe() hass.bus.listen(EVENT_HOMEASSISTANT_START, start_subscription) def stop_subscription(event): """Stop the pubnub subscription.""" hass.data[DOMAIN]['pubnub'].unsubscribe() hass.bus.listen(EVENT_HOMEASSISTANT_STOP, stop_subscription) def save_credentials(event): """Save currently set oauth credentials.""" if hass.data[DOMAIN]["oauth"].get("email") is None: config_path = hass.config.path(WINK_CONFIG_FILE) _config = pywink.get_current_oauth_credentials() _write_config_file(config_path, _config) hass.bus.listen(EVENT_HOMEASSISTANT_STOP, save_credentials) def force_update(call): """Force all devices to poll the Wink API.""" _LOGGER.info("Refreshing Wink states from API") for entity_list in hass.data[DOMAIN]['entities'].values(): # Throttle the calls to Wink API for entity in entity_list: time.sleep(1) entity.schedule_update_ha_state(True) hass.services.register(DOMAIN, SERVICE_REFRESH_STATES, force_update) def pull_new_devices(call): """Pull new devices added to users Wink account since startup.""" _LOGGER.info("Getting new devices from Wink API") for _component in WINK_COMPONENTS: discovery.load_platform(hass, _component, DOMAIN, {}, config) hass.services.register(DOMAIN, SERVICE_ADD_NEW_DEVICES, pull_new_devices) # Load components for the devices in Wink that we support for component in WINK_COMPONENTS: hass.data[DOMAIN]['entities'][component] = [] discovery.load_platform(hass, component, DOMAIN, {}, config) return True
def test_services(self): """Test the provided services.""" platform = loader.get_component('light.test') platform.init() self.assertTrue( setup_component(self.hass, light.DOMAIN, {light.DOMAIN: { CONF_PLATFORM: 'test' }})) dev1, dev2, dev3 = platform.DEVICES # Test init self.assertTrue(light.is_on(self.hass, dev1.entity_id)) self.assertFalse(light.is_on(self.hass, dev2.entity_id)) self.assertFalse(light.is_on(self.hass, dev3.entity_id)) # Test basic turn_on, turn_off, toggle services light.turn_off(self.hass, entity_id=dev1.entity_id) light.turn_on(self.hass, entity_id=dev2.entity_id) self.hass.block_till_done() self.assertFalse(light.is_on(self.hass, dev1.entity_id)) self.assertTrue(light.is_on(self.hass, dev2.entity_id)) # turn on all lights light.turn_on(self.hass) self.hass.block_till_done() self.assertTrue(light.is_on(self.hass, dev1.entity_id)) self.assertTrue(light.is_on(self.hass, dev2.entity_id)) self.assertTrue(light.is_on(self.hass, dev3.entity_id)) # turn off all lights light.turn_off(self.hass) self.hass.block_till_done() self.assertFalse(light.is_on(self.hass, dev1.entity_id)) self.assertFalse(light.is_on(self.hass, dev2.entity_id)) self.assertFalse(light.is_on(self.hass, dev3.entity_id)) # toggle all lights light.toggle(self.hass) self.hass.block_till_done() self.assertTrue(light.is_on(self.hass, dev1.entity_id)) self.assertTrue(light.is_on(self.hass, dev2.entity_id)) self.assertTrue(light.is_on(self.hass, dev3.entity_id)) # toggle all lights light.toggle(self.hass) self.hass.block_till_done() self.assertFalse(light.is_on(self.hass, dev1.entity_id)) self.assertFalse(light.is_on(self.hass, dev2.entity_id)) self.assertFalse(light.is_on(self.hass, dev3.entity_id)) # Ensure all attributes process correctly light.turn_on(self.hass, dev1.entity_id, transition=10, brightness=20, color_name='blue') light.turn_on(self.hass, dev2.entity_id, rgb_color=(255, 255, 255), white_value=255) light.turn_on(self.hass, dev3.entity_id, xy_color=(.4, .6)) self.hass.block_till_done() _, data = dev1.last_call('turn_on') self.assertEqual( { light.ATTR_TRANSITION: 10, light.ATTR_BRIGHTNESS: 20, light.ATTR_RGB_COLOR: (0, 0, 255) }, data) _, data = dev2.last_call('turn_on') self.assertEqual( { light.ATTR_RGB_COLOR: (255, 255, 255), light.ATTR_WHITE_VALUE: 255 }, data) _, data = dev3.last_call('turn_on') self.assertEqual({light.ATTR_XY_COLOR: (.4, .6)}, data) # One of the light profiles prof_name, prof_x, prof_y, prof_bri = 'relax', 0.5119, 0.4147, 144 # Test light profiles light.turn_on(self.hass, dev1.entity_id, profile=prof_name) # Specify a profile and a brightness attribute to overwrite it light.turn_on(self.hass, dev2.entity_id, profile=prof_name, brightness=100) self.hass.block_till_done() _, data = dev1.last_call('turn_on') self.assertEqual( { light.ATTR_BRIGHTNESS: prof_bri, light.ATTR_XY_COLOR: (prof_x, prof_y) }, data) _, data = dev2.last_call('turn_on') self.assertEqual( { light.ATTR_BRIGHTNESS: 100, light.ATTR_XY_COLOR: (.5119, .4147) }, data) # Test bad data light.turn_on(self.hass) light.turn_on(self.hass, dev1.entity_id, profile="nonexisting") light.turn_on(self.hass, dev2.entity_id, xy_color=["bla-di-bla", 5]) light.turn_on(self.hass, dev3.entity_id, rgb_color=[255, None, 2]) self.hass.block_till_done() _, data = dev1.last_call('turn_on') self.assertEqual({}, data) _, data = dev2.last_call('turn_on') self.assertEqual({}, data) _, data = dev3.last_call('turn_on') self.assertEqual({}, data) # faulty attributes will not trigger a service call light.turn_on(self.hass, dev1.entity_id, profile=prof_name, brightness='bright') light.turn_on(self.hass, dev1.entity_id, rgb_color='yellowish') light.turn_on(self.hass, dev2.entity_id, white_value='high') self.hass.block_till_done() _, data = dev1.last_call('turn_on') self.assertEqual({}, data) _, data = dev2.last_call('turn_on') self.assertEqual({}, data)
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up Broadlink switches.""" import broadlink devices = config.get(CONF_SWITCHES, {}) ip_addr = config.get(CONF_HOST) friendly_name = config.get(CONF_FRIENDLY_NAME) mac_addr = binascii.unhexlify( config.get(CONF_MAC).encode().replace(b':', b'')) switch_type = config.get(CONF_TYPE) persistent_notification = loader.get_component('persistent_notification') @asyncio.coroutine def _learn_command(call): try: auth = yield from hass.async_add_job(broadlink_device.auth) except socket.timeout: _LOGGER.error("Failed to connect to device, timeout") return if not auth: _LOGGER.error("Failed to connect to device") return yield from hass.async_add_job(broadlink_device.enter_learning) _LOGGER.info("Press the key you want HASS to learn") start_time = utcnow() while (utcnow() - start_time) < timedelta(seconds=20): packet = yield from hass.async_add_job(broadlink_device.check_data) if packet: log_msg = "Recieved packet is: {}".\ format(b64encode(packet).decode('utf8')) _LOGGER.info(log_msg) persistent_notification.async_create(hass, log_msg, title='Broadlink switch') return yield from asyncio.sleep(1, loop=hass.loop) _LOGGER.error("Did not received any signal") persistent_notification.async_create(hass, "Did not received any signal", title='Broadlink switch') @asyncio.coroutine def _send_packet(call): packets = call.data.get('packet', []) for packet in packets: for retry in range(DEFAULT_RETRY): try: payload = b64decode(packet) yield from hass.async_add_job(broadlink_device.send_data, payload) break except (socket.timeout, ValueError): try: yield from hass.async_add_job(broadlink_device.auth) except socket.timeout: if retry == DEFAULT_RETRY - 1: _LOGGER.error("Failed to send packet to device") if switch_type in RM_TYPES: broadlink_device = broadlink.rm((ip_addr, 80), mac_addr) hass.services.register(DOMAIN, SERVICE_LEARN + '_' + ip_addr.replace('.', '_'), _learn_command) hass.services.register(DOMAIN, SERVICE_SEND + '_' + ip_addr.replace('.', '_'), _send_packet) switches = [] for object_id, device_config in devices.items(): switches.append( BroadlinkRMSwitch( device_config.get(CONF_FRIENDLY_NAME, object_id), broadlink_device, device_config.get(CONF_COMMAND_ON), device_config.get(CONF_COMMAND_OFF))) elif switch_type in SP1_TYPES: broadlink_device = broadlink.sp1((ip_addr, 80), mac_addr) switches = [BroadlinkSP1Switch(friendly_name, broadlink_device)] elif switch_type in SP2_TYPES: broadlink_device = broadlink.sp2((ip_addr, 80), mac_addr) switches = [BroadlinkSP2Switch(friendly_name, broadlink_device)] broadlink_device.timeout = config.get(CONF_TIMEOUT) try: broadlink_device.auth() except socket.timeout: _LOGGER.error("Failed to connect to device") add_devices(switches)
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the demo image processing platform.""" app_id = config.get(CONF_APP_ID) api_key = config.get(CONF_API_KEY) secret_key = config.get(CONF_SECRET_KEY) snapshot_filepath = config.get(CONF_SNAPSHOT_FILEPATH) resize = config.get(CONF_RESIZE) ha_url = config.get(CONF_HA_URL) ha_password = config.get(CONF_HA_PASSWORD) detect_top_num = config.get(CONF_DETECT_TOP_NUM) entities = [] for camera in config[CONF_SOURCE]: entities.append( BaiduFaceIdentifyEntity(hass, camera[CONF_ENTITY_ID], camera.get(CONF_NAME), app_id, api_key, secret_key, snapshot_filepath, resize, ha_url, ha_password, detect_top_num)) add_devices(entities) persistent_notification = loader.get_component('persistent_notification') def getAccessToken(): #请求参数 client_id = api_key client_secret = secret_key grant_type = 'client_credentials' request_url = 'https://aip.baidubce.com/oauth/2.0/token' params = { 'client_id': client_id, 'client_secret': client_secret, 'grant_type': grant_type } r = requests.get(url=request_url, params=params) access_token = json.loads(r.text)['access_token'] return access_token def get_image_base64(image_path): with open(image_path, 'rb') as fp: return base64.b64encode(fp.read()) #人脸注册服务 def registerUserFace(service): def register(): user_info = service.data[ATTR_USERINFO] uid = service.data[ATTR_UID] image = service.data[ATTR_IMAGE] group_id = GROUP_ID request_url = "https://aip.baidubce.com/rest/2.0/face/v2/faceset/user/add" img = get_image_base64(image) params = {'access_token': getAccessToken()} data = { "group_id": group_id, "image": img, "uid": uid, "user_info": user_info } r = requests.post(url=request_url, params=params, data=data) resultjson = json.loads(r.text) if 'error_code' in resultjson: persistent_notification.create(hass, '人脸数据注册失败', title='百度人脸识别') elif 'error_msg' in resultjson: if resultjson['error_msg'] == 'image exist': persistent_notification.create(hass, '此人脸已经使用过', title='百度人脸识别') else: persistent_notification.create(hass, '人脸数据注册成功', title='百度人脸识别') return json.loads(r.text) threading.Thread(target=register).start() hass.services.register(DOMAIN, SERVUCE_REGISTERUSERFACE, registerUserFace, schema=SERVUCE_REGISTERUSERFACE_SCHEMA) #人脸数据查询服务 def getUserInfo(service): def userinfo(): group_id = service.data[ATTR_GROUPID] request_url = "https://aip.baidubce.com/rest/2.0/face/v2/faceset/group/getusers" params = {'access_token': getAccessToken()} data = {"group_id": group_id} r = requests.post(url=request_url, params=params, data=data) resultjson = json.loads(r.text) #outputst = '' br_string = '' if resultjson['result_num'] == 0: persistent_notification.create(hass, '无人脸注册数据', title='百度人脸识别') elif 'error_msg' in resultjson: if resultjson['error_msg'] == 'image exist': persistent_notification.create(hass, '此人脸已经使用过', title='百度人脸识别') else: for i in range(len(resultjson['result'])): br_string = br_string + str( resultjson['result'][i]) + '<br />' persistent_notification.create(hass, br_string, title='百度人脸识别') return json.loads(r.text) threading.Thread(target=userinfo).start() hass.services.register(DOMAIN, SERVUCE_GETUSERLIST, getUserInfo, schema=SERVUCE_GETUSERLIST_SCHEMA) #人脸数据删除服务 def delUserInfo(service): def deluserinfo(): uid = service.data[ATTR_UID] request_url = "https://aip.baidubce.com/rest/2.0/face/v2/faceset/user/delete" params = {'access_token': getAccessToken()} data = {'uid': uid} r = requests.post(url=request_url, params=params, data=data) resultjson = json.loads(r.text) if 'error_code' in resultjson: persistent_notification.create(hass, '人脸数据删除失败<br />检查uid是否正确', title='百度人脸识别') elif 'error_msg' in resultjson: if resultjson['error_msg'] == 'user not exist': persistent_notification.create(hass, '该uid不存在', title='百度人脸识别') else: persistent_notification.create(hass, '人脸数据删除成功', title='百度人脸识别') return json.loads(r.text) threading.Thread(target=deluserinfo).start() hass.services.register(DOMAIN, SERVUCE_DELETEUSER, delUserInfo, schema=SERVUCE_DELETEUSER_SCHEMA) def details_faceinfo(json): resultinfo = '' for key, value in json.items(): if key in face_fields: if face_fields[key][1] == "%": resultinfo = resultinfo + face_fields[key][0] + ":" + str( round(json[key] * 100, 2)) + "%<br />" elif face_fields[key][1] == "int": resultinfo = resultinfo + face_fields[key][0] + ":" + str( json[key]) + "<br />" elif face_fields[key][1] == None: resultinfo = resultinfo + face_fields[key][ 0] + ":" + face_fields[key][2][str( json[key])] + "<br />" else: resultinfo = resultinfo + face_fields[key][0] + ":" + str( json[key]) + face_fields[key][1] + "<br />" elif key == 'faceshape': for i in range(len(json['faceshape'])): resultinfo = resultinfo + faceshape[ json['faceshape'][i]['type']][0] + ":" + str( round(json['faceshape'][i]['probability'] * 100, 2)) + "%<br />" elif key == 'qualities': resultinfo = resultinfo + human_type['human'][0] + ":" + str( round(json['qualities']['type']['human'] * 100, 2)) + "%<br />" resultinfo = resultinfo + human_type['cartoon'][0] + ":" + str( round(json['qualities']['type']['cartoon'] * 100, 2)) + "%<br />" return resultinfo #人脸检测服务 def detectface(service): def detect(): image = service.data[ATTR_IMAGE] request_url = "https://aip.baidubce.com/rest/2.0/face/v2/detect" img = get_image_base64(image) params = { 'access_token': getAccessToken(), 'face_fields': 'age,beauty,expression,faceshape,gender,glasses,race,qualities', } data = {"image": img} r = requests.post(url=request_url, params=params, data=data) resultjson = json.loads(r.text) if resultjson['result_num'] == 0: persistent_notification.create(hass, '没检测到人脸存在!', title='百度人脸识别') elif 'error_msg' in resultjson: if resultjson[ 'error_msg'] == 'Access token invalid or no longer valid': persistent_notification.create(hass, '百度Access Token获取失败!', title='百度人脸识别') elif resultjson[ 'error_msg'] == 'Open api qps request limit reached': persistent_notification.create(hass, 'QPS超限额', title='百度人脸识别') else: persistent_notification.create(hass, resultjson['error_msg'], title='百度人脸识别') else: persistent_notification.create(hass, details_faceinfo( resultjson['result'][0]), title='百度人脸识别') return json.loads(r.text) threading.Thread(target=detect).start() hass.services.register(DOMAIN, SERVUCE_DETECTFACE, detectface, schema=SERVUCE_DETECTFACE_SCHEMA)