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
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)
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')
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
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)
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"})
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)])
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])
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)
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)
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"})
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)
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
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)
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")
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=".")
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)
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)
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)])
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)
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)])
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)
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"})
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)
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
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
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"})
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
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
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)
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
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)
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)
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')
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)
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)
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)
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)
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
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)
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()
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")
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)])
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)])
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.')
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)])
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)
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")
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)
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
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)
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)
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
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.')
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()
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()