pycupra 0.0.14__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {pycupra-0.0.14/pycupra.egg-info → pycupra-0.1.0}/PKG-INFO +12 -2
  2. {pycupra-0.0.14 → pycupra-0.1.0}/README.md +11 -1
  3. {pycupra-0.0.14 → pycupra-0.1.0}/example/PyCupra.py +24 -8
  4. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/__init__.py +1 -0
  5. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/__version__.py +1 -1
  6. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/connection.py +66 -9
  7. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/const.py +11 -0
  8. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/dashboard.py +34 -0
  9. pycupra-0.1.0/pycupra/firebase.py +73 -0
  10. pycupra-0.1.0/pycupra/firebase_messaging/__init__.py +9 -0
  11. pycupra-0.1.0/pycupra/firebase_messaging/const.py +32 -0
  12. pycupra-0.1.0/pycupra/firebase_messaging/fcmpushclient.py +796 -0
  13. pycupra-0.1.0/pycupra/firebase_messaging/fcmregister.py +519 -0
  14. pycupra-0.1.0/pycupra/firebase_messaging/proto/android_checkin.proto +96 -0
  15. pycupra-0.1.0/pycupra/firebase_messaging/proto/android_checkin_pb2.py +36 -0
  16. pycupra-0.1.0/pycupra/firebase_messaging/proto/android_checkin_pb2.pyi +257 -0
  17. pycupra-0.1.0/pycupra/firebase_messaging/proto/checkin.proto +155 -0
  18. pycupra-0.1.0/pycupra/firebase_messaging/proto/checkin_pb2.py +31 -0
  19. pycupra-0.1.0/pycupra/firebase_messaging/proto/checkin_pb2.pyi +424 -0
  20. pycupra-0.1.0/pycupra/firebase_messaging/proto/mcs.proto +328 -0
  21. pycupra-0.1.0/pycupra/firebase_messaging/proto/mcs_pb2.py +65 -0
  22. pycupra-0.1.0/pycupra/firebase_messaging/proto/mcs_pb2.pyi +1118 -0
  23. pycupra-0.1.0/pycupra/firebase_messaging/py.typed +0 -0
  24. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/vehicle.py +272 -13
  25. {pycupra-0.0.14 → pycupra-0.1.0/pycupra.egg-info}/PKG-INFO +12 -2
  26. pycupra-0.1.0/pycupra.egg-info/SOURCES.txt +36 -0
  27. pycupra-0.0.14/pycupra.egg-info/SOURCES.txt +0 -21
  28. {pycupra-0.0.14 → pycupra-0.1.0}/.gitignore +0 -0
  29. {pycupra-0.0.14 → pycupra-0.1.0}/LICENSE +0 -0
  30. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/exceptions.py +0 -0
  31. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra/utilities.py +0 -0
  32. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra.egg-info/dependency_links.txt +0 -0
  33. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra.egg-info/requires.txt +0 -0
  34. {pycupra-0.0.14 → pycupra-0.1.0}/pycupra.egg-info/top_level.txt +0 -0
  35. /pycupra-0.0.14/cupra_credentials.json.demo → /pycupra-0.1.0/pycupra_credentials.json.demo +0 -0
  36. {pycupra-0.0.14 → pycupra-0.1.0}/requirements.txt +0 -0
  37. {pycupra-0.0.14 → pycupra-0.1.0}/setup.cfg +0 -0
  38. {pycupra-0.0.14 → pycupra-0.1.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycupra
3
- Version: 0.0.14
3
+ Version: 0.1.0
4
4
  Summary: A library to read and send vehicle data via Cupra/Seat portal using the same API calls as the MyCupra/MySeat mobile app.
5
5
  Home-page: https://github.com/WulfgarW/pycupra
6
6
  Author: WulfgarW
@@ -39,11 +39,21 @@ No licence, public domain, no guarantees, feel free to use for anything. Please
39
39
 
40
40
  ## Breaking changes
41
41
 
42
+ - The method vehicle.update(updateType) supports 3 different update types:
43
+ - updateType=0: Small update (=only get_basiccardata() and get_statusreport are called). If the last full update is more than 1100 seconds ago, then a full update is performed.
44
+ - updateType=1: Full update (nearly all get-methods are called. The model images and the capabilitites are refreshed only every 2 hours.)
45
+ - updateType=2: Like updateType=0, but ignoring the nightly update reduction
46
+
47
+ - Nightly update reduction: If nightly reduction is activated and the current time is within the time frame between 22:00 and 05:00, then vehicle.update(0) performs a full update, if the last full update is more than 1700 seconds ago. If that's not the case, vehicle.update(0) does nothing.
48
+
49
+ - PyCupra can ask the Seat/Cupra portal to send push notifications to PyCupra if the charging status or the climatisation status has changed or when the API has finished a request like lock or unlock vehicle, start or stop charging or change departure timers or ....
50
+
42
51
  ## Thanks to
43
52
 
44
53
  - [RobinostLund](https://github.com/robinostlund/volkswagencarnet) for initial project for Volkswagen Carnet I was able to fork
45
54
  - [Farfar](https://github.com/Farfar) for modifications related to electric engines
46
55
  - [tanelvakker](https://github.com/tanelvakker) for modifications related to correct SPIN handling for various actions and using correct URLs also for MY2021
56
+ - [sdb9696](https://github.com/sdb9696) for the firebase-messaging package that is used in PyCupra with only minor modifications
47
57
 
48
58
  ### Example
49
59
 
@@ -52,6 +62,6 @@ When logged in the library will automatically create a vehicle object for every
52
62
  Method get_vehicles will fetch vehicle basic information and create Vehicle class objects for all associated vehicles in account.
53
63
  To update all available data use the update_all method of the Connect class. This will call the update function for all registered vehicles, which in turn will fetch data from all available API endpoints.
54
64
 
55
- The file *cupra_credentials.json.demo* explains the data structure of the credentials file.
65
+ The file *pycupra_credentials.json.demo* explains the data structure of the credentials file.
56
66
 
57
67
 
@@ -14,11 +14,21 @@ No licence, public domain, no guarantees, feel free to use for anything. Please
14
14
 
15
15
  ## Breaking changes
16
16
 
17
+ - The method vehicle.update(updateType) supports 3 different update types:
18
+ - updateType=0: Small update (=only get_basiccardata() and get_statusreport are called). If the last full update is more than 1100 seconds ago, then a full update is performed.
19
+ - updateType=1: Full update (nearly all get-methods are called. The model images and the capabilitites are refreshed only every 2 hours.)
20
+ - updateType=2: Like updateType=0, but ignoring the nightly update reduction
21
+
22
+ - Nightly update reduction: If nightly reduction is activated and the current time is within the time frame between 22:00 and 05:00, then vehicle.update(0) performs a full update, if the last full update is more than 1700 seconds ago. If that's not the case, vehicle.update(0) does nothing.
23
+
24
+ - PyCupra can ask the Seat/Cupra portal to send push notifications to PyCupra if the charging status or the climatisation status has changed or when the API has finished a request like lock or unlock vehicle, start or stop charging or change departure timers or ....
25
+
17
26
  ## Thanks to
18
27
 
19
28
  - [RobinostLund](https://github.com/robinostlund/volkswagencarnet) for initial project for Volkswagen Carnet I was able to fork
20
29
  - [Farfar](https://github.com/Farfar) for modifications related to electric engines
21
30
  - [tanelvakker](https://github.com/tanelvakker) for modifications related to correct SPIN handling for various actions and using correct URLs also for MY2021
31
+ - [sdb9696](https://github.com/sdb9696) for the firebase-messaging package that is used in PyCupra with only minor modifications
22
32
 
23
33
  ### Example
24
34
 
@@ -27,6 +37,6 @@ When logged in the library will automatically create a vehicle object for every
27
37
  Method get_vehicles will fetch vehicle basic information and create Vehicle class objects for all associated vehicles in account.
28
38
  To update all available data use the update_all method of the Connect class. This will call the update function for all registered vehicles, which in turn will fetch data from all available API endpoints.
29
39
 
30
- The file *cupra_credentials.json.demo* explains the data structure of the credentials file.
40
+ The file *pycupra_credentials.json.demo* explains the data structure of the credentials file.
31
41
 
32
42
 
@@ -26,9 +26,10 @@ BRAND = 'cupra' # or 'seat' (Change it to 'seat' if you want to connect to the M
26
26
 
27
27
  PRINTRESPONSE = True
28
28
  INTERVAL = 5
29
- TOKEN_FILE_NAME_AND_PATH='./cupra_token.json'
30
- CREDENTIALS_FILE_NAME_AND_PATH='./cupra_credentials.json'
31
- ALL_ATTRIBUTES_FILE_NAME_AND_PATH='./cupra_all_attributes.txt'
29
+ TOKEN_FILE_NAME_AND_PATH='./pycupra_token.json'
30
+ CREDENTIALS_FILE_NAME_AND_PATH='./pycupra_credentials.json'
31
+ FIREBASE_CREDENTIALS_FILE_NAME_AND_PATH='./pycupra_firebase_credentials.json'
32
+ ALL_ATTRIBUTES_FILE_NAME_AND_PATH='./pycupra_all_attributes.txt'
32
33
 
33
34
 
34
35
  COMPONENTS = {
@@ -97,7 +98,9 @@ RESOURCES = [
97
98
  "requests_remaining",
98
99
  "service_inspection",
99
100
  "service_inspection_distance",
101
+ "slow_charge",
100
102
  "sunroof_closed",
103
+ "target_soc",
101
104
  "trip_last_average_auxillary_consumption",
102
105
  "trip_last_average_electric_consumption",
103
106
  "trip_last_average_fuel_consumption",
@@ -374,7 +377,7 @@ async def main():
374
377
  print('# Logging on to seat.cloud.vwgroup.com #')
375
378
  print('########################################')
376
379
  print(f"Initiating new session to Seat Cloud with {credentials.get('username')} as username")
377
- connection = Connection(session, BRAND, credentials.get('username'), credentials.get('password'), PRINTRESPONSE)
380
+ connection = Connection(session, BRAND, credentials.get('username'), credentials.get('password'), PRINTRESPONSE, nightlyUpdateReduction=False)
378
381
  print("Attempting to login to the Seat Cloud service")
379
382
  print(datetime.now())
380
383
  if await connection.doLogin(tokenFile=TOKEN_FILE_NAME_AND_PATH, apiKey=credentials.get('apiKey',None)):
@@ -393,6 +396,13 @@ async def main():
393
396
  instruments = set()
394
397
  for vehicle in connection.vehicles:
395
398
  txt = vehicle.vin
399
+ if vehicle == connection.vehicles[0]: # Firebase can only be activated for one vehicle. So we use it for the first one
400
+ newStatus = await vehicle.initialiseFirebase(FIREBASE_CREDENTIALS_FILE_NAME_AND_PATH, vehicle.update)
401
+ print('########################################')
402
+ print('# Initialisation of firebase #')
403
+ print(txt.center(40, '#'))
404
+ print(f"New status of firebase={newStatus}")
405
+
396
406
  print('')
397
407
  print('########################################')
398
408
  print('# Setting up dashboard #')
@@ -523,7 +533,7 @@ async def main():
523
533
  # Examples for using set functions:
524
534
 
525
535
  #await demo_set_charger(vehicle, action = "start") # action = "start" or "stop"
526
- #await demo_set_charger_current(vehicle, value='reduced') # value = 1-255/Maximum/Reduced (PHEV: 252 for reduced and 254 for max, EV: Maximum/Reduced)
536
+ #await demo_set_charger_current(vehicle, value='reduced') # value = 1-255/Maximum/Reduced (PHEV: 252 for reduced and 254 for max, EV: Maximum/Reduced)
527
537
 
528
538
  #await demo_set_climatisation(vehicle, action = "start", temp=18.0) # action = "auxilliary", "electric" or "off". spin is S-PIN and only needed for aux heating
529
539
  #await demo_set_climatisation_temp(vehicle, temp = 18.0) # temp = integer from 16 to 30
@@ -561,9 +571,15 @@ async def main():
561
571
  print('Export of all attributes successfully completed')
562
572
  else:
563
573
  print('Export of all attributes failed')
564
-
565
- print(f"Sleeping for {INTERVAL} seconds")
566
- await asyncio.sleep(INTERVAL)
574
+
575
+ if vehicle.firebaseStatus== 1: # firebase messaging activated
576
+ # Do an endless loop to wait and receive firebase messages
577
+ i=0
578
+ while True:
579
+ print(f"Sleeping for {6*INTERVAL} seconds")
580
+ await asyncio.sleep(6*INTERVAL)
581
+ i=i+1
582
+ _LOGGER.debug(f'Round {i}')
567
583
 
568
584
  exit
569
585
 
@@ -5,3 +5,4 @@ For more details and documentation, visit the github page at https://github.com/
5
5
  """
6
6
 
7
7
  from pycupra.connection import Connection
8
+
@@ -3,4 +3,4 @@ pycupra - A Python 3 library for interacting with the My Cupra/My Seat portal.
3
3
 
4
4
  For more details and documentation, visit the github page at https://github.com/WulfgarW/pycupra
5
5
  """
6
- __version__ = "0.0.14"
6
+ __version__ = "0.1.0"
@@ -46,7 +46,7 @@ from requests_oauthlib import OAuth2Session
46
46
  from oauthlib.oauth2.rfc6749.parameters import parse_authorization_code_response, parse_token_response, prepare_grant_uri
47
47
 
48
48
  from aiohttp import ClientSession, ClientTimeout
49
- from aiohttp.hdrs import METH_GET, METH_POST, METH_PUT
49
+ from aiohttp.hdrs import METH_GET, METH_POST, METH_PUT, METH_DELETE
50
50
 
51
51
  from .const import (
52
52
  HEADERS_SESSION,
@@ -643,6 +643,8 @@ class Connection:
643
643
  res = {'status_code': response.status}
644
644
  elif response.status == 202 and method==METH_PUT:
645
645
  res = response
646
+ elif response.status == 200 and method==METH_DELETE:
647
+ res = response
646
648
  elif response.status >= 200 or response.status <= 300:
647
649
  # If this is a revoke token url, expect Content-Length 0 and return
648
650
  if int(response.headers.get('Content-Length', 0)) == 0 and 'revoke' in url:
@@ -759,7 +761,7 @@ class Connection:
759
761
  # Check if user needs to update consent
760
762
  try:
761
763
  await self.set_token(self._session_auth_brand)
762
- _LOGGER.debug('Achtung! getConsentInfo auskommentiert')
764
+ #_LOGGER.debug('Achtung! getConsentInfo auskommentiert')
763
765
  response = await self.get(eval(f"f'{API_MBB_STATUSDATA}'"))
764
766
  if response.get('profileCompleted','incomplete'):
765
767
  if response.get('profileCompleted',False):
@@ -1182,6 +1184,8 @@ class Connection:
1182
1184
  """Get charger data."""
1183
1185
  await self.set_token(self._session_auth_brand)
1184
1186
  try:
1187
+ chargingStatus = {}
1188
+ chargingInfo = {}
1185
1189
  response = await self.get(eval(f"f'{API_CHARGING}/status'"))
1186
1190
  if response.get('battery', {}):
1187
1191
  chargingStatus = response
@@ -1203,12 +1207,16 @@ class Connection:
1203
1207
  _LOGGER.warning(f'Could not fetch charging modes, HTTP status code: {response.get("status_code")}')
1204
1208
  else:
1205
1209
  _LOGGER.info('Unhandled error while trying to fetch charging modes')"""
1206
- data = {'charging': {
1207
- 'status': chargingStatus,
1208
- 'info' : chargingInfo,
1209
- #'modes' : chargingModes,
1210
- }
1211
- }
1210
+ if chargingStatus != {} and chargingInfo != {}:
1211
+ data = {'charging': {
1212
+ 'status': chargingStatus,
1213
+ 'info' : chargingInfo,
1214
+ #'modes' : chargingModes,
1215
+ }
1216
+ }
1217
+ else:
1218
+ _LOGGER.warning(f'getCharger() got no valid data. Returning None')
1219
+ return None
1212
1220
  return data
1213
1221
  except Exception as error:
1214
1222
  _LOGGER.warning(f'Could not fetch charger, error: {error}')
@@ -1321,7 +1329,7 @@ class Connection:
1321
1329
  if 'state' in k.lower():
1322
1330
  data['state'] = response.get(key).get(k)
1323
1331
  else:
1324
- if 'Id' in key:
1332
+ if 'Id' in key or 'id' in key:
1325
1333
  data['id'] = str(response.get(key))
1326
1334
  if 'State' in key:
1327
1335
  data['state'] = response.get(key)
@@ -1365,6 +1373,55 @@ class Connection:
1365
1373
  raise
1366
1374
  return False
1367
1375
 
1376
+ async def subscribe(self, vin, credentials):
1377
+ url = f'{APP_URI}/v2/subscriptions'
1378
+ deviceId = credentials.get('gcm',{}).get('app_id','')
1379
+ token = credentials.get('fcm',{}).get('registration',{}).get('token','')
1380
+
1381
+ data = {
1382
+ "deviceId": deviceId,
1383
+ "locale":"en_GB",
1384
+ "services":{"charging":True,"climatisation":True},
1385
+ "token": token,
1386
+ "userId": self._user_id,
1387
+ "vin":vin
1388
+ }
1389
+ return await self._setViaAPI(url, json=data)
1390
+
1391
+ async def deleteSubscription(self, credentials):
1392
+ await self.set_token(self._session_auth_brand)
1393
+ try:
1394
+ id = credentials.get('subscription',{}).get('id','')
1395
+ url = f'{APP_URI}/v1/subscriptions/{id}'
1396
+ response = await self._request(METH_DELETE, url)
1397
+ if response.status==200:
1398
+ _LOGGER.debug(f'Subscription {id} successfully deleted.')
1399
+ return response
1400
+ else:
1401
+ _LOGGER.debug(f'API did not successfully delete subscription.')
1402
+ raise SeatException(f'Invalid or no response for endpoint {url}')
1403
+ return response
1404
+ except aiohttp.client_exceptions.ClientResponseError as error:
1405
+ _LOGGER.debug(f'Request failed. Id: {id}, HTTP request headers: {self._session_headers}')
1406
+ if error.status == 401:
1407
+ _LOGGER.error('Unauthorized')
1408
+ elif error.status == 400:
1409
+ _LOGGER.error(f'Bad request')
1410
+ elif error.status == 429:
1411
+ _LOGGER.warning('Too many requests. Further requests can only be made after the end of next trip in order to protect your vehicles battery.')
1412
+ return 429
1413
+ elif error.status == 500:
1414
+ _LOGGER.error('Internal server error, server might be temporarily unavailable')
1415
+ elif error.status == 502:
1416
+ _LOGGER.error('Bad gateway, this function may not be implemented for this vehicle')
1417
+ else:
1418
+ _LOGGER.error(f'Unhandled HTTP exception: {error}')
1419
+ #return False
1420
+ except Exception as error:
1421
+ _LOGGER.error(f'Error: {error}')
1422
+ raise
1423
+ return False
1424
+
1368
1425
  async def setCharger(self, vin, baseurl, mode, data):
1369
1426
  """Start/Stop charger."""
1370
1427
  if mode in {'start', 'stop'}:
@@ -181,3 +181,14 @@ REQ_STATUS = {
181
181
  'vsr': 'fs-car/bs/vsr/v1/{BRAND}/{COUNTRY}/vehicles/{vin}/requests/{id}/jobstatus',
182
182
  'default': 'fs-car/bs/{section}/v1/{BRAND}/{COUNTRY}/vehicles/{vin}/requests/{id}/status'
183
183
  }
184
+
185
+ FCM_PROJECT_ID='ola-apps-prod'
186
+ FCM_APP_ID={
187
+ 'cupra': '1:530284123617:android:9b9ba5a87c7ffd37fbeea0',
188
+ 'seat': '1:530284123617:android:d6187613ac3d7b08fbeea0'
189
+ }
190
+ FCM_API_KEY='AIzaSyCoSp1zitklb1EDj5yQumN0VNhDizJQHLk'
191
+ FIREBASE_STATUS_NOT_INITIALISED= 0
192
+ FIREBASE_STATUS_ACTIVATED= 1
193
+ FIREBASE_STATUS_NOT_WANTED= -2
194
+ FIREBASE_STATUS_ACTIVATION_FAILED= -1
@@ -715,6 +715,32 @@ class PHeaterVentilation(Switch):
715
715
  return dict(last_result = self.vehicle.pheater_action_status)
716
716
 
717
717
 
718
+ class SlowCharge(Switch):
719
+ def __init__(self):
720
+ super().__init__(attr="slow_charge", name="Slow charge", icon="mdi:battery")
721
+
722
+ @property
723
+ def state(self):
724
+ return self.vehicle.slow_charge
725
+
726
+ async def turn_on(self):
727
+ await self.vehicle.set_charger_current('reduced')
728
+ #await self.vehicle.update()
729
+
730
+ async def turn_off(self):
731
+ await self.vehicle.set_charger_current('maximum')
732
+ #await self.vehicle.update()
733
+
734
+ @property
735
+ def assumed_state(self):
736
+ return False
737
+
738
+
739
+ @property
740
+ def attributes(self):
741
+ return dict(last_result = self.vehicle.charger_action_status)
742
+
743
+
718
744
  class Warnings(Sensor):
719
745
  def __init__(self):
720
746
  super().__init__(attr="warnings", name="Warnings", icon="mdi:alarm-light")
@@ -981,6 +1007,7 @@ def create_instruments():
981
1007
  #CombustionClimatisationClimate(),
982
1008
  Charging(),
983
1009
  Warnings(),
1010
+ SlowCharge(),
984
1011
  RequestResults(),
985
1012
  DepartureTimer1(),
986
1013
  DepartureTimer2(),
@@ -1010,6 +1037,13 @@ def create_instruments():
1010
1037
  unit="%",
1011
1038
  device_class="battery"
1012
1039
  ),
1040
+ Sensor(
1041
+ attr="target_soc",
1042
+ name="Target state of charge",
1043
+ icon="mdi:battery-positive",
1044
+ unit="%",
1045
+ device_class="battery"
1046
+ ),
1013
1047
  Sensor(
1014
1048
  attr="adblue_level",
1015
1049
  name="Adblue level",
@@ -0,0 +1,73 @@
1
+ import logging
2
+ import asyncio
3
+ import os
4
+ import json
5
+ import string
6
+ import secrets
7
+
8
+ from .firebase_messaging import FcmPushClient, FcmRegisterConfig
9
+
10
+ from .const import (
11
+ FCM_PROJECT_ID,
12
+ FCM_API_KEY,
13
+ FCM_APP_ID
14
+ )
15
+
16
+ _LOGGER = logging.getLogger(__name__)
17
+
18
+ class Firebase():
19
+ async def firebaseStart(self, onNotificationFunc, firebaseCredentialsFileName, brand='cupra'):
20
+ """ Starts the firebase cloud messaging receiver """
21
+ loop = asyncio.get_running_loop()
22
+ credentials = await loop.run_in_executor(None, readFCMCredsFile, firebaseCredentialsFileName)
23
+ #credentials = readFCMCredsFile(firebaseCredentialsFileName)
24
+ if credentials == {}:
25
+ credentials =''
26
+
27
+ fcm_project_id=FCM_PROJECT_ID
28
+ fcm_app_id=FCM_APP_ID[brand]
29
+ fcm_api_key=FCM_API_KEY
30
+ chars = string.ascii_letters + string.digits
31
+ fcmMessageSenderId = ''.join(secrets.choice(chars) for i in range(16))
32
+ fcmMessageSenderId= 'fxpWQ_'+fcmMessageSenderId
33
+
34
+
35
+ fcm_config = FcmRegisterConfig(fcm_project_id, fcm_app_id, fcm_api_key, fcmMessageSenderId)
36
+ pc = FcmPushClient(onNotificationFunc, fcm_config, credentials, onFCMCredentialsUpdated)
37
+ fcm_token = await pc.checkin_or_register(firebaseCredentialsFileName)
38
+ _LOGGER.debug(f'Firebase.checkin_or_register() returned a token:{fcm_token}')
39
+ await pc.start()
40
+ await asyncio.sleep(5)
41
+ return pc.is_started()
42
+
43
+ def readFCMCredsFile(credsFile):
44
+ """ Reads the firebase cloud messaging credentials from file"""
45
+ try:
46
+ if os.path.isfile(credsFile):
47
+ with open(credsFile, "r") as f:
48
+ credString=f.read()
49
+ f.close()
50
+ creds=json.loads(credString)
51
+ return creds
52
+ else:
53
+ _LOGGER.debug(f'{credsFile} not found.')
54
+ return {}
55
+ except:
56
+ _LOGGER.warning('readFCMCredsFile() not successful.')
57
+ return ''
58
+
59
+ def writeFCMCredsFile(creds, firebaseCredentialsFileName):
60
+ """ Saves the firebase cloud messaging credentials to a file for future use """
61
+ try:
62
+ with open(firebaseCredentialsFileName, "w") as f:
63
+ f.write(json.dumps(creds))
64
+ f.close()
65
+ except Exception as e:
66
+ _LOGGER.warning(f'writeFCMCredsFile() not successful. Error: {e}')
67
+
68
+ async def onFCMCredentialsUpdated(creds, firebaseCredentialsFileName):
69
+ """ Is called from firebase-messaging package """
70
+ loop = asyncio.get_running_loop()
71
+ await loop.run_in_executor(None, writeFCMCredsFile, creds, firebaseCredentialsFileName)
72
+ #writeFCMCredsFile(creds, firebaseCredentialsFileName)
73
+
@@ -0,0 +1,9 @@
1
+ from .fcmpushclient import FcmPushClient, FcmPushClientConfig, FcmPushClientRunState
2
+ from .fcmregister import FcmRegisterConfig
3
+
4
+ __all__ = [
5
+ "FcmPushClientConfig",
6
+ "FcmPushClient",
7
+ "FcmPushClientRunState",
8
+ "FcmRegisterConfig",
9
+ ]
@@ -0,0 +1,32 @@
1
+ """Constants module."""
2
+
3
+ GCM_REGISTER_URL = "https://android.clients.google.com/c2dm/register3"
4
+ GCM_CHECKIN_URL = "https://android.clients.google.com/checkin"
5
+ GCM_SERVER_KEY_BIN = (
6
+ b"\x04\x33\x94\xf7\xdf\xa1\xeb\xb1\xdc\x03\xa2\x5e\x15\x71\xdb\x48\xd3"
7
+ + b"\x2e\xed\xed\xb2\x34\xdb\xb7\x47\x3a\x0c\x8f\xc4\xcc\xe1\x6f\x3c"
8
+ + b"\x8c\x84\xdf\xab\xb6\x66\x3e\xf2\x0c\xd4\x8b\xfe\xe3\xf9\x76\x2f"
9
+ + b"\x14\x1c\x63\x08\x6a\x6f\x2d\xb1\x1a\x95\xb0\xce\x37\xc0\x9c\x6e"
10
+ )
11
+ # urlsafe b64 encoding of the binary key with = padding removed
12
+ GCM_SERVER_KEY_B64 = (
13
+ "BDOU99-h67HcA6JeFXHbSNMu7e2yNNu3RzoM"
14
+ + "j8TM4W88jITfq7ZmPvIM1Iv-4_l2LxQcYwhqby2xGpWwzjfAnG4"
15
+ )
16
+
17
+ FCM_SUBSCRIBE_URL = "https://fcm.googleapis.com/fcm/connect/subscribe/"
18
+ FCM_SEND_URL = "https://fcm.googleapis.com/fcm/send/"
19
+
20
+ FCM_API = "https://fcm.googleapis.com/v1/"
21
+ FCM_REGISTRATION = "https://fcmregistrations.googleapis.com/v1/"
22
+ FCM_INSTALLATION = "https://firebaseinstallations.googleapis.com/v1/"
23
+ AUTH_VERSION = "FIS_v2"
24
+ SDK_VERSION = "w:0.6.6"
25
+
26
+ DOORBELLS_ENDPOINT = "/clients_api/doorbots/{0}"
27
+
28
+ MCS_VERSION = 41
29
+ MCS_HOST = "mtalk.google.com"
30
+ MCS_PORT = 5228
31
+ MCS_SELECTIVE_ACK_ID = 12
32
+ MCS_STREAM_ACK_ID = 13