homebridge-eosstb 2.4.0-alpha.4 → 2.4.0-alpha.6

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.

Potentially problematic release.


This version of homebridge-eosstb might be problematic. Click here for more details.

Files changed (3) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/index.js +611 -796
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -106,7 +106,7 @@ const SETTOPBOX_NAME_MINLEN = 3; // min len of the set-top box name
106
106
  const SETTOPBOX_NAME_MAXLEN = 14; // max len of the set-top box name
107
107
 
108
108
  // state constants. Need to add an array for any characteristic that is not an array, or the array is not contiguous
109
- const sessionState = {
109
+ const sessionState = Object.freeze({
110
110
  DISCONNECTED: 0,
111
111
  LOADING: 1,
112
112
  LOGGING_IN: 2,
@@ -114,15 +114,14 @@ const sessionState = {
114
114
  VERIFYING: 4,
115
115
  AUTHENTICATED: 5,
116
116
  CONNECTED: 6,
117
- }; // custom
118
- const powerStateName = ["OFF", "ON"]; // custom
119
- const recordingState = { IDLE: 0, ONGOING_NDVR: 1, ONGOING_LOCALDVR: 2 }; // custom
120
- const statusActiveName = ["NOT_ACTIVE", "ACTIVE"]; // custom, characteristic is boolean, not an array
121
-
122
- Object.freeze(sessionState);
123
- Object.freeze(powerStateName);
124
- Object.freeze(recordingState);
125
- Object.freeze(statusActiveName);
117
+ });
118
+ const powerStateName = Object.freeze(["OFF", "ON"]); // custom
119
+ const recordingState = Object.freeze({
120
+ IDLE: 0,
121
+ ONGOING_NDVR: 1,
122
+ ONGOING_LOCALDVR: 2,
123
+ }); // custom
124
+ const statusActiveName = Object.freeze(["NOT_ACTIVE", "ACTIVE"]); // custom, characteristic is boolean, not an array
126
125
 
127
126
  // exec spawns child process to run a bash script
128
127
  const exec = require("child_process").exec;
@@ -136,6 +135,7 @@ let currentSessionState;
136
135
  let isShuttingDown = false; // to handle reboots cleanly
137
136
 
138
137
  let Accessory, Characteristic, Service, Categories, UUID;
138
+ let CHAR_NAMES; // declared here, populated once Characteristic is available
139
139
 
140
140
  // Returns the path to the system Chromium/Chrome executable.
141
141
  // Used by getSessionCH() which requires puppeteer-core (no bundled browser).
@@ -190,10 +190,48 @@ function getTimestampInSeconds() {
190
190
  // transform current media state of 0,1,2,4,5 to 1,2,3,4,5 to work with Object.keys
191
191
  function currentMediaStateName(currentMediaState) {
192
192
  let i = currentMediaState + 1; // get the new index
193
+ // modify if > 3 to get 1,2,3,4,5
193
194
  if (i > 3) {
194
195
  i = i - 1;
195
- } // modify if > 3 to get 1,2,3,4,5
196
- return Object.keys(Characteristic.CurrentMediaState)[i];
196
+ }
197
+ return CHAR_NAMES.CurrentMediaState[i];
198
+ }
199
+
200
+ // helper function to create consistent log text for set-to vs changed-from
201
+ function logCharValueChange(
202
+ log,
203
+ name,
204
+ label,
205
+ input,
206
+ previousValue,
207
+ previousLabel,
208
+ currentValue,
209
+ currentLabel,
210
+ ) {
211
+ if (previousValue === null || previousValue === undefined) {
212
+ log("%s: %s set to %s [%s]", name, label, currentValue, currentLabel);
213
+ } else if (input === null || input === undefined) {
214
+ log(
215
+ "%s: %s changed from %s [%s] to %s [%s]",
216
+ name,
217
+ label,
218
+ previousValue,
219
+ previousLabel,
220
+ currentValue,
221
+ currentLabel,
222
+ );
223
+ } else {
224
+ log(
225
+ "%s: %s changed on input %s from %s [%s] to %s [%s]",
226
+ name,
227
+ label,
228
+ input,
229
+ previousValue,
230
+ previousLabel,
231
+ currentValue,
232
+ currentLabel,
233
+ );
234
+ }
197
235
  }
198
236
 
199
237
  // clean a name so it is acceptable for HomeKit
@@ -271,7 +309,24 @@ module.exports = (api) => {
271
309
  Service = api.hap.Service;
272
310
  Categories = api.hap.Categories;
273
311
  UUID = api.hap.uuid;
312
+
313
+ // Build CHAR_NAMES here, now that Characteristic is assigned
314
+ // Cached key name arrays for logging. These enums never change at runtime so build them once.
315
+ CHAR_NAMES = Object.freeze({
316
+ StatusFault: Object.keys(Characteristic.StatusFault),
317
+ ProgramMode: Object.keys(Characteristic.ProgramMode),
318
+ InputDeviceType: Object.keys(Characteristic.InputDeviceType),
319
+ InputSourceType: Object.keys(Characteristic.InputSourceType),
320
+ ClosedCaptions: Object.keys(Characteristic.ClosedCaptions),
321
+ PictureMode: Object.keys(Characteristic.PictureMode),
322
+ InUse: Object.keys(Characteristic.InUse),
323
+ CurrentVisibilityState: Object.keys(Characteristic.CurrentVisibilityState),
324
+ CurrentMediaState: Object.keys(Characteristic.CurrentMediaState),
325
+ TargetMediaState: Object.keys(Characteristic.TargetMediaState),
326
+ });
327
+
274
328
  const isDynamicPlatform = true;
329
+
275
330
  api.registerPlatform(
276
331
  PLUGIN_NAME,
277
332
  PLATFORM_NAME,
@@ -291,6 +346,7 @@ class StbPlatform {
291
346
  this.stbDevices = []; // store stbDevice in this.stbDevices
292
347
  this.masterChannelList = [];
293
348
  this.isDev = config.devMode === true;
349
+ this.debugLevel = this.config.debugLevel || 0; // debugLevel defaults to 0 (minimum)
294
350
 
295
351
  // show some useful version info
296
352
  this.log.info(
@@ -349,7 +405,7 @@ class StbPlatform {
349
405
  */
350
406
  //this.api.on('didFinishLaunching', () => {
351
407
  this.api.on("didFinishLaunching", async () => {
352
- if (this.config.debugLevel > 2) {
408
+ if (this.debugLevel > 2) {
353
409
  this.log.warn("API event: didFinishLaunching");
354
410
  }
355
411
  debug("StbPlatform:apievent :: didFinishLaunching");
@@ -374,20 +430,20 @@ class StbPlatform {
374
430
  // check for a channel list update every MASTER_CHANNEL_LIST_REFRESH_CHECK_INTERVAL_S seconds
375
431
  this.checkChannelListInterval = setInterval(() => {
376
432
  // check if master channel list has expired. If it has, refresh auth token, then refresh channel list
377
- if (this.config.debugLevel >= 1) {
433
+ if (this.debugLevel >= 1) {
378
434
  this.log.warn("StbPlatform: checkChannelListInterval Start");
379
435
  }
380
436
  if (this.masterChannelListExpiryDate <= Date.now()) {
381
437
  // must check and refresh auth token before each call to refresh master channel list
382
438
  this.refreshAccessToken()
383
439
  .then((response) => {
384
- if (this.config.debugLevel >= 1) {
440
+ if (this.debugLevel >= 1) {
385
441
  this.log.warn("StbPlatform: refreshAccessToken completed OK");
386
442
  }
387
443
  return this.refreshMasterChannelList();
388
444
  })
389
445
  .then((response) => {
390
- if (this.config.debugLevel >= 1) {
446
+ if (this.debugLevel >= 1) {
391
447
  this.log.warn(
392
448
  "StbPlatform: refreshMasterChannelList completed OK",
393
449
  );
@@ -411,7 +467,7 @@ class StbPlatform {
411
467
  );
412
468
  }
413
469
  });
414
- if (this.config.debugLevel >= 1) {
470
+ if (this.debugLevel >= 1) {
415
471
  this.log.warn("StbPlatform: checkChannelListInterval End");
416
472
  }
417
473
  }
@@ -426,7 +482,7 @@ class StbPlatform {
426
482
  */
427
483
  this.api.on("shutdown", () => {
428
484
  debug("StbPlatform:apievent :: shutdown");
429
- if (this.config.debugLevel > 2) {
485
+ if (this.debugLevel > 2) {
430
486
  this.log.warn("API event: shutdown");
431
487
  }
432
488
  isShuttingDown = true;
@@ -521,7 +577,7 @@ class StbPlatform {
521
577
  const debugPrefix = "\x1b[33msessionWatchdog :: "; // 33=yellow
522
578
 
523
579
  debug(debugPrefix + "started");
524
- if (this.config.debugLevel > 2) {
580
+ if (this.debugLevel > 2) {
525
581
  this.log.warn(
526
582
  "%s: Started watchdog instance %s",
527
583
  watchdogInstance,
@@ -539,7 +595,7 @@ class StbPlatform {
539
595
 
540
596
  // Build a status snapshot for debug logging (only when needed to avoid the
541
597
  // string allocation cost on every tick at lower debug levels).
542
- if (this.config.debugLevel > 0) {
598
+ if (this.debugLevel > 0) {
543
599
  const statusOverview = [
544
600
  `sessionState=${Object.keys(sessionState)[currentSessionState]}`,
545
601
  `mqttClient.connected=${mqttClient.connected}`,
@@ -551,7 +607,7 @@ class StbPlatform {
551
607
  // ── Early-exit checks (in order of likelihood) ───────────────────────────
552
608
 
553
609
  if (isShuttingDown) {
554
- if (this.config.debugLevel > 2) {
610
+ if (this.debugLevel > 2) {
555
611
  this.log.warn(
556
612
  "%s: Homebridge is shutting down, exiting without action",
557
613
  watchdogInstance,
@@ -563,7 +619,7 @@ class StbPlatform {
563
619
  // A previous watchdog invocation is still executing its startup sequence.
564
620
  // Return immediately — the running instance will reset the flag when done.
565
621
  if (this.sessionWatchdogRunning) {
566
- if (this.config.debugLevel > 2) {
622
+ if (this.debugLevel > 2) {
567
623
  this.log.warn(
568
624
  "%s: Previous watchdog still running, exiting without action",
569
625
  watchdogInstance,
@@ -577,7 +633,7 @@ class StbPlatform {
577
633
  currentSessionState === sessionState.CONNECTED &&
578
634
  mqttClient.connected
579
635
  ) {
580
- if (this.config.debugLevel > 2) {
636
+ if (this.debugLevel > 2) {
581
637
  this.log.warn(
582
638
  "%s: Session and mqtt fully connected, exiting without action",
583
639
  watchdogInstance,
@@ -592,7 +648,7 @@ class StbPlatform {
592
648
  currentSessionState === sessionState.CONNECTED &&
593
649
  this.mqttClientConnecting
594
650
  ) {
595
- if (this.config.debugLevel > 2) {
651
+ if (this.debugLevel > 2) {
596
652
  this.log.warn(
597
653
  "%s: Session connected, mqtt still connecting, exiting without action",
598
654
  watchdogInstance,
@@ -607,7 +663,7 @@ class StbPlatform {
607
663
  currentSessionState !== sessionState.DISCONNECTED &&
608
664
  currentSessionState !== sessionState.CONNECTED
609
665
  ) {
610
- if (this.config.debugLevel > 2) {
666
+ if (this.debugLevel > 2) {
611
667
  this.log.warn(
612
668
  "%s: Session is connecting (state=%s), exiting without action",
613
669
  watchdogInstance,
@@ -631,7 +687,7 @@ class StbPlatform {
631
687
  // IMPORTANT: this must be set BEFORE any await so that a concurrent watchdog
632
688
  // tick that fires before the first await sees the flag and exits early.
633
689
  this.sessionWatchdogRunning = true;
634
- if (this.config.debugLevel > 2) {
690
+ if (this.debugLevel > 2) {
635
691
  this.log.warn(
636
692
  "%s: sessionWatchdogRunning set to true, beginning reconnect",
637
693
  watchdogInstance,
@@ -643,9 +699,7 @@ class StbPlatform {
643
699
  // Uses config.json "devMode": true or NODE_ENV
644
700
  // Uses NODE_ENV so any developer can activate it without touching the code.
645
701
  if (!PLUGIN_ENV) {
646
- if (
647
- this.isDev || process.env.NODE_ENV === "development"
648
- ) {
702
+ if (this.isDev || process.env.NODE_ENV === "development") {
649
703
  PLUGIN_ENV = " DEV";
650
704
  }
651
705
  }
@@ -671,7 +725,7 @@ class StbPlatform {
671
725
  } finally {
672
726
  // Reset the running flag so the next watchdog tick can proceed.
673
727
  this.sessionWatchdogRunning = false;
674
- if (this.config.debugLevel > 2) {
728
+ if (this.debugLevel > 2) {
675
729
  this.log.warn(
676
730
  "%s: sessionWatchdogRunning reset to false, exiting",
677
731
  watchdogInstance,
@@ -1076,7 +1130,7 @@ class StbPlatform {
1076
1130
  async refreshAccessToken() {
1077
1131
  // robustness: exit immediately if no session expiry exists yet (session not yet established)
1078
1132
  if (!this.session.accessTokenExpiry) {
1079
- if (this.config.debugLevel >= 1) {
1133
+ if (this.debugLevel >= 1) {
1080
1134
  this.log.warn(
1081
1135
  "refreshAccessToken: No access token expiry set yet, session not yet established. Exiting.",
1082
1136
  );
@@ -1086,7 +1140,7 @@ class StbPlatform {
1086
1140
 
1087
1141
  // exit immediately if access token has not expired
1088
1142
  if (this.session.accessTokenExpiry > Date.now()) {
1089
- if (this.config.debugLevel >= 1) {
1143
+ if (this.debugLevel >= 1) {
1090
1144
  this.log.warn(
1091
1145
  "refreshAccessToken: Access token has not expired yet. Next refresh will occur after %s",
1092
1146
  this.session.accessTokenExpiry.toLocaleString(),
@@ -1095,7 +1149,7 @@ class StbPlatform {
1095
1149
  return true;
1096
1150
  }
1097
1151
 
1098
- if (this.config.debugLevel >= 1) {
1152
+ if (this.debugLevel >= 1) {
1099
1153
  this.log.warn(
1100
1154
  "refreshAccessToken: Access token has expired at %s. Requesting refresh",
1101
1155
  this.session.accessTokenExpiry.toLocaleString(),
@@ -1124,7 +1178,7 @@ class StbPlatform {
1124
1178
  },
1125
1179
  };
1126
1180
 
1127
- if (this.config.debugLevel >= 1) {
1181
+ if (this.debugLevel >= 1) {
1128
1182
  this.log.warn(
1129
1183
  "refreshAccessToken: Post auth refresh request to",
1130
1184
  axiosConfig.url,
@@ -1134,7 +1188,7 @@ class StbPlatform {
1134
1188
  // throws on HTTP/network error → caller's catch or .catch() handles it
1135
1189
  const response = await axiosWS(axiosConfig);
1136
1190
 
1137
- if (this.config.debugLevel >= 2) {
1191
+ if (this.debugLevel >= 2) {
1138
1192
  this.log(
1139
1193
  "refreshAccessToken: auth refresh response:",
1140
1194
  response.status,
@@ -1294,7 +1348,7 @@ class StbPlatform {
1294
1348
  "&language=en";
1295
1349
 
1296
1350
  this.log("Step 1 of 7: get authentication details");
1297
- if (this.config.debugLevel > 1) {
1351
+ if (this.debugLevel > 1) {
1298
1352
  this.log.warn(
1299
1353
  "Step 1 of 7: get authentication details from",
1300
1354
  apiAuthorizationUrl,
@@ -1591,7 +1645,7 @@ class StbPlatform {
1591
1645
  "Cookies for the session:",
1592
1646
  cookieJar.getCookies(sessionUrl),
1593
1647
  );
1594
- if (this.config.debugLevel > 2) {
1648
+ if (this.debugLevel > 2) {
1595
1649
  this.log(
1596
1650
  "getSessionGB: response data (saved to this.session):",
1597
1651
  );
@@ -1753,7 +1807,7 @@ class StbPlatform {
1753
1807
  "Step 1 of 1: logging in with username %s",
1754
1808
  this.config.username,
1755
1809
  );
1756
- if (this.config.debugLevel > 1) {
1810
+ if (this.debugLevel > 1) {
1757
1811
  this.log.warn("Step 1 of 1: post login to", axiosConfig.url);
1758
1812
  }
1759
1813
  axiosWS(axiosConfig)
@@ -1763,7 +1817,7 @@ class StbPlatform {
1763
1817
  response.status,
1764
1818
  response.statusText,
1765
1819
  );
1766
- if (this.config.debugLevel > 2) {
1820
+ if (this.debugLevel > 2) {
1767
1821
  this.log("getSession: response data (saved to this.session):");
1768
1822
  this.log(response.data);
1769
1823
  }
@@ -1900,7 +1954,7 @@ class StbPlatform {
1900
1954
  let apiAuthorizationUrl =
1901
1955
  this.configsvc.authorizationService.URL + "/v1/sso/authorization";
1902
1956
  this.log("Step 1 of 6: get authentication details");
1903
- if (this.config.debugLevel > 1) {
1957
+ if (this.debugLevel > 1) {
1904
1958
  this.log.warn(
1905
1959
  "Step 1 of 6: get authentication details from",
1906
1960
  apiAuthorizationUrl,
@@ -2122,7 +2176,7 @@ class StbPlatform {
2122
2176
  response.status,
2123
2177
  response.statusText,
2124
2178
  );
2125
- if (this.config.debugLevel > 2) {
2179
+ if (this.debugLevel > 2) {
2126
2180
  this.log(
2127
2181
  "getSessionBE: response data (saved to this.session):",
2128
2182
  );
@@ -2278,7 +2332,7 @@ class StbPlatform {
2278
2332
  this.configsvc.authorizationService.URL + "/v1/sso/authorization";
2279
2333
 
2280
2334
  this.log("Step 1 of 5: get authentication details");
2281
- if (this.config.debugLevel > 1) {
2335
+ if (this.debugLevel > 1) {
2282
2336
  this.log.warn("Step 1 of 5: GET", apiAuthorizationUrl);
2283
2337
  }
2284
2338
 
@@ -2294,12 +2348,14 @@ class StbPlatform {
2294
2348
  },
2295
2349
  });
2296
2350
 
2297
- this.log.warn(
2298
- "Step 1 of 5: response:",
2299
- step1Response.status,
2300
- step1Response.statusText,
2301
- );
2302
- if (this.config.debugLevel > 2) {
2351
+ if (this.debugLevel > 1) {
2352
+ this.log.warn(
2353
+ "Step 1 of 5: response:",
2354
+ step1Response.status,
2355
+ step1Response.statusText,
2356
+ );
2357
+ }
2358
+ if (this.debugLevel > 2) {
2303
2359
  this.log.warn("Step 1 of 5: response data:", step1Response.data);
2304
2360
  }
2305
2361
 
@@ -2377,7 +2433,7 @@ class StbPlatform {
2377
2433
  }
2378
2434
  });
2379
2435
 
2380
- if (this.config.debugLevel > 2) {
2436
+ if (this.debugLevel > 2) {
2381
2437
  page.on("framenavigated", (frame) => {
2382
2438
  if (frame === page.mainFrame()) {
2383
2439
  this.log.warn("Step 2: navigated to:", frame.url());
@@ -2399,8 +2455,8 @@ class StbPlatform {
2399
2455
  //
2400
2456
  // waitUntil: "networkidle2" waits until there are no more than 2
2401
2457
  // in-flight requests for 500ms, giving Akamai JS time to initialise.
2402
- this.log.warn("Step 2 of 5: navigate to Keycloak login page");
2403
- if (this.config.debugLevel > 1) {
2458
+ if (this.debugLevel > 1) {
2459
+ this.log.warn("Step 2 of 5: navigate to Keycloak login page");
2404
2460
  this.log.warn("Step 2 of 5: navigating to:", authorizationUri);
2405
2461
  }
2406
2462
 
@@ -2417,7 +2473,7 @@ class StbPlatform {
2417
2473
  try {
2418
2474
  await page.waitForSelector(usernameSelector, { timeout: 10000 });
2419
2475
  } catch (e) {
2420
- if (this.config.debugLevel > 1) {
2476
+ if (this.debugLevel > 1) {
2421
2477
  const html = await page.content();
2422
2478
  this.log.warn(
2423
2479
  "Step 2 of 5: unexpected page content (first 1000 chars):",
@@ -2442,7 +2498,7 @@ class StbPlatform {
2442
2498
  await page.type(usernameSelector, this.config.username, { delay: 50 });
2443
2499
  await page.type(passwordSelector, this.config.password, { delay: 50 });
2444
2500
 
2445
- if (this.config.debugLevel > 1) {
2501
+ if (this.debugLevel > 1) {
2446
2502
  this.log.warn("Step 3 of 5: credentials entered, submitting form");
2447
2503
  }
2448
2504
 
@@ -2466,7 +2522,7 @@ class StbPlatform {
2466
2522
  ]);
2467
2523
 
2468
2524
  if (!submitSelector) {
2469
- if (this.config.debugLevel > 1) {
2525
+ if (this.debugLevel > 1) {
2470
2526
  const html = await page.content();
2471
2527
  this.log.warn(
2472
2528
  "Step 3 of 5: page HTML (first 2000 chars):",
@@ -2479,7 +2535,7 @@ class StbPlatform {
2479
2535
  );
2480
2536
  }
2481
2537
 
2482
- if (this.config.debugLevel > 1) {
2538
+ if (this.debugLevel > 1) {
2483
2539
  this.log.warn("Step 3 of 5: submit button found:", submitSelector);
2484
2540
  }
2485
2541
 
@@ -2491,7 +2547,7 @@ class StbPlatform {
2491
2547
  ]);
2492
2548
 
2493
2549
  const finalUrl = page.url();
2494
- if (this.config.debugLevel > 1) {
2550
+ if (this.debugLevel > 1) {
2495
2551
  this.log.warn("Step 3 of 5: post-submit URL:", finalUrl);
2496
2552
  }
2497
2553
 
@@ -2503,7 +2559,7 @@ class StbPlatform {
2503
2559
  this.log("Step 4 of 5: extract authorization code");
2504
2560
 
2505
2561
  if (!finalUrl.startsWith(CH_LOGIN_SUCCESS_URL)) {
2506
- if (this.config.debugLevel > 1) {
2562
+ if (this.debugLevel > 1) {
2507
2563
  const html = await page.content();
2508
2564
  this.log.warn(
2509
2565
  "Step 4 of 5: unexpected final URL:",
@@ -2525,8 +2581,8 @@ class StbPlatform {
2525
2581
  );
2526
2582
  }
2527
2583
 
2528
- this.log.warn("Step 4 of 5: authorization code OK");
2529
- if (this.config.debugLevel > 2) {
2584
+ if (this.debugLevel > 2) {
2585
+ this.log.warn("Step 4 of 5: authorization code OK");
2530
2586
  this.log.warn("Step 4 of 5: authorizationCode:", authorizationCode);
2531
2587
  }
2532
2588
 
@@ -2538,7 +2594,7 @@ class StbPlatform {
2538
2594
  this.log("Step 5 of 5: exchange authorization code");
2539
2595
  currentSessionState = sessionState.AUTHENTICATING;
2540
2596
 
2541
- if (this.config.debugLevel > 1) {
2597
+ if (this.debugLevel > 1) {
2542
2598
  this.log.warn("Step 5 of 5: POST to", apiAuthorizationUrl);
2543
2599
  }
2544
2600
 
@@ -2561,12 +2617,12 @@ class StbPlatform {
2561
2617
  },
2562
2618
  );
2563
2619
 
2564
- this.log.warn(
2565
- "Step 5 of 5: response:",
2566
- step5Response.status,
2567
- step5Response.statusText,
2568
- );
2569
- if (this.config.debugLevel > 2) {
2620
+ if (this.debugLevel > 2) {
2621
+ this.log.warn(
2622
+ "Step 5 of 5: response:",
2623
+ step5Response.status,
2624
+ step5Response.statusText,
2625
+ );
2570
2626
  this.log.warn("Step 5 of 5: response data:", step5Response.data);
2571
2627
  }
2572
2628
 
@@ -2639,7 +2695,7 @@ class StbPlatform {
2639
2695
  // const GB_AUTH_OESP_URL = 'https://web-api-prod-obo.horizon.tv/oesp/v4/GB/eng/web';
2640
2696
  let apiAuthorizationUrl = GB_AUTH_OESP_URL + "/authorization";
2641
2697
  this.log("Step 1 of 7: get authentication details");
2642
- if (this.config.debugLevel > 1) {
2698
+ if (this.debugLevel > 1) {
2643
2699
  this.log.warn(
2644
2700
  "Step 1 of 7: get authentication details from",
2645
2701
  apiAuthorizationUrl,
@@ -2922,7 +2978,7 @@ class StbPlatform {
2922
2978
  "Cookies for the session:",
2923
2979
  cookieJar.getCookies(sessionUrl),
2924
2980
  );
2925
- if (this.config.debugLevel > 2) {
2981
+ if (this.debugLevel > 2) {
2926
2982
  this.log(
2927
2983
  "getSessionGB: response data (saved to this.session):",
2928
2984
  );
@@ -3033,7 +3089,7 @@ class StbPlatform {
3033
3089
 
3034
3090
  // exit immediately if the session does not exist
3035
3091
  if (currentSessionState !== sessionState.CONNECTED) {
3036
- if (this.config.debugLevel > 1) {
3092
+ if (this.debugLevel > 1) {
3037
3093
  this.log.warn(
3038
3094
  "refreshMasterChannelList: Session does not exist, exiting",
3039
3095
  );
@@ -3044,7 +3100,7 @@ class StbPlatform {
3044
3100
 
3045
3101
  // exit immediately if channel list has not expired
3046
3102
  if (this.masterChannelListExpiryDate > Date.now()) {
3047
- if (this.config.debugLevel >= 1) {
3103
+ if (this.debugLevel >= 1) {
3048
3104
  this.log.warn(
3049
3105
  "refreshMasterChannelList: Master channel list has not expired yet. Next refresh will occur after %s",
3050
3106
  this.masterChannelListExpiryDate.toLocaleString(),
@@ -3076,7 +3132,7 @@ class StbPlatform {
3076
3132
  url = url + "&language=en"; // language
3077
3133
  url = url + "&productClass=Orion-DASH"; // productClass, must be Orion-DASH
3078
3134
  //url = url + '&includeNotEntitled=false' // includeNotEntitled testing to see if this parameter is accepted
3079
- if (this.config.debugLevel > 0) {
3135
+ if (this.debugLevel > 0) {
3080
3136
  this.log.warn("refreshMasterChannelList: GET %s", url);
3081
3137
  }
3082
3138
  // call the webservice to get all available channels
@@ -3091,7 +3147,7 @@ class StbPlatform {
3091
3147
  };
3092
3148
  axiosWS(axiosConfig)
3093
3149
  .then((response) => {
3094
- if (this.config.debugLevel > 0) {
3150
+ if (this.debugLevel > 0) {
3095
3151
  this.log.warn(
3096
3152
  "refreshMasterChannelList: response: %s %s",
3097
3153
  response.status,
@@ -3119,7 +3175,7 @@ class StbPlatform {
3119
3175
  this.log.debug("Channels to process:", channels.length);
3120
3176
  for (let i = 0; i < channels.length; i++) {
3121
3177
  const channel = channels[i];
3122
- if (this.config.debugLevel > 2) {
3178
+ if (this.debugLevel > 2) {
3123
3179
  this.log(
3124
3180
  "Processing channel:",
3125
3181
  i,
@@ -3129,8 +3185,8 @@ class StbPlatform {
3129
3185
  );
3130
3186
  } // for debug purposes
3131
3187
  // log the detail of logicalChannelNumber 60 nicktoons, for which I have no subscription, as a test of entitlements
3132
- //if (this.config.debugLevel > 0) { if (channel.logicalChannelNumber === 60){ this.log('DEV: Logging Channel 60 to check entitlements :',channel); } }
3133
- //if (this.config.debugLevel > 0) { if (channel.logicalChannelNumber === 60){ this.log('DEV: Logging Channel 60 to check entitlements :',channel); } }
3188
+ //if (this.debugLevel > 0) { if (channel.logicalChannelNumber === 60){ this.log('DEV: Logging Channel 60 to check entitlements :',channel); } }
3189
+ //if (this.debugLevel > 0) { if (channel.logicalChannelNumber === 60){ this.log('DEV: Logging Channel 60 to check entitlements :',channel); } }
3134
3190
  this.masterChannelList.push({
3135
3191
  id: channel.id,
3136
3192
  name: cleanNameForHomeKit(channel.name),
@@ -3138,8 +3194,12 @@ class StbPlatform {
3138
3194
  linearProducts: channel.linearProducts,
3139
3195
  });
3140
3196
  }
3197
+ // add a map for faster access to the master channel list
3198
+ this.masterChannelMap = new Map(
3199
+ this.masterChannelList.map((ch) => [ch.id, ch]),
3200
+ );
3141
3201
 
3142
- if (this.config.debugLevel > 0) {
3202
+ if (this.debugLevel > 0) {
3143
3203
  this.log.warn(
3144
3204
  "refreshMasterChannelList: Master channel list refreshed with %s channels, valid until %s",
3145
3205
  this.masterChannelList.length,
@@ -3209,20 +3269,20 @@ class StbPlatform {
3209
3269
  "/" +
3210
3270
  ctryCodeForUrl +
3211
3271
  "/en/config-service/conf/web/backoffice.json";
3212
- if (this.config.debugLevel > 0) {
3272
+ if (this.debugLevel > 0) {
3213
3273
  this.log.warn("getConfig: GET %s", url);
3214
3274
  }
3215
3275
  axiosWS
3216
3276
  .get(url)
3217
3277
  .then((response) => {
3218
- if (this.config.debugLevel > 0) {
3278
+ if (this.debugLevel > 0) {
3219
3279
  this.log.warn(
3220
3280
  "getConfig: response: %s %s",
3221
3281
  response.status,
3222
3282
  response.statusText,
3223
3283
  );
3224
3284
  }
3225
- if (this.config.debugLevel > 2) {
3285
+ if (this.debugLevel > 2) {
3226
3286
  this.log.warn(
3227
3287
  "getConfig: response data (saved to this.configsvc):",
3228
3288
  );
@@ -3289,21 +3349,21 @@ class StbPlatform {
3289
3349
  },
3290
3350
  };
3291
3351
  }
3292
- if (this.config.debugLevel > 0) {
3352
+ if (this.debugLevel > 0) {
3293
3353
  this.log.warn("getPersonalizationData: GET %s", url);
3294
3354
  }
3295
3355
  // this.log('getPersonalizationData: GET %s', url);
3296
3356
  axiosWS
3297
3357
  .get(url, config)
3298
3358
  .then((response) => {
3299
- if (this.config.debugLevel > 0) {
3359
+ if (this.debugLevel > 0) {
3300
3360
  this.log.warn(
3301
3361
  "getPersonalizationData: response: %s %s",
3302
3362
  response.status,
3303
3363
  response.statusText,
3304
3364
  );
3305
3365
  }
3306
- if (this.config.debugLevel > 2) {
3366
+ if (this.debugLevel > 2) {
3307
3367
  // DEBUG
3308
3368
  this.log.warn(
3309
3369
  "getPersonalizationData: response data (saved to this.customer):",
@@ -3319,7 +3379,7 @@ class StbPlatform {
3319
3379
  //this.log('getPersonalizationData: this.stbDevices.length:', this.stbDevices.length)
3320
3380
  if (this.stbDevices.length > 0) {
3321
3381
  this.devices.forEach((device) => {
3322
- if (this.config.debugLevel > 2) {
3382
+ if (this.debugLevel > 2) {
3323
3383
  // DEBUG
3324
3384
  this.log.warn(
3325
3385
  "getPersonalizationData: device settings for device %s:",
@@ -3411,7 +3471,7 @@ class StbPlatform {
3411
3471
 
3412
3472
  // set the Personalization Data for the current device via web request PUT
3413
3473
  async setPersonalizationDataForDevice(deviceId, deviceSettings) {
3414
- if (this.config.debugLevel > 0) {
3474
+ if (this.debugLevel > 0) {
3415
3475
  this.log.warn(
3416
3476
  "setPersonalizationDataForDevice: deviceSettings:",
3417
3477
  deviceSettings,
@@ -3446,14 +3506,14 @@ class StbPlatform {
3446
3506
  },
3447
3507
  };
3448
3508
  }
3449
- if (this.config.debugLevel > 0) {
3509
+ if (this.debugLevel > 0) {
3450
3510
  this.log.warn("setPersonalizationDataForDevice: PUT %s", url);
3451
3511
  }
3452
3512
  axiosWS
3453
3513
  .put(url, data, config)
3454
3514
  .then((response) => {
3455
3515
  // returns 204 No Content when successful
3456
- if (this.config.debugLevel > 0) {
3516
+ if (this.debugLevel > 0) {
3457
3517
  this.log.warn(
3458
3518
  "setPersonalizationDataForDevice: response: %s %s",
3459
3519
  response.status,
@@ -3507,28 +3567,28 @@ class StbPlatform {
3507
3567
  },
3508
3568
  };
3509
3569
  }
3510
- if (this.config.debugLevel > 0) {
3570
+ if (this.debugLevel > 0) {
3511
3571
  this.log.warn("getEntitlements: GET %s", url);
3512
3572
  }
3513
3573
  // this.log('getEntitlements: GET %s', url);
3514
3574
  axiosWS
3515
3575
  .get(url, config)
3516
3576
  .then((response) => {
3517
- if (this.config.debugLevel > 0) {
3577
+ if (this.debugLevel > 0) {
3518
3578
  this.log.warn(
3519
3579
  "getEntitlements: response: %s %s",
3520
3580
  response.status,
3521
3581
  response.statusText,
3522
3582
  );
3523
3583
  }
3524
- if (this.config.debugLevel > 2) {
3584
+ if (this.debugLevel > 2) {
3525
3585
  this.log.warn(
3526
3586
  "getEntitlements: response data (saved to this.entitlements):",
3527
3587
  );
3528
3588
  this.log.warn(response.data);
3529
3589
  }
3530
3590
  this.entitlements = response.data; // store the entire entitlements data for future use in this.customer.entitlements
3531
- if (this.config.debugLevel > 0) {
3591
+ if (this.debugLevel > 0) {
3532
3592
  this.log.warn(
3533
3593
  "getEntitlements: entitlements found:",
3534
3594
  this.entitlements.entitlements.length,
@@ -3591,21 +3651,21 @@ class StbPlatform {
3591
3651
  "/customers/" +
3592
3652
  householdId +
3593
3653
  "/recordings/state"; // limit to 20 recordings for performance
3594
- if (this.config.debugLevel > 0) {
3654
+ if (this.debugLevel > 0) {
3595
3655
  this.log.warn("getRecordingState: GET %s", url);
3596
3656
  }
3597
3657
  axiosWS
3598
3658
  .get(url, config)
3599
3659
  .then((response) => {
3600
3660
  // log at level 1, 2
3601
- if (this.config.debugLevel > 0) {
3661
+ if (this.debugLevel > 0) {
3602
3662
  this.log.warn(
3603
3663
  "getRecordingState: response: %s %s",
3604
3664
  response.status,
3605
3665
  response.statusText,
3606
3666
  );
3607
3667
  }
3608
- if (this.config.debugLevel > 1) {
3668
+ if (this.debugLevel > 1) {
3609
3669
  this.log.warn("getRecordingState: response data:");
3610
3670
  this.log.warn(response.data);
3611
3671
  }
@@ -3622,7 +3682,7 @@ class StbPlatform {
3622
3682
  // mostRelevantEpisode.recordingState: 'ongoing',
3623
3683
  // mostRelevantEpisode.recordingType: 'nDVR',
3624
3684
  // logging at level 2
3625
- if (this.config.debugLevel > 1) {
3685
+ if (this.debugLevel > 1) {
3626
3686
  this.log.warn(
3627
3687
  "getRecordingState: Recordings length %s:",
3628
3688
  response.data.data.length,
@@ -3642,7 +3702,7 @@ class StbPlatform {
3642
3702
  networkOngoingRecordings = 0;
3643
3703
 
3644
3704
  // look for planned network single recordings: (type = "single" = one object, type = "season" = array)
3645
- if (this.config.debugLevel > 0) {
3705
+ if (this.debugLevel > 0) {
3646
3706
  this.log.warn(
3647
3707
  "getRecordingState: searching for ongoing network recordings",
3648
3708
  );
@@ -3658,7 +3718,7 @@ class StbPlatform {
3658
3718
 
3659
3719
  // find if any local device recordings are ongoing, for each device, as each device can have a HDD
3660
3720
  this.devices.forEach((device) => {
3661
- if (this.config.debugLevel > 0) {
3721
+ if (this.debugLevel > 0) {
3662
3722
  this.log.warn(
3663
3723
  "getRecordingState: Checking device %s for ongoing local HDD recordings",
3664
3724
  device.deviceId,
@@ -3667,7 +3727,7 @@ class StbPlatform {
3667
3727
  if (device.capabilities.hasHDD) {
3668
3728
  // device has HDD, look for local recordings
3669
3729
  // look for ongoing local single recordings: (type = "single" = one object, type = "season" = array)
3670
- if (this.config.debugLevel > 0) {
3730
+ if (this.debugLevel > 0) {
3671
3731
  this.log.warn(
3672
3732
  "getRecordingState: %s: searching for ongoing local recordings for this device",
3673
3733
  device.deviceId,
@@ -3803,21 +3863,21 @@ class StbPlatform {
3803
3863
  "/customers/" +
3804
3864
  householdId +
3805
3865
  "/bookings?limit=10&sort=time&sortOrder=asc"; // limit to 10 recordings for performance
3806
- if (this.config.debugLevel > 0) {
3866
+ if (this.debugLevel > 0) {
3807
3867
  this.log.warn("getRecordingBookings: GET %s", url);
3808
3868
  }
3809
3869
  axiosWS
3810
3870
  .get(url, config)
3811
3871
  .then((response) => {
3812
3872
  // log at level 1, 2
3813
- if (this.config.debugLevel > 0) {
3873
+ if (this.debugLevel > 0) {
3814
3874
  this.log.warn(
3815
3875
  "getRecordingBookings: response: %s %s",
3816
3876
  response.status,
3817
3877
  response.statusText,
3818
3878
  );
3819
3879
  }
3820
- if (this.config.debugLevel > 1) {
3880
+ if (this.debugLevel > 1) {
3821
3881
  this.log.warn("getRecordingBookings: response data:");
3822
3882
  this.log.warn(response.data);
3823
3883
  }
@@ -3834,7 +3894,7 @@ class StbPlatform {
3834
3894
  // mostRelevantEpisode.recordingState: 'ongoing',
3835
3895
  // mostRelevantEpisode.recordingType: 'nDVR',
3836
3896
  // logging at level 2
3837
- if (this.config.debugLevel > 1) {
3897
+ if (this.debugLevel > 1) {
3838
3898
  this.log.warn(
3839
3899
  "getRecordingBookings: Recordings length %s:",
3840
3900
  response.data.data.length,
@@ -3856,7 +3916,7 @@ class StbPlatform {
3856
3916
  networkPlannedRecordings = 0;
3857
3917
 
3858
3918
  // look for planned network recordings: (type = "single" = one object, type = "season" = array)
3859
- if (this.config.debugLevel > 0) {
3919
+ if (this.debugLevel > 0) {
3860
3920
  this.log.warn(
3861
3921
  "getRecordingBookings: searching for planned network recordings",
3862
3922
  );
@@ -3881,7 +3941,7 @@ class StbPlatform {
3881
3941
 
3882
3942
  // find if any local recordings are booked, for each device, as each device can have a HDD
3883
3943
  this.devices.forEach((device) => {
3884
- if (this.config.debugLevel > 0) {
3944
+ if (this.debugLevel > 0) {
3885
3945
  this.log.warn(
3886
3946
  "getRecordingBookings: Checking device %s for planned local HDD recordings",
3887
3947
  device.deviceId,
@@ -3890,7 +3950,7 @@ class StbPlatform {
3890
3950
  if (device.capabilities.hasHDD) {
3891
3951
  // device has HDD, look for local recordings
3892
3952
  // look for planned local single recordings: (type = "single")
3893
- if (this.config.debugLevel > 0) {
3953
+ if (this.debugLevel > 0) {
3894
3954
  this.log.warn(
3895
3955
  "getRecordingBookings: %s: searching for planned local recordings for this device",
3896
3956
  device.deviceId,
@@ -3930,7 +3990,7 @@ class StbPlatform {
3930
3990
  localPlannedRecordings,
3931
3991
  networkPlannedRecordings,
3932
3992
  currProgramMode,
3933
- Object.keys(Characteristic.ProgramMode)[currProgramMode + 1],
3993
+ CHAR_NAMES.ProgramMode[currProgramMode + 1],
3934
3994
  );
3935
3995
  // mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType) {
3936
3996
  this.mqttDeviceStateHandler(
@@ -4051,7 +4111,7 @@ class StbPlatform {
4051
4111
  async getMqttToken(oespUsername, accessToken, householdId) {
4052
4112
  this.log.debug("Getting mqtt token for householdId %s", householdId);
4053
4113
  // get a JSON web token from the supplied accessToken and householdId
4054
- if (this.config.debugLevel > 1) {
4114
+ if (this.debugLevel > 1) {
4055
4115
  this.log.warn("getMqttToken");
4056
4116
  }
4057
4117
  // robustness checks
@@ -4095,7 +4155,7 @@ class StbPlatform {
4095
4155
 
4096
4156
  try {
4097
4157
  const response = await axiosWS(mqttAxiosConfig);
4098
- if (this.config.debugLevel > 0) {
4158
+ if (this.debugLevel > 0) {
4099
4159
  this.log.warn("getMqttToken: response.data:", response.data);
4100
4160
  }
4101
4161
  mqttUsername = householdId;
@@ -4115,7 +4175,7 @@ class StbPlatform {
4115
4175
  startMqttClient(mqttUsername, mqttPassword) {
4116
4176
  return new Promise((resolve, reject) => {
4117
4177
  try {
4118
- if (this.config.debugLevel > 0) {
4178
+ if (this.debugLevel > 0) {
4119
4179
  this.log("Starting mqttClient...");
4120
4180
  }
4121
4181
  if (currentSessionState !== sessionState.CONNECTED) {
@@ -4134,7 +4194,7 @@ class StbPlatform {
4134
4194
 
4135
4195
  // connect to the MQTT broker
4136
4196
  const mqttBrokerUrl = this.configsvc.mqttBroker.URL;
4137
- if (this.config.debugLevel > 0) {
4197
+ if (this.debugLevel > 0) {
4138
4198
  this.log.warn("startMqttClient: mqttBrokerUrl:", mqttBrokerUrl);
4139
4199
  this.log.warn(
4140
4200
  "startMqttClient: Creating mqttClient with username %s, password %s",
@@ -4168,7 +4228,7 @@ class StbPlatform {
4168
4228
  username: mqttUsername,
4169
4229
  password: mqttPassword,
4170
4230
  });
4171
- if (this.config.debugLevel > 0) {
4231
+ if (this.debugLevel > 0) {
4172
4232
  this.log.warn(
4173
4233
  "startMqttClient: mqttBroker connect request sent using mqttClientId %s, waiting for connect event",
4174
4234
  mqttClientId,
@@ -4267,7 +4327,7 @@ class StbPlatform {
4267
4327
  this.lastMqttMessageReceived = Date.now();
4268
4328
 
4269
4329
  let mqttMessage = JSON.parse(message);
4270
- if (this.config.debugLevel > 0) {
4330
+ if (this.debugLevel > 0) {
4271
4331
  this.log.warn(
4272
4332
  "mqttClient: Received Message: \r\nTopic: %s\r\nMessage: (next log entry)",
4273
4333
  topic,
@@ -4294,14 +4354,14 @@ class StbPlatform {
4294
4354
  // Message: { action: 'OPS.getProfilesUpdate', source: '3C36E4-EOSSTB-00365657xxxx', ... }
4295
4355
  // Message: { action: 'OPS.getDeviceUpdate', source: '3C36E4-EOSSTB-00365657xxxx', deviceId: '3C36E4-EOSSTB-00365657xxxx' }
4296
4356
  if (topic.includes(mqttUsername + "/personalizationService")) {
4297
- if (this.config.debugLevel > 0) {
4357
+ if (this.debugLevel > 0) {
4298
4358
  this.log.warn("mqttClient: %s: action", mqttMessage.action);
4299
4359
  }
4300
4360
  if (
4301
4361
  mqttMessage.action === "OPS.getProfilesUpdate" ||
4302
4362
  mqttMessage.action === "OPS.getDeviceUpdate"
4303
4363
  ) {
4304
- if (this.config.debugLevel > 0) {
4364
+ if (this.debugLevel > 0) {
4305
4365
  this.log.warn(
4306
4366
  "mqttClient: %s, calling getPersonalizationData",
4307
4367
  mqttMessage.action,
@@ -4317,7 +4377,7 @@ class StbPlatform {
4317
4377
  // Topic: Topic: 107xxxx_ch/recordingStatus
4318
4378
  // Message: {"id":"crid:~~2F~~2Fgn.tv~~2F2004781~~2FEP019440730003,imi:2d369682b865679f2e5182ea52a93410171cfdc8","event":"scheduleEvent","transactionId":"/CH/eng/web/networkdvrrecordings - 013f12fc-23ef-4b77-a244-eeeea0c6901c"}
4319
4379
  if (topic.includes(mqttUsername + "/recordingStatus")) {
4320
- if (this.config.debugLevel > 0) {
4380
+ if (this.debugLevel > 0) {
4321
4381
  this.log.warn("mqttClient: event: %s", mqttMessage.event);
4322
4382
  }
4323
4383
  this.refreshRecordings(this.session.householdId); // request a refresh of recording data
@@ -4328,7 +4388,7 @@ class StbPlatform {
4328
4388
  // Message: {"deviceType":"STB","source":"3C36E4-EOSSTB-00365657xxxx","state":"ONLINE_RUNNING","mac":"F8:F5:32:45:DE:52","ipAddress":"192.168.0.33/255.255.255.0"}
4329
4389
  if (topic.includes("/status")) {
4330
4390
  if (mqttMessage.deviceType === "STB") {
4331
- if (this.config.debugLevel > 0) {
4391
+ if (this.debugLevel > 0) {
4332
4392
  this.log.warn(
4333
4393
  "mqttClient: STB status: Detecting Power State: Received Message of deviceType %s for %s",
4334
4394
  mqttMessage.deviceType,
@@ -4373,7 +4433,7 @@ class StbPlatform {
4373
4433
  );
4374
4434
  break;
4375
4435
  }
4376
- if (this.config.debugLevel > 0) {
4436
+ if (this.debugLevel > 0) {
4377
4437
  this.log.warn("mqttClient: %s %s", deviceId, stbState);
4378
4438
  }
4379
4439
  }
@@ -4399,7 +4459,7 @@ class StbPlatform {
4399
4459
  uiStatusFingerprint === this.lastUiStatusFingerprint &&
4400
4460
  now - this.lastUiStatusFingerprintTime < 500
4401
4461
  ) {
4402
- if (this.config.debugLevel > 0) {
4462
+ if (this.debugLevel > 0) {
4403
4463
  this.log.warn(
4404
4464
  "mqttClient: CPE.uiStatus: duplicate state fingerprint within 500ms, skipping",
4405
4465
  );
@@ -4409,7 +4469,7 @@ class StbPlatform {
4409
4469
  this.lastUiStatusFingerprint = uiStatusFingerprint;
4410
4470
  this.lastUiStatusFingerprintTime = now;
4411
4471
 
4412
- if (this.config.debugLevel > 0) {
4472
+ if (this.debugLevel > 0) {
4413
4473
  this.log.warn(
4414
4474
  "mqttClient: CPE.uiStatus received from %s",
4415
4475
  mqttMessage.source,
@@ -4424,7 +4484,7 @@ class StbPlatform {
4424
4484
  );
4425
4485
  }
4426
4486
  // if we have this message, then the power is on. Sometimes the message arrives before the status topic with the power state
4427
- currStatusActive = Characteristic.Active.ACTIVE; // ensure statusActive is set to Active
4487
+ currStatusActive = Characteristic.Active.ACTIVE; // ensure StatusActive is set to Active
4428
4488
  currPowerState = Characteristic.Active.ACTIVE; // ensure power is set to ON
4429
4489
 
4430
4490
  this.lastMqttUiStatusMessageReceived = now;
@@ -4432,7 +4492,7 @@ class StbPlatform {
4432
4492
  const cpeUiStatus = mqttMessage.status;
4433
4493
  // normal TV: cpeUiStatus = mainUI
4434
4494
  // app: cpeUiStatus = apps (YouTube, Netflix, etc)
4435
- if (this.config.debugLevel > 0) {
4495
+ if (this.debugLevel > 0) {
4436
4496
  this.log.warn(
4437
4497
  "mqttClient: CPE.uiStatus: cpeUiStatus:",
4438
4498
  cpeUiStatus,
@@ -4449,7 +4509,7 @@ class StbPlatform {
4449
4509
  // destructure playerState for cleaner access to nested properties
4450
4510
  const playerState = cpeUiStatus.playerState;
4451
4511
  currSourceType = playerState.sourceType;
4452
- if (this.config.debugLevel > 1) {
4512
+ if (this.debugLevel > 1) {
4453
4513
  this.log.warn(
4454
4514
  "mqttClient: mainUI: Detected mqtt playerState.speed:",
4455
4515
  playerState.speed,
@@ -4519,11 +4579,10 @@ class StbPlatform {
4519
4579
  currChannelId =
4520
4580
  playerState.source.channelId || NO_CHANNEL_ID; // must be a string
4521
4581
  currEventId = playerState.source.eventId; // the title (program) id
4522
- if (this.config.debugLevel > 0 && this.masterChannelList) {
4582
+ if (this.debugLevel > 0 && this.masterChannelList) {
4523
4583
  let currentChannelName; // let is scoped to the current {} block
4524
- let curChannel = this.masterChannelList.find(
4525
- (channel) => channel.id === currChannelId,
4526
- );
4584
+ let curChannel =
4585
+ this.masterChannelMap?.get(currChannelId);
4527
4586
  if (curChannel) {
4528
4587
  currentChannelName = curChannel.name;
4529
4588
  }
@@ -4567,11 +4626,7 @@ class StbPlatform {
4567
4626
  default:
4568
4627
  // check if the app channel exists in the master channel list, if not, push it, using the user-defined name if one exists
4569
4628
  currChannelId = cpeUiStatus.appsState.id;
4570
- if (
4571
- this.masterChannelList.findIndex(
4572
- (channel) => channel.id === currChannelId,
4573
- ) === -1
4574
- ) {
4629
+ if (!this.masterChannelMap.has(currChannelId)) {
4575
4630
  this.log(
4576
4631
  "App %s detected. Adding to the master channel list at index %s with channelId %s",
4577
4632
  cpeUiStatus.appsState.appName,
@@ -4581,16 +4636,17 @@ class StbPlatform {
4581
4636
  const entitlementId =
4582
4637
  this.entitlements.entitlements[0].id;
4583
4638
  // for easy identification, make the logicalChannelNumber and channelNumber app10000 + the index number
4584
- this.masterChannelList.push({
4639
+ const newAppChannel = {
4585
4640
  id: currChannelId,
4586
4641
  name: cleanNameForHomeKit(
4587
4642
  cpeUiStatus.appsState.appName,
4588
4643
  ),
4589
4644
  logicalChannelNumber:
4590
- 10000 + this.masterChannelList.length, // integer
4591
- linearProducts: entitlementId, // must be a valid entitlement id
4592
- //channelNumber: 'app' + (10000 + this.masterChannelList.length)
4593
- });
4645
+ 10000 + this.masterChannelList.length,
4646
+ linearProducts: entitlementId,
4647
+ };
4648
+ this.masterChannelList.push(newAppChannel);
4649
+ this.masterChannelMap.set(currChannelId, newAppChannel);
4594
4650
  }
4595
4651
  }
4596
4652
  break;
@@ -4606,7 +4662,7 @@ class StbPlatform {
4606
4662
  // there's also a pullFromTV
4607
4663
  // {"source":"7028f103-8494-4f79-9b76-beb67a2e5caa","type":"CPE.pullFromTV","runtimeType":"pull"}
4608
4664
  if (mqttMessage.type === "CPE.pushToTV.rsp") {
4609
- if (this.config.debugLevel > 0) {
4665
+ if (this.debugLevel > 0) {
4610
4666
  this.log.warn(
4611
4667
  "mqttClient: CPE.pushToTV.rsp: received from %s",
4612
4668
  mqttMessage.source,
@@ -4804,7 +4860,7 @@ class StbPlatform {
4804
4860
  // end the mqtt session cleanly
4805
4861
  endMqttSession() {
4806
4862
  return new Promise((resolve, reject) => {
4807
- if (this.config.debugLevel > -1) {
4863
+ if (this.debugLevel > -1) {
4808
4864
  this.log("Shutting down mqttClient...");
4809
4865
  }
4810
4866
  // https://github.com/mqttjs/MQTT.js#end
@@ -4834,7 +4890,7 @@ class StbPlatform {
4834
4890
  currInputSourceType,
4835
4891
  ) {
4836
4892
  try {
4837
- if (this.config.debugLevel > 1) {
4893
+ if (this.debugLevel > 1) {
4838
4894
  this.log.warn(
4839
4895
  "mqttDeviceStateHandler: calling updateDeviceState with deviceId %s, powerState %s, mediaState %s, channelId %s, eventId %s, sourceType %s, profileDataChanged %s, statusFault %s, programMode %s, statusActive %s, currInputDeviceType %s, currInputSourceType %s",
4840
4896
  deviceId,
@@ -4882,7 +4938,7 @@ class StbPlatform {
4882
4938
  mqttPublishMessage(Topic, Message, Options) {
4883
4939
  try {
4884
4940
  // Syntax: {'test1': {qos: 0}, 'test2': {qos: 1}}
4885
- if (this.config.debugLevel > 0) {
4941
+ if (this.debugLevel > 0) {
4886
4942
  this.log.warn(
4887
4943
  "mqttPublishMessage: Publish Message:\r\nTopic: %s\r\nMessage: %s\r\nOptions: %s",
4888
4944
  Topic,
@@ -4901,7 +4957,7 @@ class StbPlatform {
4901
4957
  errDetail,
4902
4958
  );
4903
4959
  } else {
4904
- if (this.config.debugLevel > 0) {
4960
+ if (this.debugLevel > 0) {
4905
4961
  this.log.warn("mqttPublishMessage: Published OK to %s", Topic);
4906
4962
  }
4907
4963
  }
@@ -4914,7 +4970,7 @@ class StbPlatform {
4914
4970
 
4915
4971
  // subscribe to an mqtt topic, with logging, to help in debugging
4916
4972
  mqttSubscribeToTopic(Topic) {
4917
- if (this.config.debugLevel > 0) {
4973
+ if (this.debugLevel > 0) {
4918
4974
  this.log.warn("mqttSubscribeToTopic: Subscribe to topic:", Topic);
4919
4975
  }
4920
4976
  mqttClient.subscribe(Topic, (err, granted) => {
@@ -4939,11 +4995,13 @@ class StbPlatform {
4939
4995
  Topic,
4940
4996
  );
4941
4997
  } else {
4942
- this.log.warn(
4943
- "mqttSubscribeToTopic: Subscribed OK to %s (granted QoS: %s)",
4944
- Topic,
4945
- grantedQos,
4946
- );
4998
+ if (this.debugLevel > 0) {
4999
+ this.log.warn(
5000
+ "mqttSubscribeToTopic: Subscribed OK to %s (granted QoS: %s)",
5001
+ Topic,
5002
+ grantedQos,
5003
+ );
5004
+ }
4947
5005
  }
4948
5006
  }
4949
5007
  });
@@ -4951,7 +5009,7 @@ class StbPlatform {
4951
5009
 
4952
5010
  // unsubscribe to an mqtt topic, with logging, to help in debugging
4953
5011
  mqttUnsubscribeToTopic(Topic) {
4954
- if (this.config.debugLevel > 0) {
5012
+ if (this.debugLevel > 0) {
4955
5013
  this.log.warn("mqttUnsubscribeToTopic: Unsubscribe from topic:", Topic);
4956
5014
  }
4957
5015
  mqttClient.unsubscribe(Topic, (err) => {
@@ -4965,7 +5023,7 @@ class StbPlatform {
4965
5023
  errDetail,
4966
5024
  );
4967
5025
  } else {
4968
- if (this.config.debugLevel > 0) {
5026
+ if (this.debugLevel > 0) {
4969
5027
  this.log.warn(
4970
5028
  "mqttUnsubscribeToTopic: Unsubscribed OK from %s",
4971
5029
  Topic,
@@ -4986,7 +5044,7 @@ class StbPlatform {
4986
5044
  mac: "",
4987
5045
  ipAddress: "",
4988
5046
  });
4989
- if (this.config.debugLevel > 0) {
5047
+ if (this.debugLevel > 0) {
4990
5048
  this.log.warn("setHgoOnlineRunning: publishing to topic:", topic);
4991
5049
  }
4992
5050
  this.mqttPublishMessage(topic, message, { qos: 2, retain: true });
@@ -4997,7 +5055,7 @@ class StbPlatform {
4997
5055
  // the friendlyDeviceName appears on the TV in a popup window
4998
5056
  switchChannel(deviceId, deviceName, channelId, channelName) {
4999
5057
  try {
5000
- if (this.config.debugLevel > 0) {
5058
+ if (this.debugLevel > 0) {
5001
5059
  this.log.warn(
5002
5060
  "switchChannel: channelId %s %s on %s %s",
5003
5061
  channelId,
@@ -5029,7 +5087,7 @@ class StbPlatform {
5029
5087
  speed: 1,
5030
5088
  },
5031
5089
  });
5032
- if (this.config.debugLevel > 0) {
5090
+ if (this.debugLevel > 0) {
5033
5091
  this.log.warn("switchChannel: publishing to topic:", topic);
5034
5092
  }
5035
5093
  this.mqttPublishMessage(topic, payload, {
@@ -5050,7 +5108,7 @@ class StbPlatform {
5050
5108
  // speed can be one of: -64 -30 -6 -2 0 2 6 30 64. 0=Paused, 1=Play, >1=FastForward, <0=Rewind
5051
5109
  setMediaState(deviceId, deviceName, channelId, speed) {
5052
5110
  try {
5053
- if (this.config.debugLevel > 0) {
5111
+ if (this.debugLevel > 0) {
5054
5112
  this.log.warn(
5055
5113
  "setMediaState: set state to %s for channelId %s on %s %s",
5056
5114
  speed,
@@ -5094,7 +5152,7 @@ class StbPlatform {
5094
5152
  // Retain: false, QOS: 0
5095
5153
  setPlayerPosition(deviceId, deviceName, relativePosition) {
5096
5154
  try {
5097
- if (this.config.debugLevel > 0) {
5155
+ if (this.debugLevel > 0) {
5098
5156
  this.log.warn("setPlayerPosition: deviceId:", deviceId);
5099
5157
  }
5100
5158
  if (mqttUsername) {
@@ -5125,7 +5183,7 @@ class StbPlatform {
5125
5183
  // send a remote control keySequence to the settopbox via mqtt
5126
5184
  async sendKey(deviceId, deviceName, keySequence) {
5127
5185
  try {
5128
- if (this.config.debugLevel > 0) {
5186
+ if (this.debugLevel > 0) {
5129
5187
  this.log.warn(
5130
5188
  "sendKey: keySequence %s, deviceName %s, deviceId %s",
5131
5189
  keySequence,
@@ -5350,7 +5408,7 @@ class StbPlatform {
5350
5408
  "eventType": "keyDownUp"
5351
5409
  }
5352
5410
  }
5353
- */
5411
+ */
5354
5412
  const payload = JSON.stringify({
5355
5413
  source: mqttClientId,
5356
5414
  type: "CPE.KeyEvent",
@@ -5362,9 +5420,7 @@ class StbPlatform {
5362
5420
  eventType: "keyDownUp",
5363
5421
  },
5364
5422
  });
5365
- this.mqttPublishMessage(topic,payload,
5366
- { qos: 2, retain: true },
5367
- );
5423
+ this.mqttPublishMessage(topic, payload, { qos: 2, retain: true });
5368
5424
  this.log.debug("sendKey: key %s: send %s done", i + 1, keyName);
5369
5425
 
5370
5426
  // set the Target Media State after key has been sent
@@ -5431,7 +5487,7 @@ class StbPlatform {
5431
5487
  // get the settopbox UI status from the settopbox via mqtt
5432
5488
  getUiStatus(deviceId, mqttClientId) {
5433
5489
  try {
5434
- if (this.config.debugLevel > 0) {
5490
+ if (this.debugLevel > 0) {
5435
5491
  this.log.warn("getUiStatus for deviceId %s", deviceId);
5436
5492
  }
5437
5493
  if (mqttUsername) {
@@ -5475,6 +5531,7 @@ class StbDevice {
5475
5531
  this.entitlements = this.platform.entitlements;
5476
5532
 
5477
5533
  this.deviceId = this.device.deviceId;
5534
+ this._configDevice = this._getConfigDevice(); // cache it once, never changes
5478
5535
  this.profileId = -1; // default -1
5479
5536
 
5480
5537
  // set default name on restart, max 14 char
@@ -5487,9 +5544,7 @@ class StbDevice {
5487
5544
 
5488
5545
  // allow user override of device name via config, but limit to max 14 char
5489
5546
  if (this.config.devices) {
5490
- const configDevice = this.config.devices.find(
5491
- (device) => device.deviceId === this.deviceId,
5492
- );
5547
+ const configDevice = this._configDevice;
5493
5548
  if (configDevice && configDevice.name) {
5494
5549
  this.name = configDevice.name.substring(0, SETTOPBOX_NAME_MAXLEN);
5495
5550
  }
@@ -5507,29 +5562,28 @@ class StbDevice {
5507
5562
  this.accessoryConfigured; // true when the accessory is configured
5508
5563
 
5509
5564
  // initial states. Will be updated by mqtt messages
5565
+ this.currentStatusFault = Characteristic.StatusFault.NO_FAULT;
5566
+ this.currentInUse = Characteristic.InUse.NOT_IN_USE;
5510
5567
  this.currentPowerState = Characteristic.Active.INACTIVE;
5511
- this.previousPowerState = Characteristic.Active.INACTIVE;
5568
+ this.currentStatusActive = Characteristic.Active.INACTIVE;
5569
+
5512
5570
  this.currentChannelId = NO_CHANNEL_ID; // string eg SV09038
5513
- this.lastKeyMacroChannelId = null; // string eg $KeyMacro1
5514
- this.currentClosedCaptionsState = Characteristic.ClosedCaptions.DISABLED;
5515
- this.previousClosedCaptionsState = Characteristic.ClosedCaptions.DISABLED;
5571
+ this.currentClosedCaptions = Characteristic.ClosedCaptions.DISABLED;
5516
5572
  this.currentMediaState = Characteristic.CurrentMediaState.STOP;
5517
5573
  this.targetMediaState = Characteristic.TargetMediaState.STOP;
5518
5574
  this.currentPictureMode = Characteristic.PictureMode.STANDARD;
5519
- this.previousPictureMode = null;
5520
5575
  this.currentRecordingState = recordingState.IDLE;
5521
- this.previousRecordingState = null;
5522
- this.customPictureMode = 0; // default 0
5523
- this.currentSourceType = "UNKNOWN";
5524
- // custom characteristics, default values must be legal values otherwise Homebridge shows a warning
5525
- this.currentStatusFault = Characteristic.StatusFault.NO_FAULT;
5526
- this.currentInUse = Characteristic.InUse.NOT_IN_USE;
5527
- this.previousInUse = Characteristic.InUse.NOT_IN_USE;
5528
5576
  this.currentProgramMode = Characteristic.ProgramMode.NO_PROGRAM_SCHEDULED;
5529
- this.currentStatusActive = Characteristic.Active.ACTIVE; // bool, use o=NotStatusActive, 1=StatusActive
5530
5577
  this.currentInputSourceType = Characteristic.InputSourceType.TUNER;
5531
5578
  this.currentInputDeviceType = Characteristic.InputDeviceType.TV;
5532
5579
 
5580
+ this.lastKeyMacroChannelId = null; // string eg $KeyMacro1
5581
+
5582
+ this.customPictureMode = 0; // default 0
5583
+ this.currentSourceType = "UNKNOWN";
5584
+
5585
+ // set defaults for the monitored characteristics
5586
+
5533
5587
  this.lastRemoteKeyPressed = -1; // holds the last key pressed, -1 = no key
5534
5588
  this.lastRemoteKeyPress0 = []; // holds the time value of the last remote button press for key index i
5535
5589
  this.lastRemoteKeyPress1 = []; // holds the time value of the last-1 remote button press for key index i
@@ -5570,7 +5624,7 @@ class StbDevice {
5570
5624
  */
5571
5625
  prepareAccessory() {
5572
5626
  // Trace-level debug logging (level 3+)
5573
- if (this.config.debugLevel > 2) {
5627
+ if (this.debugLevel > 2) {
5574
5628
  this.log.warn("prepareAccessory");
5575
5629
  }
5576
5630
 
@@ -5615,14 +5669,16 @@ class StbDevice {
5615
5669
 
5616
5670
  // --- Post-publish Characteristic Defaults ---
5617
5671
  // Set DisplayOrder after publish (TLV8 base64-encoded byte array)
5618
- this.televisionService.getCharacteristic(
5672
+ this.televisionService.updateCharacteristic(
5619
5673
  Characteristic.DisplayOrder,
5620
- ).value = Buffer.from(this.displayOrder).toString("base64");
5674
+ Buffer.from(this.displayOrder).toString("base64"),
5675
+ );
5621
5676
 
5622
5677
  // Default to no active input on startup
5623
- this.televisionService
5624
- .getCharacteristic(Characteristic.ActiveIdentifier)
5625
- .updateValue(NO_INPUT_ID);
5678
+ this.televisionService.updateCharacteristic(
5679
+ Characteristic.ActiveIdentifier,
5680
+ NO_INPUT_ID,
5681
+ );
5626
5682
 
5627
5683
  this.accessoryConfigured = true;
5628
5684
  }
@@ -5688,7 +5744,7 @@ class StbDevice {
5688
5744
  * FirmwareRevision MUST be a numeric string (e.g. "1.2.3") or it won't display in Home app.
5689
5745
  */
5690
5746
  prepareAccessoryInformationService() {
5691
- if (this.config.debugLevel > 1) {
5747
+ if (this.debugLevel > 1) {
5692
5748
  this.log.warn("prepareAccessoryInformationService");
5693
5749
  }
5694
5750
 
@@ -5800,7 +5856,7 @@ class StbDevice {
5800
5856
  * These appear as "Custom" in the Shortcuts app.
5801
5857
  */
5802
5858
  prepareTelevisionService() {
5803
- if (this.config.debugLevel > 1) {
5859
+ if (this.debugLevel > 1) {
5804
5860
  this.log.warn("prepareTelevisionService");
5805
5861
  }
5806
5862
 
@@ -6001,7 +6057,7 @@ class StbDevice {
6001
6057
  * Must be linked to the Television service so HomeKit associates them.
6002
6058
  */
6003
6059
  prepareTelevisionSpeakerService() {
6004
- if (this.config.debugLevel > 1) {
6060
+ if (this.debugLevel > 1) {
6005
6061
  this.log.warn("prepareTelevisionSpeakerService");
6006
6062
  }
6007
6063
 
@@ -6054,7 +6110,7 @@ class StbDevice {
6054
6110
  * Terminated with: [0x00, 0x00]
6055
6111
  */
6056
6112
  prepareInputSourceServices() {
6057
- if (this.config.debugLevel > 1) {
6113
+ if (this.debugLevel > 1) {
6058
6114
  this.log.warn("prepareInputSourceServices");
6059
6115
  }
6060
6116
 
@@ -6087,7 +6143,7 @@ class StbDevice {
6087
6143
  let chId = `HIDDEN_${i}`;
6088
6144
  let visState = Characteristic.CurrentVisibilityState.HIDDEN;
6089
6145
  let configState = Characteristic.IsConfigured.NOT_CONFIGURED;
6090
- this.log.warn(
6146
+ this.log.debug(
6091
6147
  "prepareInputSourceServices loading channelList index %s input %s: %s",
6092
6148
  i,
6093
6149
  i + 1,
@@ -6118,7 +6174,7 @@ class StbDevice {
6118
6174
  chFixedName = chName;
6119
6175
  }
6120
6176
 
6121
- if (this.config.debugLevel > 2) {
6177
+ if (this.debugLevel > 2) {
6122
6178
  // log 1 of 95 to 95 of 95 (1-based)
6123
6179
  this.log.warn(
6124
6180
  "prepareInputSourceServices Adding service %s of %s: %s",
@@ -6185,9 +6241,9 @@ class StbDevice {
6185
6241
 
6186
6242
  // --- Build TLV8 DisplayOrder entry ---
6187
6243
  // Format: [type=0x01, length=0x01, value=identifier & 0xFF]
6188
- // The identifier is masked to 8 bits; supports up to 255 inputs.
6244
+ // The identifier (1-based) is masked to 8 bits; supports up to 255 inputs.
6189
6245
  // Reference: https://github.com/homebridge/HAP-NodeJS/issues/644
6190
- this.displayOrder.push(0x01, 0x01, i & 0xff);
6246
+ this.displayOrder.push(0x01, 0x01, (i + 1) & 0xff);
6191
6247
 
6192
6248
  // Register and link the service
6193
6249
  this.accessory.addService(inputService);
@@ -6227,7 +6283,7 @@ class StbDevice {
6227
6283
  // runs at the very start, and then every few seconds, so don't log it unless debugging
6228
6284
  // doesn't get the data direct from the settop box, but rather: gets it from the this.currentPowerState and this.currentChannelId variables
6229
6285
  // which are received by the mqtt messages, which occurs very often
6230
- if (this.config.debugLevel > 0) {
6286
+ if (this.debugLevel > 0) {
6231
6287
  this.log.warn(
6232
6288
  "%s: updateDeviceState: powerState %s, mediaState %s [%s], recState %s [%s], channelId %s, eventId %s, sourceType %s, profileDataChanged %s, statusFault %s [%s], programMode %s [%s], statusActive %s [%s], inputDeviceType %s [%s], inputSourceType %s [%s]",
6233
6289
  this.name,
@@ -6241,25 +6297,20 @@ class StbDevice {
6241
6297
  sourceType,
6242
6298
  profileDataChanged,
6243
6299
  statusFault,
6244
- Object.keys(Characteristic.StatusFault)[statusFault + 1],
6300
+ CHAR_NAMES.StatusFault[statusFault + 1],
6245
6301
  programMode,
6246
- Object.keys(Characteristic.ProgramMode)[programMode + 1],
6302
+ CHAR_NAMES.ProgramMode[programMode + 1],
6247
6303
  statusActive,
6248
6304
  statusActiveName[statusActive],
6249
6305
  inputDeviceType,
6250
- Object.keys(Characteristic.InputDeviceType)[inputDeviceType + 1],
6306
+ CHAR_NAMES.InputDeviceType[inputDeviceType + 1],
6251
6307
  inputSourceType,
6252
- Object.keys(Characteristic.InputSourceType)[inputSourceType + 1],
6308
+ CHAR_NAMES.InputSourceType[inputSourceType + 1],
6253
6309
  );
6254
6310
  }
6255
6311
 
6256
6312
  // get the config for the device, needed for a few status checks
6257
- let configDevice;
6258
- if (this.config.devices) {
6259
- configDevice = this.config.devices.find(
6260
- (device) => device.deviceId === this.deviceId,
6261
- );
6262
- }
6313
+ const configDevice = this._configDevice;
6263
6314
 
6264
6315
  // grab the input variables
6265
6316
  // A small helper — reads clearly at point of use
@@ -6298,30 +6349,30 @@ class StbDevice {
6298
6349
  }
6299
6350
 
6300
6351
  // profile data is stored on the platform
6301
- // get the currentClosedCaptionsState from the currently selected profile (stored in this.profileId)
6352
+ // get the currentClosedCaptions from the currently selected profile (stored in this.profileId)
6302
6353
  if (
6303
6354
  this.customer.profiles[this.profileId] &&
6304
6355
  this.customer.profiles[this.profileId].options.showSubtitles
6305
6356
  ) {
6306
- this.currentClosedCaptionsState = Characteristic.ClosedCaptions.ENABLED;
6357
+ this.currentClosedCaptions = Characteristic.ClosedCaptions.ENABLED;
6307
6358
  } else {
6308
- this.currentClosedCaptionsState =
6359
+ this.currentClosedCaptions =
6309
6360
  Characteristic.ClosedCaptions.DISABLED;
6310
6361
  }
6311
6362
 
6312
6363
  // debugging, helps a lot to see channelName
6313
- if (this.config.debugLevel > 0) {
6364
+ if (this.debugLevel > 0) {
6314
6365
  let curChannel, currentChannelName;
6315
6366
  if (this.platform.masterChannelList) {
6316
- curChannel = this.platform.masterChannelList.find(
6317
- (channel) => channel.id === this.currentChannelId,
6318
- ); // this.currentChannelId is a string eg SV09038
6367
+ curChannel = this.platform.masterChannelMap?.get(
6368
+ this.currentChannelId,
6369
+ );
6319
6370
  if (curChannel) {
6320
6371
  currentChannelName = curChannel.name;
6321
6372
  }
6322
6373
  }
6323
6374
  this.log.warn(
6324
- "%s: updateDeviceState: currentPowerState %s, currentMediaState %s [%s], currentRecordingState %s [%s], currentChannelId %s [%s], currentSourceType %s, currentClosedCaptionsState %s [%s], currentPictureMode %s [%s], profileDataChanged %s, currentStatusFault %s [%s], currentProgramMode %s [%s], currentStatusActive %s",
6375
+ "%s: updateDeviceState: currentPowerState %s, currentMediaState %s [%s], currentRecordingState %s [%s], currentChannelId %s [%s], currentSourceType %s, currentClosedCaptions %s [%s], currentPictureMode %s [%s], profileDataChanged %s, currentStatusFault %s [%s], currentProgramMode %s [%s], currentStatusActive %s",
6325
6376
  this.name,
6326
6377
  this.currentPowerState,
6327
6378
  this.currentMediaState,
@@ -6331,17 +6382,15 @@ class StbDevice {
6331
6382
  this.currentChannelId,
6332
6383
  currentChannelName,
6333
6384
  this.currentSourceType,
6334
- this.currentClosedCaptionsState,
6335
- Object.keys(Characteristic.ClosedCaptions)[
6336
- this.currentClosedCaptionsState + 1
6337
- ],
6385
+ this.currentClosedCaptions,
6386
+ CHAR_NAMES.ClosedCaptions[this.currentClosedCaptions + 1],
6338
6387
  this.currentPictureMode,
6339
- Object.keys(Characteristic.PictureMode)[this.currentPictureMode + 1],
6388
+ CHAR_NAMES.PictureMode[this.currentPictureMode + 1],
6340
6389
  this.profileDataChanged,
6341
6390
  this.currentStatusFault,
6342
- Object.keys(Characteristic.StatusFault)[this.currentStatusFault + 1],
6391
+ CHAR_NAMES.StatusFault[this.currentStatusFault + 1],
6343
6392
  this.currentProgramMode,
6344
- Object.keys(Characteristic.ProgramMode)[this.currentProgramMode + 1],
6393
+ CHAR_NAMES.ProgramMode[this.currentProgramMode + 1],
6345
6394
  this.currentStatusActive,
6346
6395
  );
6347
6396
  }
@@ -6375,46 +6424,49 @@ class StbDevice {
6375
6424
  currentDeviceName,
6376
6425
  );
6377
6426
  this.name = currentDeviceName;
6378
- this.televisionService
6379
- .getCharacteristic(Characteristic.ConfiguredName)
6380
- .updateValue(currentDeviceName);
6427
+ this.televisionService.updateCharacteristic(
6428
+ Characteristic.ConfiguredName,
6429
+ currentDeviceName,
6430
+ );
6381
6431
  }
6382
6432
 
6383
6433
  // check for change of StatusFault state
6384
6434
  if (this.previousStatusFault !== this.currentStatusFault) {
6385
- this.log(
6386
- "%s: Status Fault changed from %s [%s] to %s [%s]",
6435
+ logCharValueChange(
6436
+ this.log,
6387
6437
  this.name,
6388
- this.previousStatusFault || "-",
6389
- Object.keys(Characteristic.StatusFault)[
6390
- this.previousStatusFault + 1
6391
- ] || "unknown",
6438
+ "Status Fault",
6439
+ null,
6440
+ this.previousStatusFault,
6441
+ CHAR_NAMES.StatusFault[this.previousStatusFault + 1],
6442
+ this.currentStatusFault,
6443
+ CHAR_NAMES.StatusFault[this.currentStatusFault + 1],
6444
+ );
6445
+ this.televisionService.updateCharacteristic(
6446
+ Characteristic.StatusFault,
6392
6447
  this.currentStatusFault,
6393
- Object.keys(Characteristic.StatusFault)[
6394
- this.currentStatusFault + 1
6395
- ],
6396
6448
  );
6449
+ this.previousStatusFault = this.currentStatusFault;
6397
6450
  }
6398
- this.televisionService
6399
- .getCharacteristic(Characteristic.StatusFault)
6400
- .updateValue(this.currentStatusFault);
6401
- this.previousStatusFault = this.currentStatusFault;
6402
6451
 
6403
6452
  // check for change of StatusActive state
6404
6453
  if (this.previousStatusActive !== this.currentStatusActive) {
6405
- this.log(
6406
- "%s: Status Active changed from %s [%s] to %s [%s]",
6454
+ logCharValueChange(
6455
+ this.log,
6407
6456
  this.name,
6408
- this.previousStatusActive || "-",
6409
- statusActiveName[this.previousStatusActive] || "unknown",
6457
+ "Status Active",
6458
+ null,
6459
+ this.previousStatusActive,
6460
+ statusActiveName[this.previousStatusActive],
6410
6461
  this.currentStatusActive,
6411
6462
  statusActiveName[this.currentStatusActive],
6412
6463
  );
6464
+ this.televisionService.updateCharacteristic(
6465
+ Characteristic.StatusActive,
6466
+ this.currentStatusActive,
6467
+ );
6468
+ this.previousStatusActive = this.currentStatusActive;
6413
6469
  }
6414
- this.televisionService
6415
- .getCharacteristic(Characteristic.StatusActive)
6416
- .updateValue(this.currentStatusActive);
6417
- this.previousStatusActive = this.currentStatusActive;
6418
6470
 
6419
6471
  // check for change of power state
6420
6472
  // The accessory changes state immediately, and the box takes time to catch up
@@ -6424,133 +6476,103 @@ class StbDevice {
6424
6476
  //this.log("Wanted device power state: %s %s", this.currentPowerState, powerStateName[this.currentPowerState]);
6425
6477
  //let oldPowerState = this.televisionService.getCharacteristic(Characteristic.Active).value;
6426
6478
  if (this.previousPowerState !== this.currentPowerState) {
6427
- this.log(
6428
- "%s: Power changed from %s [%s] to %s [%s]",
6479
+ logCharValueChange(
6480
+ this.log,
6429
6481
  this.name,
6482
+ "Power",
6483
+ null,
6430
6484
  this.previousPowerState,
6431
- powerStateName[this.previousPowerState] || "unknown",
6485
+ powerStateName[this.previousPowerState],
6432
6486
  this.currentPowerState,
6433
6487
  powerStateName[this.currentPowerState],
6434
6488
  );
6489
+ this.televisionService.updateCharacteristic(
6490
+ Characteristic.Active,
6491
+ this.currentPowerState,
6492
+ );
6493
+ this.previousPowerState = this.currentPowerState;
6435
6494
  }
6436
- this.televisionService
6437
- .getCharacteristic(Characteristic.Active)
6438
- .updateValue(this.currentPowerState);
6439
- this.previousPowerState = this.currentPowerState;
6440
6495
 
6441
6496
  // check for change of InUse state
6442
6497
  if (this.previousInUse !== this.currentInUse) {
6443
- this.log(
6444
- "%s: In Use changed from %s [%s] to %s [%s]",
6498
+ logCharValueChange(
6499
+ this.log,
6445
6500
  this.name,
6501
+ "In Use",
6502
+ null,
6446
6503
  this.previousInUse,
6447
- Object.keys(Characteristic.InUse)[this.previousInUse + 1],
6504
+ CHAR_NAMES.InUse[this.previousInUse + 1],
6505
+ this.currentInUse,
6506
+ CHAR_NAMES.InUse[this.currentInUse + 1],
6507
+ );
6508
+ this.televisionService.updateCharacteristic(
6509
+ Characteristic.InUse,
6448
6510
  this.currentInUse,
6449
- Object.keys(Characteristic.InUse)[this.currentInUse + 1],
6450
6511
  );
6512
+ this.previousInUse = this.currentInUse;
6451
6513
  }
6452
- this.televisionService
6453
- .getCharacteristic(Characteristic.InUse)
6454
- .updateValue(this.currentInUse);
6455
- this.previousInUse = this.currentInUse;
6456
6514
 
6457
6515
  // check for change of closed captions state
6458
- //this.log("Previous closed captions state: %s %s", this.previousClosedCaptionsState, closedCaptionsStateName[this.previousClosedCaptionsState]);
6459
- //this.log("Current closed captions state: %s %s", this.televisionService.getCharacteristic(Characteristic.ClosedCaptions).value, closedCaptionsStateName[this.televisionService.getCharacteristic(Characteristic.ClosedCaptions).value]);
6460
- //this.log("Wanted closed captions state: %s %s", this.currentClosedCaptionsState, closedCaptionsStateName[this.currentClosedCaptionsState]);
6461
6516
  if (
6462
- this.previousClosedCaptionsState !== this.currentClosedCaptionsState
6517
+ this.previousClosedCaptionsState !== this.currentClosedCaptions
6463
6518
  ) {
6464
- this.log(
6465
- "%s: Closed Captions state changed from %s [%s] to %s [%s]",
6519
+ logCharValueChange(
6520
+ this.log,
6466
6521
  this.name,
6467
- this.previousClosedCaptionsState || "-",
6468
- Object.keys(Characteristic.ClosedCaptions)[
6469
- this.previousClosedCaptionsState + 1
6470
- ] || "unknown",
6471
- this.currentClosedCaptionsState,
6472
- Object.keys(Characteristic.ClosedCaptions)[
6473
- this.currentClosedCaptionsState + 1
6474
- ],
6522
+ "Closed Captions",
6523
+ null,
6524
+ this.previousClosedCaptionsState,
6525
+ CHAR_NAMES.ClosedCaptions[this.previousClosedCaptionsState + 1],
6526
+ this.currentClosedCaptions,
6527
+ CHAR_NAMES.ClosedCaptions[this.currentClosedCaptions + 1],
6528
+ );
6529
+ this.televisionService.updateCharacteristic(
6530
+ Characteristic.ClosedCaptions,
6531
+ this.currentClosedCaptions,
6475
6532
  );
6533
+ this.previousClosedCaptionsState = this.currentClosedCaptions;
6476
6534
  }
6477
- this.televisionService
6478
- .getCharacteristic(Characteristic.ClosedCaptions)
6479
- .updateValue(this.currentClosedCaptionsState);
6480
- this.previousClosedCaptionsState = this.currentClosedCaptionsState;
6481
6535
 
6482
6536
  // check for change of picture mode or recordingState (both stored in picture mode)
6483
- // customPictureMode deprecated from v2.0.0 and removed from config.json, as its function is handled by inUse.
6484
- // Nov 2022: disabled code, will remove in a future version
6485
- if (
6486
- (configDevice || {}).customPictureMode === "recordingState" &&
6487
- 1 === 0
6488
- ) {
6489
- // PictureMode is used for recordingState function, this is a custom characteristic, not supported by HomeKit. we can use values 0...7
6490
- //this.log("previousRecordingState", this.previousRecordingState);
6491
- //this.log("currentRecordingState", this.currentRecordingState);
6492
- if (this.previousRecordingState !== this.currentRecordingState) {
6493
- this.log(
6494
- "%s: Recording State changed from %s [%s] to %s [%s]",
6495
- this.name,
6496
- this.previousRecordingState || "-",
6497
- Object.keys(recordingState)[this.previousRecordingState] ||
6498
- "unknown",
6499
- this.currentRecordingState,
6500
- Object.keys(recordingState)[this.currentRecordingState],
6501
- );
6502
- }
6503
- //this.log("configDevice.customPictureMode found %s, setting PictureMode to %s", (configDevice || {}).customPictureMode, this.currentRecordingState);
6504
- this.customPictureMode = this.currentRecordingState;
6505
- this.previousRecordingState = this.currentRecordingState;
6506
- } else {
6507
- // PictureMode is used for default function: pictureMode
6508
- //this.log("previousPictureMode", this.previousPictureMode);
6509
- //this.log("currentPictureMode", this.currentPictureMode);
6510
- if (this.previousPictureMode !== this.currentPictureMode) {
6511
- this.log(
6512
- "%s: Picture Mode changed from %s [%s] to %s [%s]",
6513
- this.name,
6514
- this.previousPictureMode || "-",
6515
- Object.keys(Characteristic.PictureMode)[
6516
- this.previousPictureMode + 1
6517
- ] || "unknown",
6518
- this.currentPictureMode,
6519
- Object.keys(Characteristic.PictureMode)[
6520
- this.currentPictureMode + 1
6521
- ],
6522
- );
6523
- }
6524
- //this.log("configDevice.customPictureMode not found %s, setting PictureMode to %s", (configDevice || {}).customPictureMode, this.currentPictureMode);
6525
- this.customPictureMode = this.currentPictureMode;
6537
+ // PictureMode is used for default function: pictureMode
6538
+ if (this.previousPictureMode !== this.currentPictureMode) {
6539
+ logCharValueChange(
6540
+ this.log,
6541
+ this.name,
6542
+ "Picture Mode",
6543
+ null,
6544
+ this.previousPictureMode,
6545
+ CHAR_NAMES.PictureMode[this.previousPictureMode + 1],
6546
+ this.currentPictureMode,
6547
+ CHAR_NAMES.PictureMode[this.currentPictureMode + 1],
6548
+ );
6549
+ this.televisionService.updateCharacteristic(
6550
+ Characteristic.PictureMode,
6551
+ this.customPictureMode,
6552
+ );
6526
6553
  this.previousPictureMode = this.currentPictureMode;
6527
6554
  }
6528
- //this.log("setting PictureMode to %s", this.customPictureMode);
6529
- this.televisionService
6530
- .getCharacteristic(Characteristic.PictureMode)
6531
- .updateValue(this.customPictureMode);
6532
6555
 
6533
- // check for change of ProgramMode state
6556
+ // check for change of ProgramMode
6534
6557
  if (this.previousProgramMode !== this.currentProgramMode) {
6535
- this.log(
6536
- "%s: Program Mode changed from %s [%s] to %s [%s]",
6558
+ logCharValueChange(
6559
+ this.log,
6537
6560
  this.name,
6538
- this.previousProgramMode || "-",
6539
- Object.keys(Characteristic.ProgramMode)[
6540
- this.previousProgramMode + 1
6541
- ] || "unknown",
6561
+ "Program Mode",
6562
+ null,
6563
+ this.previousProgramMode,
6564
+ CHAR_NAMES.ProgramMode[this.previousProgramMode + 1],
6542
6565
  this.currentProgramMode,
6543
- Object.keys(Characteristic.ProgramMode)[
6544
- this.currentProgramMode + 1
6545
- ],
6566
+ CHAR_NAMES.ProgramMode[this.currentProgramMode + 1],
6546
6567
  );
6568
+ this.televisionService.updateCharacteristic(
6569
+ Characteristic.ProgramMode,
6570
+ this.currentProgramMode,
6571
+ );
6572
+ this.previousProgramMode = this.currentProgramMode;
6547
6573
  }
6548
- this.televisionService
6549
- .getCharacteristic(Characteristic.ProgramMode)
6550
- .updateValue(this.currentProgramMode);
6551
- this.previousProgramMode = this.currentProgramMode;
6552
6574
 
6553
- // check for change of active identifier (channel)
6575
+ // check for change of ActiveIdentifier (channel, 1-based)
6554
6576
  // temporarily wrapped this in a try-catch to capture any errors
6555
6577
  //this.log("Before error trap");
6556
6578
  let searchChannelId = this.currentChannelId; // this.currentChannelId is a string eg SV09038
@@ -6564,9 +6586,8 @@ class StbDevice {
6564
6586
  searchChannelId,
6565
6587
  );
6566
6588
  // get the name from the master channel list
6567
- let masterChannelApp = this.platform.masterChannelList.find(
6568
- (channel) => channel.id === searchChannelId,
6569
- );
6589
+ let masterChannelApp =
6590
+ this.platform.masterChannelMap.get(searchChannelId);
6570
6591
  //this.log("found masterChannelApp", masterChannelApp)
6571
6592
  // now look again in the master channel list to find this channel with the same name but not an app id
6572
6593
  if (masterChannelApp) {
@@ -6597,34 +6618,30 @@ class StbDevice {
6597
6618
  //this.log("After error trap. searchChannelId:", searchChannelId);
6598
6619
 
6599
6620
  // search by subtype in the inputServices array, index 0 = input 1, subtype: 'input_SV09038',
6600
- const oldActiveIdentifier = this.televisionService.getCharacteristic(
6601
- Characteristic.ActiveIdentifier,
6602
- ).value;
6603
- //this.log("updateDeviceState: oldActiveIdentifier %s, currentActiveIdentifier %s, searchChannelId %s", oldActiveIdentifier, currentActiveIdentifier, searchChannelId)
6604
- //this.log("this.inputServices")
6605
- //this.log(this.inputServices)
6606
6621
  // ActiveIdentifier: 1-based: 1=Input1, 2=Input2, etc
6607
6622
  // channelList: 0-based: 0=index0, 1=index1, etc
6623
+ // Single scan: reused for both ActiveIdentifier and InputDeviceType/InputSourceType updates below.
6624
+ // searchChannelId equals this.currentChannelId except when an app ID has been remapped,
6625
+ // in which case the remapped ID is the correct one to locate in inputServices.
6626
+ let currInputIndex = this.inputServices.findIndex(
6627
+ (InputSource) => InputSource.subtype === "input_" + searchChannelId,
6628
+ );
6629
+ // if nothing found, set to NO_INPUT_ID to clear the name from the Home app tile
6608
6630
  currentActiveIdentifier =
6609
- this.inputServices.findIndex(
6610
- (InputSource) => InputSource.subtype === "input_" + searchChannelId,
6611
- ) + 1;
6612
- //this.log("found searchChannelId %s at currentActiveIdentifier %s", 'input_' + searchChannelId , currentActiveIdentifier)
6613
- if (currentActiveIdentifier <= 0) {
6614
- currentActiveIdentifier = NO_INPUT_ID;
6615
- } // if nothing found, set to NO_INPUT_ID to clear the name from the Home app tile
6616
- //this.log("found searchChannelId at currentActiveIdentifier ", currentActiveIdentifier)
6617
-
6618
- if (oldActiveIdentifier !== currentActiveIdentifier) {
6631
+ currInputIndex >= 0 ? currInputIndex + 1 : NO_INPUT_ID;
6632
+ let currInputNumber = currInputIndex >= 0 ? currInputIndex + 1 : null;
6633
+ if (currInputIndex < 0) currInputIndex = null;
6634
+
6635
+ if (this.previousActiveIdentifier !== currentActiveIdentifier) {
6619
6636
  // get names from loaded channel list. Using Ch Up/Ch Down buttons on the remote rolls around the profile channel list
6620
6637
  // what happens if the TV is changed to another profile?
6621
6638
  let oldName = NO_CHANNEL_NAME,
6622
6639
  newName = oldName; // default to UNKNOWN
6623
6640
  if (
6624
- oldActiveIdentifier !== NO_INPUT_ID &&
6625
- this.channelList[oldActiveIdentifier - 1]
6641
+ this.previousActiveIdentifier !== NO_INPUT_ID &&
6642
+ this.channelList[this.previousActiveIdentifier - 1]
6626
6643
  ) {
6627
- oldName = this.channelList[oldActiveIdentifier - 1].name;
6644
+ oldName = this.channelList[this.previousActiveIdentifier - 1].name;
6628
6645
  }
6629
6646
 
6630
6647
  if (
@@ -6633,18 +6650,21 @@ class StbDevice {
6633
6650
  ) {
6634
6651
  newName = this.channelList[currentActiveIdentifier - 1].name;
6635
6652
  }
6636
- this.log(
6637
- "%s: Channel changed from %s [%s] to %s [%s]",
6653
+ logCharValueChange(
6654
+ this.log,
6638
6655
  this.name,
6639
- oldActiveIdentifier || "-",
6640
- oldName || "unknown",
6656
+ "Channel",
6657
+ null,
6658
+ this.previousActiveIdentifier,
6659
+ oldName,
6641
6660
  currentActiveIdentifier,
6642
6661
  newName,
6643
6662
  );
6644
- this.televisionService
6645
- .getCharacteristic(Characteristic.ActiveIdentifier)
6646
- .updateValue(currentActiveIdentifier);
6647
- this.previousActiveIdentifier = this.currentActiveIdentifier;
6663
+ this.televisionService.updateCharacteristic(
6664
+ Characteristic.ActiveIdentifier,
6665
+ currentActiveIdentifier,
6666
+ );
6667
+ this.previousActiveIdentifier = currentActiveIdentifier;
6648
6668
  }
6649
6669
 
6650
6670
  // +++++++++++++++ Input Service characteristics ++++++++++++++
@@ -6660,97 +6680,76 @@ class StbDevice {
6660
6680
  .setCharacteristic(Characteristic.TargetVisibilityState, visState);
6661
6681
  */
6662
6682
  // check for change of InputDeviceType state: (a characteristic of Input Source)
6663
-
6664
- //this.log('looking for input subtype ', 'input_' + this.currentChannelId)
6665
- let currInputIndex = this.inputServices.findIndex(
6666
- (InputSource) =>
6667
- InputSource.subtype === "input_" + this.currentChannelId,
6668
- );
6669
- let currInputNumber = currInputIndex + 1;
6670
- if (currInputIndex < 0) {
6671
- currInputIndex = null;
6672
- currInputNumber = null;
6673
- }
6674
6683
  //this.log('found input index %s input %s subtype %s', currInputIndex, currInputIndex+1, (this.inputServices[currInputIndex] || {}).subtype)
6675
6684
  if (this.previousInputDeviceType !== this.currentInputDeviceType) {
6676
- this.log(
6677
- "%s: Input Device Type changed on input %s %s from %s [%s] to %s [%s]",
6685
+ logCharValueChange(
6686
+ this.log,
6678
6687
  this.name,
6679
- currInputNumber,
6680
- this.currentChannelId,
6681
- this.previousInputDeviceType || "-",
6682
- Object.keys(Characteristic.InputDeviceType)[
6683
- this.previousInputDeviceType + 1
6684
- ] || "unknown",
6688
+ "Input Device Type",
6689
+ currInputNumber + " " +this.currentChannelId,
6690
+ this.previousInputDeviceType,
6691
+ CHAR_NAMES.InputDeviceType[this.previousInputDeviceType + 1],
6685
6692
  this.currentInputDeviceType,
6686
- Object.keys(Characteristic.InputDeviceType)[
6687
- this.currentInputDeviceType + 1
6688
- ],
6689
- );
6690
- }
6691
- //this.televisionService.getCharacteristic(Characteristic.InputDeviceType).updateValue(this.currentInputDeviceType);
6692
- if (currInputIndex) {
6693
- this.inputServices[currInputIndex]
6694
- .getCharacteristic(Characteristic.InputDeviceType)
6695
- .updateValue(this.currentInputDeviceType);
6693
+ CHAR_NAMES.InputDeviceType[this.currentInputDeviceType + 1],
6694
+ );
6695
+ if (currInputIndex !== null) {
6696
+ this.inputServices[currInputIndex].updateCharacteristic(
6697
+ Characteristic.InputDeviceType,
6698
+ this.currentInputDeviceType,
6699
+ );
6700
+ }
6701
+ this.previousInputDeviceType = this.currentInputDeviceType;
6696
6702
  }
6697
- this.previousInputDeviceType = this.currentInputDeviceType;
6698
6703
 
6699
6704
  // check for change of InputSourceType state: (a characteristic of Input Source)
6700
6705
  if (this.previousInputSourceType !== this.currentInputSourceType) {
6701
- this.log(
6702
- "%s: Input Source Type changed on input %s %s from %s [%s] to %s [%s]",
6706
+ logCharValueChange(
6707
+ this.log,
6703
6708
  this.name,
6704
- currInputNumber,
6705
- this.currentChannelId,
6706
- this.previousInputSourceType || "-",
6707
- Object.keys(Characteristic.InputSourceType)[
6708
- this.previousInputSourceType + 1
6709
- ] || "unknown",
6709
+ "Input Source Type",
6710
+ currInputNumber + " " +this.currentChannelId,
6711
+ this.previousInputSourceType,
6712
+ CHAR_NAMES.InputSourceType[this.previousInputSourceType + 1],
6710
6713
  this.currentInputSourceType,
6711
- Object.keys(Characteristic.InputSourceType)[
6712
- this.currentInputSourceType + 1
6713
- ],
6714
- );
6714
+ CHAR_NAMES.InputSourceType[this.currentInputSourceType + 1],
6715
+ );
6716
+ if (currInputIndex !== null) {
6717
+ this.inputServices[currInputIndex].updateCharacteristic(
6718
+ Characteristic.InputSourceType,
6719
+ this.currentInputSourceType,
6720
+ );
6721
+ } // generates Homebridge warning
6722
+ this.previousInputSourceType = this.currentInputSourceType;
6715
6723
  }
6716
- // [12/11/2022, 12:22:37] [homebridge-eosstb] This plugin generated a warning from the characteristic 'Input Source Type': Characteristic not in required or optional characteristic section for service Television. Adding anyway.. See https://homebridge.io/w/JtMGR for more info.
6717
- if (currInputIndex) {
6718
- this.inputServices[currInputIndex]
6719
- .getCharacteristic(Characteristic.InputSourceType)
6720
- .updateValue(this.currentInputSourceType);
6721
- } // generates Homebridge warning
6722
- this.previousInputSourceType = this.currentInputSourceType;
6723
- //this.log('++++DEBUG: this.inputServices[currInputIndex]')
6724
- //this.log(this.inputServices[currInputIndex])
6725
6724
 
6726
6725
  // +++++++++++++++ end of Input Service characteristics ++++++++++++++
6727
6726
 
6728
6727
  // check for change of current media state
6729
- let prevCurrentMediaState = this.televisionService.getCharacteristic(
6730
- Characteristic.CurrentMediaState,
6731
- ).value;
6732
- if (prevCurrentMediaState !== this.currentMediaState) {
6733
- this.log(
6734
- "%s: Current Media state changed from %s [%s] to %s [%s]",
6728
+ if (this.previousMediaState !== this.currentMediaState) {
6729
+ logCharValueChange(
6730
+ this.log,
6735
6731
  this.name,
6736
- prevCurrentMediaState,
6737
- currentMediaStateName(prevCurrentMediaState),
6732
+ "Media State",
6733
+ null,
6734
+ this.previousMediaState,
6735
+ currentMediaStateName(this.previousMediaState),
6738
6736
  this.currentMediaState,
6739
6737
  currentMediaStateName(this.currentMediaState),
6740
6738
  );
6741
-
6742
6739
  // set targetMediaState to same as currentMediaState as long as currentMediaState is <= 2 (supports 0 PLAY, 1 PAUSE, 2 STOP)
6743
6740
  if (this.currentMediaState <= Characteristic.TargetMediaState.STOP) {
6744
6741
  this.targetMediaState = this.currentMediaState;
6745
- this.televisionService
6746
- .getCharacteristic(Characteristic.TargetMediaState)
6747
- .updateValue(this.targetMediaState);
6742
+ this.televisionService.updateCharacteristic(
6743
+ Characteristic.TargetMediaState,
6744
+ this.targetMediaState,
6745
+ );
6748
6746
  }
6747
+ this.televisionService.updateCharacteristic(
6748
+ Characteristic.CurrentMediaState,
6749
+ this.currentMediaState,
6750
+ );
6751
+ this.previousMediaState = this.currentMediaState;
6749
6752
  }
6750
- this.televisionService
6751
- .getCharacteristic(Characteristic.CurrentMediaState)
6752
- .updateValue(this.currentMediaState);
6753
- this.previousMediaState = this.currentMediaState;
6754
6753
 
6755
6754
  // check for change of profile
6756
6755
  if (this.profileDataChanged) {
@@ -6775,7 +6774,7 @@ class StbDevice {
6775
6774
  // refresh the channel list that shows in the Home app
6776
6775
  async refreshDeviceChannelList(callback) {
6777
6776
  try {
6778
- if (this.config.debugLevel > 1) {
6777
+ if (this.debugLevel > 1) {
6779
6778
  this.log.warn("%s: refreshDeviceChannelList", this.name);
6780
6779
  }
6781
6780
  this.log("%s: Refreshing device channel list...", this.name);
@@ -6802,24 +6801,15 @@ class StbDevice {
6802
6801
 
6803
6802
  // limit the amount of max channels to load as Apple HomeKit is limited to 100 services per accessory.
6804
6803
  // if a config exists for this device, read the users configured maxSources, if it exists
6805
- let maxSources = MAX_INPUT_SOURCES;
6806
- let configDevice = {};
6807
- if (this.config.devices) {
6808
- configDevice = this.config.devices.find(
6809
- (device) => device.deviceId === this.deviceId,
6810
- );
6811
- if (configDevice) {
6812
- // homebridge config for this device exists, read maxSources (if exists)
6813
- maxSources = Math.min(
6814
- configDevice.maxChannels || maxSources,
6815
- maxSources,
6816
- );
6817
- }
6818
- }
6804
+ const configDevice = this._configDevice;
6805
+ const maxChannels = configDevice.maxChannels;
6806
+ const maxSources = maxChannels
6807
+ ? Math.min(maxChannels, MAX_INPUT_SOURCES)
6808
+ : MAX_INPUT_SOURCES;
6819
6809
 
6820
6810
  // get a user configured Profile, if it exists, otherwise we will use the default Profile for the channel list
6821
6811
  let wantedProfile;
6822
- if (this.config.debugLevel > 1) {
6812
+ if (this.debugLevel > 1) {
6823
6813
  this.log.warn("%s: Getting profile data from config", this.name);
6824
6814
  }
6825
6815
  if (configDevice) {
@@ -6828,7 +6818,7 @@ class StbDevice {
6828
6818
  (profile) => profile.name === configDevice.profile,
6829
6819
  );
6830
6820
  if (wantedProfile) {
6831
- if (this.config.debugLevel > 1) {
6821
+ if (this.debugLevel > 1) {
6832
6822
  this.log.warn(
6833
6823
  "%s: Configured profile found: '%s'",
6834
6824
  this.name,
@@ -6843,7 +6833,7 @@ class StbDevice {
6843
6833
  wantedProfile = this.customer.profiles.find(
6844
6834
  (profile) => profile.profileId === this.device.defaultProfileId,
6845
6835
  );
6846
- if (this.config.debugLevel > 1) {
6836
+ if (this.debugLevel > 1) {
6847
6837
  this.log.warn(
6848
6838
  "%s: No user-configured profile found, reverting to default profile: '%s'",
6849
6839
  this.name,
@@ -6875,7 +6865,7 @@ class StbDevice {
6875
6865
  );
6876
6866
  let subscribedChIds = []; // an array of channelIds: SV00302, SV09091, etc
6877
6867
  if (wantedProfile.favoriteChannels.length > 0) {
6878
- if (this.config.debugLevel > 1) {
6868
+ if (this.debugLevel > 1) {
6879
6869
  this.log.warn(
6880
6870
  "%s: Loading channels from profile '%s' into the subscribedChIds",
6881
6871
  this.name,
@@ -6896,7 +6886,7 @@ class StbDevice {
6896
6886
  (this.mostWatched || []).length > 0
6897
6887
  ) {
6898
6888
  // load by mostWatched sort order
6899
- if (this.config.debugLevel > 1) {
6889
+ if (this.debugLevel > 1) {
6900
6890
  this.log.warn(
6901
6891
  "%s: Loading channel using most watched sort order",
6902
6892
  this.name,
@@ -6906,7 +6896,7 @@ class StbDevice {
6906
6896
  const favoriteChannelsSet = new Set(wantedProfile.favoriteChannels);
6907
6897
  this.mostWatched.forEach((mostWatchedChannelId) => {
6908
6898
  if (favoriteChannelsSet.has(mostWatchedChannelId)) {
6909
- if (this.config.debugLevel > 2) {
6899
+ if (this.debugLevel > 2) {
6910
6900
  this.log.warn(
6911
6901
  "%s: Loading channel using most watched sort order. Channel %s found, loading at index %s",
6912
6902
  this.name,
@@ -6919,14 +6909,14 @@ class StbDevice {
6919
6909
  });
6920
6910
  } else {
6921
6911
  // load by standard sort order
6922
- if (this.config.debugLevel > 1) {
6912
+ if (this.debugLevel > 1) {
6923
6913
  this.log.warn(
6924
6914
  "%s: Loading channel using standard sort order",
6925
6915
  this.name,
6926
6916
  );
6927
6917
  }
6928
6918
  wantedProfile.favoriteChannels.forEach((channel) => {
6929
- if (this.config.debugLevel > 1) {
6919
+ if (this.debugLevel > 1) {
6930
6920
  this.log.warn(
6931
6921
  "%s: Loading channel using standard sort order. Channel %s found, loading at index %s",
6932
6922
  this.name,
@@ -6938,7 +6928,7 @@ class StbDevice {
6938
6928
  });
6939
6929
  }
6940
6930
  }
6941
- if (this.config.debugLevel > 1) {
6931
+ if (this.debugLevel > 1) {
6942
6932
  this.log.warn(
6943
6933
  "%s: subscribedChIds.length: %s",
6944
6934
  this.name,
@@ -6954,11 +6944,11 @@ class StbDevice {
6954
6944
 
6955
6945
  // if the subscribedChIds is empty, load the channels from the master channel list
6956
6946
  // sorted by logicalChannelNumber, including only entitled channels
6957
- //if (this.config.debugLevel > 1) { this.log.warn("%s: Checking if subscribed channel list is needed", this.name); }
6947
+ //if (this.debugLevel > 1) { this.log.warn("%s: Checking if subscribed channel list is needed", this.name); }
6958
6948
  //this.log.warn("%s: this.customer.profiles", this.name, this.customer.profiles);
6959
6949
  //wantedProfile = this.customer.profiles.find(profile => profile.profileId === this.device.defaultProfileId);
6960
6950
  if (subscribedChIds.length === 0) {
6961
- if (this.config.debugLevel > 1) {
6951
+ if (this.debugLevel > 1) {
6962
6952
  this.log(
6963
6953
  "%s: Profile '%s' contains 0 favorite channels. Channel list will be loaded from master channel list",
6964
6954
  this.name,
@@ -6967,13 +6957,18 @@ class StbDevice {
6967
6957
  }
6968
6958
  // get a clean list of entitled channels (will not be in correct order)
6969
6959
  // some entitlements are not in the masterchannelList, these must be ignored
6970
- //if (this.config.debugLevel > 1) { this.log.warn("%s: Checking %s entitlements within %s channels in the master channel list", this.name, this.platform.session.entitlements.length, this.platform.masterChannelList.length); }
6960
+ //if (this.debugLevel > 1) { this.log.warn("%s: Checking %s entitlements within %s channels in the master channel list", this.name, this.platform.session.entitlements.length, this.platform.masterChannelList.length); }
6971
6961
 
6972
6962
  // entitlements needs to be reworked, currently load everything
6973
6963
  //this.log.warn("%s: Loading all channels into the subscribedChIds", this.name)
6974
6964
  //this.log.warn("%s: masterChannelList.length", this.name, this.platform.masterChannelList.length)
6975
6965
  //this.log.warn("%s: this.entitlements %s", this.name, this.entitlements)
6976
6966
  //this.log.warn("%s: this.platform.entitlements.entitlements %s", this.name, this.platform.entitlements.entitlements)
6967
+ // build Set once BEFORE the masterChannelList.forEach loop:
6968
+ const entitlementIdSet = new Set(
6969
+ this.platform.entitlements.entitlements.map((e) => e.id),
6970
+ );
6971
+
6977
6972
  this.platform.masterChannelList.forEach((channel) => {
6978
6973
  // check entitlements of this channel
6979
6974
  // channel.linearProducts is an array of entitlement codes assigned to a channel, each channel can have multiple entitlement codes
@@ -6982,7 +6977,6 @@ class StbDevice {
6982
6977
  // [{ casIndicator: 0, id: '600000001' }, { casIndicator: 0, id: '600000080' }, { casIndicator: 1, id: '600000070' }, { casIndicator: 1, id: '600000300' }]
6983
6978
  // control channel: SV09038 SRF 1 HD (entitled), with "linearProducts": [ '100000000', '100000001', '600000300' ],
6984
6979
  // control channel: SV06321 Nicktoons (not entitled), with linearProducts: [ '601007005' ],
6985
- let isEntitled = false;
6986
6980
  this.log.debug(
6987
6981
  "%s: checking entitlements for %s %s",
6988
6982
  this.name,
@@ -6994,18 +6988,8 @@ class StbDevice {
6994
6988
  this.name,
6995
6989
  channel.linearProducts,
6996
6990
  );
6997
- this.platform.entitlements.entitlements.forEach(
6998
- (subscribedlEntitlement) => {
6999
- if (channel.linearProducts.includes(subscribedlEntitlement.id)) {
7000
- this.log.debug(
7001
- "%s: channel channelId %s, linearProducts includes subscribedlEntitlement.id %s, channel is entitled",
7002
- this.name,
7003
- channel.id,
7004
- subscribedlEntitlement.id,
7005
- );
7006
- isEntitled = true;
7007
- }
7008
- },
6991
+ const isEntitled = channel.linearProducts.some((id) =>
6992
+ entitlementIdSet.has(id),
7009
6993
  );
7010
6994
  if (isEntitled) {
7011
6995
  subscribedChIds.push(channel.id);
@@ -7024,7 +7008,7 @@ class StbDevice {
7024
7008
  subscribedChIds.length,
7025
7009
  );
7026
7010
  }
7027
- if (this.config.debugLevel > 1) {
7011
+ if (this.debugLevel > 1) {
7028
7012
  this.log(
7029
7013
  "%s: Subscribed channel list loaded with %s channels",
7030
7014
  this.name,
@@ -7033,7 +7017,7 @@ class StbDevice {
7033
7017
  }
7034
7018
 
7035
7019
  // recently viewed apps
7036
- if (this.config.debugLevel > 1) {
7020
+ if (this.debugLevel > 1) {
7037
7021
  this.log.warn(
7038
7022
  "%s: refreshDeviceChannelList: recentlyUsedApps",
7039
7023
  this.name,
@@ -7041,24 +7025,6 @@ class StbDevice {
7041
7025
  );
7042
7026
  }
7043
7027
 
7044
- // grab the current ActiveIdentifier, and currentChannel, it might change during the channel refresh
7045
- /*
7046
- let currentActiveIdentifier = NO_INPUT_ID, currentChannel, currentChannelName = NO_CHANNEL_NAME;
7047
- if (this.accessoryConfigured) {
7048
- currentActiveIdentifier = this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier).value;
7049
- }
7050
- if (currentActiveIdentifier !== NO_INPUT_ID) {
7051
- currentChannel = this.inputServices[currentActiveIdentifier - 1];
7052
- currentChannelName = this.inputServices[currentActiveIdentifier - 1].getCharacteristic(Characteristic.ConfiguredName).value;
7053
- }
7054
- if (this.config.debugLevel > 0) {
7055
- this.log.warn("%s: refreshDeviceChannelList: before channel refresh: this.currentChannelId %s currentActiveIdentifier %s currentChannelName %s", this.name, this.currentChannelId, currentActiveIdentifier, currentChannelName);
7056
- }
7057
- */
7058
-
7059
- //const currentInpIndex = this.inputServices.findIndex(channel => channel.subtype === 'input_' + this.currentChannelId);
7060
- //this.log("Found before channel load: this.currentChannelId %s found at currentInpIndex %s", this.currentChannelId, currentInpIndex);
7061
-
7062
7028
  ////////////////////////////////////////////////////////
7063
7029
  // load the input list
7064
7030
  ////////////////////////////////////////////////////////
@@ -7144,30 +7110,26 @@ class StbDevice {
7144
7110
  i,
7145
7111
  subscribedChIds[i],
7146
7112
  );
7147
- channel = this.platform.masterChannelList.find(
7148
- (channel) => channel.id === subscribedChIds[i],
7149
- );
7113
+ channel = this.platform.masterChannelMap.get(subscribedChIds[i]);
7150
7114
  if (!channel) {
7151
- const newChName =
7152
- customChannel.name || "Channel " + subscribedChIds[i];
7115
+ const newChannel = {
7116
+ id: subscribedChIds[i],
7117
+ name: customChannel.name || "Channel " + subscribedChIds[i],
7118
+ logicalChannelNumber:
7119
+ 10000 + this.platform.masterChannelList.length,
7120
+ linearProducts: this.platform.entitlements.entitlements[0].id,
7121
+ };
7122
+ newChannel.configuredName = newChannel.name;
7153
7123
  this.log(
7154
7124
  "%s: Unknown channel %s [%s] discovered. Adding to the master channel list",
7155
7125
  this.name,
7156
- subscribedChIds[i],
7157
- newChName,
7126
+ newChannel.id,
7127
+ newChannel.name,
7158
7128
  );
7159
- this.platform.masterChannelList.push({
7160
- id: subscribedChIds[i],
7161
- name: newChName,
7162
- logicalChannelNumber:
7163
- 10000 + this.platform.masterChannelList.length, // integer
7164
- linearProducts: this.platform.entitlements.entitlements[0].id, // must be a valid entitlement id
7165
- });
7166
7129
  // refresh channel as the not found channel will now be in the masterChannelList
7167
- channel = this.platform.masterChannelList.find(
7168
- (channel) => channel.id === subscribedChIds[i],
7169
- );
7170
- channel.configuredName = channel.name; // set a configured name same as name
7130
+ this.platform.masterChannelList.push(newChannel);
7131
+ this.platform.masterChannelMap.set(subscribedChIds[i], newChannel);
7132
+ channel = newChannel;
7171
7133
  } else {
7172
7134
  // show some useful debug data
7173
7135
  this.log.debug(
@@ -7256,7 +7218,7 @@ class StbDevice {
7256
7218
  // update accessory only when configured, as this.inputServices[i] can only be updated when it exists
7257
7219
  if (this.accessoryConfigured) {
7258
7220
  // update existing services
7259
- if (this.config.debugLevel > 2) {
7221
+ if (this.debugLevel > 2) {
7260
7222
  this.log.warn(
7261
7223
  "Adding %s %s to input %s at index %s",
7262
7224
  channel.id,
@@ -7303,9 +7265,10 @@ class StbDevice {
7303
7265
  //this.log("DEBUG: this.currentChannelId %s", this.currentChannelId)
7304
7266
  //this.log("DEBUG: currentInput %s", currentInput)
7305
7267
  if (currentInput) {
7268
+ const currentInputIndex = this.inputServices.indexOf(currentInput);
7306
7269
  this.televisionService.updateCharacteristic(
7307
- Characteristic.ActiveIdentifier, // 1-based
7308
- currentInput.getCharacteristic(Characteristic.Identifier).value,
7270
+ Characteristic.ActiveIdentifier,
7271
+ currentInputIndex + 1, // Identifier is always index + 1
7309
7272
  );
7310
7273
  } else {
7311
7274
  // not found, set to NO_INPUT_ID
@@ -7317,40 +7280,6 @@ class StbDevice {
7317
7280
  }
7318
7281
  }
7319
7282
 
7320
- // save to disk
7321
- // not yet active
7322
- // this.platform.persistConfig(this.deviceId, this.channelList);
7323
-
7324
- // add the recently used apps, if we are loading from a user profile
7325
- /*
7326
- if (this.profileId > 0) {
7327
- // apps have a channel number starting with "app"
7328
- let appsToload = this.platform.profiles[this.profileId].recentlyUsedApps;
7329
- appsToload.forEach( (appId, i) => {
7330
- this.log("loading app", i, appId);
7331
- if (i <= maxChs) {
7332
- // get the channel
7333
- let foundIndex = this.platform.masterChannelList.findIndex(channel => channel.id === appId);
7334
- //this.log("foundIndex", foundIndex);
7335
- if (foundIndex >= 0) {
7336
- let channel = this.platform.masterChannelList[foundIndex];
7337
- this.log("loading app", channel);
7338
-
7339
- // update existing services
7340
-
7341
- const inputService = this.inputServices[i];
7342
- inputService
7343
- .updateCharacteristic(Characteristic.ConfiguredName, channel.name)
7344
- .updateCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED)
7345
- .updateCharacteristic(Characteristic.CurrentVisibilityState, Characteristic.CurrentVisibilityState.SHOWN)
7346
- .updateCharacteristic(Characteristic.TargetVisibilityState, Characteristic.TargetVisibilityState.SHOWN);
7347
-
7348
- }
7349
- }
7350
- });
7351
- }
7352
- */
7353
-
7354
7283
  // for any remaining inputs that may have been previously visible, set to not configured and hidden
7355
7284
  //this.log("channelList pre filter:", this.channelList);
7356
7285
  //let loadedChs = Math.min(chs, MAX_INPUT_SOURCES);
@@ -7404,7 +7333,7 @@ class StbDevice {
7404
7333
  // this is for the web session type as of 13.10.2022
7405
7334
  async getMostWatchedChannels(profileId, callback) {
7406
7335
  try {
7407
- if (this.config.debugLevel > 1) {
7336
+ if (this.debugLevel > 1) {
7408
7337
  this.log(
7409
7338
  "%s: getMostWatchedChannels started with %s",
7410
7339
  this.name,
@@ -7434,14 +7363,14 @@ class StbDevice {
7434
7363
  "x-profile": profile.profileId,
7435
7364
  },
7436
7365
  };
7437
- if (this.config.debugLevel > 0) {
7366
+ if (this.debugLevel > 0) {
7438
7367
  this.log.warn("getMostWatchedChannels: GET %s", url);
7439
7368
  }
7440
7369
  // this.log('getMostWatchedChannels: GET %s', url);
7441
7370
  axiosWS
7442
7371
  .get(url, config)
7443
7372
  .then((response) => {
7444
- if (this.config.debugLevel > 0) {
7373
+ if (this.debugLevel > 0) {
7445
7374
  this.log.warn(
7446
7375
  "getMostWatchedChannels: Profile %s: response: %s %s",
7447
7376
  profile.name,
@@ -7449,7 +7378,7 @@ class StbDevice {
7449
7378
  response.statusText,
7450
7379
  );
7451
7380
  }
7452
- if (this.config.debugLevel > 2) {
7381
+ if (this.debugLevel > 2) {
7453
7382
  this.log.warn(
7454
7383
  "getMostWatchedChannels: %s: response data:",
7455
7384
  profile.name,
@@ -7494,21 +7423,10 @@ class StbDevice {
7494
7423
  getMaxSources(callback) {
7495
7424
  // limit the amount of max channels to load as Apple HomeKit is limited to 100 services per accessory.
7496
7425
  // if a config exists for this device, read the users configured maxSources, if it exists
7497
- let maxSources = MAX_INPUT_SOURCES;
7498
- let configDevice = {};
7499
- if (this.config.devices) {
7500
- configDevice = this.config.devices.find(
7501
- (device) => device.deviceId === this.deviceId,
7502
- );
7503
- if (configDevice) {
7504
- // homebridge config for this device exists, read maxSources (if exists)
7505
- maxSources = Math.min(
7506
- configDevice.maxChannels || maxSources,
7507
- maxSources,
7508
- );
7509
- }
7510
- }
7511
- return maxSources;
7426
+ const maxChannels = this._configDevice.maxChannels;
7427
+ return maxChannels
7428
+ ? Math.min(maxChannels, MAX_INPUT_SOURCES)
7429
+ : MAX_INPUT_SOURCES;
7512
7430
  //this.log("%s: Setting maxSources to %s", this.name, maxSources);
7513
7431
  }
7514
7432
 
@@ -7522,12 +7440,12 @@ class StbDevice {
7522
7440
  //+++++++++++++++++++++++++++++++++++++++++++++++++++++
7523
7441
 
7524
7442
  // get power state
7525
- async getPower(callback) {
7443
+ getPower(callback) {
7526
7444
  // fired when the user clicks away from the Remote Control, regardless of which TV was selected
7527
7445
  // fired when the Home app wants to refresh the TV tile. Refresh occurs when tile is displayed.
7528
7446
  // this.currentPowerState is updated by the polling mechanism
7529
7447
  //this.log('getPowerState current power state:', this.currentPowerState);
7530
- if (this.config.debugLevel > 1) {
7448
+ if (this.debugLevel > 1) {
7531
7449
  this.log.warn(
7532
7450
  "%s: getPower returning %s [%s]",
7533
7451
  this.name,
@@ -7539,12 +7457,12 @@ class StbDevice {
7539
7457
  }
7540
7458
 
7541
7459
  // set power state
7542
- async setPower(targetPowerState, callback) {
7460
+ setPower(targetPowerState, callback) {
7543
7461
  // fired when the user clicks the power button in the TV accessory in the Home app
7544
7462
  // fired when the user clicks the TV tile in the Home app
7545
7463
  // fired when the first key is pressed after opening the Remote Control
7546
7464
  // wantedPowerState is the wanted power state: 0=off, 1=on
7547
- if (this.config.debugLevel > 1) {
7465
+ if (this.debugLevel > 1) {
7548
7466
  this.log.warn(
7549
7467
  "%s: setPower targetPowerState:",
7550
7468
  this.name,
@@ -7560,29 +7478,21 @@ class StbDevice {
7560
7478
  }
7561
7479
 
7562
7480
  // get device name (the accessory visible name)
7563
- async getDeviceName(callback) {
7481
+ getDeviceName(callback) {
7564
7482
  // fired by the user changing a the accessory name in Home app accessory setup
7565
7483
  // a user can rename any box at any time
7566
- if (this.config.debugLevel > 1) {
7484
+ if (this.debugLevel > 1) {
7567
7485
  this.log.warn("%s: getDeviceName returning '%s'", this.name, this.name);
7568
7486
  }
7569
7487
  callback(null, this.name);
7570
7488
  }
7571
7489
 
7572
7490
  // set device name (change the accessory visible name)
7573
- async setDeviceName(deviceName, callback) {
7491
+ setDeviceName(deviceName, callback) {
7574
7492
  // fired by the user changing the accessory name in Home app accessory setup
7575
7493
 
7576
7494
  // check if user wants to sync the box name
7577
- let syncName = true; // default true
7578
- if (this.config.devices) {
7579
- const configDevice = this.config.devices.find(
7580
- (device) => device.deviceId === this.deviceId,
7581
- );
7582
- if (configDevice && configDevice.syncName === false) {
7583
- syncName = configDevice.syncName;
7584
- }
7585
- }
7495
+ const syncName = this._configDevice.syncName !== false; // default true
7586
7496
 
7587
7497
  // sync name to physical device if enabled
7588
7498
  if (syncName) {
@@ -7617,7 +7527,7 @@ class StbDevice {
7617
7527
  deviceName,
7618
7528
  );
7619
7529
  } else {
7620
- if (this.config.debugLevel > 1) {
7530
+ if (this.debugLevel > 1) {
7621
7531
  this.log.warn(
7622
7532
  "%s: setDeviceName: deviceName %s",
7623
7533
  this.name,
@@ -7635,20 +7545,11 @@ class StbDevice {
7635
7545
  callback(null);
7636
7546
  }
7637
7547
 
7638
- // get mute state
7639
- async getMute(callback) {
7640
- // not supported, but might use somehow in the future
7641
- if (this.config.debugLevel > 1) {
7642
- this.log.warn("getMute");
7643
- }
7644
- callback(null);
7645
- }
7646
-
7647
7548
  // set mute state
7648
- async setMute(muteState, callback) {
7549
+ setMute(muteState, callback) {
7649
7550
  // sends the mute command. Mute is boolean
7650
7551
  // works for TVs that accept a mute toggle command
7651
- if (this.config.debugLevel > 1) {
7552
+ if (this.debugLevel > 1) {
7652
7553
  this.log.warn("%s: setMute muteState: %s", this.name, muteState);
7653
7554
  }
7654
7555
 
@@ -7660,43 +7561,27 @@ class StbDevice {
7660
7561
  this.log("Send key Mute to %s ", this.name);
7661
7562
 
7662
7563
  // Execute command to toggle mute
7663
- if (this.config.devices) {
7664
- const device = this.config.devices.find(
7665
- (device) => device.deviceId === this.deviceId,
7666
- );
7667
- if (device && device.muteCommand) {
7668
- // assumes the end device toggles between mute on and mute off with each command
7669
- exec(device.muteCommand, (error, stdout, stderr) => {
7670
- // Error detection. error is true when an exec error occured
7671
- if (error) {
7672
- this.log.warn("setMute Error:", stderr.trim());
7673
- }
7674
- });
7675
- } else {
7676
- this.log("%s: Mute command not configured", this.name);
7677
- }
7564
+ if (this._configDevice.muteCommand) {
7565
+ // assumes the end device toggles between mute on and mute off with each command
7566
+ exec(this._configDevice.muteCommand, (error, stdout, stderr) => {
7567
+ // Error detection. error is true when an exec error occured
7568
+ if (error) {
7569
+ this.log.warn("setMute Error:", stderr.trim());
7570
+ }
7571
+ });
7678
7572
  } else {
7679
7573
  this.log("%s: Mute command not configured", this.name);
7680
7574
  }
7681
7575
  }
7682
7576
 
7683
- // get volume
7684
- async getVolume(callback) {
7685
- // not supported, but might use somehow in the future
7686
- if (this.config.debugLevel > 1) {
7687
- this.log.warn("getVolume");
7688
- }
7689
- callback(null);
7690
- }
7691
-
7692
7577
  // set volume
7693
- async setVolume(volumeSelectorValue, callback) {
7578
+ setVolume(volumeSelectorValue, callback) {
7694
7579
  // set the volume of the TV using bash scripts
7695
7580
  // the ARRIS box remote control communicates with the stereo via IR commands, not over mqtt
7696
7581
  // so volume must be handled over a different method
7697
7582
  // here we send execute a bash command on the raspberry pi using the samsungctl command
7698
7583
  // to control the authors samsung stereo at 192.168.0.152
7699
- if (this.config.debugLevel > 1) {
7584
+ if (this.debugLevel > 1) {
7700
7585
  this.log.warn(
7701
7586
  "%s: setVolume volumeSelectorValue:",
7702
7587
  this.name,
@@ -7751,29 +7636,23 @@ class StbDevice {
7751
7636
  return false;
7752
7637
  } else {
7753
7638
  // Execute command to change volume, but only if command exists
7754
- if (this.config.devices) {
7755
- const device = this.config.devices.find(
7756
- (device) => device.deviceId === this.deviceId,
7639
+ if (this._configDevice.volUpCommand) {
7640
+ // assumes the end device toggles between mute on and mute off with each command
7641
+ exec(
7642
+ volumeSelectorValue === Characteristic.VolumeSelector.DECREMENT
7643
+ ? this._configDevice.volDownCommand
7644
+ : this._configDevice.volUpCommand,
7645
+ (error, _stdout, stderr) => {
7646
+ // Error detection. error is true when an exec error occured
7647
+ if (error) {
7648
+ this.log.warn(
7649
+ "%s: setVolume Error: %s",
7650
+ this.name,
7651
+ stderr.trim(),
7652
+ );
7653
+ }
7654
+ },
7757
7655
  );
7758
- if (device && device.volUpCommand && device.volDownCommand) {
7759
- exec(
7760
- volumeSelectorValue === Characteristic.VolumeSelector.DECREMENT
7761
- ? device.volDownCommand
7762
- : device.volUpCommand,
7763
- (error, _stdout, stderr) => {
7764
- // Error detection. error is true when an exec error occured
7765
- if (error) {
7766
- this.log.warn(
7767
- "%s: setVolume Error: %s",
7768
- this.name,
7769
- stderr.trim(),
7770
- );
7771
- }
7772
- },
7773
- );
7774
- } else {
7775
- this.log("%s: Volume commands not configured", this.name);
7776
- }
7777
7656
  } else {
7778
7657
  this.log("%s: Volume commands not configured", this.name);
7779
7658
  }
@@ -7781,39 +7660,20 @@ class StbDevice {
7781
7660
  }
7782
7661
 
7783
7662
  // get input (TV channel)
7784
- async getInput(callback) {
7663
+ getInput(callback) {
7785
7664
  // fired when the user clicks away from the iOS Device TV Remote Control, regardless of which TV was selected
7786
7665
  // fired when the icon is clicked in the Home app and HomeKit requests a refresh
7787
7666
  // fired when the Home app is opened
7788
- // this.currentChannelId is updated by mqtt
7789
- // must return a valid index, and must never return null
7790
- //if (this.config.debugLevel > 1) { this.log.warn('%s: getInput currentChannelId %s',this.name, this.currentChannelId); }
7791
-
7792
- // find the this.currentChannelId (eg SV09038) in the accessory inputs and return the inputindex once found
7793
- // this allows HomeKit to show the selected current channel
7794
- // as we cannot guarantee the list order due to personalizationServices changing it at any time
7795
- // we must search by input_channelId within the current accessory InputSource.subtype
7796
- //this.log.warn('%s: getInput looking for this.currentChannelId %s in this.inputServices', this.name, this.currentChannelId);
7797
- let currentChannelName = NO_CHANNEL_NAME;
7798
- let currentInputIndex = this.inputServices.findIndex(
7799
- (InputSource) => InputSource.subtype === "input_" + this.currentChannelId,
7800
- );
7801
- if (currentInputIndex === -1) {
7802
- currentInputIndex = NO_INPUT_ID - 1;
7803
- } // if nothing found, set to NO_INPUT_ID to clear the name from the Home app tile
7804
- if (currentInputIndex > -1 && currentInputIndex !== NO_INPUT_ID - 1) {
7805
- currentChannelName = this.inputServices[
7806
- currentInputIndex
7807
- ].getCharacteristic(Characteristic.ConfiguredName).value;
7808
- }
7809
- const currentActiveInput = currentInputIndex + 1;
7810
- if (this.config.debugLevel > 1) {
7667
+ // polled by HomeKit every 2-15 minutes
7668
+ const currentActiveInput = this.previousActiveIdentifier ?? NO_INPUT_ID;
7669
+ if (this.debugLevel > 1) {
7670
+ const ch = this.channelList[currentActiveInput - 1];
7811
7671
  this.log.warn(
7812
7672
  "%s: getInput returning input %s %s [%s]",
7813
7673
  this.name,
7814
7674
  currentActiveInput,
7815
7675
  this.currentChannelId,
7816
- currentChannelName,
7676
+ (ch || {}).configuredName || NO_CHANNEL_NAME,
7817
7677
  );
7818
7678
  }
7819
7679
 
@@ -7821,9 +7681,9 @@ class StbDevice {
7821
7681
  }
7822
7682
 
7823
7683
  // set input (change the TV channel)
7824
- async setInput(input, callback) {
7684
+ setInput(input, callback) {
7825
7685
  input = input ?? {}; // ensure input is never null or undefined
7826
- if (this.config.debugLevel > 1) {
7686
+ if (this.debugLevel > 1) {
7827
7687
  this.log.warn(
7828
7688
  "%s: setInput input %s %s",
7829
7689
  this.name,
@@ -7833,25 +7693,16 @@ class StbDevice {
7833
7693
  }
7834
7694
  callback(); // for rapid response
7835
7695
  // get current channel, also finds keyMacro channels
7836
- let channel = this.channelList.find(
7837
- (channel) => channel.id === this.currentChannelId,
7838
- );
7839
- // if not found look in the master channel list
7840
- if (!channel) {
7841
- channel = this.platform.masterChannelList.find(
7842
- (channel) => channel.id === this.currentChannelId,
7843
- );
7844
- }
7845
-
7846
- // robustness: only try to switch channel if an input.id exists. Handle KeyMacros
7696
+ const prevChannel = this.channelList[this.previousActiveIdentifier - 1];
7847
7697
  this.log(
7848
7698
  "%s: Change channel from %s [%s] to %s [%s]",
7849
7699
  this.name,
7850
7700
  this.currentChannelId,
7851
- (channel || {}).name || NO_CHANNEL_NAME,
7701
+ (prevChannel || {}).name || NO_CHANNEL_NAME,
7852
7702
  input.id,
7853
7703
  input.name,
7854
7704
  );
7705
+ // robustness: only try to switch channel if an input.id exists. Handle KeyMacros
7855
7706
  if (input.id && input.id.startsWith("$KeyMacro")) {
7856
7707
  this.lastKeyMacroChannelId = input.id; // remember last keyMacro id
7857
7708
  this.platform.sendKey(this.deviceId, this.name, input.keyMacro);
@@ -7869,11 +7720,11 @@ class StbDevice {
7869
7720
  }
7870
7721
 
7871
7722
  // get input name (the TV channel name)
7872
- async getInputName(inputId, callback) {
7723
+ getInputName(inputId, callback) {
7873
7724
  // fired by the user changing a channel name in Home app accessory setup
7874
- //if (this.config.debugLevel > 1) { this.log.warn('%s: getInputName inputId %s', this.name, inputId); }
7725
+ //if (this.debugLevel > 1) { this.log.warn('%s: getInputName inputId %s', this.name, inputId); }
7875
7726
  const inputName = (this.channelList[inputId] || {}).configuredName || ""; // Empty string if not found
7876
- if (this.config.debugLevel > 1) {
7727
+ if (this.debugLevel > 1) {
7877
7728
  this.log.warn(
7878
7729
  "%s: getInputName for input %s returning '%s'",
7879
7730
  this.name,
@@ -7885,7 +7736,7 @@ class StbDevice {
7885
7736
  }
7886
7737
 
7887
7738
  // set input name (change the TV channel name)
7888
- async setInputName(inputId, newInputName, callback) {
7739
+ setInputName(inputId, newInputName, callback) {
7889
7740
  // fired by the user changing a channel name in Home app accessory setup
7890
7741
  // we cannot handle this as we don't know which channel got renamed
7891
7742
  // as user could name multiple channels to xxx
@@ -7893,7 +7744,7 @@ class StbDevice {
7893
7744
  // channel 3 renamed from BBC Four/Cbeebies HD to BBC Four Cbeebies HD (valid only for HomeKit)
7894
7745
  // inputId is an integer of the input
7895
7746
 
7896
- if (this.config.debugLevel > 1) {
7747
+ if (this.debugLevel > 1) {
7897
7748
  this.log.warn(
7898
7749
  "%s: setInputName for input %s to inputName %s",
7899
7750
  this.name,
@@ -7904,7 +7755,7 @@ class StbDevice {
7904
7755
 
7905
7756
  // store in channelList array and write to disk at every change
7906
7757
  // ensure that this.channelList bounds are not exceeded!
7907
- //if (this.config.debugLevel > 1) { this.log('%s: DEBUG setInputName inputId %s, this.channelList.length %s', this.name, inputId, this.channelList.length); }
7758
+ //if (this.debugLevel > 1) { this.log('%s: DEBUG setInputName inputId %s, this.channelList.length %s', this.name, inputId, this.channelList.length); }
7908
7759
  // not yet working, sometimes causes errors when channelList is not full of data, so commented out
7909
7760
  /*
7910
7761
  this.log.warn('%s: setInputName inputId %s, this.channelList.length %s', this.name, inputId, this.channelList.length);
@@ -7918,7 +7769,7 @@ class StbDevice {
7918
7769
  //this.platform.persistConfig(this.deviceId, this.channelList);
7919
7770
 
7920
7771
  // maybe suppress
7921
- if (this.config.debugLevel > 2) {
7772
+ if (this.debugLevel > 2) {
7922
7773
  //this.log('%s: Renamed channel %s from %s to %s (valid only for HomeKit)', this.name, inputId+1, oldInputName, newInputName);
7923
7774
  this.log(
7924
7775
  "%s: Renamed channel %s to %s (valid only for HomeKit)",
@@ -7933,13 +7784,13 @@ class StbDevice {
7933
7784
  // get current channel id (the TV channel identifier, a string)
7934
7785
  // added in v2.1.0
7935
7786
  // custom characteristic, returns a string, the event updates the characteristic value automatically
7936
- async getCurrentChannelId(callback, currentChannelId) {
7787
+ getCurrentChannelId(callback, currentChannelId) {
7937
7788
  // fired by the user reading the Custom characteristic in Shortcuts
7938
7789
  // fired when the accessory is first created and HomeKit requests a refresh
7939
7790
  // fired when the icon is clicked in the Home app and HomeKit requests a refresh
7940
7791
  // fired when the Home app is opened
7941
7792
  currentChannelId = this.currentChannelId || ""; // this.currentChannelId is a string eg SV09038. Empty string if not found
7942
- if (this.config.debugLevel > 1) {
7793
+ if (this.debugLevel > 1) {
7943
7794
  this.log.warn(
7944
7795
  "%s: getCurrentChannelId returning '%s'",
7945
7796
  this.name,
@@ -7952,17 +7803,17 @@ class StbDevice {
7952
7803
  // get current channel name (the TV channel name)
7953
7804
  // added in v2.1.0
7954
7805
  // custom characteristic, returns a string, the event updates the characteristic value automatically
7955
- async getCurrentChannelName(callback, currentChannelName) {
7806
+ getCurrentChannelName(callback, currentChannelName) {
7956
7807
  // fired by the user reading the Custom characteristic in Shortcuts
7957
7808
  // fired when the accessory is first created and HomeKit requests a refresh
7958
7809
  // fired when the icon is clicked in the Home app and HomeKit requests a refresh
7959
7810
  // fired when the Home app is opened
7960
- const curChannel = this.platform.masterChannelList.find(
7961
- (channel) => channel.id === this.currentChannelId,
7962
- ); // this.currentChannelId is a string eg SV09038
7811
+ const curChannel = this.platform.masterChannelMap?.get(
7812
+ this.currentChannelId,
7813
+ );
7963
7814
  // consider setting to Radio if radio is playing
7964
7815
  currentChannelName = (curChannel || {}).name || ""; // Empty string if not found
7965
- if (this.config.debugLevel > 1) {
7816
+ if (this.debugLevel > 1) {
7966
7817
  this.log.warn(
7967
7818
  "%s: getCurrentChannelName returning '%s'",
7968
7819
  this.name,
@@ -7973,9 +7824,9 @@ class StbDevice {
7973
7824
  }
7974
7825
 
7975
7826
  // get input visibility state (of the TV channel in the accessory)
7976
- async getInputVisibilityState(inputId, callback) {
7827
+ getInputVisibilityState(inputId, callback) {
7977
7828
  // fired when ??
7978
- if (this.config.debugLevel > 1) {
7829
+ if (this.debugLevel > 1) {
7979
7830
  this.log.warn(
7980
7831
  "%s: getInputVisibilityState inputId %s",
7981
7832
  this.name,
@@ -7986,36 +7837,37 @@ class StbDevice {
7986
7837
  //if (this.channelList[inputId-1].name === 'HIDDEN') {
7987
7838
  // visibilityState = Characteristic.CurrentVisibilityState.HIDDEN;
7988
7839
  // }
7989
- const visibilityState = this.inputServices[inputId].getCharacteristic(
7990
- Characteristic.CurrentVisibilityState,
7991
- ).value;
7992
- if (this.config.debugLevel > 2) {
7840
+ const visibilityState =
7841
+ (this.channelList[inputId] || {}).visibilityState ??
7842
+ Characteristic.CurrentVisibilityState.HIDDEN;
7843
+ if (this.debugLevel > 2) {
7993
7844
  this.log.warn(
7994
7845
  "%s: getInputVisibilityState input %s returning %s [%s]",
7995
7846
  this.name,
7996
7847
  inputId,
7997
7848
  visibilityState,
7998
- Object.keys(Characteristic.CurrentVisibilityState)[visibilityState + 1],
7849
+ CHAR_NAMES.CurrentVisibilityState[visibilityState + 1],
7999
7850
  );
8000
7851
  }
8001
7852
  callback(null, visibilityState);
8002
7853
  }
8003
7854
 
8004
7855
  // set input visibility state (show or hide the TV channel)
8005
- async setInputVisibilityState(inputId, visibilityState, callback) {
7856
+ setInputVisibilityState(inputId, visibilityState, callback) {
8006
7857
  // fired when ??
8007
- if (this.config.debugLevel > 1) {
7858
+ if (this.debugLevel > 1) {
8008
7859
  this.log.warn(
8009
7860
  "%s: setInputVisibilityState for input %s inputVisibilityState %s [%s]",
8010
7861
  this.name,
8011
7862
  inputId,
8012
7863
  visibilityState,
8013
- Object.keys(Characteristic.CurrentVisibilityState)[visibilityState + 1],
7864
+ CHAR_NAMES.CurrentVisibilityState[visibilityState + 1],
8014
7865
  );
8015
7866
  }
8016
- this.inputServices[inputId]
8017
- .getCharacteristic(Characteristic.CurrentVisibilityState)
8018
- .updateValue(visibilityState);
7867
+ this.inputServices[inputId].updateCharacteristic(
7868
+ Characteristic.CurrentVisibilityState,
7869
+ visibilityState,
7870
+ );
8019
7871
 
8020
7872
  // store in channelList array and write to disk at every change
8021
7873
  this.channelList[inputId].visibilityState = visibilityState;
@@ -8026,54 +7878,42 @@ class StbDevice {
8026
7878
  }
8027
7879
 
8028
7880
  // get closed captions state
8029
- async getClosedCaptions(callback) {
7881
+ getClosedCaptions(callback) {
8030
7882
  // fired when the Home app wants to refresh the TV tile. Refresh occurs when tile is displayed.
8031
- if (this.config.debugLevel > 1) {
7883
+ if (this.debugLevel > 1) {
8032
7884
  this.log.warn(
8033
7885
  "%s: getClosedCaptions returning %s [%s]",
8034
7886
  this.name,
8035
- this.currentClosedCaptionsState,
8036
- Object.keys(Characteristic.ClosedCaptions)[
8037
- this.currentClosedCaptionsState + 1
8038
- ],
7887
+ this.currentClosedCaptions,
7888
+ CHAR_NAMES.ClosedCaptions[this.currentClosedCaptions + 1],
8039
7889
  );
8040
7890
  }
8041
- callback(null, this.currentClosedCaptionsState); // return current state
7891
+ callback(null, this.currentClosedCaptions); // return current state
8042
7892
  }
8043
7893
 
8044
7894
  // set closed captions state
8045
- async setClosedCaptions(targetClosedCaptionsState, callback) {
7895
+ setClosedCaptions(targetClosedCaptionsState, callback) {
8046
7896
  // fired when ?? Apple HomeKit has no ability to control setClosedCaptions
8047
7897
  // targetClosedCaptionsState is the wanted state
8048
- if (this.config.debugLevel > 1) {
7898
+ if (this.debugLevel > 1) {
8049
7899
  this.log.warn(
8050
7900
  "%s: setClosedCaptions targetClosedCaptionsState:",
8051
7901
  this.name,
8052
7902
  targetClosedCaptionsState,
8053
- Object.keys(Characteristic.ClosedCaptions)[
8054
- targetClosedCaptionsState + 1
8055
- ],
7903
+ CHAR_NAMES.ClosedCaptions[targetClosedCaptionsState + 1],
8056
7904
  );
8057
7905
  }
8058
- if (this.currentClosedCaptionsState !== targetClosedCaptionsState) {
7906
+ if (this.currentClosedCaptions !== targetClosedCaptionsState) {
8059
7907
  this.log("setClosedCaptions: not yet implemented");
8060
7908
  }
8061
7909
  callback();
8062
7910
  }
8063
7911
 
8064
7912
  // get picture mode state
8065
- async getPictureMode(callback) {
7913
+ getPictureMode(callback) {
8066
7914
  // fired when the Home app wants to refresh the TV tile. Refresh occurs when tile is displayed.
8067
- if (this.config.debugLevel > 1) {
8068
- //this.log.warn('%s: getPictureMode', this.name);
8069
- // get the config for the device, needed for a few status checks
8070
- let configDevice;
8071
- if (this.config.devices) {
8072
- configDevice = this.config.devices.find(
8073
- (device) => device.deviceId === this.deviceId,
8074
- );
8075
- }
8076
- if ((configDevice || {}).customPictureMode === "recordingState") {
7915
+ if (this.debugLevel > 1) {
7916
+ if (this._configDevice.customPictureMode === "recordingState") {
8077
7917
  this.log.warn(
8078
7918
  "%s: getPictureMode returning %s [%s]",
8079
7919
  this.name,
@@ -8085,7 +7925,7 @@ class StbDevice {
8085
7925
  "%s: getPictureMode returning %s [%s]",
8086
7926
  this.name,
8087
7927
  this.customPictureMode,
8088
- Object.keys(Characteristic.PictureMode)[this.customPictureMode + 1],
7928
+ CHAR_NAMES.PictureMode[this.customPictureMode + 1],
8089
7929
  );
8090
7930
  }
8091
7931
  }
@@ -8093,15 +7933,15 @@ class StbDevice {
8093
7933
  }
8094
7934
 
8095
7935
  // set picture mode state
8096
- async setPictureMode(targetPictureMode, callback) {
7936
+ setPictureMode(targetPictureMode, callback) {
8097
7937
  // The current Home app (iOS 16.0) does not support setting this characteristic, thus is never fired
8098
7938
  // targetClosedCaptionsState is the wanted state
8099
- if (this.config.debugLevel > 1) {
7939
+ if (this.debugLevel > 1) {
8100
7940
  this.log.warn(
8101
7941
  "%s: setPictureMode targetPictureMode:",
8102
7942
  this.name,
8103
7943
  targetPictureMode,
8104
- Object.keys(Characteristic.PictureMode)[targetPictureMode + 1],
7944
+ CHAR_NAMES.PictureMode[targetPictureMode + 1],
8105
7945
  );
8106
7946
  }
8107
7947
  if (this.customPictureMode !== targetPictureMode) {
@@ -8111,12 +7951,11 @@ class StbDevice {
8111
7951
  }
8112
7952
 
8113
7953
  // set power mode selection (View TV Settings menu option)
8114
- async setPowerModeSelection(state, callback) {
7954
+ setPowerModeSelection(state, callback) {
8115
7955
  // fired by the View TV Settings command in the Home app TV accessory Settings
8116
- if (this.config.debugLevel > 1) {
7956
+ if (this.debugLevel > 1) {
8117
7957
  this.log.warn("%s: setPowerModeSelection state:", this.name, state);
8118
7958
  }
8119
- callback(false); // for rapid response
8120
7959
  this.log("Menu command: View TV Settings");
8121
7960
  // only send the keys if the power is on
8122
7961
  if (this.currentPowerState === Characteristic.Active.ACTIVE) {
@@ -8130,10 +7969,10 @@ class StbDevice {
8130
7969
  }
8131
7970
 
8132
7971
  // get current media state
8133
- async getCurrentMediaState(callback) {
7972
+ getCurrentMediaState(callback) {
8134
7973
  // The current Home app (iOS 16.0) does not support setting this characteristic, thus is never fired
8135
7974
  // cannot be controlled by Apple Home app, but could be controlled by other HomeKit apps
8136
- if (this.config.debugLevel > 1) {
7975
+ if (this.debugLevel > 1) {
8137
7976
  this.log.warn(
8138
7977
  "%s: getCurrentMediaState returning %s [%s]",
8139
7978
  this.name,
@@ -8145,11 +7984,11 @@ class StbDevice {
8145
7984
  }
8146
7985
 
8147
7986
  // get target media state
8148
- async getTargetMediaState(callback) {
7987
+ getTargetMediaState(callback) {
8149
7988
  // The current Home app (iOS 16.0) does not support getting this characteristic, thus is never fired
8150
7989
  // cannot be controlled by Apple Home app, but could be controlled by other HomeKit apps
8151
7990
  // must never return null, so send STOP as default value
8152
- if (this.config.debugLevel > 1) {
7991
+ if (this.debugLevel > 1) {
8153
7992
  this.log.warn(
8154
7993
  "%s: getTargetMediaState returning %s [%s]",
8155
7994
  this.name,
@@ -8161,34 +8000,20 @@ class StbDevice {
8161
8000
  }
8162
8001
 
8163
8002
  // set target media state
8164
- async setTargetMediaState(targetMediaState, logChangeOnly, callback) {
8003
+ setTargetMediaState(targetMediaState, logChangeOnly, callback) {
8165
8004
  // The current Home app (iOS 16.0) does not support setting this characteristic, thus is never fired
8166
8005
  // cannot be controlled by Apple Home app, but could be controlled by other HomeKit apps
8167
8006
  // can be controlled by the Apple TV Remote Control, and is called when changing Play / Pause / Stop
8168
8007
  // logChangeOnly = TRUE: only the changes are logged, no media state change occurs. Needed when sending remote keypresses to prevent double commands
8169
- if (this.config.debugLevel > 1) {
8008
+ if (this.debugLevel > 1) {
8170
8009
  this.log.warn(
8171
8010
  "%s: setTargetMediaState targetMediaState:",
8172
8011
  this.name,
8173
8012
  targetMediaState,
8174
- Object.keys(Characteristic.TargetMediaState)[targetMediaState + 1],
8013
+ CHAR_NAMES.TargetMediaState[targetMediaState + 1],
8175
8014
  );
8176
8015
  }
8177
8016
 
8178
- let prevTargetMediaState = this.televisionService.getCharacteristic(
8179
- Characteristic.TargetMediaState,
8180
- ).value;
8181
- if (prevTargetMediaState !== targetMediaState) {
8182
- this.targetMediaState = targetMediaState;
8183
- this.log(
8184
- "%s: Target Media state changed from %s [%s] to %s [%s]",
8185
- this.name,
8186
- prevTargetMediaState,
8187
- Object.keys(Characteristic.TargetMediaState)[prevTargetMediaState + 1],
8188
- targetMediaState,
8189
- Object.keys(Characteristic.TargetMediaState)[targetMediaState + 1],
8190
- );
8191
- }
8192
8017
  if (!logChangeOnly) {
8193
8018
  // send the setMediaState command if we are not just logging the change
8194
8019
  const boxMediaStatePAUSE = 0,
@@ -8205,10 +8030,10 @@ class StbDevice {
8205
8030
  newBoxMediaState = boxMediaStatePAUSE;
8206
8031
  break;
8207
8032
  }
8208
- if (this.config.debugLevel > 1) {
8033
+ if (this.debugLevel > 1) {
8209
8034
  this.log(
8210
8035
  "setTargetMediaState: Set media to %s for",
8211
- Object.keys(Characteristic.TargetMediaState)[targetMediaState + 1],
8036
+ CHAR_NAMES.TargetMediaState[targetMediaState + 1],
8212
8037
  this.currentChannelId,
8213
8038
  );
8214
8039
  }
@@ -8221,11 +8046,11 @@ class StbDevice {
8221
8046
  }
8222
8047
  if (callback && typeof callback === "function") {
8223
8048
  callback();
8224
- } // for rapid response
8049
+ }
8225
8050
  }
8226
8051
 
8227
8052
  // get display order
8228
- async getDisplayOrder(callback) {
8053
+ getDisplayOrder(callback) {
8229
8054
  // fired when the user clicks away from the iOS Device TV Remote Control, regardless of which TV was selected
8230
8055
  // fired when the icon is clicked in the Home app and the Home app requests a refresh
8231
8056
 
@@ -8233,17 +8058,17 @@ class StbDevice {
8233
8058
  let dispOrder = this.televisionService.getCharacteristic(
8234
8059
  Characteristic.DisplayOrder,
8235
8060
  ).value;
8236
- if (this.config.debugLevel > 1) {
8061
+ if (this.debugLevel > 1) {
8237
8062
  this.log.warn("%s: getDisplayOrder returning '%s'", this.name, dispOrder);
8238
8063
  }
8239
8064
  callback(null, dispOrder);
8240
8065
  }
8241
8066
 
8242
8067
  // set display order
8243
- async setDisplayOrder(displayOrder, callback) {
8068
+ setDisplayOrder(displayOrder, callback) {
8244
8069
  // fired when the user clicks away from the iOS Device TV Remote Control, regardless of which TV was selected
8245
8070
  // fired when the icon is clicked in the Home app and the Home app requests a refresh
8246
- if (this.config.debugLevel > 1) {
8071
+ if (this.debugLevel > 1) {
8247
8072
  this.log.warn(
8248
8073
  "%s: setDisplayOrder displayOrder",
8249
8074
  this.name,
@@ -8254,40 +8079,40 @@ class StbDevice {
8254
8079
  }
8255
8080
 
8256
8081
  // get in use
8257
- async getInUse(callback) {
8082
+ getInUse(callback) {
8258
8083
  // useful in Shortcuts and Automations
8259
8084
  // log the inUse value
8260
- if (this.config.debugLevel > 1) {
8085
+ if (this.debugLevel > 1) {
8261
8086
  this.log.warn(
8262
8087
  "%s: getInUse returning %s [%s]",
8263
8088
  this.name,
8264
8089
  this.currentInUse,
8265
- Object.keys(Characteristic.InUse)[this.currentInUse + 1],
8090
+ CHAR_NAMES.InUse[this.currentInUse + 1],
8266
8091
  );
8267
8092
  }
8268
8093
  callback(null, this.currentInUse);
8269
8094
  }
8270
8095
 
8271
8096
  // get program mode (recording scheduled, not scheduled)
8272
- async getProgramMode(callback) {
8097
+ getProgramMode(callback) {
8273
8098
  // useful in Shortcuts and Automations
8274
8099
  // log the programMode value
8275
- if (this.config.debugLevel > 1) {
8100
+ if (this.debugLevel > 1) {
8276
8101
  this.log.warn(
8277
8102
  "%s: getProgramMode returning %s [%s]",
8278
8103
  this.name,
8279
8104
  this.currentProgramMode,
8280
- Object.keys(Characteristic.ProgramMode)[this.currentProgramMode + 1],
8105
+ CHAR_NAMES.ProgramMode[this.currentProgramMode + 1],
8281
8106
  );
8282
8107
  }
8283
8108
  callback(null, this.currentProgramMode);
8284
8109
  }
8285
8110
 
8286
8111
  // get StatusActive state
8287
- async getStatusActive(callback) {
8112
+ getStatusActive(callback) {
8288
8113
  // useful in Shortcuts and Automations
8289
8114
  // log the StatusActive value
8290
- if (this.config.debugLevel > 1) {
8115
+ if (this.debugLevel > 1) {
8291
8116
  this.log.warn(
8292
8117
  "%s: getStatusActive returning %s [%s]",
8293
8118
  this.name,
@@ -8299,57 +8124,53 @@ class StbDevice {
8299
8124
  }
8300
8125
 
8301
8126
  // get status fault
8302
- async getStatusFault(callback) {
8127
+ getStatusFault(callback) {
8303
8128
  // useful in Shortcuts and Automations
8304
8129
  // log the StatusFault
8305
- if (this.config.debugLevel > 1) {
8130
+ if (this.debugLevel > 1) {
8306
8131
  this.log.warn(
8307
8132
  "%s: getStatusFault returning %s [%s]",
8308
8133
  this.name,
8309
8134
  this.currentStatusFault,
8310
- Object.keys(Characteristic.StatusFault)[this.currentStatusFault + 1],
8135
+ CHAR_NAMES.StatusFault[this.currentStatusFault + 1],
8311
8136
  );
8312
8137
  }
8313
8138
  callback(null, this.currentStatusFault);
8314
8139
  }
8315
8140
 
8316
8141
  // get InputSourceType state
8317
- async getInputSourceType(callback) {
8142
+ getInputSourceType(callback) {
8318
8143
  // useful in Shortcuts and Automations
8319
8144
  // log the InputSourceType value
8320
- if (this.config.debugLevel > 1) {
8145
+ if (this.debugLevel > 1) {
8321
8146
  this.log.warn(
8322
8147
  "%s: getInputSourceType returning %s [%s]",
8323
8148
  this.name,
8324
8149
  this.currentInputSourceType,
8325
- Object.keys(Characteristic.InputSourceType)[
8326
- this.currentInputSourceType + 1
8327
- ],
8150
+ CHAR_NAMES.InputSourceType[this.currentInputSourceType + 1],
8328
8151
  );
8329
8152
  }
8330
8153
  callback(null, this.currentInputSourceType);
8331
8154
  }
8332
8155
 
8333
8156
  // get InputDeviceType state
8334
- async getInputDeviceType(callback) {
8157
+ getInputDeviceType(callback) {
8335
8158
  // useful in Shortcuts and Automations
8336
8159
  // log the InputDeviceType value
8337
- if (this.config.debugLevel > 1) {
8160
+ if (this.debugLevel > 1) {
8338
8161
  this.log.warn(
8339
8162
  "%s: getInputDeviceType returning %s [%s]",
8340
8163
  this.name,
8341
8164
  this.currentInputDeviceType,
8342
- Object.keys(Characteristic.InputDeviceType)[
8343
- this.currentInputDeviceType + 1
8344
- ],
8165
+ CHAR_NAMES.InputDeviceType[this.currentInputDeviceType + 1],
8345
8166
  );
8346
8167
  }
8347
8168
  callback(null, this.currentInputDeviceType);
8348
8169
  }
8349
8170
 
8350
8171
  // set remote key
8351
- async setRemoteKey(remoteKey, callback) {
8352
- if (this.config.debugLevel > 1) {
8172
+ setRemoteKey(remoteKey, callback) {
8173
+ if (this.debugLevel > 1) {
8353
8174
  this.log.warn("%s: setRemoteKey remoteKey:", this.name, remoteKey);
8354
8175
  }
8355
8176
  callback(null); // for rapid response
@@ -8378,13 +8199,7 @@ class StbDevice {
8378
8199
  //this.log.debug("%s: setRemoteKey: remoteKey %s, lastKeyPressTime %s",this.name, remoteKey, lastKeyPressTime);
8379
8200
 
8380
8201
  // get the config for this device
8381
- let configDevice;
8382
- if (this.config.devices) {
8383
- configDevice = this.config.devices.find(
8384
- (device) => device.deviceId === this.deviceId,
8385
- );
8386
- }
8387
-
8202
+ const configDevice = this._configDevice;
8388
8203
  const DEFAULT_DOUBLE_PRESS_DELAY_TIME = 300; // default, in case config missing
8389
8204
  const DEFAULT_TRIPLE_PRESS_DELAY_TIME = 800; // default, in case config missing
8390
8205