homebridge-eosstb 2.2.8 → 2.2.9

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 (3) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/index.js +120 -37
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,11 @@ Please restart Homebridge after every plugin update.
10
10
  * Implement refreshToken capabilities
11
11
 
12
12
 
13
+ ## 2.2.9 (2023-05-29)
14
+ * Fixed bug with access token expiring causing 401 unauthorised errors when refreshing channel list
15
+ * Cleaned up some code
16
+
17
+
13
18
  ## 2.2.8 (2023-05-22)
14
19
  * Updated iOS version references
15
20
  * Fixed bug causing Homebridge crash when master channel list is not yet loaded
package/index.js CHANGED
@@ -352,18 +352,28 @@ class stbPlatform {
352
352
 
353
353
 
354
354
  this.checkChannelListInterval = setInterval(() => {
355
- // refreshMasterChannelList is an async function returning a promise, so handle any rejects
356
- this.refreshMasterChannelList()
357
- .then( response => {
358
- if (this.config.debugLevel >= 0) {
359
- this.log.warn('stbPlatform: refreshMasterChannelList completed OK');
360
- }
361
- })
362
- .catch(error => {
363
- if (this.config.debugLevel >= 0) {
364
- this.log.warn('stbPlatform: could not refresh master channel list:', error);
365
- }
366
- })
355
+ // check if master channel list has expired. If it has, refresh auth token, then refresh channel list
356
+ if (this.config.debugLevel >= 1) { this.log.warn('stbPlatform: checkChannelListInterval Start'); }
357
+ if (this.masterChannelListExpiryDate <= Date.now()) {
358
+ // must check and refresh auth token before each call to refresh master channel list
359
+ this.refreshAccessToken()
360
+ .then(response => {
361
+ if (this.config.debugLevel >= 1) { this.log.warn('stbPlatform: refreshAccessToken completed OK'); }
362
+ return this.refreshMasterChannelList()
363
+ })
364
+ .then(response => {
365
+ if (this.config.debugLevel >= 1) { this.log.warn('stbPlatform: refreshMasterChannelList completed OK'); }
366
+ return true
367
+ })
368
+ .catch(error => {
369
+ if (error.code) {
370
+ this.log.warn('stbPlatform: checkChannelListInterval Error', (error.syscall || '') + ' ' + (error.code || '') + ' ' + (error.config.url || error.hostname || ''));
371
+ } else {
372
+ this.log.warn('stbPlatform: checkChannelListInterval Error', error);
373
+ }
374
+ })
375
+ if (this.config.debugLevel >= 1) { this.log.warn('stbPlatform: checkChannelListInterval end'); }
376
+ }
367
377
  }, MASTER_CHANNEL_LIST_REFRESH_CHECK_INTERVAL_S * 1000 ) // need to pass ms
368
378
 
369
379
  debug('stbPlatform:apievent :: didFinishLaunching end of code block')
@@ -581,17 +591,17 @@ class stbPlatform {
581
591
  .then((objStbDevices) => {
582
592
  this.log('Discovery completed');
583
593
  this.log.debug('%s: ++++++ step 8: devices found:', watchdogInstance, this.devices.length)
584
- this.log.debug('%s: ++++++ step 8: calling getJwtToken', watchdogInstance)
594
+ this.log.debug('%s: ++++++ step 8: calling getMqttToken', watchdogInstance)
585
595
  errorTitle = 'Failed to start mqtt session';
586
- debug(debugPrefix + 'calling getJwtToken')
587
- return this.getJwtToken(this.session.username, this.session.accessToken, this.session.householdId);
596
+ debug(debugPrefix + 'calling getMqttToken')
597
+ return this.getMqttToken(this.session.username, this.session.accessToken, this.session.householdId);
588
598
  })
589
- .then((jwToken) => {
590
- this.log.debug('%s: ++++++ step 9: getJwtToken token was retrieved, token %s', watchdogInstance, jwToken)
599
+ .then((mqttToken) => {
600
+ this.log.debug('%s: ++++++ step 9: getMqttToken token was retrieved, token %s', watchdogInstance, mqttToken)
591
601
  this.log.debug('%s: ++++++ step 9: start mqtt client', watchdogInstance)
592
602
  debug(debugPrefix + 'calling startMqttClient')
593
- return this.startMqttClient(this, this.session.householdId, jwToken); // returns true
594
- })
603
+ return this.startMqttClient(this, this.session.householdId, mqttToken); // returns true
604
+ })
595
605
  .catch(errorReason => {
596
606
  // log any errors and set the currentSessionState
597
607
  this.log.warn(errorTitle + ' - %s', errorReason);
@@ -695,6 +705,73 @@ class stbPlatform {
695
705
  }
696
706
 
697
707
 
708
+ // get a new access token
709
+ async refreshAccessToken() {
710
+ return new Promise((resolve, reject) => {
711
+
712
+ // exit immediately if access token has not expired
713
+ if (this.session.accessTokenExpiry > Date.now()) {
714
+ if (this.config.debugLevel >= 1) { this.log.warn('refreshAccessToken: Access token has not expired yet. Next refresh will occur after %s', this.session.accessTokenExpiry.toLocaleString()); }
715
+ resolve(true);
716
+ return
717
+ }
718
+
719
+ if (this.config.debugLevel >= 1) { this.log.warn('refreshAccessToken: Access token has expired at %s. Requesting refresh', this.session.accessTokenExpiry.toLocaleString()); }
720
+
721
+ const axiosConfig = {
722
+ method: 'POST',
723
+ // https://prod.spark.sunrisetv.ch/auth-service/v1/authorization/refresh
724
+ url: countryBaseUrlArray[this.config.country.toLowerCase()] + '/auth-service/v1/authorization/refresh',
725
+ headers: {
726
+ 'x-oesp-username': this.session.username
727
+ },
728
+ jar: cookieJar,
729
+ data: {
730
+ refreshToken: this.session.refreshToken,
731
+ username: this.config.username
732
+ }
733
+ };
734
+
735
+ if (this.config.debugLevel >=1) { this.log.warn('refreshAccessToken: Post auth refresh request to',axiosConfig.url); }
736
+ axiosWS(axiosConfig)
737
+ .then(response => {
738
+ if (this.config.debugLevel >= 2) {
739
+ this.log('refreshAccessToken: auth refresh response:',response.status, response.statusText);
740
+ this.log('refreshAccessToken: response data (saved to this.session):');
741
+ this.log(response.data);
742
+ //this.log(response.headers);
743
+ }
744
+ this.session = response.data;
745
+
746
+ // add an expiry date for the access token: 2 min (120000ms) after created date
747
+ this.session.accessTokenExpiry = new Date(new Date().getTime() + 2*60000);
748
+
749
+ // check if householdId exists, if so, we have authenticated ok
750
+ if (this.session.householdId) { currentSessionState = sessionState.AUTHENTICATED; }
751
+ this.log.debug('Session username:', this.session.username);
752
+ this.log.debug('Session householdId:', this.session.householdId);
753
+ this.log.debug('Session accessToken:', this.session.accessToken);
754
+ this.log.debug('Session accessTokenExpiry:', this.session.accessTokenExpiry);
755
+ this.log.debug('Session refreshToken:', this.session.refreshToken);
756
+ this.log.debug('Session refreshTokenExpiry:', this.session.refreshTokenExpiry);
757
+ // Robustness: Observed that new APLSTB Apollo box on NL did not always return username during session logon, so store username from settings if missing
758
+ if (this.session.username == '') {
759
+ this.log.debug('Session username empty, setting to %s', this.config.username);
760
+ this.session.username = this.config.username;
761
+ } else {
762
+ this.log.debug('Session username exists: %s', this.session.username);
763
+ }
764
+ currentSessionState = sessionState.CONNECTED;
765
+ this.currentStatusFault = Characteristic.StatusFault.NO_FAULT;
766
+ resolve(this.session.householdId) // resolve the promise with the householdId
767
+ })
768
+ .catch(error => {
769
+ this.log.debug('refreshAccessToken: error:', error);
770
+ reject(error); // reject the promise and return the error
771
+ });
772
+ })
773
+ }
774
+
698
775
 
699
776
  // select the right session to create
700
777
  async createSession(country) {
@@ -785,11 +862,16 @@ class stbPlatform {
785
862
  this.log(response.data);
786
863
  }
787
864
  this.session = response.data;
865
+
866
+ // add an expiry date for the access token: 2 min (120000ms) after created date
867
+ this.session.accessTokenExpiry = new Date(new Date().getTime() + 2*60000);
868
+
788
869
  // check if householdId exists, if so, we have authenticated ok
789
870
  if (this.session.householdId) { currentSessionState = sessionState.AUTHENTICATED; }
790
871
  this.log.debug('Session username:', this.session.username);
791
872
  this.log.debug('Session householdId:', this.session.householdId);
792
873
  this.log.debug('Session accessToken:', this.session.accessToken);
874
+ this.log.debug('Session accessTokenExpiry:', this.session.accessTokenExpiry);
793
875
  this.log.debug('Session refreshToken:', this.session.refreshToken);
794
876
  this.log.debug('Session refreshTokenExpiry:', this.session.refreshTokenExpiry);
795
877
  // Robustness: Observed that new APLSTB Apollo box on NL did not always return username during session logon, so store username from settings if missing
@@ -812,8 +894,10 @@ class stbPlatform {
812
894
  errReason = 'try again later: ' + error.response.status + ' ' + (error.response.statusText || '');
813
895
  } else if (error.response && error.response.status) {
814
896
  errReason = 'check your internet connection: ' + error.response.status + ' ' + (error.response.statusText || '');
815
- } else {
897
+ } else if (error.code) {
816
898
  errReason = 'check your internet connection: ' + error.code + ' ' + (error.hostname || '');
899
+ } else {
900
+ errReason = 'unexpected error: ' + error;
817
901
  }
818
902
  //this.log('%s %s', errText, (errReason || ''));
819
903
  this.log.debug('getSession: error:', error);
@@ -1353,7 +1437,7 @@ class stbPlatform {
1353
1437
 
1354
1438
  // exit immediately if channel list has not expired
1355
1439
  if (this.masterChannelListExpiryDate > Date.now()) {
1356
- if (this.config.debugLevel > 1) { this.log.warn('refreshMasterChannelList: Master channel list has not expired yet. Next refresh will occur after %s', this.masterChannelListExpiryDate.toLocaleString()); }
1440
+ if (this.config.debugLevel >= 1) { this.log.warn('refreshMasterChannelList: Master channel list has not expired yet. Next refresh will occur after %s', this.masterChannelListExpiryDate.toLocaleString()); }
1357
1441
  resolve(true);
1358
1442
  return
1359
1443
  }
@@ -1987,28 +2071,28 @@ class stbPlatform {
1987
2071
  // START session handler mqtt
1988
2072
  //+++++++++++++++++++++++++++++++++++++++++++++++++++++
1989
2073
 
1990
- // get a Json Web Token
1991
- async getJwtToken(oespUsername, accessToken, householdId){
2074
+ // get the mqtt token
2075
+ async getMqttToken(oespUsername, accessToken, householdId){
1992
2076
  return new Promise((resolve, reject) => {
1993
- this.log.debug("Getting jwt token for householdId %s", householdId);
2077
+ this.log.debug("Getting mqtt token for householdId %s", householdId);
1994
2078
  // get a JSON web token from the supplied accessToken and householdId
1995
- if (this.config.debugLevel > 1) { this.log.warn('getJwtToken'); }
2079
+ if (this.config.debugLevel > 1) { this.log.warn('getMqttToken'); }
1996
2080
  // robustness checks
1997
2081
  if (currentSessionState !== sessionState.CONNECTED) {
1998
- this.log.warn('Cannot get JWT token: currentSessionState incorrect:', currentSessionState);
2082
+ this.log.warn('Cannot get mqtt token: currentSessionState incorrect:', currentSessionState);
1999
2083
  return false;
2000
2084
  }
2001
2085
  if (!accessToken) {
2002
- this.log.warn('Cannot get JWT token: accessToken not set');
2086
+ this.log.warn('Cannot get mqtt token: accessToken not set');
2003
2087
  return false;
2004
2088
  }
2005
2089
 
2006
- //this.log.warn('getJwtToken disabled while I build channel list');
2090
+ //this.log.warn('getMqttToken disabled while I build channel list');
2007
2091
  //return false;
2008
2092
 
2009
- const jwtAxiosConfig = {
2093
+ const mqttAxiosConfig = {
2010
2094
  method: 'GET',
2011
- url: countryBaseUrlArray[this.config.country.toLowerCase()] + '/tokens/jwt',
2095
+ //url: countryBaseUrlArray[this.config.country.toLowerCase()] + '/tokens/jwt', prior to October 2022
2012
2096
  // examples of auth-service/v1/mqtt/token urls:
2013
2097
  // https://prod.spark.ziggogo.tv/auth-service/v1/mqtt/token
2014
2098
  // https://prod.spark.sunrisetv.ch/auth-service/v1/mqtt/token
@@ -2018,23 +2102,22 @@ class stbPlatform {
2018
2102
  'X-OESP-Username': oespUsername,
2019
2103
  }
2020
2104
  };
2021
- this.log.debug("getJwtToken: jwtAxiosConfig:", jwtAxiosConfig)
2022
- axiosWS(jwtAxiosConfig)
2105
+ this.log.debug("getMqttToken: mqttAxiosConfig:", mqttAxiosConfig)
2106
+ axiosWS(mqttAxiosConfig)
2023
2107
  .then(response => {
2024
2108
  if (this.config.debugLevel > 0) {
2025
- this.log.warn("getJwtToken: response.data:", response.data)
2109
+ this.log.warn("getMqttToken: response.data:", response.data)
2026
2110
  }
2027
- //this.jwtToken = response.data.token; // store the token
2028
2111
  mqttUsername = householdId; // used in sendKey to ensure that mqtt is connected
2029
- resolve(response.data.token); // resolve with the tokwn
2112
+ resolve(response.data.token); // resolve with the token
2030
2113
  //this.startMqttClient(this, householdId, response.data.token); // this starts the mqtt session
2031
2114
 
2032
2115
  })
2033
2116
  .catch(error => {
2034
- this.log.debug('getJwtToken error details:', error);
2117
+ this.log.debug('getMqttToken error details:', error);
2035
2118
  // set session flag to disconnected to force a session reconnect
2036
2119
  currentSessionState = sessionState.DISCONNECTED;
2037
- reject('Failed to get jwtToken: ', error.code + ' ' + (error.hostname || ''));
2120
+ reject('Failed to get mqtt token: ', error.code + ' ' + (error.hostname || ''));
2038
2121
  });
2039
2122
  })
2040
2123
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "displayName": "Homebridge EOSSTB",
4
4
  "description": "homebridge-plugin - Add your set-top box to Homekit (for Magenta AT, Telenet BE, Sunrise CH, Virgin Media GB & IE, Ziggo NL)",
5
5
  "author": "Jochen Siegenthaler (https://github.com/jsiegenthaler/)",
6
- "version": "2.2.8",
6
+ "version": "2.2.9",
7
7
  "platformname": "eosstb",
8
8
  "dependencies": {
9
9
  "axios-cookiejar-support": "^4.0.6",