Skip to content

Commit

Permalink
Merge pull request #205 from sockless-coding/next-2
Browse files Browse the repository at this point in the history
Async and Settings Issues
  • Loading branch information
sockless-coding authored Jun 22, 2024
2 parents 674d37f + a407209 commit 2219006
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 31 deletions.
2 changes: 1 addition & 1 deletion custom_components/panasonic_cc/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "panasonic_cc",
"name": "Panasonic Comfort Cloud",
"after_dependencies": ["http"],
"version": "1.0.42",
"version": "1.0.43",
"config_flow": true,
"documentation": "https://github.com/sockless-coding/panasonic_cc/",
"dependencies": [],
Expand Down
9 changes: 5 additions & 4 deletions custom_components/panasonic_cc/panasonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from homeassistant.const import ATTR_TEMPERATURE
from homeassistant.core import HomeAssistant
from homeassistant.components.climate.const import ATTR_HVAC_MODE
from .pcomfortcloud.apiclient import ApiClient

from .const import PRESET_LIST, OPERATION_LIST

Expand All @@ -19,7 +20,7 @@

class PanasonicApiDevice:

def __init__(self, hass: HomeAssistant, api, device, force_outside_sensor, enable_energy_sensor): # noqa: E501
def __init__(self, hass: HomeAssistant, api: ApiClient, device, force_outside_sensor, enable_energy_sensor): # noqa: E501
from .pcomfortcloud import constants
self.hass = hass
self._api = api
Expand Down Expand Up @@ -69,10 +70,10 @@ async def do_update(self):
except:
_LOGGER.debug("Error trying to get device {id} state, probably expired token, trying to update it...".format(**self.device)) # noqa: E501
try:
await self.hass.async_add_executor_job(self._api.login)
await self._api.refresh_token()
data = await self._api.get_device(self.id)
except:
_LOGGER.debug("Failed to renew token for device {id}, giving up for now".format(**self.device)) # noqa: E501
_LOGGER.warning("Failed to renew token for device {id}, giving up for now".format(**self.device)) # noqa: E501
return

if data is None:
Expand Down Expand Up @@ -116,7 +117,7 @@ async def do_update_energy(self):
except:
_LOGGER.debug("Error trying to get device {id} state, probably expired token, trying to update it...".format(**self.device)) # noqa: E501
try:
await self.hass.async_add_executor_job(self._api.login)
await self._api.refresh_token()
data= await self._api.get_device(self.id) # noqa: E501
except:
_LOGGER.debug("Failed to renew token for device {id}, giving up for now".format(**self.device)) # noqa: E501
Expand Down
7 changes: 5 additions & 2 deletions custom_components/panasonic_cc/pcomfortcloud/apiclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ async def start_session(self):
await super().start_session()
await self._get_groups()

async def refresh_token(self):
await super().start_session()

async def _get_groups(self):
self._groups = await self.execute_get(
self._get_group_url(),
Expand Down Expand Up @@ -251,13 +254,13 @@ def _get_group_url(self):
def _get_device_status_url(self, guid):
return '{base_url}/deviceStatus/{guid}'.format(
base_url=constants.BASE_PATH_ACC,
guid=re.sub('(?i)2f', 'f', quote_plus(guid))
guid=re.sub('(?i)\%2f', 'f', quote_plus(guid))
)

def _get_device_status_now_url(self, guid):
return '{base_url}/deviceStatus/now/{guid}'.format(
base_url=constants.BASE_PATH_ACC,
guid=re.sub('(?i)2f', 'f', quote_plus(guid))
guid=re.sub('(?i)\%2f', 'f', quote_plus(guid))
)

def _get_device_status_control_url(self):
Expand Down
4 changes: 1 addition & 3 deletions custom_components/panasonic_cc/pcomfortcloud/ccappversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ class CCAppVersion:
def __init__(self, client: aiohttp.ClientSession, settings: PanasonicSettings) -> None:
self._client = client
self._settings = settings
self._appVersion = settings._version

async def get(self):
if self._settings.is_version_expired:
await self._update()
return self._appVersion
return self._settings._version

async def _update(self):
_LOGGER.debug("Fetching latest app version")
Expand All @@ -26,7 +25,6 @@ async def _update(self):
version = data['files']['comfort-cloud-version']['content']
if version is not None:
_LOGGER.debug(f"Found app version: {version}")
self._appVersion = version
self._settings.version = version
return
except Exception as e:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
def generate_random_string(length):
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length))

def check_response(response: aiohttp.ClientResponse, function_description, expected_status):
async def check_response(response: aiohttp.ClientResponse, function_description, expected_status):

if response.status != expected_status:
response_text = await response.text()
_LOGGER.error("%s: Expected status code %s, received: %s: %s", function_description, expected_status, response.status, response_text)
raise exceptions.ResponseError(
f"({function_description}: Expected status code {expected_status}, received: {response.status}: " +
f"{response.text}"
f"{response_text}"
)

def get_querystring_parameter_from_header_entry_url(response: aiohttp.ClientResponse, header_entry, querystring_parameter):
Expand Down Expand Up @@ -86,7 +88,7 @@ async def refresh_token(self):
"grant_type": "refresh_token"
},
allow_redirects=False)
check_response(response, 'refresh_token', 200)
await check_response(response, 'refresh_token', 200)
token_response = json.loads(await response.text())
self._set_token(token_response, unix_time_token_received)

Expand Down Expand Up @@ -115,7 +117,7 @@ async def _authorize(self, challenge) -> aiohttp.ClientResponse:
"state": state,
},
allow_redirects=False)
check_response(response, 'authorize', 302)
await check_response(response, 'authorize', 302)
return response


Expand All @@ -127,7 +129,7 @@ async def _login(self, authorization_response: aiohttp.ClientResponse, username,
response = await self._client.get(
f"{BASE_PATH_AUTH}/{location}",
allow_redirects=False)
check_response(response, 'authorize_redirect', 200)
await check_response(response, 'authorize_redirect', 200)

# get the "_csrf" cookie
csrf = response.cookies['_csrf']
Expand Down Expand Up @@ -158,7 +160,7 @@ async def _login(self, authorization_response: aiohttp.ClientResponse, username,
"connection": "PanasonicID-Authentication"
},
allow_redirects=False)
check_response(response, 'login', 200)
await check_response(response, 'login', 200)

# -------------------------------------------------------------------
# CALLBACK
Expand All @@ -180,7 +182,7 @@ async def _login(self, authorization_response: aiohttp.ClientResponse, username,
"User-Agent": AUTH_BROWSER_USER_AGENT,
},
allow_redirects=False)
check_response(response, 'login_callback', 302)
await check_response(response, 'login_callback', 302)

# ------------------------------------------------------------------
# FOLLOW REDIRECT
Expand All @@ -191,7 +193,7 @@ async def _login(self, authorization_response: aiohttp.ClientResponse, username,
response = await self._client.get(
f"{BASE_PATH_AUTH}/{location}",
allow_redirects=False)
check_response(response, 'login_redirect', 302)
await check_response(response, 'login_redirect', 302)

return get_querystring_parameter_from_header_entry_url(
response, 'Location', 'code')
Expand All @@ -217,7 +219,7 @@ async def _request_new_token(self, code, code_verifier):
"code_verifier": code_verifier
},
allow_redirects=False)
check_response(response, 'get_token', 200)
await check_response(response, 'get_token', 200)

token_response = json.loads(await response.text())
self._set_token(token_response, unix_time_token_received)
Expand All @@ -233,13 +235,14 @@ async def _retrieve_client_acc(self):
# ------------------------------------------------------------------
# RETRIEVE ACC_CLIENT_ID
# ------------------------------------------------------------------
_LOGGER.debug("Retrieving acc client id using access token: %s", self._settings.access_token)
response = await self._client.post(
f'{BASE_PATH_ACC}/auth/v2/login',
headers = await PanasonicRequestHeader.get(self._settings, self._app_version),
headers = await PanasonicRequestHeader.get(self._settings, self._app_version, include_client_id= False),
json={
"language": 0
})
check_response(response, 'get_acc_client_id', 200)
await check_response(response, 'get_acc_client_id', 200)

json_body = json.loads(await response.text())
self._settings.clientId = json_body["clientId"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
class PanasonicRequestHeader:

@staticmethod
async def get(settings: PanasonicSettings, app_version: CCAppVersion):
async def get(settings: PanasonicSettings, app_version: CCAppVersion, include_client_id = True):
now = datetime.datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
headers={
"Content-Type": "application/json;charset=utf-8",
"User-Agent": "G-RAC",
Expand All @@ -21,7 +21,7 @@ async def get(settings: PanasonicSettings, app_version: CCAppVersion):
"X-CFC-API-KEY": PanasonicRequestHeader._get_api_key(),
"X-User-Authorization-V2": "Bearer " + settings.access_token
}
if (settings.clientId):
if (include_client_id and settings.clientId):
headers["X-Client-Id"] = settings.clientId
return headers

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ async def start_session(self):
if (not self._settings.has_refresh_token):
await self._authentication.authenticate(self._username, self._password)
if (not self._settings.is_access_token_valid):
await self._authentication.refresh_token()



try:
await self._authentication.refresh_token()
except:
await self._authentication.authenticate(self._username, self._password)

async def stop_session(self):
_LOGGER.debug("Stopping Session")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ async def _load(self):
return
try:
async with aiofiles.open(self._fileName) as json_file:
data = json.load(await json_file.read())
data = json.loads(await json_file.read())
self._version = data[SETTING_VERSION]
self._versionDate = date.fromisoformat(data[SETTING_VERSION_DATE])
self._access_token = data[SETTING_ACCESS_TOKEN]
Expand All @@ -53,8 +53,8 @@ async def _load(self):
self._clientId = data[SETTING_CLIENT_ID]
self._scope = data[SETTING_SCOPE]
_LOGGER.debug("Loaded settings from '%s'", self._fileName)
except:
_LOGGER.debug("Failed to loaded settings from '%s'", self._fileName)
except Exception as ex:
_LOGGER.warning("Failed to loaded settings from '%s'", self._fileName, exc_info = ex)
pass

def _save(self):
Expand Down Expand Up @@ -95,6 +95,8 @@ def version(self,value):
def is_version_expired(self):
if self._version is None:
return True
if not self._version:
return True
if self._versionDate is None:
return True
delta = date.today() - self._versionDate
Expand Down

0 comments on commit 2219006

Please sign in to comment.