Ejemplo n.º 1
0
    def get(self, request):
        """Finish OAuth callback request."""
        from oauthlib.oauth2.rfc6749.errors import MismatchingStateError
        from oauthlib.oauth2.rfc6749.errors import MissingTokenError

        hass = request.app["hass"]
        data = request.query

        response_message = """Fitbit has been successfully authorized!
        You can close this window now!"""

        result = None
        if data.get("code") is not None:
            redirect_uri = "{}{}".format(hass.config.api.base_url,
                                         FITBIT_AUTH_CALLBACK_PATH)

            try:
                result = self.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!
                """

        if result is None:
            _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)

        if result:
            config_contents = {
                ATTR_ACCESS_TOKEN: result.get("access_token"),
                ATTR_REFRESH_TOKEN: result.get("refresh_token"),
                ATTR_CLIENT_ID: self.oauth.client_id,
                ATTR_CLIENT_SECRET: self.oauth.client_secret,
                ATTR_LAST_SAVED_AT: int(time.time()),
            }
        save_json(hass.config.path(FITBIT_CONFIG_FILE), config_contents)

        hass.async_add_job(setup_platform, hass, self.config,
                           self.add_entities)

        return html_response
Ejemplo n.º 2
0
 def success():
     """Signal successful setup."""
     conf = load_json(hass.config.path(CONFIG_FILE))
     conf[host] = {CONF_API_KEY: api_key}
     save_json(hass.config.path(CONFIG_FILE), conf)
     req_config = _CONFIGURING.pop(host)
     configurator.request_done(req_config)
Ejemplo n.º 3
0
    def get(self, request):
        """Finish OAuth callback request."""
        from aiohttp import web

        hass = request.app['hass']
        data = request.query

        response_message = """Wink has been successfully authorized!
         You can close this window now! For the best results you should reboot
         HomeAssistant"""
        html_response = """<html><head><title>Wink Auth</title></head>
                <body><h1>{}</h1></body></html>"""

        if data.get('code') is not None:
            response = self.request_token(data.get('code'),
                                          self.config_file['client_secret'])

            config_contents = {
                ATTR_ACCESS_TOKEN: response['access_token'],
                ATTR_REFRESH_TOKEN: response['refresh_token'],
                ATTR_CLIENT_ID: self.config_file['client_id'],
                ATTR_CLIENT_SECRET: self.config_file['client_secret']
            }
            save_json(hass.config.path(WINK_CONFIG_FILE), config_contents)

            hass.async_add_job(setup, hass, self.config)

            return web.Response(text=html_response.format(response_message),
                                content_type='text/html')

        error_msg = "No code returned from Wink API"
        _LOGGER.error(error_msg)
        return web.Response(text=html_response.format(error_msg),
                            content_type='text/html')
Ejemplo n.º 4
0
def setup(hass, config):
    """Set up the Daikin Skyport Thermostat.

    Will automatically load thermostat and sensor components to support
    devices discovered on the network.
    """

    # Create daikinskyport.conf if it doesn't exist
    if not os.path.isfile(hass.config.path(DAIKINSKYPORT_CONFIG_FILE)):
        jsonconfig = {
            "EMAIL": config[DOMAIN].get(CONF_EMAIL),
            "PASSWORD": config[DOMAIN].get(CONF_PASSWORD)
        }
        save_json(hass.config.path(DAIKINSKYPORT_CONFIG_FILE), jsonconfig)

    data = DaikinSkyportData(hass.config.path(DAIKINSKYPORT_CONFIG_FILE))
    hass.data[DOMAIN] = data

    hold_temp = config[DOMAIN].get(CONF_HOLD_TEMP)

    discovery.load_platform(hass, "climate", DOMAIN, {"hold_temp": hold_temp},
                            config)
    discovery.load_platform(hass, "sensor", DOMAIN, {}, config)
    #    discovery.load_platform(hass, "binary_sensor", DOMAIN, {}, config)
    discovery.load_platform(hass, "weather", DOMAIN, {}, config)

    return True
Ejemplo n.º 5
0
 def success():
     """Set up was successful."""
     conf = load_json(hass.config.path(config.get(CONF_FILENAME)))
     conf[host] = {'credentials': credentials}
     save_json(hass.config.path(config.get(CONF_FILENAME)), conf)
     req_config = _CONFIGURING.pop(host)
     hass.async_add_job(configurator.request_done, req_config)
Ejemplo n.º 6
0
    def post(self, request):
        """Handle the POST request for device identification."""
        try:
            data = yield from request.json()
        except ValueError:
            return self.json_message("Invalid JSON", HTTP_BAD_REQUEST)

        # Commented for now while iOS app is getting frequent updates
        # try:
        #     data = IDENTIFY_SCHEMA(req_data)
        # except vol.Invalid as ex:
        #     return self.json_message(humanize_error(request.json, ex),
        #                              HTTP_BAD_REQUEST)

        data[ATTR_LAST_SEEN_AT] = datetime.datetime.now().isoformat()

        name = data.get(ATTR_DEVICE_ID)

        CONFIG_FILE[ATTR_DEVICES][name] = data

        try:
            save_json(CONFIG_FILE_PATH, CONFIG_FILE)
        except HomeAssistantError:
            return self.json_message("Error saving device.",
                                     HTTP_INTERNAL_SERVER_ERROR)

        return self.json({"status": "registered"})
Ejemplo n.º 7
0
def setup_bravia(config, pin, hass, add_devices):
    """Set up a Sony Bravia TV based on host parameter."""
    host = config.get(CONF_HOST)
    name = config.get(CONF_NAME)

    if pin is None:
        request_configuration(config, hass, add_devices)
        return
    else:
        mac = _get_mac_address(host)
        if mac is not None:
            mac = mac.decode('utf8')
        # If we came here and configuring this host, mark as done
        if host in _CONFIGURING:
            request_id = _CONFIGURING.pop(host)
            configurator = hass.components.configurator
            configurator.request_done(request_id)
            _LOGGER.info("Discovery configuration done")

        # Save config
        save_json(hass.config.path(BRAVIA_CONFIG_FILE),
                  {host: {
                      'pin': pin,
                      'host': host,
                      'mac': mac
                  }})

        add_devices([BraviaTVDevice(host, mac, name, pin)])
Ejemplo n.º 8
0
def setup_sonymediaplayer(config, sony_device, hass, add_devices):
    """Set up a Sony Media Player based on host parameter."""
    host = config.get(CONF_HOST)
    broadcast = config.get(CONF_BROADCAST_ADDRESS)

    if sony_device is None:
        request_configuration(config, hass, add_devices)
    else:
        # If we came here and configuring this host, mark as done
        if host in _CONFIGURING:
            request_id = _CONFIGURING.pop(host)
            configurator = hass.components.configurator
            configurator.request_done(request_id)
            _LOGGER.info("Discovery configuration done")

        if broadcast:
            sony_device.broadcast = broadcast
        hass_device = SonyMediaPlayerDevice(sony_device)

        # Save config, we need the mac address to support wake on LAN
        save_json(hass.config.path(SONY_CONFIG_FILE),
                  {host: {
                      'device': hass_device.sonydevice.save_to_json()
                  }})

        add_devices([hass_device])
Ejemplo n.º 9
0
async def scan_device(app, listener, ieee, cmd, data, service):
    if ieee is None:
        LOGGER.error("missing ieee")
        return

    LOGGER.debug("running 'scan_device' command: %s", service)
    device = app.get_device(ieee=ieee)
    scan = await scan_results(device)

    model = scan.get("model")
    manufacturer = scan.get("manufacturer")
    if model is not None and manufacturer is not None:
        ieee_tail = "".join(["%02x" % (o, ) for o in ieee[-4:]])
        file_name = "{}_{}_{}_scan_results.txt".format(model, manufacturer,
                                                       ieee_tail)
    else:
        ieee_tail = "".join(["%02x" % (o, ) for o in ieee])
        file_name = "{}_scan_results.txt".format(ieee_tail)

    conf_dir = listener._hass.config.config_dir
    scan_dir = os.path.join(conf_dir, "scans")
    if not os.path.isdir(scan_dir):
        os.mkdir(scan_dir)
    file_name = os.path.join(scan_dir, file_name)
    save_json(file_name, scan)
    LOGGER.debug("Finished writing scan results int '%s'", file_name)
Ejemplo n.º 10
0
def save_games(hass: HomeAssistant, games: dict, unique_id: str):
    """Save games to file."""
    g_file = hass.config.path(GAMES_FILE.format(unique_id))
    try:
        save_json(g_file, games)
    except OSError as error:
        _LOGGER.error("Could not save game list, %s", error)
Ejemplo n.º 11
0
    def post(self, request):
        """Handle the POST request for device identification."""
        try:
            data = yield from request.json()
        except ValueError:
            return self.json_message("Invalid JSON", HTTP_BAD_REQUEST)

        # Commented for now while iOS app is getting frequent updates
        # try:
        #     data = IDENTIFY_SCHEMA(req_data)
        # except vol.Invalid as ex:
        #     return self.json_message(humanize_error(request.json, ex),
        #                              HTTP_BAD_REQUEST)

        data[ATTR_LAST_SEEN_AT] = datetime.datetime.now()

        name = data.get(ATTR_DEVICE_ID)

        CONFIG_FILE[ATTR_DEVICES][name] = data

        try:
            save_json(CONFIG_FILE_PATH, CONFIG_FILE)
        except HomeAssistantError:
            return self.json_message("Error saving device.",
                                     HTTP_INTERNAL_SERVER_ERROR)

        return self.json({"status": "registered"})
Ejemplo n.º 12
0
 def success():
     """Setup was successful."""
     conf = load_json(hass.config.path(CONFIG_FILE))
     conf[host] = {CONF_API_KEY: api_key}
     save_json(hass.config.path(CONFIG_FILE), conf)
     req_config = _CONFIGURING.pop(host)
     configurator.request_done(req_config)
Ejemplo n.º 13
0
    def configuration_callback(callback_data):
        """Call when configuration is submitted."""
        if CONF_INCLUDE not in callback_data:
            configurator.notify_errors(request_id, "Functionality mandatory.")
            return False

        callback_data[CONF_INCLUDE] = callback_data[CONF_INCLUDE].split()
        callback_data[CONF_HOST] = host

        if CONF_NAME not in callback_data:
            callback_data[CONF_NAME] = name

        try:
            device_config = DEVICE_SCHEMA(callback_data)
        except vol.Invalid:
            configurator.notify_errors(request_id,
                                       "Bad input, please check spelling.")
            return False

        if setup_device(hass, config, device_config):
            config_file = load_json(hass.config.path(CONFIG_FILE))
            config_file[serialnumber] = dict(device_config)
            save_json(hass.config.path(CONFIG_FILE), config_file)
            configurator.request_done(request_id)
        else:
            configurator.notify_errors(
                request_id, "Failed to register, please try again.")
            return False
Ejemplo n.º 14
0
        def configuration_callback(callback_data):
            """Handle the submitted configuration."""
            session.authorize()
            res = setup(hass, config, session)
            if not res:
                configurator.notify_errors(hass.data[KEY_CONFIG].get(data_key),
                                           'Unable to connect.')
                return

            conf.update(
                {host: {
                    CONF_HOST: host,
                    CONF_TOKEN: session.access_token
                }} if host else {
                    DOMAIN: {
                        CONF_TOKEN: session.access_token,
                        CONF_TOKEN_SECRET: session.access_token_secret
                    }
                })
            save_json(config_filename, conf)
            # Close all open configurators: for now, we only support one
            # tellstick device, and configuration via either cloud service
            # or via local API, not both at the same time
            for instance in hass.data[KEY_CONFIG].values():
                configurator.request_done(instance)
Ejemplo n.º 15
0
 def success():
     """Set up was successful."""
     conf = load_json(hass.config.path(CONFIG_FILE))
     conf[host] = {'api_key': api_key}
     save_json(hass.config.path(CONFIG_FILE), conf)
     req_config = _CONFIGURING.pop(host)
     configurator.async_request_done(req_config)
    async def get(self, request):
        hass = request.app["hass"]
        data = request.query

        html_response = """<html><head><title>Microsoft To Do authorization</title></head>
                           <body><h1>{}</h1></body></html>"""

        if data.get("code") is None:
            error_msg = "No code returned from Microsoft Graph Auth API"
            _LOGGER.error(error_msg)
            return Response(text=html_response.format(error_msg),
                            content_type="text/html")

        token = await hass.async_add_executor_job(self.get_token,
                                                  data.get("code"))

        save_json(hass.config.path(MS_TODO_AUTH_FILE), token)

        response_message = """Microsoft To Do has been successfully authorized!
                              You can close this window now!"""

        hass.async_add_job(setup_platform, *self.setup_args)

        return Response(text=html_response.format(response_message),
                        content_type="text/html")
Ejemplo n.º 17
0
 def _generate_backup_contents(
     self,
     tar_file_path: Path,
     backup_data: dict[str, Any],
 ) -> None:
     """Generate backup contents."""
     with TemporaryDirectory() as tmp_dir, SecureTarFile(
         tar_file_path, "w", gzip=False
     ) as tar_file:
         tmp_dir_path = Path(tmp_dir)
         json_util.save_json(
             tmp_dir_path.joinpath("./backup.json").as_posix(),
             backup_data,
         )
         with SecureTarFile(
             tmp_dir_path.joinpath("./homeassistant.tar.gz").as_posix(),
             "w",
         ) as core_tar:
             atomic_contents_add(
                 tar_file=core_tar,
                 origin_path=Path(self.hass.config.path()),
                 excludes=EXCLUDE_FROM_BACKUP,
                 arcname="data",
             )
         tar_file.add(tmp_dir_path, arcname=".")
Ejemplo n.º 18
0
    def save_task(self):
        save_json(self.hass.config.path(PERSISTENCE + "_" + self._name),
                  [item.to_map() for item in self._tasks])

        asyncio.run_coroutine_threadsafe(
            entity_component.async_update_entity(self.hass, self.entity_id),
            self.hass.loop)
Ejemplo n.º 19
0
    def _write_data(self, path: str, data: Dict):
        """Write the data."""
        if not os.path.isdir(os.path.dirname(path)):
            os.makedirs(os.path.dirname(path))

        _LOGGER.debug('Writing data for %s', self.key)
        json.save_json(path, data, self._private)
Ejemplo n.º 20
0
def setup_bravia(config, pin, hass, add_devices):
    """Set up a Sony Bravia TV based on host parameter."""
    host = config.get(CONF_HOST)
    name = config.get(CONF_NAME)

    if pin is None:
        request_configuration(config, hass, add_devices)
        return

    mac = _get_mac_address(host)
    if mac is not None:
        mac = mac.decode('utf8')
    # If we came here and configuring this host, mark as done
    if host in _CONFIGURING:
        request_id = _CONFIGURING.pop(host)
        configurator = hass.components.configurator
        configurator.request_done(request_id)
        _LOGGER.info("Discovery configuration done")

    # Save config
    save_json(
        hass.config.path(BRAVIA_CONFIG_FILE),
        {host: {'pin': pin, 'host': host, 'mac': mac}})

    add_devices([BraviaTVDevice(host, mac, name, pin)])
Ejemplo n.º 21
0
    def _write_data(self, path: str, data: Dict):
        """Write the data."""
        if not os.path.isdir(os.path.dirname(path)):
            os.makedirs(os.path.dirname(path))

        _LOGGER.debug('Writing data for %s', self.key)
        json.save_json(path, data)
 def success():
     """Set up was successful."""
     conf = load_json(hass.config.path(CONFIG_FILE))
     conf[host] = {'identity': identity,
                   'key': key}
     save_json(hass.config.path(CONFIG_FILE), conf)
     configurator.request_done(instance)
Ejemplo n.º 23
0
def setup_bravia(config, pin, hass, add_entities):
    """Set up a Sony Bravia TV based on host parameter."""
    host = config.get(CONF_HOST)
    name = config.get(CONF_NAME)

    if pin is None:
        request_configuration(config, hass, add_entities)
        return

    try:
        if ipaddress.ip_address(host).version == 6:
            mode = 'ip6'
        else:
            mode = 'ip'
    except ValueError:
        mode = 'hostname'
    mac = get_mac_address(**{mode: host})

    # If we came here and configuring this host, mark as done
    if host in _CONFIGURING:
        request_id = _CONFIGURING.pop(host)
        configurator = hass.components.configurator
        configurator.request_done(request_id)
        _LOGGER.info("Discovery configuration done")

    # Save config
    save_json(
        hass.config.path(BRAVIA_CONFIG_FILE),
        {host: {'pin': pin, 'host': host, 'mac': mac}})

    add_entities([BraviaTVDevice(host, mac, name, pin)])
Ejemplo n.º 24
0
def save_games(hass: HomeAssistantType, games: dict):
    """Save games to file."""
    g_file = hass.config.path(GAMES_FILE)
    try:
        save_json(g_file, games)
    except OSError as error:
        _LOGGER.error("Could not save game list, %s", error)
Ejemplo n.º 25
0
    async def post(self, request):
        """Handle the POST request for device identification."""
        try:
            data = await request.json()
        except ValueError:
            return self.json_message("Invalid JSON", HTTP_BAD_REQUEST)

        hass = request.app['hass']

        # Commented for now while iOS app is getting frequent updates
        # try:
        #     data = IDENTIFY_SCHEMA(req_data)
        # except vol.Invalid as ex:
        #     return self.json_message(
        #         vol.humanize.humanize_error(request.json, ex),
        #         HTTP_BAD_REQUEST)

        data[ATTR_LAST_SEEN_AT] = datetime.datetime.now().isoformat()

        name = data.get(ATTR_DEVICE_ID)

        hass.data[DOMAIN][ATTR_DEVICES][name] = data

        try:
            save_json(self._config_path, hass.data[DOMAIN])
        except HomeAssistantError:
            return self.json_message("Error saving device.",
                                     HTTP_INTERNAL_SERVER_ERROR)

        return self.json({"status": "registered"})
Ejemplo n.º 26
0
    def _write_data(self, path: str, data: dict) -> None:
        """Write the data."""
        if not os.path.isdir(os.path.dirname(path)):
            os.makedirs(os.path.dirname(path))

        _LOGGER.debug("Writing data for %s to %s", self.key, path)
        json_util.save_json(path, data, self._private, encoder=self._encoder)
Ejemplo n.º 27
0
def test_overwrite_and_reload():
    """Test that we can overwrite an existing file and read back."""
    fname = _path_for("test3")
    save_json(fname, TEST_JSON_A)
    save_json(fname, TEST_JSON_B)
    data = load_json(fname)
    assert data == TEST_JSON_B
Ejemplo n.º 28
0
    def configuration_callback(callback_data):
        """Call when configuration is submitted."""
        if CONF_INCLUDE not in callback_data:
            configurator.notify_errors(
                request_id, "Functionality mandatory.")
            return False

        callback_data[CONF_INCLUDE] = callback_data[CONF_INCLUDE].split()
        callback_data[CONF_HOST] = host

        if CONF_NAME not in callback_data:
            callback_data[CONF_NAME] = name

        try:
            device_config = DEVICE_SCHEMA(callback_data)
        except vol.Invalid:
            configurator.notify_errors(
                request_id, "Bad input, please check spelling.")
            return False

        if setup_device(hass, config, device_config):
            del device_config['events']
            del device_config['signal']
            config_file = load_json(hass.config.path(CONFIG_FILE))
            config_file[serialnumber] = dict(device_config)
            save_json(hass.config.path(CONFIG_FILE), config_file)
            configurator.request_done(request_id)
        else:
            configurator.notify_errors(
                request_id, "Failed to register, please try again.")
            return False
Ejemplo n.º 29
0
    async def post(self, request):
        """Handle the POST request for device identification."""
        try:
            data = await request.json()
        except ValueError:
            return self.json_message("Invalid JSON", HTTP_BAD_REQUEST)

        hass = request.app['hass']

        # Commented for now while iOS app is getting frequent updates
        # try:
        #     data = IDENTIFY_SCHEMA(req_data)
        # except vol.Invalid as ex:
        #     return self.json_message(
        #         vol.humanize.humanize_error(request.json, ex),
        #         HTTP_BAD_REQUEST)

        data[ATTR_LAST_SEEN_AT] = datetime.datetime.now().isoformat()

        name = data.get(ATTR_DEVICE_ID)

        hass.data[DOMAIN][ATTR_DEVICES][name] = data

        try:
            save_json(self._config_path, hass.data[DOMAIN])
        except HomeAssistantError:
            return self.json_message("Error saving device.",
                                     HTTP_INTERNAL_SERVER_ERROR)

        return self.json({"status": "registered"})
Ejemplo n.º 30
0
 def gpmdp_configuration_callback(callback_data):
     """Handle configuration changes."""
     while True:
         from websocket import _exceptions
         try:
             msg = json.loads(websocket.recv())
         except _exceptions.WebSocketConnectionClosedException:
             continue
         if msg['channel'] != 'connect':
             continue
         if msg['payload'] != "CODE_REQUIRED":
             continue
         pin = callback_data.get('pin')
         websocket.send(json.dumps({'namespace': 'connect',
                                    'method': 'connect',
                                    'arguments': ['Home Assistant', pin]}))
         tmpmsg = json.loads(websocket.recv())
         if tmpmsg['channel'] == 'time':
             _LOGGER.error("Error setting up GPMDP. Please pause "
                           "the desktop player and try again")
             break
         code = tmpmsg['payload']
         if code == 'CODE_REQUIRED':
             continue
         setup_gpmdp(hass, config, code,
                     add_devices_callback)
         save_json(hass.config.path(GPMDP_CONFIG_FILE), {"CODE": code})
         websocket.send(json.dumps({'namespace': 'connect',
                                    'method': 'connect',
                                    'arguments': ['Home Assistant', code]}))
         websocket.close()
         break
Ejemplo n.º 31
0
 def gpmdp_configuration_callback(callback_data):
     """Handle configuration changes."""
     while True:
         from websocket import _exceptions
         try:
             msg = json.loads(websocket.recv())
         except _exceptions.WebSocketConnectionClosedException:
             continue
         if msg['channel'] != 'connect':
             continue
         if msg['payload'] != "CODE_REQUIRED":
             continue
         pin = callback_data.get('pin')
         websocket.send(json.dumps({'namespace': 'connect',
                                    'method': 'connect',
                                    'arguments': ['Home Assistant', pin]}))
         tmpmsg = json.loads(websocket.recv())
         if tmpmsg['channel'] == 'time':
             _LOGGER.error("Error setting up GPMDP. Please pause "
                           "the desktop player and try again")
             break
         code = tmpmsg['payload']
         if code == 'CODE_REQUIRED':
             continue
         setup_gpmdp(hass, config, code,
                     add_entities_callback)
         save_json(hass.config.path(GPMDP_CONFIG_FILE), {"CODE": code})
         websocket.send(json.dumps({'namespace': 'connect',
                                    'method': 'connect',
                                    'arguments': ['Home Assistant', code]}))
         websocket.close()
         break
Ejemplo n.º 32
0
 def success():
     """Set up was successful."""
     conf = load_json(hass.config.path(CONFIG_FILE))
     conf[host] = {'identity': identity,
                   'key': key}
     save_json(hass.config.path(CONFIG_FILE), conf)
     configurator.request_done(instance)
Ejemplo n.º 33
0
    def get(self, request):
        """Finish OAuth callback request."""
        from oauthlib.oauth2.rfc6749.errors import MismatchingStateError
        from oauthlib.oauth2.rfc6749.errors import MissingTokenError

        hass = request.app['hass']
        data = request.query

        response_message = """Fitbit has been successfully authorized!
        You can close this window now!"""

        result = None
        if data.get('code') is not None:
            redirect_uri = '{}{}'.format(
                hass.config.api.base_url, FITBIT_AUTH_CALLBACK_PATH)

            try:
                result = self.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!
                """

        if result is None:
            _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)

        if result:
            config_contents = {
                ATTR_ACCESS_TOKEN: result.get('access_token'),
                ATTR_REFRESH_TOKEN: result.get('refresh_token'),
                ATTR_CLIENT_ID: self.oauth.client_id,
                ATTR_CLIENT_SECRET: self.oauth.client_secret,
                ATTR_LAST_SAVED_AT: int(time.time())
            }
        save_json(hass.config.path(FITBIT_CONFIG_FILE), config_contents)

        hass.async_add_job(setup_platform, hass, self.config,
                           self.add_entities)

        return html_response
Ejemplo n.º 34
0
 def success():
     """Set up was successful."""
     conf = load_json(hass.config.path(CONFIG_FILE))
     conf[host] = {'api_key': api_key}
     save_json(hass.config.path(CONFIG_FILE), conf)
     req_config = _CONFIGURING.pop(host)
     hass.async_add_job(configurator.request_done, req_config)
Ejemplo n.º 35
0
 def test_overwrite_and_reload(self):
     """Test that we can overwrite an existing file and read back."""
     fname = self._path_for("test3")
     save_json(fname, TEST_JSON_A)
     save_json(fname, TEST_JSON_B)
     data = load_json(fname)
     self.assertEqual(data, TEST_JSON_B)
Ejemplo n.º 36
0
    def get(self, request):
        """Finish OAuth callback request."""
        from aiohttp import web

        hass = request.app['hass']
        data = request.query

        response_message = """Wink has been successfully authorized!
         You can close this window now! For the best results you should reboot
         HomeAssistant"""
        html_response = """<html><head><title>Wink Auth</title></head>
                <body><h1>{}</h1></body></html>"""

        if data.get('code') is not None:
            response = self.request_token(
                data.get('code'), self.config_file['client_secret'])

            config_contents = {
                ATTR_ACCESS_TOKEN: response['access_token'],
                ATTR_REFRESH_TOKEN: response['refresh_token'],
                ATTR_CLIENT_ID: self.config_file['client_id'],
                ATTR_CLIENT_SECRET: self.config_file['client_secret']
            }
            save_json(hass.config.path(WINK_CONFIG_FILE), config_contents)

            hass.async_add_job(setup, hass, self.config)

            return web.Response(text=html_response.format(response_message),
                                content_type='text/html')

        error_msg = "No code returned from Wink API"
        _LOGGER.error(error_msg)
        return web.Response(text=html_response.format(error_msg),
                            content_type='text/html')
Ejemplo n.º 37
0
    def update(self):
        """Get the latest data from the Fitbit API and update the states."""
        if self.resource_type == "devices/battery" and self.extra:
            registered_devs = self.client.get_devices()
            device_id = self.extra.get("id")
            self.extra = list(
                filter(lambda device: device.get("id") == device_id, registered_devs)
            )[0]
            self._state = self.extra.get("battery")

        else:
            container = self.resource_type.replace("/", "-")
            response = self.client.time_series(self.resource_type, period="7d")
            raw_state = response[container][-1].get("value")
            if self.resource_type == "activities/distance":
                self._state = format(float(raw_state), ".2f")
            elif self.resource_type == "activities/tracker/distance":
                self._state = format(float(raw_state), ".2f")
            elif self.resource_type == "body/bmi":
                self._state = format(float(raw_state), ".1f")
            elif self.resource_type == "body/fat":
                self._state = format(float(raw_state), ".1f")
            elif self.resource_type == "body/weight":
                self._state = format(float(raw_state), ".1f")
            elif self.resource_type == "sleep/startTime":
                if raw_state == "":
                    self._state = "-"
                elif self.clock_format == "12H":
                    hours, minutes = raw_state.split(":")
                    hours, minutes = int(hours), int(minutes)
                    setting = "AM"
                    if hours > 12:
                        setting = "PM"
                        hours -= 12
                    elif hours == 0:
                        hours = 12
                    self._state = f"{hours}:{minutes:02d} {setting}"
                else:
                    self._state = raw_state
            else:
                if self.is_metric:
                    self._state = raw_state
                else:
                    try:
                        self._state = f"{int(raw_state):,}"
                    except TypeError:
                        self._state = raw_state

        if self.resource_type == "activities/heart":
            self._state = response[container][-1].get("value").get("restingHeartRate")

        token = self.client.client.session.token
        config_contents = {
            ATTR_ACCESS_TOKEN: token.get("access_token"),
            ATTR_REFRESH_TOKEN: token.get("refresh_token"),
            CONF_CLIENT_ID: self.client.client.client_id,
            CONF_CLIENT_SECRET: self.client.client.client_secret,
            ATTR_LAST_SAVED_AT: int(time.time()),
        }
        save_json(self.config_path, config_contents)
Ejemplo n.º 38
0
def test_save_bad_data():
    """Test error from trying to save unserialisable data."""
    with pytest.raises(SerializationError) as excinfo:
        save_json("test4", {"hello": set()})

    assert (
        "Failed to serialize to JSON: test4. Bad data at $.hello=set()(<class 'set'>"
        in str(excinfo.value))
 def update_state(self):
     cur_time = time.time()
     json_data = {"last_time": cur_time, "last_state": self.state}
     if self._energy_updates is not None:
         for real_type in self._energy_updates:
             json_data[real_type] = self._energy_updates[real_type](
                 cur_time, self.state)
         save_json(self.hass.config.path(self._record_file), json_data)
Ejemplo n.º 40
0
 def test_save_and_load_private(self):
     """Test we can load private files and that they are protected."""
     fname = self._path_for("test2")
     save_json(fname, TEST_JSON_A, private=True)
     data = load_json(fname)
     self.assertEqual(data, TEST_JSON_A)
     stats = os.stat(fname)
     self.assertEqual(stats.st_mode & 0o77, 0)
Ejemplo n.º 41
0
        def config_callback(fields):
            configurator.request_done(self._configuring)
            self._configuring = None

            _LOGGER.debug(fields)
            if fields.get('agree') == 'ok':
                self.config_done(data)
                save_json(self.hass.config.path('.' + self.name), self.conf)
Ejemplo n.º 42
0
def test_save_and_load_private():
    """Test we can load private files and that they are protected."""
    fname = _path_for("test2")
    save_json(fname, TEST_JSON_A, private=True)
    data = load_json(fname)
    assert data == TEST_JSON_A
    stats = os.stat(fname)
    assert stats.st_mode & 0o77 == 0
Ejemplo n.º 43
0
    def update(self):
        """Get the latest data from the Fitbit API and update the states."""
        if self.resource_type == 'devices/battery' and self.extra:
            self._state = self.extra.get('battery')
        else:
            container = self.resource_type.replace("/", "-")
            response = self.client.time_series(self.resource_type, period='7d')
            raw_state = response[container][-1].get('value')
            if self.resource_type == 'activities/distance':
                self._state = format(float(raw_state), '.2f')
            elif self.resource_type == 'activities/tracker/distance':
                self._state = format(float(raw_state), '.2f')
            elif self.resource_type == 'body/bmi':
                self._state = format(float(raw_state), '.1f')
            elif self.resource_type == 'body/fat':
                self._state = format(float(raw_state), '.1f')
            elif self.resource_type == 'body/weight':
                self._state = format(float(raw_state), '.1f')
            elif self.resource_type == 'sleep/startTime':
                if raw_state == '':
                    self._state = '-'
                elif self.clock_format == '12H':
                    hours, minutes = raw_state.split(':')
                    hours, minutes = int(hours), int(minutes)
                    setting = 'AM'
                    if hours > 12:
                        setting = 'PM'
                        hours -= 12
                    elif hours == 0:
                        hours = 12
                    self._state = '{}:{:02d} {}'.format(hours, minutes,
                                                        setting)
                else:
                    self._state = raw_state
            else:
                if self.is_metric:
                    self._state = raw_state
                else:
                    try:
                        self._state = '{0:,}'.format(int(raw_state))
                    except TypeError:
                        self._state = raw_state

        if self.resource_type == 'activities/heart':
            self._state = response[container][-1]. \
                    get('value').get('restingHeartRate')

        token = self.client.client.session.token
        config_contents = {
            ATTR_ACCESS_TOKEN: token.get('access_token'),
            ATTR_REFRESH_TOKEN: token.get('refresh_token'),
            ATTR_CLIENT_ID: self.client.client.client_id,
            ATTR_CLIENT_SECRET: self.client.client.client_secret,
            ATTR_LAST_SAVED_AT: int(time.time())
        }
        save_json(self.config_path, config_contents)
Ejemplo n.º 44
0
    def save_games(self, games):
        """Save games to file."""
        g_file = self._games_filename
        try:
            save_json(g_file, games)
        except OSError as error:
            _LOGGER.error("Could not save game list, %s", error)

        # Retry loading file
        if games is None:
            self.load_games()
Ejemplo n.º 45
0
    def test_custom_encoder(self):
        """Test serializing with a custom encoder."""
        class MockJSONEncoder(JSONEncoder):
            """Mock JSON encoder."""

            def default(self, o):
                """Mock JSON encode method."""
                return "9"

        fname = self._path_for("test6")
        save_json(fname, Mock(), encoder=MockJSONEncoder)
        data = load_json(fname)
        self.assertEqual(data, "9")
Ejemplo n.º 46
0
def setup_light(device_id, name, insteonhub, hass, add_devices_callback):
    """Set up the light."""
    if device_id in _CONFIGURING:
        request_id = _CONFIGURING.pop(device_id)
        configurator = hass.components.configurator
        configurator.request_done(request_id)
        _LOGGER.debug("Device configuration done")

    conf_lights = load_json(hass.config.path(INSTEON_LOCAL_LIGHTS_CONF))
    if device_id not in conf_lights:
        conf_lights[device_id] = name

    save_json(hass.config.path(INSTEON_LOCAL_LIGHTS_CONF), conf_lights)

    device = insteonhub.dimmer(device_id)
    add_devices_callback([InsteonLocalDimmerDevice(device, name)])
Ejemplo n.º 47
0
def setup_fan(device_id, name, insteonhub, hass, add_devices_callback):
    """Set up the fan."""
    if device_id in _CONFIGURING:
        request_id = _CONFIGURING.pop(device_id)
        configurator = hass.components.configurator
        configurator.request_done(request_id)
        _LOGGER.info("Device configuration done!")

    conf_fans = load_json(hass.config.path(INSTEON_LOCAL_FANS_CONF))
    if device_id not in conf_fans:
        conf_fans[device_id] = name

    save_json(hass.config.path(INSTEON_LOCAL_FANS_CONF), conf_fans)

    device = insteonhub.fan(device_id)
    add_devices_callback([InsteonLocalFanDevice(device, name)])
Ejemplo n.º 48
0
    def delete(self, request):
        """Delete a registration."""
        try:
            data = yield from request.json()
        except ValueError:
            return self.json_message('Invalid JSON', HTTP_BAD_REQUEST)

        subscription = data.get(ATTR_SUBSCRIPTION)

        found = None

        for key, registration in self.registrations.items():
            if registration.get(ATTR_SUBSCRIPTION) == subscription:
                found = key
                break

        if not found:
            # If not found, unregistering was already done. Return 200
            return self.json_message('Registration not found.')

        reg = self.registrations.pop(found)

        if not save_json(self.json_path, self.registrations):
            self.registrations[found] = reg
            return self.json_message(
                'Error saving registration.', HTTP_INTERNAL_SERVER_ERROR)

        return self.json_message('Push notification subscriber unregistered.')
Ejemplo n.º 49
0
def setup_switch(device_id, name, insteonhub, hass, add_devices_callback):
    """Set up the switch."""
    if device_id in _CONFIGURING:
        request_id = _CONFIGURING.pop(device_id)
        configurator = hass.components.configurator
        configurator.request_done(request_id)
        _LOGGER.info("Device configuration done")

    conf_switch = load_json(hass.config.path(INSTEON_LOCAL_SWITCH_CONF))
    if device_id not in conf_switch:
        conf_switch[device_id] = name

    save_json(hass.config.path(INSTEON_LOCAL_SWITCH_CONF), conf_switch)

    device = insteonhub.switch(device_id)
    add_devices_callback([InsteonLocalSwitchDevice(device, name)])
Ejemplo n.º 50
0
def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the Nanoleaf light."""
    from pynanoleaf import Nanoleaf, Unavailable
    if DATA_NANOLEAF not in hass.data:
        hass.data[DATA_NANOLEAF] = dict()

    token = ''
    if discovery_info is not None:
        host = discovery_info['host']
        name = discovery_info['hostname']
        # if device already exists via config, skip discovery setup
        if host in hass.data[DATA_NANOLEAF]:
            return
        _LOGGER.info("Discovered a new Nanoleaf: %s", discovery_info)
        conf = load_json(hass.config.path(CONFIG_FILE))
        if conf.get(host, {}).get('token'):
            token = conf[host]['token']
    else:
        host = config[CONF_HOST]
        name = config[CONF_NAME]
        token = config[CONF_TOKEN]

    nanoleaf_light = Nanoleaf(host)

    if not token:
        token = nanoleaf_light.request_token()
        if not token:
            _LOGGER.error("Could not generate the auth token, did you press "
                          "and hold the power button on %s"
                          "for 5-7 seconds?", name)
            return
        conf = load_json(hass.config.path(CONFIG_FILE))
        conf[host] = {'token': token}
        save_json(hass.config.path(CONFIG_FILE), conf)

    nanoleaf_light.token = token

    try:
        nanoleaf_light.available
    except Unavailable:
        _LOGGER.error(
            "Could not connect to Nanoleaf Light: %s on %s", name, host)
        return

    hass.data[DATA_NANOLEAF][host] = nanoleaf_light
    add_entities([NanoleafLight(nanoleaf_light, name)], True)
Ejemplo n.º 51
0
    def _push_message(self, payload, **kwargs):
        """Send the message."""
        import jwt
        from pywebpush import WebPusher, webpush

        timestamp = int(time.time())

        payload['timestamp'] = (timestamp*1000)  # Javascript ms since epoch

        targets = kwargs.get(ATTR_TARGET)

        if not targets:
            targets = self.registrations.keys()

        for target in list(targets):
            info = self.registrations.get(target)
            if info is None:
                _LOGGER.error("%s is not a valid HTML5 push notification"
                              " target", target)
                continue

            jwt_exp = (datetime.datetime.fromtimestamp(timestamp) +
                       datetime.timedelta(days=JWT_VALID_DAYS))
            jwt_secret = info[ATTR_SUBSCRIPTION][ATTR_KEYS][ATTR_AUTH]
            jwt_claims = {'exp': jwt_exp, 'nbf': timestamp,
                          'iat': timestamp, ATTR_TARGET: target,
                          ATTR_TAG: payload[ATTR_TAG]}
            jwt_token = jwt.encode(jwt_claims, jwt_secret).decode('utf-8')
            payload[ATTR_DATA][ATTR_JWT] = jwt_token

            if self._vapid_prv and self._vapid_claims:
                response = webpush(
                    info[ATTR_SUBSCRIPTION],
                    json.dumps(payload),
                    vapid_private_key=self._vapid_prv,
                    vapid_claims=self._vapid_claims
                )
            else:
                # Only pass the gcm key if we're actually using GCM
                # If we don't, notifications break on FireFox
                gcm_key = self._gcm_key \
                    if 'googleapis.com' \
                    in info[ATTR_SUBSCRIPTION][ATTR_ENDPOINT] \
                    else None
                response = WebPusher(info[ATTR_SUBSCRIPTION]).send(
                    json.dumps(payload), gcm_key=gcm_key, ttl='86400'
                )

            if response.status_code == 410:
                _LOGGER.info("Notification channel has expired")
                reg = self.registrations.pop(target)
                if not save_json(self.registrations_json_path,
                                 self.registrations):
                    self.registrations[target] = reg
                    _LOGGER.error("Error saving registration")
                else:
                    _LOGGER.info("Configuration saved")
Ejemplo n.º 52
0
    def wink_configuration_callback(callback_data):
        """Handle configuration updates."""
        _config_path = hass.config.path(WINK_CONFIG_FILE)
        if not os.path.isfile(_config_path):
            setup(hass, config)
            return

        client_id = callback_data.get('client_id').strip()
        client_secret = callback_data.get('client_secret').strip()
        if None not in (client_id, client_secret):
            save_json(_config_path,
                      {ATTR_CLIENT_ID: client_id,
                       ATTR_CLIENT_SECRET: client_secret})
            setup(hass, config)
            return
        error_msg = "Your input was invalid. Please try again."
        _configurator = hass.data[DOMAIN]['configuring'][DOMAIN]
        configurator.notify_errors(_configurator, error_msg)
Ejemplo n.º 53
0
    def entity_id_to_number(self, entity_id):
        """Get a unique number for the entity id."""
        if self.type == TYPE_ALEXA:
            return entity_id

        if self.numbers is None:
            self.numbers = _load_json(self.hass.config.path(NUMBERS_FILE))

        # Google Home
        for number, ent_id in self.numbers.items():
            if entity_id == ent_id:
                return number

        number = '1'
        if self.numbers:
            number = str(max(int(k) for k in self.numbers) + 1)
        self.numbers[number] = entity_id
        save_json(self.hass.config.path(NUMBERS_FILE), self.numbers)
        return number
Ejemplo n.º 54
0
def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the Nanoleaf Aurora device."""
    import nanoleaf
    import nanoleaf.setup
    if DATA_NANOLEAF_AURORA not in hass.data:
        hass.data[DATA_NANOLEAF_AURORA] = dict()

    token = ''
    if discovery_info is not None:
        host = discovery_info['host']
        name = discovery_info['hostname']
        # if device already exists via config, skip discovery setup
        if host in hass.data[DATA_NANOLEAF_AURORA]:
            return
        _LOGGER.info("Discovered a new Aurora: %s", discovery_info)
        conf = load_json(hass.config.path(CONFIG_FILE))
        if conf.get(host, {}).get('token'):
            token = conf[host]['token']
    else:
        host = config[CONF_HOST]
        name = config[CONF_NAME]
        token = config[CONF_TOKEN]

    if not token:
        token = nanoleaf.setup.generate_auth_token(host)
        if not token:
            _LOGGER.error("Could not generate the auth token, did you press "
                          "and hold the power button on %s"
                          "for 5-7 seconds?", name)
            return
        conf = load_json(hass.config.path(CONFIG_FILE))
        conf[host] = {'token': token}
        save_json(hass.config.path(CONFIG_FILE), conf)

    aurora_light = nanoleaf.Aurora(host, token)

    if aurora_light.on is None:
        _LOGGER.error(
            "Could not connect to Nanoleaf Aurora: %s on %s", name, host)
        return

    hass.data[DATA_NANOLEAF_AURORA][host] = aurora_light
    add_devices([AuroraLight(aurora_light, name)], True)
Ejemplo n.º 55
0
        def configuration_callback(callback_data):
            """Handle the submitted configuration."""
            session.authorize()
            res = setup(hass, config, session)
            if not res:
                configurator.notify_errors(
                    hass.data[KEY_CONFIG].get(data_key),
                    'Unable to connect.')
                return

            conf.update(
                {host: {CONF_HOST: host,
                        CONF_TOKEN: session.access_token}} if host else
                {DOMAIN: {CONF_TOKEN: session.access_token,
                          CONF_TOKEN_SECRET: session.access_token_secret}})
            save_json(config_filename, conf)
            # Close all open configurators: for now, we only support one
            # tellstick device, and configuration via either cloud service
            # or via local API, not both at the same time
            for instance in hass.data[KEY_CONFIG].values():
                configurator.request_done(instance)
Ejemplo n.º 56
0
def setup(hass, config):
    """Set up the Ecobee.

    Will automatically load thermostat and sensor components to support
    devices discovered on the network.
    """
    global NETWORK

    if 'ecobee' in _CONFIGURING:
        return

    # Create ecobee.conf if it doesn't exist
    if not os.path.isfile(hass.config.path(ECOBEE_CONFIG_FILE)):
        jsonconfig = {"API_KEY": config[DOMAIN].get(CONF_API_KEY)}
        save_json(hass.config.path(ECOBEE_CONFIG_FILE), jsonconfig)

    NETWORK = EcobeeData(hass.config.path(ECOBEE_CONFIG_FILE))

    setup_ecobee(hass, NETWORK.ecobee, config)

    return True
Ejemplo n.º 57
0
    def post(self, request):
        """Accept the POST request for push registrations from a browser."""
        try:
            data = yield from request.json()
        except ValueError:
            return self.json_message('Invalid JSON', HTTP_BAD_REQUEST)

        try:
            data = REGISTER_SCHEMA(data)
        except vol.Invalid as ex:
            return self.json_message(
                humanize_error(data, ex), HTTP_BAD_REQUEST)

        name = ensure_unique_string('unnamed device', self.registrations)

        self.registrations[name] = data

        if not save_json(self.json_path, self.registrations):
            return self.json_message(
                'Error saving registration.', HTTP_INTERNAL_SERVER_ERROR)

        return self.json_message('Push notification subscriber registered.')
Ejemplo n.º 58
0
    def __init__(self, hass, config, see):
        """Initialize."""
        from pytile import Client

        _LOGGER.debug('Received configuration data: %s', config)

        # Load the client UUID (if it exists):
        config_data = load_json(hass.config.path(CLIENT_UUID_CONFIG_FILE))
        if config_data:
            _LOGGER.debug('Using existing client UUID')
            self._client = Client(
                config[CONF_USERNAME],
                config[CONF_PASSWORD],
                config_data['client_uuid'])
        else:
            _LOGGER.debug('Generating new client UUID')
            self._client = Client(
                config[CONF_USERNAME],
                config[CONF_PASSWORD])

            if not save_json(
                    hass.config.path(CLIENT_UUID_CONFIG_FILE),
                    {'client_uuid': self._client.client_uuid}):
                _LOGGER.error("Failed to save configuration file")

        _LOGGER.debug('Client UUID: %s', self._client.client_uuid)
        _LOGGER.debug('User UUID: %s', self._client.user_uuid)

        self._types = config.get(CONF_MONITORED_VARIABLES)

        self.devices = {}
        self.see = see

        track_utc_time_change(
            hass, self._update_info, second=range(0, 60, 30))

        self._update_info()
Ejemplo n.º 59
0
def setup_plexserver(
        host, token, has_ssl, verify_ssl, hass, config, add_devices_callback):
    """Set up a plexserver based on host parameter."""
    import plexapi.server
    import plexapi.exceptions

    cert_session = None
    http_prefix = 'https' if has_ssl else 'http'
    if has_ssl and (verify_ssl is False):
        _LOGGER.info("Ignoring SSL verification")
        cert_session = requests.Session()
        cert_session.verify = False
    try:
        plexserver = plexapi.server.PlexServer(
            '%s://%s' % (http_prefix, host),
            token, cert_session
        )
        _LOGGER.info("Discovery configuration done (no token needed)")
    except (plexapi.exceptions.BadRequest, plexapi.exceptions.Unauthorized,
            plexapi.exceptions.NotFound) as error:
        _LOGGER.info(error)
        # No token or wrong token
        request_configuration(host, hass, 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 = hass.components.configurator
        configurator.request_done(request_id)
        _LOGGER.info("Discovery configuration done")

    # Save config
    save_json(
        hass.config.path(PLEX_CONFIG_FILE), {host: {
            'token': token,
            'ssl': has_ssl,
            'verify': verify_ssl,
        }})

    _LOGGER.info('Connected to: %s://%s', http_prefix, 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 requests.exceptions.RequestException as ex:
            _LOGGER.error("Could not connect to plex server at http://%s (%s)",
                          host, ex)
            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(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].refresh(device, None)

        # add devices with a session and no client (ex. PlexConnect Apple TV's)
        if config.get(CONF_INCLUDE_NON_CLIENTS):
            for machine_identifier, session in plex_sessions.items():
                if (machine_identifier not in plex_clients
                        and machine_identifier is not None):
                    new_client = PlexClient(config, None, session,
                                            plex_sessions, update_devices,
                                            update_sessions)
                    plex_clients[machine_identifier] = new_client
                    new_plex_clients.append(new_client)
                else:
                    plex_clients[machine_identifier].refresh(None, session)

        for machine_identifier, client in plex_clients.items():
            # force devices to idle that do not have a valid session
            if client.session is None:
                client.force_idle()

        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
        except requests.exceptions.RequestException as ex:
            _LOGGER.error("Could not connect to plex server at http://%s (%s)",
                          host, ex)
            return

        plex_sessions.clear()
        for session in sessions:
            for player in session.players:
                plex_sessions[player.machineIdentifier] = session

    update_sessions()
    update_devices()