async def __open( self, url: Text, method: Text = "get", headers=None, cookies=None, data=None, baseurl: Text = "", ) -> None: """Open url.""" headers = headers or {} cookies = cookies or {} if not baseurl: baseurl = self.baseurl url: URL = URL(baseurl + url) _LOGGER.debug("%s: %s %s", method, url, data) try: if data: resp = await getattr(self.websession, method)( str(url), data=data, headers=headers, cookies=cookies ) else: resp = await getattr(self.websession, method)( str(url), headers=headers, cookies=cookies ) data = resp.json() _LOGGER.debug("%s: %s", resp.status_code, json.dumps(data)) if resp.status_code > 299: if resp.status_code == 401: if data and data.get("error") == "invalid_token": raise TeslaException(resp.status_code, "invalid_token") elif resp.status_code == 408: raise TeslaException(resp.status_code, "vehicle_unavailable") raise TeslaException(resp.status_code) if data.get("error"): # known errors: # 'vehicle unavailable: {:error=>"vehicle unavailable:"}', # "upstream_timeout", "vehicle is curently in service" _LOGGER.debug( "Raising exception for : %s", f'{data.get("error")}:{data.get("error_description")}', ) raise TeslaException( f'{data.get("error")}:{data.get("error_description")}' ) except httpx.HTTPStatusError as exception_: raise TeslaException(exception_.request.status_code) from exception_ return data
async def _process_messages() -> None: """Start Async WebSocket Listener.""" nonlocal last_message_time nonlocal disconnected async for msg in self.websocket: _LOGGER.debug("msg: %s", msg) if msg.type == aiohttp.WSMsgType.BINARY: msg_json = json.loads(msg.data) if msg_json["msg_type"] == "control:hello": _LOGGER.debug( "%s:Succesfully connected to websocket %s", vin[-5:], self.websocket_url, ) if msg_json["msg_type"] == "data:update": last_message_time = time.time() if (msg_json["msg_type"] == "data:error" and msg_json["value"] == "Can't validate token. "): raise TeslaException( "Can't validate token for websocket connection.") if (msg_json["msg_type"] == "data:error" and msg_json["value"] == "disconnected"): if kwargs.get("on_disconnect"): kwargs.get("on_disconnect")(msg_json) disconnected = True if kwargs.get("on_message"): kwargs.get("on_message")(msg_json) elif msg.type == aiohttp.WSMsgType.ERROR: _LOGGER.debug("WSMsgType error") break
def __open(self, url, headers=None, data=None, baseurl=""): """Use raw urlopen command.""" headers = headers or {} if not baseurl: baseurl = self.baseurl req = Request("%s%s" % (baseurl, url), headers=headers) _LOGGER.debug(url) try: req.data = urlencode(data).encode('utf-8') except TypeError: pass opener = build_opener() try: resp = opener.open(req) charset = resp.info().get('charset', 'utf-8') data = json.loads(resp.read().decode(charset)) opener.close() _LOGGER.debug(json.dumps(data)) return data except HTTPError as exception_: if exception_.code == 408: _LOGGER.debug("%s", exception_) return False raise TeslaException(exception_.code)
async def __open( self, url: Text, method: Text = "get", headers=None, data=None, baseurl: Text = "", ) -> None: """Open url.""" headers = headers or {} if not baseurl: baseurl = self.baseurl url: URL = URL(baseurl + url) _LOGGER.debug("%s: %s %s", method, url, data) try: resp = await getattr(self.websession, method)(url, headers=headers, data=data) data = await resp.json() _LOGGER.debug("%s: %s", resp.status, json.dumps(data)) if resp.status > 299: if resp.status == 401: if data and data.get("error") == "invalid_token": raise TeslaException(resp.status, "invalid_token") elif resp.status == 408: raise TeslaException(resp.status, "vehicle_unavailable") raise TeslaException(resp.status) if data.get("error"): # known errors: # 'vehicle unavailable: {:error=>"vehicle unavailable:"}', # "upstream_timeout", "vehicle is curently in service" _LOGGER.debug( "Raising exception for : %s", f'{data.get("error")}:{data.get("error_description")}', ) raise TeslaException( f'{data.get("error")}:{data.get("error_description")}') except aiohttp.ClientResponseError as exception_: raise TeslaException(exception_.status) return data
async def test_form_invalid_auth(hass): """Test we handle invalid auth.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "custom_components.tesla_custom.config_flow.TeslaAPI.connect", side_effect=TeslaException(401), ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_USERNAME: TEST_USERNAME, CONF_TOKEN: TEST_TOKEN}, ) assert result2["type"] == "form" assert result2["errors"] == {"base": "invalid_auth"}
async def test_form_cannot_connect(hass): """Test we handle cannot connect error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "custom_components.tesla_custom.config_flow.TeslaAPI.connect", side_effect=TeslaException(code=HTTPStatus.NOT_FOUND), ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_TOKEN: TEST_TOKEN, CONF_USERNAME: TEST_USERNAME}, ) assert result2["type"] == "form" assert result2["errors"] == {"base": "cannot_connect"}
async def test_form_cannot_connect(opp): """Test we handle cannot connect error.""" result = await opp.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}) with patch( "openpeerpower.components.tesla.config_flow.TeslaAPI.connect", side_effect=TeslaException(code=HTTP_NOT_FOUND), ): result2 = await opp.config_entries.flow.async_configure( result["flow_id"], { CONF_PASSWORD: TEST_PASSWORD, CONF_USERNAME: TEST_USERNAME }, ) assert result2["type"] == "form" assert result2["errors"] == {"base": "cannot_connect"}
async def wake_up(wrapped, instance, args, kwargs) -> Callable: # pylint: disable=protected-access """Wrap a API func so it will attempt to wake the vehicle if asleep. The command wrapped is run once if the car_id was last reported online. If wrapped detects the car_id is offline, five attempts will be made to wake the vehicle to retry the command. Raises RetryLimitError: The wake_up has exceeded the 5 attempts. TeslaException: Tesla connection errors Returns Callable: Wrapped function that will wake_up """ def valid_result(result): """Check if TeslaAPI result succesful. Parameters ---------- result : tesla API result This is the result of a Tesla Rest API call. Returns ------- bool Tesla API failure can be checked in a dict with a bool in ['response']['result'], a bool, or None or ['response']['reason'] == 'could_not_wake_buses' Returns true when a failure state not detected. """ try: return (result is not None and result is not False and (result is True or (isinstance(result, dict) and isinstance(result["response"], dict) and (result["response"].get("result") is True or result["response"].get("reason") != "could_not_wake_buses")))) except TypeError as exception: _LOGGER.error("Result: %s, %s", result, exception) return False retries = 0 sleep_delay = 2 car_id = args[0] is_wake_command = len(args) >= 2 and args[1] == "wake_up" result = None if instance.car_online.get(instance._id_to_vin(car_id)) or is_wake_command: try: result = await wrapped(*args, **kwargs) except TeslaException as ex: _LOGGER.debug("Exception: %s\n%s(%s %s)", str(ex), wrapped.__name__, args, kwargs) raise if valid_result(result) or is_wake_command: return result _LOGGER.debug( "wake_up needed for %s -> %s \n" "Info: args:%s, kwargs:%s, " "VIN:%s, car_online:%s", wrapped.__name__, result, args, kwargs, instance._id_to_vin(car_id)[-5:] if car_id else None, instance.car_online, ) instance.car_online[instance._id_to_vin(car_id)] = False while (kwargs.get("wake_if_asleep") and # Check online state (car_id is None or (not instance._id_to_vin(car_id) or not instance.car_online.get(instance._id_to_vin(car_id))))): _LOGGER.debug("Attempting to wake up") result = await instance._wake_up(car_id) _LOGGER.debug( "%s(%s): Wake Attempt(%s): %s", wrapped.__name__, instance._id_to_vin(car_id)[-5:], retries, result, ) if not result: if retries < 5: await asyncio.sleep(15 + sleep_delay**(retries + 2)) retries += 1 continue instance.car_online[instance._id_to_vin(car_id)] = False raise RetryLimitError("Reached retry limit; aborting wake up") break instance.car_online[instance._id_to_vin(car_id)] = True # retry function _LOGGER.debug("Retrying %s(%s %s)", wrapped.__name__, args, kwargs) try: result = await wrapped(*args, **kwargs) _LOGGER.debug( "Retry after wake up succeeded: %s", "True" if valid_result(result) else result, ) except TeslaException as ex: _LOGGER.debug("Exception: %s\n%s(%s %s)", str(ex), wrapped.__name__, args, kwargs) raise if valid_result(result): return result raise TeslaException("could_not_wake_buses")