homebridge-eosstb 2.4.0-beta.1 → 2.4.0-beta.2
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 +10 -0
- package/index.js +211 -51
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
See the [Readme file](https://github.com/jsiegenthaler/homebridge-eosstb/blob/master/README.md) for full plugin documentation.
|
|
5
5
|
Please restart Homebridge after every plugin update.
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
## 2.4.0-beta.2 (2026-05-09)
|
|
9
|
+
|
|
10
|
+
This release focusses on ensuring mqtt long-term stability, and fixes an issue where the channel name was not shown on startup.
|
|
11
|
+
|
|
12
|
+
- Rescheduled nightly channel list refresh to 0000-0400 instead of 0000-0600
|
|
13
|
+
- Added an automatic daily mqtt reconnect at a random time between 0400-0600 to avoid long running mqtt sessions. Only restarts if settop box is turned off
|
|
14
|
+
- Fixed issue where current channel was not displayed on plugin startup
|
|
15
|
+
|
|
16
|
+
|
|
7
17
|
## 2.4.0-beta.1 (2026-05-09)
|
|
8
18
|
|
|
9
19
|
This release represents a major rewrite of the plugin, significantly improving robustness, HAP compliance, and code quality throughout, and making it work for Switzerland.
|
package/index.js
CHANGED
|
@@ -357,6 +357,7 @@ class StbPlatform {
|
|
|
357
357
|
this.masterChannelList = [];
|
|
358
358
|
this.masterChannelListExpiryDate = 0; // epoch = always expired on first run
|
|
359
359
|
this.checkChannelListTimeout = null; // nightly scheduler handler
|
|
360
|
+
this.mqttReconnecting = false; // nightly reconnect indicator
|
|
360
361
|
this.isDev = config.devMode === true;
|
|
361
362
|
this.debugLevel = this.config.debugLevel || 0; // debugLevel defaults to 0 (minimum)
|
|
362
363
|
|
|
@@ -822,7 +823,7 @@ class StbPlatform {
|
|
|
822
823
|
|
|
823
824
|
/**
|
|
824
825
|
* Schedule the next nightly master channel list refresh.
|
|
825
|
-
* Picks a random time between 00:00 and
|
|
826
|
+
* Picks a random time between 00:00 and 04:00 the following day,
|
|
826
827
|
* then reschedules itself so the pattern repeats indefinitely.
|
|
827
828
|
*
|
|
828
829
|
* Using setTimeout (not setInterval) means each day gets a fresh
|
|
@@ -834,9 +835,9 @@ class StbPlatform {
|
|
|
834
835
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
835
836
|
tomorrow.setHours(0, 0, 0, 0);
|
|
836
837
|
|
|
837
|
-
// Add a random offset: anywhere from 0 ms up to (but not including)
|
|
838
|
-
const
|
|
839
|
-
const randomOffsetMs = Math.floor(Math.random() *
|
|
838
|
+
// Add a random offset: anywhere from 0 ms up to (but not including) 4 hours
|
|
839
|
+
const FOUR_HOURS_MS = 4 * 60 * 60 * 1000;
|
|
840
|
+
const randomOffsetMs = Math.floor(Math.random() * FOUR_HOURS_MS);
|
|
840
841
|
|
|
841
842
|
const nextRefreshAt = new Date(tomorrow.getTime() + randomOffsetMs);
|
|
842
843
|
const msUntilRefresh = nextRefreshAt.getTime() - Date.now();
|
|
@@ -850,11 +851,121 @@ class StbPlatform {
|
|
|
850
851
|
// Store the timer handle so shutdown can cancel it
|
|
851
852
|
this.checkChannelListTimeout = setTimeout(async () => {
|
|
852
853
|
if (this.isShuttingDown) return; // bail out if we're going down
|
|
854
|
+
|
|
855
|
+
// if an MQTT reconnect is in progress, wait a few minutes before
|
|
856
|
+
// refreshing to avoid a race condition during session startup
|
|
857
|
+
if (this.mqttReconnecting) {
|
|
858
|
+
const THREE_MIN_MS = 3 * 60 * 1000;
|
|
859
|
+
const retryDelayMs = THREE_MIN_MS + Math.floor(Math.random() * THREE_MIN_MS);
|
|
860
|
+
this.log.info(
|
|
861
|
+
'StbPlatform: channel list refresh deferred - MQTT reconnect in progress, retrying in a few minutes',
|
|
862
|
+
);
|
|
863
|
+
this.checkChannelListTimeout = setTimeout(async () => {
|
|
864
|
+
if (this.isShuttingDown) return;
|
|
865
|
+
await this._refreshChannelList();
|
|
866
|
+
this._scheduleNightlyChannelListRefresh();
|
|
867
|
+
}, retryDelayMs);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
853
871
|
await this._refreshChannelList();
|
|
854
872
|
this._scheduleNightlyChannelListRefresh(); // reschedule for the next day
|
|
855
873
|
}, msUntilRefresh);
|
|
856
874
|
} // end of _scheduleNightlyChannelListRefresh
|
|
857
875
|
|
|
876
|
+
/**
|
|
877
|
+
* Schedule the next nightly MQTT reconnect.
|
|
878
|
+
* Picks a random time between 04:00 and 06:00 the following day
|
|
879
|
+
* to avoid overlapping with the channel list refresh (00:00–04:00).
|
|
880
|
+
* Reschedules itself so the pattern repeats indefinitely.
|
|
881
|
+
*/
|
|
882
|
+
_scheduleNightlyMqttReconnect() {
|
|
883
|
+
const tomorrow = new Date();
|
|
884
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
885
|
+
tomorrow.setHours(4, 0, 0, 0);
|
|
886
|
+
|
|
887
|
+
const TWO_HOURS_MS = 2 * 60 * 60 * 1000;
|
|
888
|
+
const randomOffsetMs = Math.floor(Math.random() * TWO_HOURS_MS);
|
|
889
|
+
|
|
890
|
+
const nextReconnectAt = new Date(tomorrow.getTime() + randomOffsetMs);
|
|
891
|
+
const msUntilReconnect = nextReconnectAt.getTime() - Date.now();
|
|
892
|
+
|
|
893
|
+
if (this.debugLevel > 0) {
|
|
894
|
+
this.log.warn(
|
|
895
|
+
`StbPlatform: next nightly MQTT reconnect scheduled for ${nextReconnectAt.toLocaleString()}`,
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
this.mqttReconnectTimeout = setTimeout(async () => {
|
|
900
|
+
if (this.isShuttingDown) return;
|
|
901
|
+
await this._attemptNightlyMqttReconnect();
|
|
902
|
+
// _attemptNightlyMqttReconnect reschedules for the next night once done
|
|
903
|
+
}, msUntilReconnect);
|
|
904
|
+
} // end of _scheduleNightlyMqttReconnect
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Attempt the nightly MQTT reconnect.
|
|
909
|
+
* If any STB is currently online (user may be watching), defers by 1 hour
|
|
910
|
+
* plus a random offset and tries again, rather than interrupting the session.
|
|
911
|
+
* Once the reconnect completes (or fails), reschedules for the next night.
|
|
912
|
+
* Retries 3 times then gives up, and the next reconnect will be the next day.
|
|
913
|
+
*/
|
|
914
|
+
async _attemptNightlyMqttReconnect(retryCount = 0) {
|
|
915
|
+
if (this.isShuttingDown) return;
|
|
916
|
+
|
|
917
|
+
const MAX_RETRIES = 3; // give up after 3 deferrals (~3-4.5 hours past 04:00)
|
|
918
|
+
|
|
919
|
+
// check if any STB is currently active - if so, defer to avoid
|
|
920
|
+
// interrupting a user who may be watching TV and using the remote
|
|
921
|
+
const anyStbOnline = this.devices.some(
|
|
922
|
+
(device) => device.currentPowerState === Characteristic.Active.ACTIVE,
|
|
923
|
+
);
|
|
924
|
+
|
|
925
|
+
if (anyStbOnline) {
|
|
926
|
+
// give up if max retries reached
|
|
927
|
+
if (retryCount >= MAX_RETRIES) {
|
|
928
|
+
this.log.info(
|
|
929
|
+
'StbPlatform: nightly MQTT reconnect skipped - STB still active after max retries, rescheduling for next night',
|
|
930
|
+
);
|
|
931
|
+
this._scheduleNightlyMqttReconnect();
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// retry in 1 hour plus a random 0–30 min buffer
|
|
936
|
+
const ONE_HOUR_MS = 60 * 60 * 1000;
|
|
937
|
+
const THIRTY_MIN_MS = 30 * 60 * 1000;
|
|
938
|
+
const retryDelayMs = ONE_HOUR_MS + Math.floor(Math.random() * THIRTY_MIN_MS);
|
|
939
|
+
const retryAt = new Date(Date.now() + retryDelayMs);
|
|
940
|
+
|
|
941
|
+
this.log.info(
|
|
942
|
+
`StbPlatform: nightly MQTT reconnect deferred - STB is active (attempt ${retryCount + 1}/${MAX_RETRIES}). Retrying at ${retryAt.toLocaleString()}`,
|
|
943
|
+
);
|
|
944
|
+
|
|
945
|
+
// store handle so shutdown can cancel the deferred retry too
|
|
946
|
+
this.mqttReconnectTimeout = setTimeout(async () => {
|
|
947
|
+
if (this.isShuttingDown) return;
|
|
948
|
+
await this._attemptNightlyMqttReconnect(retryCount + 1);
|
|
949
|
+
}, retryDelayMs);
|
|
950
|
+
return; // don't reschedule for next night yet - that happens after a successful reconnect
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// no STB is active - safe to reconnect
|
|
954
|
+
try {
|
|
955
|
+
this.mqttReconnecting = true; // signal to channel list refresh to pause
|
|
956
|
+
this.log.info('StbPlatform: nightly MQTT reconnect starting...');
|
|
957
|
+
await this.endMqttSession();
|
|
958
|
+
await this.startMqttClient();
|
|
959
|
+
this.log.info('StbPlatform: nightly MQTT reconnect completed');
|
|
960
|
+
} catch (err) {
|
|
961
|
+
this.log.error('StbPlatform: nightly MQTT reconnect failed:', err.message);
|
|
962
|
+
} finally {
|
|
963
|
+
this.mqttReconnecting = false; // always clear the flag, even on failure
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
this._scheduleNightlyMqttReconnect(); // reschedule for next night
|
|
967
|
+
} // end of _attemptNightlyMqttReconnect
|
|
968
|
+
|
|
858
969
|
/**
|
|
859
970
|
* _runFullStartupSequence
|
|
860
971
|
*
|
|
@@ -4278,12 +4389,8 @@ class StbPlatform {
|
|
|
4278
4389
|
// ------ device subscriptions ------
|
|
4279
4390
|
// subscribe only to what we need
|
|
4280
4391
|
|
|
4281
|
-
// turn on our clientId. This is similar to turning on a box, it tells the server we are online
|
|
4282
|
-
// our clientId must be up and running to send commands (power, channel, etc) to the physical device
|
|
4283
|
-
// this.setHgoOnlineRunning(householdId, mqttClientId);
|
|
4284
|
-
|
|
4285
4392
|
// householdId/mqttClientId: subscribe to own clientId to get data for ourselves
|
|
4286
|
-
// subscribe to all devices
|
|
4393
|
+
// subscribe to all devices before the setHgoState is sent
|
|
4287
4394
|
this.mqttSubscribeToTopic(
|
|
4288
4395
|
householdId + "/" + this.mqttClient.options.clientId,
|
|
4289
4396
|
); // subscribe to our own mqttClientId to get all data
|
|
@@ -4320,32 +4427,36 @@ class StbPlatform {
|
|
|
4320
4427
|
// reset so the 10-second retry fires correctly if the box doesn't respond
|
|
4321
4428
|
this.lastMqttUiStatusMessageReceived = null;
|
|
4322
4429
|
|
|
4430
|
+
// announce ourselves as an active HGO client before requesting UI status
|
|
4431
|
+
// the STB uses this retained presence message to decide which clients to respond to
|
|
4432
|
+
this.setHgoState(householdId, this.mqttClient.options.clientId, 'ONLINE_RUNNING');
|
|
4433
|
+
|
|
4434
|
+
// request initial UI status for each device, with a short delay to allow
|
|
4435
|
+
// the STB to process the HGO presence announcement first
|
|
4323
4436
|
// CPE.uiStatus messages are received via the householdId and mqttClientId
|
|
4324
4437
|
// topics which are already subscribed above.
|
|
4325
4438
|
// getUiStatus is called here to request the initial UI state from each device.
|
|
4326
4439
|
// retain: false is used (see getUiStatus) so a retry is scheduled in case the box
|
|
4327
4440
|
// is temporarily unreachable when the initial request is sent.
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4441
|
+
setTimeout(() => {
|
|
4442
|
+
this.devices.forEach((device) => {
|
|
4443
|
+
// request the initial UI status for each device
|
|
4444
|
+
this.getUiStatus(device.deviceId, this.mqttClient.options.clientId);
|
|
4445
|
+
|
|
4446
|
+
// retry after 10 seconds if no CPE.uiStatus response has arrived yet
|
|
4447
|
+
setTimeout(() => {
|
|
4448
|
+
if (!this.lastMqttUiStatusMessageReceived) {
|
|
4449
|
+
if (this.debugLevel > 0) {
|
|
4450
|
+
this.log.warn(
|
|
4451
|
+
"getUiStatus: no CPE.uiStatus received yet for %s, retrying",
|
|
4452
|
+
device.deviceId,
|
|
4453
|
+
);
|
|
4454
|
+
}
|
|
4455
|
+
this.getUiStatus(device.deviceId, this.mqttClient.options.clientId);
|
|
4341
4456
|
}
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
);
|
|
4346
|
-
}
|
|
4347
|
-
}, 10 * 1000); // 10 second retry delay
|
|
4348
|
-
});
|
|
4457
|
+
}, 10 * 1000); // 10 second retry delay
|
|
4458
|
+
});
|
|
4459
|
+
}, 500); // 500ms for STB to register our HGO presence before we request status
|
|
4349
4460
|
|
|
4350
4461
|
resolve(true); // all subscriptions registered — session is ready
|
|
4351
4462
|
} catch (err) {
|
|
@@ -4462,7 +4573,7 @@ class StbPlatform {
|
|
|
4462
4573
|
currMediaState = Characteristic.CurrentMediaState.PLAY;
|
|
4463
4574
|
if (this.debugLevel > 0) {
|
|
4464
4575
|
this.log.warn(
|
|
4465
|
-
"mqttClient: STB status:
|
|
4576
|
+
"mqttClient: STB status: Power-on transition detected for %s, setting mediaState to PLAY",
|
|
4466
4577
|
deviceId,
|
|
4467
4578
|
);
|
|
4468
4579
|
}
|
|
@@ -4496,6 +4607,12 @@ class StbPlatform {
|
|
|
4496
4607
|
this.log.warn("mqttClient: %s %s", deviceId, stbState);
|
|
4497
4608
|
}
|
|
4498
4609
|
}
|
|
4610
|
+
|
|
4611
|
+
// After the switch, if box is running, request current UI state
|
|
4612
|
+
//if (stbState === 'ONLINE_RUNNING') {
|
|
4613
|
+
// Small delay gives the STB a moment to settle before responding
|
|
4614
|
+
//setTimeout(() => this.mqttRequestUiStatus(deviceId), 500);
|
|
4615
|
+
//}
|
|
4499
4616
|
}
|
|
4500
4617
|
|
|
4501
4618
|
// handle CPE UI status messages for the STB
|
|
@@ -4878,16 +4995,26 @@ class StbPlatform {
|
|
|
4878
4995
|
return resolve(true);
|
|
4879
4996
|
}
|
|
4880
4997
|
|
|
4881
|
-
//
|
|
4998
|
+
// get all subscribed topics
|
|
4882
4999
|
const topics = this.subscribedTopics ?? [];
|
|
5000
|
+
|
|
5001
|
+
// announce HGO offline while the connection is still live, before any teardown
|
|
5002
|
+
this.setHgoState(
|
|
5003
|
+
this.session.householdId,
|
|
5004
|
+
this.mqttClient.options.clientId,
|
|
5005
|
+
'OFFLINE',
|
|
5006
|
+
);
|
|
5007
|
+
|
|
5008
|
+
// unsubscribe from all subscribedTopics before tearing down the session
|
|
4883
5009
|
if (topics.length === 0) {
|
|
4884
5010
|
this.log.info(
|
|
4885
5011
|
"mqttClient: No topics to unsubscribe from, skipping unsubscribe.",
|
|
4886
5012
|
);
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
5013
|
+
|
|
5014
|
+
this.mqttClient.end(false, {}, (endErr) => {
|
|
5015
|
+
if (endErr) {
|
|
5016
|
+
this.log.error("MQTT end error:", endErr);
|
|
5017
|
+
return reject(endErr);
|
|
4891
5018
|
}
|
|
4892
5019
|
this.log.info(
|
|
4893
5020
|
"mqttClient: Disconnected cleanly. No topics found to unsubscribe from.",
|
|
@@ -4897,15 +5024,15 @@ class StbPlatform {
|
|
|
4897
5024
|
return;
|
|
4898
5025
|
}
|
|
4899
5026
|
|
|
4900
|
-
this.mqttClient.unsubscribe(topics, (
|
|
4901
|
-
if (
|
|
4902
|
-
this.log.error("MQTT unsubscribe error:",
|
|
5027
|
+
this.mqttClient.unsubscribe(topics, (unsubErr) => {
|
|
5028
|
+
if (unsubErr) {
|
|
5029
|
+
this.log.error("MQTT unsubscribe error:", unsubErr);
|
|
4903
5030
|
// still attempt to end even if unsubscribe failed
|
|
4904
5031
|
}
|
|
4905
|
-
this.mqttClient.end(false, {}, (
|
|
4906
|
-
if (
|
|
4907
|
-
this.log.error("MQTT end error:",
|
|
4908
|
-
return reject(
|
|
5032
|
+
this.mqttClient.end(false, {}, (endErr) => {
|
|
5033
|
+
if (endErr) {
|
|
5034
|
+
this.log.error("MQTT end error:", endErr);
|
|
5035
|
+
return reject(endErr);
|
|
4909
5036
|
}
|
|
4910
5037
|
this.log.info(
|
|
4911
5038
|
"mqttClient: Disconnected cleanly. All topics unsubscribed.",
|
|
@@ -4990,7 +5117,7 @@ class StbPlatform {
|
|
|
4990
5117
|
"mqttPublishMessage: Publish Message:\r\nTopic: %s\r\nMessage: %s\r\nOptions: %s",
|
|
4991
5118
|
Topic,
|
|
4992
5119
|
Message,
|
|
4993
|
-
Options,
|
|
5120
|
+
JSON.stringify(Options),
|
|
4994
5121
|
);
|
|
4995
5122
|
}
|
|
4996
5123
|
this.mqttClient.publish(Topic, Message, Options, (err) => {
|
|
@@ -5081,23 +5208,25 @@ class StbPlatform {
|
|
|
5081
5208
|
});
|
|
5082
5209
|
}
|
|
5083
5210
|
|
|
5084
|
-
//
|
|
5085
|
-
|
|
5086
|
-
|
|
5211
|
+
// set the HGO session state (online or offline)
|
|
5212
|
+
// called on mqtt connect (ONLINE_RUNNING) and on mqtt disconnect (OFFLINE)
|
|
5213
|
+
// retain: true ensures the broker overwrites any previous retained state
|
|
5214
|
+
setHgoState(householdId, mqttClientId, state) {
|
|
5087
5215
|
const topic = `${householdId}/${mqttClientId}/status`;
|
|
5088
5216
|
const message = JSON.stringify({
|
|
5089
5217
|
source: mqttClientId,
|
|
5090
|
-
state:
|
|
5091
|
-
deviceType:
|
|
5092
|
-
mac:
|
|
5093
|
-
ipAddress:
|
|
5218
|
+
state: state,
|
|
5219
|
+
deviceType: 'HGO',
|
|
5220
|
+
mac: '',
|
|
5221
|
+
ipAddress: '',
|
|
5094
5222
|
});
|
|
5095
5223
|
if (this.debugLevel > 0) {
|
|
5096
|
-
this.log.warn(
|
|
5224
|
+
this.log.warn('setHgoState: publishing %s to topic: %s', state, topic);
|
|
5097
5225
|
}
|
|
5098
5226
|
this.mqttPublishMessage(topic, message, { qos: 2, retain: true });
|
|
5099
5227
|
}
|
|
5100
5228
|
|
|
5229
|
+
|
|
5101
5230
|
// send a channel change request to the settopbox via mqtt
|
|
5102
5231
|
// using the CPE.pushToTV message
|
|
5103
5232
|
// the friendlyDeviceName appears on the TV in a popup window
|
|
@@ -5151,6 +5280,37 @@ class StbPlatform {
|
|
|
5151
5280
|
}
|
|
5152
5281
|
}
|
|
5153
5282
|
|
|
5283
|
+
// Request the current UI status from the STB.
|
|
5284
|
+
// The STB responds with a CPE.uiStatus message on the household channel.
|
|
5285
|
+
// @param {string} deviceId - The STB device ID (e.g. "000378-EOS2STB-00852052xxxx")
|
|
5286
|
+
mqttRequestUiStatus(deviceId) {
|
|
5287
|
+
if (!this.mqttClient?.connected) {
|
|
5288
|
+
this.log.warn('%s: mqttRequestUiStatus: MQTT not connected, skipping', deviceId);
|
|
5289
|
+
return;
|
|
5290
|
+
}
|
|
5291
|
+
if (this.debugLevel > 0) {
|
|
5292
|
+
this.log.warn(
|
|
5293
|
+
"mqttRequestUiStatus: Requesting UI status for %s",
|
|
5294
|
+
deviceId,
|
|
5295
|
+
);
|
|
5296
|
+
}
|
|
5297
|
+
|
|
5298
|
+
const payload = JSON.stringify({
|
|
5299
|
+
version: '1.3.18',
|
|
5300
|
+
type: 'CPE.pullFromTV',
|
|
5301
|
+
source: this.mqttClient.options.clientId, // your mqttClientId
|
|
5302
|
+
messageTimeStamp: Date.now(),
|
|
5303
|
+
});
|
|
5304
|
+
|
|
5305
|
+
const topic = `${this.session.householdId}/${deviceId}`;
|
|
5306
|
+
|
|
5307
|
+
this.mqttPublishMessage(topic, payload, {
|
|
5308
|
+
qos: 1,
|
|
5309
|
+
retain: false,
|
|
5310
|
+
});
|
|
5311
|
+
|
|
5312
|
+
}
|
|
5313
|
+
|
|
5154
5314
|
// set the media state of the settopbox via mqtt
|
|
5155
5315
|
// media state is controlled by speedRate
|
|
5156
5316
|
// speedRate can be one of: -64 -30 -6 -2 0 2 6 30 64. 0=Paused, 1=Play, >1=FastForward, <0=Rewind
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"displayName": "Homebridge EOSSTB",
|
|
4
4
|
"description": "Add your set-top box to Homekit (for Telenet BE, Sunrise CH, UPC SK, Virgin Media GB & IE, Ziggo NL)",
|
|
5
5
|
"author": "Jochen Siegenthaler (https://github.com/jsiegenthaler/)",
|
|
6
|
-
"version": "2.4.0-beta.
|
|
6
|
+
"version": "2.4.0-beta.2",
|
|
7
7
|
"platformname": "eosstb",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"axios": "^1.16.0",
|