homebridge-eosstb 2.0.4 → 2.1.0
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.
- package/CHANGELOG.md +20 -10
- package/README.md +20 -11
- package/config.schema.json +1 -0
- package/index.js +303 -234
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,19 +3,24 @@ All notable changes to this project will be documented in this file.
|
|
|
3
3
|
See the [Readme file](https://github.com/jsiegenthaler/homebridge-eosstb/blob/master/README.md) for full plugin documentation.
|
|
4
4
|
Please restart Homebridge after every plugin update.
|
|
5
5
|
|
|
6
|
-
# IMPORTANT NOTICE
|
|
7
|
-
This (v2.x) is a major update over v1.x due to the change in endpoints in the backend systems that occured on 13.10.2022.
|
|
8
|
-
Please report all bugs and problems.
|
|
9
|
-
|
|
10
|
-
|
|
11
6
|
# Bug Fixes and Improvements
|
|
12
7
|
|
|
13
8
|
## Current To-Do and In-Work List (For Future Releases, in rough order of priority):
|
|
14
|
-
*
|
|
15
|
-
* Fix potential problem with getPersonalizationData failing after plugin has been running overnight with ERR_BAD_REQUEST
|
|
9
|
+
* Add ability to log and read current program name
|
|
16
10
|
* Implement refreshToken capabilities
|
|
17
11
|
|
|
18
12
|
|
|
13
|
+
## 2.1.0 (2023-01-03)
|
|
14
|
+
* Added KeyMacro support
|
|
15
|
+
* Added custom characteristics: Current Channel Id and Current Channel Name, useful in automations
|
|
16
|
+
* Added preparation for reading current program name (future feature)
|
|
17
|
+
* Fixed bug where Input Device Type was not always correctly logged
|
|
18
|
+
* Fixed bug where Input Source Type was not always correctly logged
|
|
19
|
+
* Updated iOS version references
|
|
20
|
+
* Bumped dependency "axios": "^1.2.2"
|
|
21
|
+
* Bumped dependency "axios-cookiejar-support": "^4.0.6"
|
|
22
|
+
|
|
23
|
+
|
|
19
24
|
## 2.0.4 (2022-12-05)
|
|
20
25
|
* Fixed model detection of HUMAX EOS1008R boxes
|
|
21
26
|
* Fixed detection of Status Active to properly show mqtt active or not using Status Active in the Home app
|
|
@@ -41,10 +46,15 @@ Please report all bugs and problems.
|
|
|
41
46
|
* Bumped Homebridge "homebridge": ">=1.6.0",
|
|
42
47
|
|
|
43
48
|
|
|
49
|
+
## 2.0.2-beta.1 (2022-11-19)
|
|
50
|
+
* Added custom characteristic Current Channel Id
|
|
51
|
+
* Added custom characteristic Current Channel Name
|
|
52
|
+
* Optimised some code
|
|
53
|
+
|
|
54
|
+
|
|
44
55
|
## 2.0.1 (2022-11-19)
|
|
45
56
|
* Increased reliability of mqtt messages by setting QoS
|
|
46
|
-
* Optimised the GB session code
|
|
47
|
-
* Removed some left over debug code
|
|
57
|
+
* Optimised the GB session code Removed some left over debug code
|
|
48
58
|
|
|
49
59
|
|
|
50
60
|
## 2.0.0 (2022-11-14)
|
|
@@ -52,4 +62,4 @@ Please report all bugs and problems.
|
|
|
52
62
|
* Major startup speed improvements after Homebridge reboot
|
|
53
63
|
* Improved mqtt performance
|
|
54
64
|
* Added support of channel sort by most watched
|
|
55
|
-
* And many more small bug fixes and improvements included
|
|
65
|
+
* And many more small bug fixes and improvements included
|
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ In March 2022, a newer version of the set-top box has started to appear in Telen
|
|
|
70
70
|
This plugin is not provided by Magenta or Telenet or Sunrise or Virgin Media or Ziggo any other affiliate of [UPC](https://en.wikipedia.org/wiki/UPC_Broadband). It is neither endorsed nor supported nor developed by [UPC](https://en.wikipedia.org/wiki/UPC_Broadband) or any affiliates. [UPC](https://en.wikipedia.org/wiki/UPC_Broadband) can change their systems at any time and that might break this plugin. But I hope not.
|
|
71
71
|
|
|
72
72
|
## Requirements
|
|
73
|
-
* An Apple iPhone or iPad with iOS/iPadOS 14.0 (or later). Developed on iOS 14.1...16.
|
|
73
|
+
* An Apple iPhone or iPad with iOS/iPadOS 14.0 (or later). Developed on iOS 14.1...16.2, earlier versions not tested.
|
|
74
74
|
* [Homebridge](https://homebridge.io/) v1.1.116 (or later). Developed on Homebridge 1.1.116....1.6.0, earlier versions not tested.
|
|
75
75
|
* A TV subscription from one of the supported countries and TV providers.
|
|
76
76
|
* An online account for viewing TV in the web app (often part of your TV package), see the table above.
|
|
@@ -83,9 +83,11 @@ This plugin is not provided by Magenta or Telenet or Sunrise or Virgin Media or
|
|
|
83
83
|
|
|
84
84
|
* **Full Remote-Control Support**: The Apple TV Remote in your iOS device can control your set-top box; including power, menu navigation, play, pause, fast-forward, rewind, channel up/down, volume and mute commands. All keys are fully configurable for single-tap and double-tap.
|
|
85
85
|
|
|
86
|
+
* **Powerful Key Macros**: You can program key macros to control your set-top box. Key macros are powerful ways of accessing any content such as radio channels that cannot be accessed directly via a channel number.
|
|
87
|
+
|
|
86
88
|
* **Siri Support** You can control your box with Siri (to the extent of what Apple Siri supports).
|
|
87
89
|
|
|
88
|
-
* **Shortcuts Support** You can read and control your box with Shortcuts and HomeKit automations (to the extent of what Apple supports), allowing you to control switch-on and channel selection in Home Automations,
|
|
90
|
+
* **Shortcuts Support** You can read and control your box with Shortcuts and HomeKit automations (to the extent of what Apple supports), allowing you to control switch-on and channel selection in Home Automations, Shortcuts and Personal Automations.
|
|
89
91
|
|
|
90
92
|
* **Synchronised Set-Top Box Name**: Changing the name of the set-top box in the Home app changes it on the TV and backend systems in real time, and vice-versa. No reboot required. You can turn off the sync if desired in the config.
|
|
91
93
|
|
|
@@ -111,7 +113,7 @@ This plugin is not provided by Magenta or Telenet or Sunrise or Virgin Media or
|
|
|
111
113
|
|
|
112
114
|
* **Fully Configurable**: A large amount of configuration items exist to allow you to configure your plugin the way you want.
|
|
113
115
|
|
|
114
|
-
* **Future Feature Support**: The plugin also supports current and target media state as well as closed captions, even though the Home app accessory cannot currently display or control this data in the home app (as at iOS 16.
|
|
116
|
+
* **Future Feature Support**: The plugin also supports current and target media state as well as closed captions, even though the Home app accessory cannot currently display or control this data in the home app (as at iOS 16.2). Hopefully, Apple will add support for these features in the future. You can however use this data in Home Automations or the Shortcuts app.
|
|
115
117
|
|
|
116
118
|
|
|
117
119
|
|
|
@@ -172,7 +174,7 @@ The table shows the default key mappings. You can map any Apple TV Remote button
|
|
|
172
174
|
The volume controls do not control the set-top box directly, as the set-top box has no volume capability. The set-top box physical remote actually sends IR commands to your TV. If you can control your TV volume via a network connection then the volume controls can be used to send volume commands to your TV via the raspberry pi. This is what the author uses.
|
|
173
175
|
|
|
174
176
|
|
|
175
|
-
## Using Profiles to
|
|
177
|
+
## Using Profiles to Better Manage your Channel List
|
|
176
178
|
Many TV providers provide hundreds of TV channels. The Home app is limited to 100 "services", which are TV channels or reserved for system control. This limits the maximum possible channels to 95, and thus the plugin will load the first 95 subscribed channels found, ignoring all non-subscribed channels.
|
|
177
179
|
|
|
178
180
|
If the channels you wish to have in the Home app are not within the first 95 subscribed channels in your TV providers channel list, then you can create a profile on the set-top box, and configure the profile with the channels you want, in the order you want. Enter the same profile name in the plugin config **Profile Name**, and the plugin will load the channels from that profile.
|
|
@@ -185,6 +187,10 @@ The profile used by the plugin does not have to be the same as the set-top box's
|
|
|
185
187
|
## Sorting the Channel list
|
|
186
188
|
The config item **Channel Sort By** allows the channels to be sorted by **Channel Order** (the standard channel order as shown on the TV) or by **Most Watched**. Most Watched is reported by the backend systems and is profile-based. It is not clear how often this list is updated, however for a TV subscription with many channels, this may be a preferable option to show your most watched channels at the top of the channel list.
|
|
187
189
|
|
|
190
|
+
## Using Key Macros to Access Extra Capabilities
|
|
191
|
+
You can program key macros to access any function of your set-top box. This is particularly useful to select radio channels. Key macros are loaded at the end of the channel list. See the [Wiki Key Macros page](https://github.com/jsiegenthaler/homebridge-eosstb/wiki/Key-Macros) for full details.
|
|
192
|
+
|
|
193
|
+
|
|
188
194
|
|
|
189
195
|
## Limitations
|
|
190
196
|
### Channel Count
|
|
@@ -195,17 +201,14 @@ Services used in this set-top box accessory are:
|
|
|
195
201
|
3. Speaker service (for the controlling the TV accessory volume)
|
|
196
202
|
4. Input service. The input (TV channels) utilises one service per input. The maximum possible channels (inputs) are thus 100 - 3 = 97. I have limited the inputs to maximum 95, but you can override this in the config (helpful to reduce log entries when debugging). The inputs are hard limited to 95 inputs.
|
|
197
203
|
|
|
198
|
-
### Web App Controllers Take Over Sometimes
|
|
199
|
-
The eosstb plugin emulates the TV service web app. If the web app is started on a web browser on a laptop or PC, the backend systems may prefer the web app to HomeKit, and disconnect HomeKit from the mqtt session. The mqtt session will try and reconnect if it gets disconnected.
|
|
200
|
-
|
|
201
204
|
### Media State (Play/Pause) Limitations
|
|
202
|
-
The eosstb plugin can detect the current and target media state and shows STOP, PLAY, PAUSE or LOADING (loading is displayed when fast-forwarding or rewinding) in the Homebridge logs. Unfortunately, the Apple Home app cannot do anything with the media state (as at iOS 16.
|
|
205
|
+
The eosstb plugin can detect the current and target media state and shows STOP, PLAY, PAUSE or LOADING (loading is displayed when fast-forwarding or rewinding) in the Homebridge logs. Unfortunately, the Apple Home app cannot do anything with the media state (as at iOS 16.2) apart from allow you to read it in Shortcuts or Automations. Hopefully this will improve in the future.
|
|
203
206
|
|
|
204
207
|
### Recording State Limitations
|
|
205
208
|
The eosstb plugin can detect the current recording state of the set-top box, both for local HDD-based recording (for boxes that have a HDD fitted) and for network recording. The plugin shows IDLE, ONGOING_NDVR or ONGOING_LOCALDVR in the Homebridge logs. DVR means digital video recorder; N for network and LOCAL for local HDD based recording. The Apple Home app cannot natively do anything with the recording state but the eosstb plugin uses it to set the inUse charateristic if the set-top box is turned on or is recording to the local HDD. This is useful in Shortcuts or Automations.
|
|
206
209
|
|
|
207
210
|
### Closed Captions Limitations
|
|
208
|
-
The eosstb plugin can detect the closed captions state (**Subtitle options** in the set-top box menu) and shows ENABLED or DISABLED in the Homebridge logs. Unfortunately, the Apple Home app cannot do anything with the closed captions state (as at iOS 16.
|
|
211
|
+
The eosstb plugin can detect the closed captions state (**Subtitle options** in the set-top box menu) and shows ENABLED or DISABLED in the Homebridge logs. Unfortunately, the Apple Home app cannot do anything with the closed captions state (as at iOS 16.2) apart from allow you to read it in Shortcuts or Automations. Hopefully this will improve in the future.
|
|
209
212
|
|
|
210
213
|
## Configuration
|
|
211
214
|
Add a new platform to the platforms section of your homebridge `config.json`.
|
|
@@ -223,7 +226,7 @@ Example minimum (mandatory) configuration:
|
|
|
223
226
|
]
|
|
224
227
|
```
|
|
225
228
|
|
|
226
|
-
Example extended configuration as used on the author with his
|
|
229
|
+
Example extended configuration as used on the author with his EOSSTB set-top box. An extended configuration allows you to customise the behaviour of each set-top box device. You must identify the devices by their deviceId:
|
|
227
230
|
|
|
228
231
|
```js
|
|
229
232
|
"platforms": [
|
|
@@ -257,6 +260,10 @@ Example extended configuration as used on the author with his Samsung TV (where
|
|
|
257
260
|
"maxChannels": 50
|
|
258
261
|
},
|
|
259
262
|
"channels": [
|
|
263
|
+
{
|
|
264
|
+
"channelKeyMacro": "TV MediaTopMenu wait(1000) ArrowRight ArrowRight Enter wait(1000) ArrowDown wait(1500) Enter wait(2000) Enter",
|
|
265
|
+
"channelName": "Last Played Radio"
|
|
266
|
+
},
|
|
260
267
|
{
|
|
261
268
|
"channelId": "SV09690",
|
|
262
269
|
"channelName": "Netflix"
|
|
@@ -358,7 +365,9 @@ Some channels such as Netflix are actually apps on the set-top box, and not norm
|
|
|
358
365
|
|
|
359
366
|
* **channelId**: The channelId, as defined by the TV provider. Unknown channelIds will appear in the Homebridge log.
|
|
360
367
|
|
|
361
|
-
* **
|
|
368
|
+
* **channelKeyMacro**: The channel key macro (key sequence) to send. If channelKeyMacro is present, channelId is ignored.
|
|
369
|
+
|
|
370
|
+
* **channelNames**: Allows you to add unknown channel names, names for key macros, or to rename any channel as you wish. Required as some channels (e.g., Netflix) are not published on the master channel list. If a channel displays in the Home app like this: "Channel SV09690", then check your TV to see the channel name, and add it to the config. An example is provided for Netflix. Optional, unknown channels are displayed as "Channel xxxxxxx" where xxxxxxx is the channelId.
|
|
362
371
|
|
|
363
372
|
|
|
364
373
|
* Telenet BE:
|
package/config.schema.json
CHANGED
package/index.js
CHANGED
|
@@ -28,7 +28,6 @@ const cookieJar = new tough.CookieJar();
|
|
|
28
28
|
const axios = require('axios') //.default; // https://github.com/axios/axios
|
|
29
29
|
axios.defaults.xsrfCookieName = undefined; // change xsrfCookieName: 'XSRF-TOKEN' to xsrfCookieName: undefined, we do not want this default,
|
|
30
30
|
const axiosWS = axios.create({
|
|
31
|
-
// axios-cookiejar-support v2.0.2 required config
|
|
32
31
|
jar: cookieJar, //added in axios-cookiejar-support v2.0.x, see https://github.com/3846masa/axios-cookiejar-support/blob/main/MIGRATION.md
|
|
33
32
|
});
|
|
34
33
|
|
|
@@ -1350,6 +1349,7 @@ class stbPlatform {
|
|
|
1350
1349
|
if (this.config.debugLevel > 2) { this.log('Processing channel:',i,channel.logicalChannelNumber,channel.id, channel.name); } // for debug purposes
|
|
1351
1350
|
// log the detail of logicalChannelNumber 60 nicktoons, for which I have no subscription, as a test of entitlements
|
|
1352
1351
|
//if (this.config.debugLevel > 0) { if (channel.logicalChannelNumber == 60){ this.log('DEV: Logging Channel 60 to check entitlements :',channel); } }
|
|
1352
|
+
//if (this.config.debugLevel > 0) { if (channel.logicalChannelNumber == 60){ this.log('DEV: Logging Channel 60 to check entitlements :',channel); } }
|
|
1353
1353
|
this.masterChannelList.push({
|
|
1354
1354
|
id: channel.id,
|
|
1355
1355
|
name: cleanNameForHomeKit(channel.name),
|
|
@@ -1379,103 +1379,6 @@ class stbPlatform {
|
|
|
1379
1379
|
})
|
|
1380
1380
|
}
|
|
1381
1381
|
|
|
1382
|
-
// load all available TV channels at regular intervals into an array
|
|
1383
|
-
// new version using endpoints available from 13.10.2022
|
|
1384
|
-
// the masterChannelList contains all possible channels, bith subscribed and non-subscribed channels
|
|
1385
|
-
async refreshMasterChannelListPrev(callback) {
|
|
1386
|
-
// called by refreshMasterChannelList (state handler), thus runs at polling interval
|
|
1387
|
-
|
|
1388
|
-
// exit immediately if the session does not exist
|
|
1389
|
-
if (currentSessionState != sessionState.CONNECTED) {
|
|
1390
|
-
if (this.config.debugLevel > 1) { this.log.warn('refreshMasterChannelList: Session does not exist, exiting'); }
|
|
1391
|
-
return false;
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
// exit immediately if channel list has not expired
|
|
1395
|
-
if (this.masterChannelListExpiryDate > Date.now()) {
|
|
1396
|
-
if (this.config.debugLevel > 1) { this.log.warn('refreshMasterChannelList: Master channel list has not expired yet. Next refresh will occur after %s', this.masterChannelListExpiryDate.toLocaleString()); }
|
|
1397
|
-
return false;
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
if (this.config.debugLevel > 1) {
|
|
1401
|
-
this.log.warn('refreshMasterChannelList: Refreshing master channel list...');
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
// only continue if a session was created. If the internet conection is down then we have no session
|
|
1405
|
-
//if (currentSessionState != sessionState.CONNECTED) { return; }
|
|
1406
|
-
|
|
1407
|
-
// channels can be retrieved for the country without having a mqtt session going but then the list is not relevant for the user's locationId
|
|
1408
|
-
// so you should add the user's locationId as a parameter, and this needs the accessToken
|
|
1409
|
-
// syntax:
|
|
1410
|
-
// https://prod.oesp.virginmedia.com/oesp/v4/GB/eng/web/channels?byLocationId=41043&includeInvisible=true&includeNotEntitled=true&personalised=true&sort=channelNumber
|
|
1411
|
-
// https://prod.spark.sunrisetv.ch/eng/web/linear-service/v2/channels?cityId=401&language=en&productClass=Orion-DASH
|
|
1412
|
-
/*
|
|
1413
|
-
let url = countryBaseUrlArray[this.config.country.toLowerCase()] + '/channels';
|
|
1414
|
-
url = url + '?byLocationId=' + this.session.locationId // locationId needed to get user-specific list
|
|
1415
|
-
url = url + '&includeInvisible=true' // includeInvisible
|
|
1416
|
-
url = url + '&includeNotEntitled=true' // includeNotEntitled
|
|
1417
|
-
url = url + '&personalised=true' // personalised
|
|
1418
|
-
url = url + '&sort=channelNumber' // sort
|
|
1419
|
-
*/
|
|
1420
|
-
//url = 'https://prod.spark.sunrisetv.ch/eng/web/linear-service/v2/channels?cityId=401&language=en&productClass=Orion-DASH'
|
|
1421
|
-
let url = countryBaseUrlArray[this.config.country.toLowerCase()] + '/eng/web/linear-service/v2/channels';
|
|
1422
|
-
url = url + '?cityId=' + this.customer.cityId; //+ this.customer.cityId // cityId needed to get user-specific list
|
|
1423
|
-
url = url + '&language=en'; // language
|
|
1424
|
-
url = url + '&entitled=true'; // testing! 760 with all,
|
|
1425
|
-
url = url + '&productClass=Orion-DASH'; // productClass, must be Orion-DASH
|
|
1426
|
-
//url = url + '&includeNotEntitled=false' // includeNotEntitled testing to see if this parameter is accepted
|
|
1427
|
-
if (this.config.debugLevel > 2) { this.log.warn('refreshMasterChannelList: loading inputs from',url); }
|
|
1428
|
-
|
|
1429
|
-
// call the webservice to get all available channels
|
|
1430
|
-
const axiosConfig = {
|
|
1431
|
-
method: 'GET',
|
|
1432
|
-
url: url
|
|
1433
|
-
};
|
|
1434
|
-
axiosWS(axiosConfig)
|
|
1435
|
-
.then(response => {
|
|
1436
|
-
//this.log(response.data);
|
|
1437
|
-
if (this.config.debugLevel > 2) { this.log.warn('refreshMasterChannelList: Processing %s channels...', response.data.length); }
|
|
1438
|
-
|
|
1439
|
-
// set the masterChannelListExpiryDate to expire at now + MASTER_CHANNEL_LIST_VALID_FOR_S
|
|
1440
|
-
this.masterChannelListExpiryDate =new Date(new Date().getTime() + (MASTER_CHANNEL_LIST_VALID_FOR_S * 1000));
|
|
1441
|
-
//this.log('MasterChannelList valid until',this.masterChannelListExpiryDate.toLocaleString())
|
|
1442
|
-
|
|
1443
|
-
// load the channel list with all channels found
|
|
1444
|
-
this.masterChannelList = [];
|
|
1445
|
-
const channels = response.data;
|
|
1446
|
-
this.log.debug('Channels to process:',channels.length);
|
|
1447
|
-
for(let i=0; i<channels.length; i++) {
|
|
1448
|
-
const channel = channels[i];
|
|
1449
|
-
// if (this.config.debugLevel > 0) { this.log('Loading channel:',i,channel.logicalChannelNumber,channel.id, channel.name); } // for debug purposes
|
|
1450
|
-
// log the detail of logicalChannelNumber 60 nicktoons, for which I have no subscription, as a test of entitlements
|
|
1451
|
-
//if (this.config.debugLevel > 0) { if (channel.logicalChannelNumber == 60){ this.log('DEV: Logging Channel 60 to check entitlements :',channel); } }
|
|
1452
|
-
this.masterChannelList.push({
|
|
1453
|
-
id: channel.id,
|
|
1454
|
-
name: cleanNameForHomeKit(channel.name),
|
|
1455
|
-
logicalChannelNumber: channel.logicalChannelNumber,
|
|
1456
|
-
linearProducts: channel.linearProducts
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
if (this.config.debugLevel > 0) {
|
|
1461
|
-
this.log.warn('refreshMasterChannelList: Master channel list refreshed with %s channels, valid until %s', this.masterChannelList.length, this.masterChannelListExpiryDate.toLocaleString());
|
|
1462
|
-
}
|
|
1463
|
-
return true;
|
|
1464
|
-
|
|
1465
|
-
})
|
|
1466
|
-
.catch(error => {
|
|
1467
|
-
let errText, errReason;
|
|
1468
|
-
errText = 'Failed to refresh the master channel list - check your internet connection:'
|
|
1469
|
-
if (error.isAxiosError) {
|
|
1470
|
-
errReason = error.code + ': ' + (error.hostname || '');
|
|
1471
|
-
// if no connection then set session to disconnected to force a session reconnect
|
|
1472
|
-
if (error.code == 'ENOTFOUND') { currentSessionState = sessionState.DISCONNECTED; }
|
|
1473
|
-
}
|
|
1474
|
-
this.log('%s %s', errText, (errReason || ''));
|
|
1475
|
-
this.log.warn(`refreshMasterChannelList error:`, error);
|
|
1476
|
-
return error;
|
|
1477
|
-
});
|
|
1478
|
-
}
|
|
1479
1382
|
|
|
1480
1383
|
|
|
1481
1384
|
// get Personalization Data via web request GET
|
|
@@ -1902,7 +1805,7 @@ class stbPlatform {
|
|
|
1902
1805
|
}
|
|
1903
1806
|
|
|
1904
1807
|
// variables for just in this function
|
|
1905
|
-
var deviceId, stbState, currPowerState, currMediaState, currChannelId, currSourceType, profileDataChanged, currRecordingState, currStatusActive, currInputDeviceType, currInputSourceType;
|
|
1808
|
+
var deviceId, stbState, currPowerState, currMediaState, currChannelId, currEventId, currSourceType, profileDataChanged, currRecordingState, currStatusActive, currInputDeviceType, currInputSourceType;
|
|
1906
1809
|
|
|
1907
1810
|
// handle personalizationService messages
|
|
1908
1811
|
// Topic: Topic: 107xxxx_ch/personalizationService
|
|
@@ -2048,6 +1951,7 @@ class stbPlatform {
|
|
|
2048
1951
|
// Careful: source is not always present in the data
|
|
2049
1952
|
if (playerState.source) {
|
|
2050
1953
|
currChannelId = playerState.source.channelId || NO_CHANNEL_ID; // must be a string
|
|
1954
|
+
currEventId = playerState.source.eventId; // the title (program) id
|
|
2051
1955
|
if (parent.config.debugLevel > 0 && parent.masterChannelList) {
|
|
2052
1956
|
let currentChannelName; // let is scoped to the current {} block
|
|
2053
1957
|
let curChannel = parent.masterChannelList.find(channel => channel.id === currChannelId);
|
|
@@ -2120,8 +2024,8 @@ class stbPlatform {
|
|
|
2120
2024
|
|
|
2121
2025
|
|
|
2122
2026
|
// update the device on every message
|
|
2123
|
-
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2124
|
-
parent.mqttDeviceStateHandler(deviceId, currPowerState, currMediaState, currRecordingState, currChannelId, currSourceType, profileDataChanged, Characteristic.StatusFault.NO_FAULT, null, currStatusActive, currInputDeviceType, currInputSourceType);
|
|
2027
|
+
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventid, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2028
|
+
parent.mqttDeviceStateHandler(deviceId, currPowerState, currMediaState, currRecordingState, currChannelId, currEventId, currSourceType, profileDataChanged, Characteristic.StatusFault.NO_FAULT, null, currStatusActive, currInputDeviceType, currInputSourceType);
|
|
2125
2029
|
|
|
2126
2030
|
//end of try
|
|
2127
2031
|
} catch (err) {
|
|
@@ -2139,12 +2043,12 @@ class stbPlatform {
|
|
|
2139
2043
|
// https://github.com/mqttjs/MQTT.js#event-close
|
|
2140
2044
|
mqttClient.on('close', function () {
|
|
2141
2045
|
try {
|
|
2142
|
-
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2046
|
+
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2143
2047
|
parent.currentMqttState = mqttState.closed;
|
|
2144
2048
|
parent.log('mqttClient: Connection closed');
|
|
2145
2049
|
currentSessionState = sessionState.DISCONNECTED; // to force a session reconnect
|
|
2146
2050
|
if (!isShuttingDown) {
|
|
2147
|
-
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2051
|
+
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2148
2052
|
}
|
|
2149
2053
|
} catch (err) {
|
|
2150
2054
|
parent.log.error("Error trapped in mqttClient close event:", err.message);
|
|
@@ -2157,10 +2061,10 @@ class stbPlatform {
|
|
|
2157
2061
|
// https://github.com/mqttjs/MQTT.js#event-reconnect
|
|
2158
2062
|
mqttClient.on('reconnect', function () {
|
|
2159
2063
|
try {
|
|
2160
|
-
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2064
|
+
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2161
2065
|
parent.currentMqttState = mqttState.reconnected;
|
|
2162
2066
|
parent.log('mqttClient: Reconnect started');
|
|
2163
|
-
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2067
|
+
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2164
2068
|
} catch (err) {
|
|
2165
2069
|
parent.log.error("Error trapped in mqttClient reconnect event:", err.message);
|
|
2166
2070
|
parent.log.error(err);
|
|
@@ -2172,11 +2076,11 @@ class stbPlatform {
|
|
|
2172
2076
|
// https://github.com/mqttjs/MQTT.js#event-disconnect
|
|
2173
2077
|
mqttClient.on('disconnect', function () {
|
|
2174
2078
|
try {
|
|
2175
|
-
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2079
|
+
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2176
2080
|
parent.currentMqttState = mqttState.disconnected;
|
|
2177
2081
|
parent.log('mqttClient: Disconnect command received');
|
|
2178
2082
|
currentSessionState = sessionState.DISCONNECTED; // to force a session reconnect
|
|
2179
|
-
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2083
|
+
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2180
2084
|
} catch (err) {
|
|
2181
2085
|
parent.log.error("Error trapped in mqttClient disconnect event:", err.message);
|
|
2182
2086
|
parent.log.error(err);
|
|
@@ -2188,11 +2092,11 @@ class stbPlatform {
|
|
|
2188
2092
|
// https://github.com/mqttjs/MQTT.js#event-disconnect
|
|
2189
2093
|
mqttClient.on('offline', function () {
|
|
2190
2094
|
try {
|
|
2191
|
-
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2095
|
+
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2192
2096
|
parent.currentMqttState = mqttState.offline;
|
|
2193
2097
|
parent.log('mqttClient: Client is offline');
|
|
2194
2098
|
currentSessionState = sessionState.DISCONNECTED; // to force a session reconnect
|
|
2195
|
-
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2099
|
+
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2196
2100
|
} catch (err) {
|
|
2197
2101
|
parent.log.error("Error trapped in mqttClient offline event:", err.message);
|
|
2198
2102
|
parent.log.error(err);
|
|
@@ -2204,12 +2108,12 @@ class stbPlatform {
|
|
|
2204
2108
|
// https://github.com/mqttjs/MQTT.js#event-error
|
|
2205
2109
|
mqttClient.on('error', function(err) {
|
|
2206
2110
|
try {
|
|
2207
|
-
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2111
|
+
// mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType)
|
|
2208
2112
|
parent.currentMqttState = mqttState.error;
|
|
2209
2113
|
parent.log.warn('mqttClient: Error', (err.syscall || '') + ' ' + (err.code || '') + ' ' + (err.hostname || ''));
|
|
2210
2114
|
parent.log.warn('mqttClient: Error object:', err);
|
|
2211
2115
|
currentSessionState = sessionState.DISCONNECTED; // to force a session reconnect
|
|
2212
|
-
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2116
|
+
parent.mqttDeviceStateHandler(null, null, null, null, null, null, null, null, Characteristic.StatusFault.GENERAL_FAULT); // set statusFault to GENERAL_FAULT
|
|
2213
2117
|
mqttClient.end();
|
|
2214
2118
|
return false;
|
|
2215
2119
|
} catch (err) {
|
|
@@ -2256,15 +2160,15 @@ class stbPlatform {
|
|
|
2256
2160
|
|
|
2257
2161
|
// handle the state change of the device, calling the updateDeviceState of the relevant device
|
|
2258
2162
|
// handles multiple devices by deviceId, should the user have more than one device
|
|
2259
|
-
mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType) {
|
|
2163
|
+
mqttDeviceStateHandler(deviceId, powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType) {
|
|
2260
2164
|
try {
|
|
2261
2165
|
if (this.config.debugLevel > 1) {
|
|
2262
|
-
this.log.warn('mqttDeviceStateHandler: calling updateDeviceState with deviceId %s, powerState %s, mediaState %s, channelId %s, sourceType %s, profileDataChanged %s, statusFault %s, programMode %s, statusActive %s, currInputDeviceType %s, currInputSourceType %s', deviceId, powerState, mediaState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType);
|
|
2166
|
+
this.log.warn('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', deviceId, powerState, mediaState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType);
|
|
2263
2167
|
}
|
|
2264
2168
|
const deviceIndex = this.devices.findIndex(device => device.deviceId == deviceId)
|
|
2265
2169
|
if (deviceIndex > -1 && this.stbDevices.length > 0) {
|
|
2266
2170
|
//this.log.warn('mqttDeviceStateHandler: stbDevices found, calling updateDeviceState');
|
|
2267
|
-
this.stbDevices[deviceIndex].updateDeviceState(powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType);
|
|
2171
|
+
this.stbDevices[deviceIndex].updateDeviceState(powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, currInputDeviceType, currInputSourceType);
|
|
2268
2172
|
}
|
|
2269
2173
|
} catch (err) {
|
|
2270
2174
|
this.log.error("Error trapped in mqttDeviceStateHandler:", err.message);
|
|
@@ -2406,45 +2310,121 @@ class stbPlatform {
|
|
|
2406
2310
|
try {
|
|
2407
2311
|
if (this.config.debugLevel > 0) { this.log.warn('sendKey: keySequence %s, deviceName %s, deviceId %s', keySequence, deviceName, deviceId); }
|
|
2408
2312
|
if (mqttUsername) {
|
|
2313
|
+
let hasJustBooted = false; // indicates if the box just booted up during this keyMacro
|
|
2314
|
+
let keyCanBeSkippedAfterBootup = false; // indicates if the current key can be skipped
|
|
2315
|
+
let firstNonSkippableKeyFound = false; // indicates if a non-skippable key was found
|
|
2316
|
+
let defaultWaitDelayActive = false; // indicates if the default wait delay is being used
|
|
2409
2317
|
|
|
2410
2318
|
let keyArray = keySequence.trim().split(' ');
|
|
2411
|
-
if (keyArray.length > 1) { this.log('sendKey: processing keySequence
|
|
2319
|
+
if (keyArray.length > 1) { this.log('sendKey: processing keySequence for %s: "%s"', deviceName, keySequence); }
|
|
2412
2320
|
// supported key1 key2 key3 wait() wait(100)
|
|
2413
2321
|
for (let i = 0; i < keyArray.length; i++) {
|
|
2414
2322
|
const keyName = keyArray[i].trim();
|
|
2415
2323
|
this.log('sendKey: processing key %s of %s: %s', i+1, keyArray.length, keyName);
|
|
2324
|
+
const defaultWaitDelay = 200; // default 200ms
|
|
2325
|
+
const maxWaitDelay = 20000; // default 200ms
|
|
2326
|
+
const waitReadyDelayStep = 500; // the ms wait time in each waitReady loop
|
|
2327
|
+
const maxWaitReadyLoops = maxWaitDelay / waitReadyDelayStep; // the max loop iterations to wait for ready
|
|
2328
|
+
const currKeyIsEscapeOrTvOrWait =
|
|
2329
|
+
keyName.toLowerCase().startsWith('wait(') // current key is a wait
|
|
2330
|
+
|| keyName.toLowerCase() == 'escape' // or current key is an Escape
|
|
2331
|
+
|| keyName.toLowerCase() == 'tv'; // or current key is TV
|
|
2332
|
+
if (!firstNonSkippableKeyFound && !currKeyIsEscapeOrTvOrWait) {
|
|
2333
|
+
firstNonSkippableKeyFound = true; // first non-escape or non-wait key found
|
|
2334
|
+
}
|
|
2335
|
+
keyCanBeSkippedAfterBootup = false; // reset for each key
|
|
2336
|
+
defaultWaitDelayActive = false; // reset for each key
|
|
2337
|
+
|
|
2338
|
+
|
|
2339
|
+
// for all keys except Power:
|
|
2340
|
+
// check if box is ready (up and running), if not, loop until we hit maxWaitDelay, waiting waitReadyDelayStep ms each loop
|
|
2341
|
+
// loop only while i < maxWaitReadyLoops and current media state = STOP
|
|
2342
|
+
// The device changes CurrentMediaState from STOP to PLAY when it has powered up and is streaming TV
|
|
2343
|
+
// CurrentMediaState=STOP only occurs when the set-top box is turned off, so is a good indicator that it is streaming content
|
|
2344
|
+
// TEST THIS WITH NETFLIX!
|
|
2345
|
+
if (keyName.toLowerCase() != 'power') {
|
|
2346
|
+
const deviceIndex = this.devices.findIndex(device => device.deviceId == deviceId)
|
|
2347
|
+
// detect CurrentMediaState=STOP to show box has just booted
|
|
2348
|
+
if (this.stbDevices[deviceIndex].currentMediaState == Characteristic.CurrentMediaState.STOP) {
|
|
2349
|
+
this.log('sendKey: key %s: waiting for ready for %s', i+1, deviceName);
|
|
2350
|
+
for (let j=0;
|
|
2351
|
+
j<maxWaitReadyLoops
|
|
2352
|
+
&& this.stbDevices[deviceIndex].currentMediaState == Characteristic.CurrentMediaState.STOP;
|
|
2353
|
+
j++) {
|
|
2354
|
+
hasJustBooted = true; // indicates that the box just booted up during this keyMacro
|
|
2355
|
+
await waitprom(waitReadyDelayStep); // wait waitReadyDelayStep ms on each loop
|
|
2356
|
+
this.log.debug('sendKey: key %s: loop %s: wait %s ms done, hasJustBooted %s, currentMediaState %s', i+1, j, hasJustBooted, waitReadyDelayStep, currentMediaStateName[this.stbDevices[deviceIndex].currentMediaState]);
|
|
2357
|
+
}
|
|
2358
|
+
this.log.debug('sendKey: key %s: waiting one more delay of %s ms', i+1, waitReadyDelayStep);
|
|
2359
|
+
await waitprom(waitReadyDelayStep); // wait waitReadyDelayStep ms one last time to ensure we have one wait after change from STOP to PLAY
|
|
2360
|
+
this.log('sendKey: key %s: waiting for ready done, hasJustBooted %s, currentMediaState %s', i+1, hasJustBooted, currentMediaStateName[this.stbDevices[deviceIndex].currentMediaState]);
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2416
2364
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2365
|
+
|
|
2366
|
+
// check if current key can be skipped.
|
|
2367
|
+
// leading Escape and wait keys can be skipped after a bootup to speed up the selection of a radio channel using a scene
|
|
2368
|
+
// any skipping must stop when the first non-Escape and non-wait key is found
|
|
2369
|
+
this.log.debug('sendKey: key %s: keyArray.length %s, prevKey %s, currKey %s, nextKey %s', i+1, keyArray.length, keyArray[i-1], keyArray[i], keyArray[i+1])
|
|
2370
|
+
if ( hasJustBooted // box has just booted
|
|
2371
|
+
&& currKeyIsEscapeOrTvOrWait // current key is escape or tv or wait
|
|
2372
|
+
&& !firstNonSkippableKeyFound // have not yet found the first non-skippable key
|
|
2373
|
+
) {
|
|
2374
|
+
keyCanBeSkippedAfterBootup = true; // we can skip this key as it is a wait or escape
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
|
|
2378
|
+
// to help with debug
|
|
2379
|
+
this.log.debug('sendKey: key %s: hasJustBooted %s, currKeyIsEscapeOrTvOrWait %s, firstNonSkippableKeyFound %s, keyCanBeSkippedAfterBootup %s', i+1, hasJustBooted, currKeyIsEscapeOrTvOrWait, firstNonSkippableKeyFound, keyCanBeSkippedAfterBootup);
|
|
2380
|
+
|
|
2381
|
+
|
|
2382
|
+
// process any wait command if found
|
|
2383
|
+
// but ignore if keyCanBeSkippedAfterBootup
|
|
2384
|
+
let waitDelay;
|
|
2385
|
+
if (keyName.toLowerCase().startsWith('wait(') && !keyCanBeSkippedAfterBootup) {
|
|
2386
|
+
this.log.debug('sendKey: key %s: reading delay from %s', i+1, keyName);
|
|
2387
|
+
// accepts wait(), wait(n)
|
|
2421
2388
|
waitDelay = keyName.toLowerCase().replace('wait(', '').replace(')','');
|
|
2422
|
-
if (waitDelay == ''){ waitDelay =
|
|
2423
|
-
|
|
2389
|
+
if (waitDelay == ''){ waitDelay = defaultWaitDelay; } // default wait
|
|
2390
|
+
if (waitDelay > maxWaitDelay){ waitDelay = maxWaitDelay; } // max wait
|
|
2391
|
+
this.log.debug('sendKey: key %s: delay read as %s', i+1, waitDelay);
|
|
2424
2392
|
}
|
|
2425
|
-
// else if not first key and previous key was not wait, and
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2393
|
+
// else if not key can be skipped, and not first key and previous key was not wait, and current key is not wait, then set a default delay of defaultWaitDelay ms
|
|
2394
|
+
else if ( !keyCanBeSkippedAfterBootup
|
|
2395
|
+
&& i>0
|
|
2396
|
+
//&& i<keyArray.length-1
|
|
2397
|
+
&& !(keyArray[i-1] || '').toLowerCase().startsWith('wait(') && !(keyArray[i] || '').toLowerCase().startsWith('wait(')
|
|
2398
|
+
) {
|
|
2399
|
+
this.log.debug('sendKey: key %s: not keyCanBeSkippedAfterBootup and not first key and neither previous key %s nor current key %s is wait(). Setting default wait of %s ms', i+1, keyArray[i-1], keyArray[i], defaultWaitDelay);
|
|
2400
|
+
defaultWaitDelayActive = true;
|
|
2401
|
+
waitDelay = defaultWaitDelay;
|
|
2429
2402
|
}
|
|
2403
|
+
|
|
2430
2404
|
|
|
2431
|
-
// add a wait if waitDelay is
|
|
2405
|
+
// add a wait if a waitDelay is set
|
|
2406
|
+
//this.log('sendKey: key %s: waitDelay', i+1, waitDelay);
|
|
2432
2407
|
if (waitDelay) {
|
|
2433
|
-
this.log('sendKey: waiting %s ms', waitDelay);
|
|
2408
|
+
if (!defaultWaitDelayActive) { this.log('sendKey: key %s: waiting %s ms', i+1, waitDelay); } // reduce logging in minimum mode if default wait
|
|
2434
2409
|
await waitprom(waitDelay);
|
|
2435
|
-
this.log.debug('sendKey:
|
|
2410
|
+
this.log.debug('sendKey: key %s: wait done', i+1);
|
|
2436
2411
|
}
|
|
2412
|
+
|
|
2437
2413
|
|
|
2438
|
-
// send the key
|
|
2439
|
-
if (
|
|
2440
|
-
|
|
2414
|
+
// send the key
|
|
2415
|
+
if (hasJustBooted && keyCanBeSkippedAfterBootup) {
|
|
2416
|
+
// when a box has just booted, leading Escapes and waits can be skipped until the first non-Escape and non-wait comand
|
|
2417
|
+
this.log('sendKey: key %s: box has just booted, skipping key %s', i+1, keyName);
|
|
2418
|
+
} else if (!keyName.toLowerCase().startsWith('wait(')) {
|
|
2419
|
+
// send the key if not a wait
|
|
2420
|
+
this.log('sendKey: key %s: sending key %s to %s %s', i+1, keyName, deviceName, deviceId);
|
|
2441
2421
|
// the web client uses qos:2, so we should as well
|
|
2442
2422
|
this.mqttPublishMessage(
|
|
2443
2423
|
mqttUsername + '/' + deviceId,
|
|
2444
2424
|
'{"id":"' + makeFormattedId(32) + '","type":"CPE.KeyEvent","source":"' + mqttClientId + '","status":{"w3cKey":"' + keyName + '","eventType":"keyDownUp"}}',
|
|
2445
2425
|
{ qos:2, retain:true }
|
|
2446
2426
|
);
|
|
2447
|
-
this.log.debug('sendKey: send %s done', keyName);
|
|
2427
|
+
this.log.debug('sendKey: key %s: send %s done', i+1, keyName);
|
|
2448
2428
|
|
|
2449
2429
|
}
|
|
2450
2430
|
|
|
@@ -2790,6 +2770,7 @@ class stbDevice {
|
|
|
2790
2770
|
this.currentPowerState = Characteristic.Active.INACTIVE;
|
|
2791
2771
|
this.previousPowerState = Characteristic.Active.INACTIVE;
|
|
2792
2772
|
this.currentChannelId = NO_CHANNEL_ID; // string eg SV09038
|
|
2773
|
+
this.lastKeyMacroChannelId = null; // string eg $KeyMacro1
|
|
2793
2774
|
this.currentClosedCaptionsState = Characteristic.ClosedCaptions.DISABLED;
|
|
2794
2775
|
this.previousClosedCaptionsState = Characteristic.ClosedCaptions.DISABLED;
|
|
2795
2776
|
this.currentMediaState = Characteristic.CurrentMediaState.STOP;
|
|
@@ -3105,6 +3086,40 @@ class stbDevice {
|
|
|
3105
3086
|
this.televisionService.getCharacteristic(Characteristic.RemoteKey)
|
|
3106
3087
|
.on('set', this.setRemoteKey.bind(this));
|
|
3107
3088
|
|
|
3089
|
+
|
|
3090
|
+
|
|
3091
|
+
|
|
3092
|
+
// Custom characteristics
|
|
3093
|
+
// these are visible in Shortcuts with the name "Custom"
|
|
3094
|
+
var hapCharacteristic = {};
|
|
3095
|
+
const BASE_UUID = "-0000-3C36-E400-3C36E4FF0012"; // a random UUID used only for my plugin's characteristics, based on 3C36E4-EOSSTB-003656123456
|
|
3096
|
+
// export const BASE_UUID = "-0000-1000-8000-0026BB765291"; // Apple HomeKit base UUID
|
|
3097
|
+
|
|
3098
|
+
// add a custom hap characteristic for the current channel id, appears as Custom in shortcuts
|
|
3099
|
+
// var hapCharacteristic = new Characteristic(characteristic.displayName, characteristic.UUID, characteristic.props);
|
|
3100
|
+
hapCharacteristic = new Characteristic("Current Channel Id", "00000001" + BASE_UUID, {
|
|
3101
|
+
format: Characteristic.Formats.STRING,
|
|
3102
|
+
perms: [Characteristic.Perms.PAIRED_READ, Characteristic.Perms.NOTIFY]
|
|
3103
|
+
})
|
|
3104
|
+
hapCharacteristic.value = ''; // add a default empty value
|
|
3105
|
+
hapCharacteristic.on('get', this.getCurrentChannelId.bind(this));
|
|
3106
|
+
this.televisionService.addCharacteristic(hapCharacteristic); // add the Characteristic to the televisionService
|
|
3107
|
+
// once added, it can be retrieved with
|
|
3108
|
+
//this.televisionService.getCharacteristic('Current Channel Id')
|
|
3109
|
+
|
|
3110
|
+
// add a custom hap characteristic for the current channel name, appears as Custom in shortcuts
|
|
3111
|
+
// var hapCharacteristic = new Characteristic(characteristic.displayName, characteristic.UUID, characteristic.props);
|
|
3112
|
+
hapCharacteristic = new Characteristic("Current Channel Name", "00000002" + BASE_UUID, {
|
|
3113
|
+
format: Characteristic.Formats.STRING,
|
|
3114
|
+
perms: [Characteristic.Perms.PAIRED_READ, Characteristic.Perms.NOTIFY]
|
|
3115
|
+
})
|
|
3116
|
+
hapCharacteristic.value = ''; // add a default empty value
|
|
3117
|
+
hapCharacteristic.on('get', this.getCurrentChannelName.bind(this));
|
|
3118
|
+
this.televisionService.addCharacteristic(hapCharacteristic); // add the Characteristic to the televisionService
|
|
3119
|
+
// once added, it can be retrieved with
|
|
3120
|
+
//this.televisionService.getCharacteristic('Current Channel Name')
|
|
3121
|
+
|
|
3122
|
+
|
|
3108
3123
|
//this.log('DEBUG: this.televisionService')
|
|
3109
3124
|
//this.log(this.televisionService)
|
|
3110
3125
|
|
|
@@ -3278,19 +3293,20 @@ class stbDevice {
|
|
|
3278
3293
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
3279
3294
|
|
|
3280
3295
|
// update the device state changed to async
|
|
3281
|
-
//async updateDeviceState(powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, callback) {
|
|
3282
|
-
async updateDeviceState(powerState, mediaState, recordingState, channelId, sourceType, profileDataChanged, statusFault, programMode, statusActive, inputDeviceType, inputSourceType, callback) {
|
|
3296
|
+
//async updateDeviceState(powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, callback) {
|
|
3297
|
+
async updateDeviceState(powerState, mediaState, recordingState, channelId, eventId, sourceType, profileDataChanged, statusFault, programMode, statusActive, inputDeviceType, inputSourceType, callback) {
|
|
3283
3298
|
try {
|
|
3284
3299
|
// runs at the very start, and then every few seconds, so don't log it unless debugging
|
|
3285
3300
|
// doesn't get the data direct from the settop box, but rather: gets it from the this.currentPowerState and this.currentChannelId variables
|
|
3286
3301
|
// which are received by the mqtt messages, which occurs very often
|
|
3287
3302
|
if (this.config.debugLevel > 0) {
|
|
3288
|
-
this.log.warn('%s: updateDeviceState: powerState %s, mediaState %s [%s], recordingState %s [%s], channelId %s, sourceType %s, profileDataChanged %s, statusFault %s [%s], programMode %s [%s], statusActive %s [%s], inputDeviceType %s [%s], inputSourceType %s [%s]',
|
|
3303
|
+
this.log.warn('%s: updateDeviceState: powerState %s, mediaState %s [%s], recordingState %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]',
|
|
3289
3304
|
this.name,
|
|
3290
3305
|
powerState,
|
|
3291
3306
|
mediaState, currentMediaStateName[mediaState],
|
|
3292
3307
|
recordingState, recordingStateName[recordingState], // custom characteristic
|
|
3293
3308
|
channelId,
|
|
3309
|
+
eventId,
|
|
3294
3310
|
sourceType,
|
|
3295
3311
|
profileDataChanged,
|
|
3296
3312
|
statusFault, Object.keys(Characteristic.StatusFault)[statusFault + 1],
|
|
@@ -3316,6 +3332,7 @@ class stbDevice {
|
|
|
3316
3332
|
if (mediaState != null) { this.currentMediaState = mediaState; }
|
|
3317
3333
|
if (recordingState != null) { this.currentRecordingState = recordingState; }
|
|
3318
3334
|
if (channelId != null) { this.currentChannelId = channelId; }
|
|
3335
|
+
if (eventId != null) { this.currentEventId = eventId; }
|
|
3319
3336
|
if (sourceType != null) { this.currentSourceType = sourceType; }
|
|
3320
3337
|
this.profileDataChanged = profileDataChanged || false;
|
|
3321
3338
|
if (statusFault != null) { this.currentStatusFault = statusFault; }
|
|
@@ -3323,6 +3340,12 @@ class stbDevice {
|
|
|
3323
3340
|
if (statusActive != null) { this.currentStatusActive = statusActive; }
|
|
3324
3341
|
if (inputDeviceType != null) { this.currentInputDeviceType = inputDeviceType; }
|
|
3325
3342
|
if (inputSourceType != null) { this.currentInputSourceType = inputSourceType; }
|
|
3343
|
+
|
|
3344
|
+
// force the keyMacro channel if a keyMacro was last selected as the input
|
|
3345
|
+
if (this.lastKeyMacroChannelId) {
|
|
3346
|
+
this.currentChannelId = this.lastKeyMacroChannelId;
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3326
3349
|
|
|
3327
3350
|
|
|
3328
3351
|
|
|
@@ -3567,6 +3590,7 @@ class stbDevice {
|
|
|
3567
3590
|
}
|
|
3568
3591
|
|
|
3569
3592
|
|
|
3593
|
+
|
|
3570
3594
|
// +++++++++++++++ Input Service characteristics ++++++++++++++
|
|
3571
3595
|
/*
|
|
3572
3596
|
inputService
|
|
@@ -3581,34 +3605,38 @@ class stbDevice {
|
|
|
3581
3605
|
*/
|
|
3582
3606
|
// check for change of InputDeviceType state: (a characteristic of Input Source)
|
|
3583
3607
|
|
|
3584
|
-
|
|
3608
|
+
//this.log('looking for input subtype ', 'input_' + this.currentChannelId)
|
|
3609
|
+
let currInputIndex = this.inputServices.findIndex( InputSource => InputSource.subtype == 'input_' + this.currentChannelId );
|
|
3610
|
+
let currinputNumber = currInputIndex + 1;
|
|
3611
|
+
if (currInputIndex < 0) { currInputIndex = null; currinputNumber = null; }
|
|
3612
|
+
//this.log('found input index %s input %s subtype %s', currInputIndex, currInputIndex+1, (this.inputServices[currInputIndex] || {}).subtype)
|
|
3585
3613
|
if (this.previousInputDeviceType !== this.currentInputDeviceType) {
|
|
3586
3614
|
this.log('%s: Input Device Type changed on input %s %s from %s [%s] to %s [%s]',
|
|
3587
3615
|
this.name,
|
|
3588
|
-
|
|
3616
|
+
currinputNumber,
|
|
3589
3617
|
this.currentChannelId,
|
|
3590
3618
|
this.previousInputDeviceType, Object.keys(Characteristic.InputDeviceType)[this.previousInputDeviceType + 1],
|
|
3591
3619
|
this.currentInputDeviceType, Object.keys(Characteristic.InputDeviceType)[this.currentInputDeviceType + 1]
|
|
3592
3620
|
);
|
|
3593
3621
|
}
|
|
3594
3622
|
//this.televisionService.getCharacteristic(Characteristic.InputDeviceType).updateValue(this.currentInputDeviceType);
|
|
3595
|
-
this.inputServices[
|
|
3623
|
+
if (currInputIndex) { this.inputServices[currInputIndex].getCharacteristic(Characteristic.InputDeviceType).updateValue(this.currentInputDeviceType); }
|
|
3596
3624
|
this.previousInputDeviceType = this.currentInputDeviceType;
|
|
3597
3625
|
|
|
3598
3626
|
// check for change of InputSourceType state: (a characteristic of Input Source)
|
|
3599
3627
|
if (this.previousInputSourceType !== this.currentInputSourceType) {
|
|
3600
3628
|
this.log('%s: Input Source Type changed on input %s %s from %s [%s] to %s [%s]',
|
|
3601
3629
|
this.name,
|
|
3602
|
-
|
|
3630
|
+
currinputNumber,
|
|
3603
3631
|
this.currentChannelId,
|
|
3604
3632
|
this.previousInputSourceType, Object.keys(Characteristic.InputSourceType)[this.previousInputSourceType + 1],
|
|
3605
3633
|
this.currentInputSourceType, Object.keys(Characteristic.InputSourceType)[this.currentInputSourceType + 1]);
|
|
3606
3634
|
}
|
|
3607
3635
|
// [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.
|
|
3608
|
-
this.inputServices[
|
|
3636
|
+
if (currInputIndex) { this.inputServices[currInputIndex].getCharacteristic(Characteristic.InputSourceType).updateValue(this.currentInputSourceType); } // generates Homebridge warning
|
|
3609
3637
|
this.previousInputSourceType = this.currentInputSourceType;
|
|
3610
|
-
//this.log('++++DEBUG: this.inputServices[
|
|
3611
|
-
//this.log(this.inputServices[
|
|
3638
|
+
//this.log('++++DEBUG: this.inputServices[currInputIndex]')
|
|
3639
|
+
//this.log(this.inputServices[currInputIndex])
|
|
3612
3640
|
|
|
3613
3641
|
// +++++++++++++++ end of Input Service characteristics ++++++++++++++
|
|
3614
3642
|
|
|
@@ -3680,7 +3708,6 @@ class stbDevice {
|
|
|
3680
3708
|
maxSources = Math.min(configDevice.maxChannels || maxSources, maxSources);
|
|
3681
3709
|
}
|
|
3682
3710
|
}
|
|
3683
|
-
//this.log("%s: Setting maxSources to %s", this.name, maxSources);
|
|
3684
3711
|
|
|
3685
3712
|
|
|
3686
3713
|
// get a user configured Profile, if it exists, otherwise we will use the default Profile for the channel list
|
|
@@ -3845,58 +3872,98 @@ class stbDevice {
|
|
|
3845
3872
|
// clear the array
|
|
3846
3873
|
this.channelList = [];
|
|
3847
3874
|
|
|
3848
|
-
//
|
|
3849
|
-
|
|
3875
|
+
// check for any custom keymacros, they consume slots at the end of the channelList
|
|
3876
|
+
this.log.debug("%s: Checking for KeyMacros", this.name);
|
|
3877
|
+
let keyMacros = [];
|
|
3878
|
+
if (this.config.channels) {
|
|
3879
|
+
keyMacros = this.config.channels.filter(channel => {
|
|
3880
|
+
if (channel.channelKeyMacro) { return true; }
|
|
3881
|
+
})
|
|
3882
|
+
this.log.debug("%s: Found keyMacros: %s", this.name, keyMacros.length);
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
// limit the amount to load to all the channels and all the keyMacros
|
|
3886
|
+
// keyMacros will occupy top slots of channel list
|
|
3887
|
+
const maxChs = Math.min(subscribedChIds.length + keyMacros.length, maxSources);
|
|
3888
|
+
const firstKeyMacroSlot = Math.max(maxChs - keyMacros.length,0); // never go below index 0
|
|
3889
|
+
//this.log("%s: Loading %s channels, starting at channel 1", this.name, subscribedChIds.length);
|
|
3890
|
+
this.log("%s: Loading %s key macros, starting at channel %s ", this.name, keyMacros.length, firstKeyMacroSlot+1);
|
|
3891
|
+
|
|
3892
|
+
// show log of what will be loaded, very useful for debugging
|
|
3850
3893
|
this.log("%s: Refreshing channels 1 to %s", this.name, maxChs);
|
|
3851
3894
|
if (maxChs < maxSources) {
|
|
3852
3895
|
this.log("%s: Hiding channels %s to %s", this.name, maxChs + 1, maxSources);
|
|
3853
3896
|
}
|
|
3854
3897
|
|
|
3855
|
-
|
|
3856
3898
|
// loop and load all channels from the subscribedChIds in the order defined by the array
|
|
3857
3899
|
//this.log("Loading all subscribed channels")
|
|
3858
|
-
|
|
3900
|
+
for ( let i = 0; i < maxChs; i++ ) {
|
|
3901
|
+
//subscribedChIds.forEach((subscribedChId, i) => {
|
|
3859
3902
|
//this.log("In forEach loop, processing index %s %s", i, subscribedChId)
|
|
3860
3903
|
|
|
3861
3904
|
// find the channel to load.
|
|
3862
3905
|
var channel = {};
|
|
3863
3906
|
var customChannel = {};
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
if
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
customChannel =
|
|
3907
|
+
let k = 0;
|
|
3908
|
+
|
|
3909
|
+
// load a channel if we are in the range of channel numbers not assigned to keymacros
|
|
3910
|
+
if ( i < firstKeyMacroSlot ) {
|
|
3911
|
+
// this slot needs to be occupied by a channel
|
|
3912
|
+
|
|
3913
|
+
// first look in the config channels list for any user-defined custom channel name
|
|
3914
|
+
if (this.config.channels) {
|
|
3915
|
+
customChannel = this.config.channels.find(channel => channel.id === subscribedChIds[i]);
|
|
3916
|
+
if ((customChannel || {}).name) {
|
|
3917
|
+
customChannel.name = cleanNameForHomeKit(customChannel.name)
|
|
3918
|
+
this.log("%s: Found %s in config channels, setting name to %s", this.name, customChannel.id, customChannel.name);
|
|
3919
|
+
} else {
|
|
3920
|
+
customChannel = {};
|
|
3921
|
+
}
|
|
3873
3922
|
}
|
|
3874
|
-
}
|
|
3875
3923
|
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3924
|
+
|
|
3925
|
+
// check if the subscribedChId exists in the master channel list, if not, push it, using the user-defined name if one exists, and channelNumber >10000
|
|
3926
|
+
this.log.debug("%s: Index %s: Finding %s in master channel list", this.name, i, subscribedChIds[i]);
|
|
3927
|
+
channel = this.platform.masterChannelList.find(channel => channel.id === subscribedChIds[i]);
|
|
3928
|
+
if (!channel) {
|
|
3929
|
+
const newChName = customChannel.name || "Channel " + subscribedChIds[i];
|
|
3930
|
+
this.log("%s: Unknown channel %s [%s] discovered. Adding to the master channel list", this.name, subscribedChIds[i], newChName);
|
|
3931
|
+
this.platform.masterChannelList.push({
|
|
3932
|
+
id: subscribedChIds[i],
|
|
3933
|
+
name: newChName,
|
|
3934
|
+
logicalChannelNumber: 10000 + this.platform.masterChannelList.length, // integer
|
|
3935
|
+
linearProducts: this.platform.entitlements.entitlements[0].id // must be a valid entitlement id
|
|
3936
|
+
});
|
|
3937
|
+
// refresh channel as the not found channel will now be in the masterChannelList
|
|
3938
|
+
channel = this.platform.masterChannelList.find(channel => channel.id === subscribedChIds[i]);
|
|
3939
|
+
channel.configuredName = channel.name; // set a configured name same as name
|
|
3940
|
+
} else {
|
|
3941
|
+
// show some useful debug data
|
|
3942
|
+
this.log.debug("%s: Index %s: Found %s %s in master channel list", this.name, i, channel.id, channel.name);
|
|
3943
|
+
}
|
|
3944
|
+
this.log.debug("%s: Index %s: Loading channel %s %s", this.name, i, i+1, channel.name);
|
|
3945
|
+
|
|
3891
3946
|
} else {
|
|
3892
|
-
|
|
3893
|
-
this
|
|
3947
|
+
|
|
3948
|
+
// this slot needs to be occupied by a keyMacro
|
|
3949
|
+
k = i - firstKeyMacroSlot
|
|
3950
|
+
this.log.debug("%s: Index %s: Loading channel %s keyMacro %s %s", this.name, i, i+1, k+1, keyMacros[k].channelName);
|
|
3951
|
+
this.log,debug("%s: Index %s: Load this keyMacro: %s", this.name, i, keyMacros[k]);
|
|
3952
|
+
channel = {
|
|
3953
|
+
"id": '$KeyMacro' + (k+1),
|
|
3954
|
+
"name": keyMacros[k].channelName,
|
|
3955
|
+
"logicalChannelNumber": 20000 + i,
|
|
3956
|
+
"linearProducts": 0,
|
|
3957
|
+
"keyMacro": keyMacros[k].channelKeyMacro,
|
|
3958
|
+
}
|
|
3894
3959
|
}
|
|
3895
3960
|
|
|
3896
|
-
|
|
3897
|
-
//
|
|
3961
|
+
|
|
3962
|
+
// load this channel/keyMacro as an input
|
|
3963
|
+
//this.log("loading input %s of %s", i + 1, maxChs)
|
|
3898
3964
|
//this.log.warn("%s: Index %s: Checking if %s %s can be loaded", this.name, i, channel.id, channel.name);
|
|
3899
|
-
if (i <
|
|
3965
|
+
if (i < maxChs) {
|
|
3966
|
+
this.log.debug("%s: Index %s: Refreshing channel", this.name, i);
|
|
3900
3967
|
|
|
3901
3968
|
// add the user-defined name if one exists
|
|
3902
3969
|
if (customChannel && customChannel.name) { channel.name = customChannel.name; }
|
|
@@ -3930,8 +3997,6 @@ class stbDevice {
|
|
|
3930
3997
|
}
|
|
3931
3998
|
this.inputServices[i].name = channel.configuredName;
|
|
3932
3999
|
this.inputServices[i].subtype = 'input_' + channel.id; // string, input_SV09038 etc
|
|
3933
|
-
//this.inputServices[i].subtype = 'input_' + i+1; // integer, generates input_1 for index 0
|
|
3934
|
-
//this.log.warn("DEBUG: input %s subtype set to %s %s",i+1, channel.id, this.inputServices[i].subtype);
|
|
3935
4000
|
|
|
3936
4001
|
// Name can only be set for SharedProfile where order can never be changed
|
|
3937
4002
|
if (this.profileid == 0) {
|
|
@@ -3942,9 +4007,13 @@ class stbDevice {
|
|
|
3942
4007
|
.updateCharacteristic(Characteristic.CurrentVisibilityState, channel.visibilityState)
|
|
3943
4008
|
.updateCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED);
|
|
3944
4009
|
//inputService.updateCharacteristic(Characteristic.CurrentVisibilityState, Characteristic.TargetVisibilityState.SHOWN);
|
|
4010
|
+
//this.log.warn('this.inputServices[i]')
|
|
4011
|
+
//this.log.warn(this.inputServices[i])
|
|
3945
4012
|
}
|
|
3946
4013
|
}
|
|
3947
|
-
|
|
4014
|
+
|
|
4015
|
+
|
|
4016
|
+
};
|
|
3948
4017
|
|
|
3949
4018
|
// after loading all the channels, reset the ActiveIdentifier (uint32) to the right Identifier (uint32), as it may have moved slots
|
|
3950
4019
|
// subtype: 'input_SV09038',
|
|
@@ -3960,33 +4029,6 @@ class stbDevice {
|
|
|
3960
4029
|
}
|
|
3961
4030
|
}
|
|
3962
4031
|
|
|
3963
|
-
// load any custom key macros: in work, not for publich consumption yet
|
|
3964
|
-
if (this.config.channels && 1==0) {
|
|
3965
|
-
this.config.channels.forEach((customChannel, i) => {
|
|
3966
|
-
this.log("In forEach loop, processing config channel index %s %s", i, customChannel)
|
|
3967
|
-
|
|
3968
|
-
this.log(customChannel)
|
|
3969
|
-
// first look in the config channels list for any user-defined custom channel name
|
|
3970
|
-
if (customChannel.channelKeyMacro){
|
|
3971
|
-
this.log("%s: Found Key Macro in config channels:", this.name, customChannel.channelKeyMacro, customChannel.channelName)
|
|
3972
|
-
let i = this.inputServices.length
|
|
3973
|
-
this.log("inputService %s is set to", i-1);
|
|
3974
|
-
this.log(this.inputServices[i-1]);
|
|
3975
|
-
this.inputServices[i]
|
|
3976
|
-
.name = customChannel.channelName
|
|
3977
|
-
//.subtype = 'input_KM' + 20000 + i; // string, input_KM20000 + channel offset. KM=KeyMacro
|
|
3978
|
-
this.inputServices[i].updateCharacteristic(Characteristic.Name, customChannel.channelName) // stays unchanged at Input 01 etc
|
|
3979
|
-
.updateCharacteristic(Characteristic.ConfiguredName, customChannel.channelName)
|
|
3980
|
-
.updateCharacteristic(Characteristic.CurrentVisibilityState, Characteristic.CurrentVisibilityState.SHOWN)
|
|
3981
|
-
.updateCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED);
|
|
3982
|
-
|
|
3983
|
-
this.log("inputService %s is set to", i);
|
|
3984
|
-
this.log(this.inputServices[i]);
|
|
3985
|
-
|
|
3986
|
-
}
|
|
3987
|
-
})
|
|
3988
|
-
}
|
|
3989
|
-
|
|
3990
4032
|
|
|
3991
4033
|
|
|
3992
4034
|
// save to disk
|
|
@@ -4001,7 +4043,7 @@ class stbDevice {
|
|
|
4001
4043
|
var appsToload = this.platform.profiles[this.profileId].recentlyUsedApps;
|
|
4002
4044
|
appsToload.forEach( (appId, i) => {
|
|
4003
4045
|
this.log("loading app", i, appId);
|
|
4004
|
-
if (i <=
|
|
4046
|
+
if (i <= maxChs) {
|
|
4005
4047
|
// get the channel
|
|
4006
4048
|
var foundIndex = this.platform.masterChannelList.findIndex(channel => channel.id === appId);
|
|
4007
4049
|
//this.log("foundIndex", foundIndex);
|
|
@@ -4056,7 +4098,7 @@ class stbDevice {
|
|
|
4056
4098
|
|
|
4057
4099
|
}
|
|
4058
4100
|
|
|
4059
|
-
this.log("%s: Channel list refreshed with %s channels", this.name, Math.min(maxChs, maxSources));
|
|
4101
|
+
this.log("%s: Channel list refreshed with %s channels (including %s key macros)", this.name, Math.min(maxChs, maxSources), keyMacros.length);
|
|
4060
4102
|
return false;
|
|
4061
4103
|
|
|
4062
4104
|
} catch (err) {
|
|
@@ -4361,17 +4403,20 @@ class stbDevice {
|
|
|
4361
4403
|
async setInput(input, callback) {
|
|
4362
4404
|
input = input ?? {} // ensure input is never null or undefined
|
|
4363
4405
|
if (this.config.debugLevel > 1) { this.log.warn('%s: setInput input %s %s',this.name, input.id, input.name); }
|
|
4364
|
-
callback(
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
if
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
//
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
this.
|
|
4406
|
+
callback(); // for rapid response
|
|
4407
|
+
// get current channel, also finds keyMacro channels
|
|
4408
|
+
let channel = this.channelList.find(channel => channel.id === this.currentChannelId);
|
|
4409
|
+
// if not found look in the master channel list
|
|
4410
|
+
if (!channel) { channel = this.platform.masterChannelList.find(channel => channel.id === this.currentChannelId); }
|
|
4411
|
+
|
|
4412
|
+
// robustness: only try to switch channel if an input.id exists. Handle KeyMacros
|
|
4413
|
+
this.log('%s: Change channel from %s [%s] to %s [%s]', this.name, this.currentChannelId, (channel || {}).name || NO_CHANNEL_NAME, input.id, input.name);
|
|
4414
|
+
if (input.id && input.id.startsWith('$KeyMacro')){
|
|
4415
|
+
this.lastKeyMacroChannelId = input.id; // remember last keyMacro id
|
|
4416
|
+
this.platform.sendKey(this.deviceId, this.name, input.keyMacro);
|
|
4417
|
+
|
|
4418
|
+
} else if (input.id){
|
|
4419
|
+
this.lastKeyMacroChannelId = null; // clear last keyMacro channelId
|
|
4375
4420
|
this.platform.switchChannel(this.deviceId, this.name, input.id, input.name);
|
|
4376
4421
|
} else {
|
|
4377
4422
|
this.log.warn('%s: setInput called with no input.id', this.name);
|
|
@@ -4384,18 +4429,13 @@ class stbDevice {
|
|
|
4384
4429
|
async getInputName(inputId, callback) {
|
|
4385
4430
|
// fired by the user changing a channel name in Home app accessory setup
|
|
4386
4431
|
//if (this.config.debugLevel > 1) { this.log.warn('%s: getInputName inputId %s', this.name, inputId); }
|
|
4387
|
-
|
|
4388
|
-
// need to read from stored cache, currently not implemented, TO-DO
|
|
4389
|
-
var chName = NO_CHANNEL_NAME; // must have a value
|
|
4390
|
-
if (this.channelList[inputId-1]) {
|
|
4391
|
-
chName = this.channelList[inputId-1].configuredName;
|
|
4392
|
-
}
|
|
4432
|
+
const inputName =(this.channelList[inputId-1] || {}).configuredName || ''; // Empty string if not found
|
|
4393
4433
|
if (this.config.debugLevel > 1) {
|
|
4394
|
-
this.log.warn("%s: getInputName for input %s returning '%s'", this.name, inputId,
|
|
4434
|
+
this.log.warn("%s: getInputName for input %s returning '%s'", this.name, inputId, inputName);
|
|
4395
4435
|
}
|
|
4396
|
-
callback(null,
|
|
4436
|
+
callback(null, inputName);
|
|
4397
4437
|
};
|
|
4398
|
-
|
|
4438
|
+
|
|
4399
4439
|
// set input name (change the TV channel name)
|
|
4400
4440
|
async setInputName(inputId, newInputName, callback) {
|
|
4401
4441
|
// fired by the user changing a channel name in Home app accessory setup
|
|
@@ -4427,10 +4467,39 @@ class stbDevice {
|
|
|
4427
4467
|
//this.log('%s: Renamed channel %s from %s to %s (valid only for HomeKit)', this.name, inputId+1, oldInputName, newInputName);
|
|
4428
4468
|
this.log('%s: Renamed channel %s to %s (valid only for HomeKit)', this.name, inputId+1, newInputName);
|
|
4429
4469
|
}
|
|
4430
|
-
callback(
|
|
4470
|
+
callback();
|
|
4431
4471
|
};
|
|
4432
4472
|
|
|
4433
4473
|
|
|
4474
|
+
// get current channel id (the TV channel identifier, a string)
|
|
4475
|
+
// added in v2.1.0
|
|
4476
|
+
// custom characteristic, returns a string, the event updates the characteristic value automatically
|
|
4477
|
+
async getCurrentChannelId(callback, currentChannelId) {
|
|
4478
|
+
// fired by the user reading the Custom characteristic in Shortcuts
|
|
4479
|
+
// fired when the accessory is first created and HomeKit requests a refresh
|
|
4480
|
+
// fired when the icon is clicked in the Home app and HomeKit requests a refresh
|
|
4481
|
+
// fired when the Home app is opened
|
|
4482
|
+
currentChannelId = this.currentChannelId || ''; // this.currentChannelId is a string eg SV09038. Empty string if not found
|
|
4483
|
+
if (this.config.debugLevel > 1) { this.log.warn("%s: getCurrentChannelId returning '%s'", this.name, currentChannelId); }
|
|
4484
|
+
callback(null, currentChannelId);
|
|
4485
|
+
};
|
|
4486
|
+
|
|
4487
|
+
|
|
4488
|
+
// get current channel name (the TV channel name)
|
|
4489
|
+
// added in v2.1.0
|
|
4490
|
+
// custom characteristic, returns a string, the event updates the characteristic value automatically
|
|
4491
|
+
async getCurrentChannelName(callback, currentChannelName) {
|
|
4492
|
+
// fired by the user reading the Custom characteristic in Shortcuts
|
|
4493
|
+
// fired when the accessory is first created and HomeKit requests a refresh
|
|
4494
|
+
// fired when the icon is clicked in the Home app and HomeKit requests a refresh
|
|
4495
|
+
// fired when the Home app is opened
|
|
4496
|
+
const curChannel = this.platform.masterChannelList.find(channel => channel.id === this.currentChannelId ); // this.currentChannelId is a string eg SV09038
|
|
4497
|
+
// consider setting to Radio if radio is playing
|
|
4498
|
+
currentChannelName = (curChannel || {}).name || ''; // Empty string if not found
|
|
4499
|
+
if (this.config.debugLevel > 1) { this.log.warn("%s: getCurrentChannelName returning '%s'", this.name, currentChannelName); }
|
|
4500
|
+
callback(null, currentChannelName);
|
|
4501
|
+
};
|
|
4502
|
+
|
|
4434
4503
|
|
|
4435
4504
|
// get input visibility state (of the TV channel in the accessory)
|
|
4436
4505
|
async getInputVisibilityState(inputId, callback) {
|
|
@@ -4442,7 +4511,7 @@ class stbDevice {
|
|
|
4442
4511
|
// }
|
|
4443
4512
|
const visibilityState = this.inputServices[inputId-1].getCharacteristic(Characteristic.CurrentVisibilityState).value;
|
|
4444
4513
|
if (this.config.debugLevel > 2) {
|
|
4445
|
-
this.log.warn('%s: getInputVisibilityState
|
|
4514
|
+
this.log.warn('%s: getInputVisibilityState input %s returning %s [%s]', this.name, inputId, visibilityState, Object.keys(Characteristic.CurrentVisibilityState)[visibilityState + 1]);
|
|
4446
4515
|
}
|
|
4447
4516
|
callback(null, visibilityState);
|
|
4448
4517
|
}
|
|
@@ -4460,7 +4529,7 @@ class stbDevice {
|
|
|
4460
4529
|
//not yet active
|
|
4461
4530
|
//this.platform.persistConfig(this.deviceId, this.channelList);
|
|
4462
4531
|
|
|
4463
|
-
callback(
|
|
4532
|
+
callback(); // for rapid response
|
|
4464
4533
|
}
|
|
4465
4534
|
|
|
4466
4535
|
|
|
@@ -4481,7 +4550,7 @@ class stbDevice {
|
|
|
4481
4550
|
if(this.currentClosedCaptionsState !== targetClosedCaptionsState){
|
|
4482
4551
|
this.log("setClosedCaptions: not yet implemented");
|
|
4483
4552
|
}
|
|
4484
|
-
callback(
|
|
4553
|
+
callback();
|
|
4485
4554
|
}
|
|
4486
4555
|
|
|
4487
4556
|
|
|
@@ -4512,7 +4581,7 @@ class stbDevice {
|
|
|
4512
4581
|
if(this.customPictureMode !== targetPictureMode){
|
|
4513
4582
|
this.log("setPictureMode: not yet implemented");
|
|
4514
4583
|
}
|
|
4515
|
-
callback(
|
|
4584
|
+
callback();
|
|
4516
4585
|
}
|
|
4517
4586
|
|
|
4518
4587
|
|
package/package.json
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
"displayName": "Homebridge EOSSTB",
|
|
4
4
|
"description": "homebridge-plugin - Add your set-top box to Homekit (for Magenta AT, Telenet BE, Sunrise CH, Virgin Media GB & IE, Ziggo NL)",
|
|
5
5
|
"author": "Jochen Siegenthaler (https://github.com/jsiegenthaler/)",
|
|
6
|
-
"version": "2.0
|
|
6
|
+
"version": "2.1.0",
|
|
7
7
|
"platformname": "eosstb",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"axios-cookiejar-support": "^4.0.
|
|
10
|
-
"axios": "^1.2.
|
|
9
|
+
"axios-cookiejar-support": "^4.0.6",
|
|
10
|
+
"axios": "^1.2.2",
|
|
11
11
|
"debug": "^4.3.4",
|
|
12
12
|
"mqtt": "^4.3.7",
|
|
13
13
|
"qs": "^6.11.0",
|