Commit 8ef39e09 authored by Samuel Elliott's avatar Samuel Elliott

Use the SELECTSOURCE command instead of iterating over inputs

parent 2956417c
......@@ -23,8 +23,7 @@ npm install -g git+https://gitlab.fancy.org.uk/samuel/homebridge-vestel-network-
- Volume
- Television service (requires configuration)
- Setting current input/app/channel
Switching inputs works by iterating over a configured list of inputs.
- Sending remote keys
[Apparently Vestel TVs support a lot more commands.](https://forum.digitalfernsehen.de/threads/medion-md-30465-42-led-backlight-tv-life%C2%AE-x17006.276321/page-64#post-7474289) I haven't tested any of these.
......@@ -100,37 +99,56 @@ I haven't updated to the iOS 12.2 beta so I haven't tested this properly.
"ip_address": "192.168.3.231",
"expose_television_service": true,
"tv_sources": {
"TV": "TUNER",
"EXT 1": "COMPOSITE_VIDEO",
"SIDE AV": "COMPOSITE_VIDEO",
"DVD": ["APPLICATION", "PLAYBACK"],
"HDMI 1": "HDMI",
"HDMI 2": "HDMI",
"YPbPr": "COMPONENT_VIDEO",
"VGA": "COMPONENT_VIDEO"
"0": ["TV", "TUNER"],
"1": ["EXT 1", "COMPOSITE_VIDEO"],
"5": ["SIDE AV", "COMPOSITE_VIDEO"],
"6": ["DVD", "APPLICATION", "PLAYBACK"],
"7": ["HDMI 1", "HDMI"],
"8": ["HDMI 2", "HDMI"],
"11": ["YPbPr", "COMPONENT_VIDEO"],
"12": ["VGA", "COMPONENT_VIDEO"]
},
"tv_input": 0,
"apple_tv_input": 4,
"default_input": 0
"default_input": 0,
"apple_tv_input": 4
}
```
The `tv_sources` object defines how the sources should be switched - it must be in exactly the same order as on your TV to work properly. The key can be anything you can use to reference the source ("HDMI 1" is preferred over "Apple TV" for example as you can set your own name in the Home app). The value is either a string (the name of an [Input Source Type](https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes-Television.js)), an array (the first being the name of an [Input Source Type](https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes-Television.js) and the second being the name of an [Input Device Type](https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes-Television.js)) or an object:
The `tv_sources` object defines how the sources should be switched. The key is the internal identifier of the source. The value is either an array (the first item can be anything you can use to reference the source ("HDMI 1" is preferred over "Apple TV" for example as you can set your own name in the Home app), the second item being the name of an [Input Source Type](https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes-Television.js) and the third being the name of an [Input Device Type](https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes-Television.js)) or an object:
```json
...,
"DVD": {
"6": {
"name": "DVD",
"input_source_type": "APPLICATION",
"input_device_type": "PLAYBACK"
},
...
```
The `tv_input` property should be set to the identifier of the TV input (default is 0, or the first source). This is used to set the source to a known position (the TV source) before iterating over sources. The TV source must be enabled, even if you don't have an aerial connected to the TV.
You can find the internal identifier of the source by trying different identifiers. These are incremental and start from `0` but include sources for other hardware models, so some do nothing.
```
npm install -g git+https://gitlab.fancy.org.uk/samuel/vestel-network-remote
vestel-followme-tv 192.168.3.246 set-source 0
```
You can also set the picture zoom after changing the source, as this is sometimes set to `auto`:
```json
...,
"7": {
"name": "HDMI 1",
"input_source_type": "HDMI",
"picture_zoom": "full"
},
...
```
[Values for `picture_zoom` can be found here.](https://gitlab.fancy.org.uk/samuel/vestel-network-remote/blob/v0.3.1/src/followtv.js#L243)
The `apple_tv_input` property should be set to the identifier of the input the Apple TV used to turn the TV on is connected to. This is used to display the correct source after turning the TV on with an Apple TV (which should set the source to the Apple TV). The default is the first HDMI source.
The `apple_tv_input` property should be set to the index of the input the Apple TV used to turn the TV on is connected to. This is used to display the correct source after turning the TV on with an Apple TV (which should set the source to the Apple TV). The default is the first HDMI source.
The `default_input` property should be set to the identifier of the input to display when the plugin doesn't know what source is currently active. The default is 0 (the first source).
The `default_input` property should be set to the index of the input to display when the plugin doesn't know what source is currently active. The default is 0 (the first source).
Enabling the Television service will disable the Switch and Speaker service. Set their flags explicitly to enable them.
......@@ -152,18 +170,19 @@ You can add TV apps by adding them to the `tv_apps` object.
```json
...,
"tv_apps": {
"Internet Portal": {
"tv_apps": [
{
"name": "Internet Portal",
"input_source_type": "HOME_SCREEN",
"remote_key": "INTERNET"
},
"Browser": {
{
"name": "Browser",
"input_device_type": "OTHER",
"app_url": "browser://open"
}
},
],
...
}
```
### TV channels
......@@ -178,5 +197,4 @@ You can add TV channels by adding them to the `tv_channels` object. Channels nam
3
],
...
}
```
......@@ -1406,7 +1406,7 @@
"dev": true
},
"vestel-network-remote": {
"version": "git+https://gitlab.fancy.org.uk/samuel/vestel-network-remote#da314e41faee8f635702459adadde890f168e843",
"version": "git+https://gitlab.fancy.org.uk/samuel/vestel-network-remote#9a13bb534fb1f7b8bd3a2b0c8b33b979a242de4e",
"from": "git+https://gitlab.fancy.org.uk/samuel/vestel-network-remote",
"requires": {
"xml2js": "^0.4.19"
......
......@@ -114,8 +114,8 @@ class TVAccessory {
}
// Connect to the TV
this.remote = new VestelNetworkRemote(this.ip_address);
this.followtv = new VestelFollowTV(this.ip_address);
this.remote = new VestelNetworkRemote(this.ip_address, config.remote_port);
this.followtv = new VestelFollowTV(this.ip_address, config.status_port);
this.handleMediaRendererEvent = this.handleMediaRendererEvent.bind(this);
......@@ -127,10 +127,14 @@ class TVAccessory {
}
// Add Input Source services
this.tv_sources = Object.entries(config.tv_sources || {});
this.tv_sources = [];
for (let [id, type] of this.tv_sources) {
this.addInputSourceService(id, type);
for (let [source_id, data] of Object.entries(config.tv_sources || {})) {
this.tv_sources.push(Object.assign(data instanceof Array ? data = {
name: data[0], input_source_type: data[1], input_device_type: data[2]
} : data, {source_id}));
this.addInputSourceService(this.tv_sources.length - 1, data);
}
for (let [id, app_config] of Object.entries(config.tv_apps || {})) {
......@@ -143,6 +147,7 @@ class TVAccessory {
const name = (this.getItemSync('CachedChannelNames') || {})[channel_number] || 'Channel ' + channel_number;
tv_channel_input_source_services.push(this.addAppInputSourceService(name, {
name,
input_source_type: this.constructor.hap.Characteristic.InputSourceType.TUNER,
channel_number,
}));
......@@ -171,7 +176,6 @@ class TVAccessory {
});
}
this.tv_input = config.tv_input || 0;
this.apple_tv_input = config.apple_tv_input || this.input_source_services.findIndex(service => service.getCharacteristic(this.constructor.hap.Characteristic.InputSourceType) === this.constructor.hap.Characteristic.InputSourceType.HDMI) || 1;
this.default_input = config.default_input || 0;
......@@ -290,8 +294,7 @@ class TVAccessory {
await this.apple_tv.sendKeyCommand(AppleTV.AppleTV.Key.Menu);
this.last_input_switched = this.apple_tv_input;
this.last_input_switched_time = Date.now();
this.tv_service.getCharacteristic(this.constructor.hap.Characteristic.ActiveIdentifier).updateValue(this.apple_tv_input);
if (this.expose_television_service) this.tv_service.getCharacteristic(this.constructor.hap.Characteristic.ActiveIdentifier).updateValue(this.apple_tv_input);
} catch (err) {
console.error(err);
throw err;
......@@ -443,80 +446,32 @@ class TVAccessory {
this.log('Setting input identifier for ' + this.name + ' to ' + identifier + ' (' + await this.getInputSourceName(identifier) + ')');
const source = this.tv_sources[identifier];
const service = this.input_source_services[identifier];
try {
if (this._setting_input_identifier) await new Promise(resolve => (this._setting_input_identifier_callbacks || (this._setting_input_identifier_callbacks = [])).push(resolve));
this._setting_input_identifier = true;
// Make sure nothing is open
await this.remote.send('EXIT');
await this.remote.send('EXIT');
await new Promise(r => setTimeout(r, 1000));
await this.remote.send('EXIT');
if (!this.tv_sources[identifier]) {
if (service.config.channel_number) {
// Switch to the TV input and show this channel
await this.followtv.setChannel(service.config.channel_number);
} else if (service.config.remote_key) {
await this.remote.send(service.config.remote_key);
} else if (service.config.app_url) {
await this.followtv.setURL(service.config.app_url);
} else {
this.log('Unsupported source');
throw new Error('Unsupported source');
}
this.last_input_switched = identifier;
this.last_input_switched_time = undefined;
return;
}
let current_input = this.last_input_switched;
if (!current_input || !this.last_input_switched_time || (Date.now() > this.last_input_switched_time + 15000)) {
// Input was last switched more than 15 seconds ago
// Loop from the default source in case the source was switched from somewhere else
this.log('Interating over source list from the start');
current_input = this.tv_input;
await this.remote.send('CHANNEL_LIST');
await new Promise(r => setTimeout(r, 1000));
await this.remote.send('EXIT');
await new Promise(r => setTimeout(r, 500));
if (source) {
this.followtv.setSource(source.source_id);
this.last_input_switched = identifier;
} else {
if (service.config.channel_number) {
// Switch to the TV input and show this channel
await this.followtv.setChannel(service.config.channel_number);
} else if (service.config.remote_key) {
await this.remote.send(service.config.remote_key);
} else if (service.config.app_url) {
await this.followtv.setURL(service.config.app_url);
} else {
this.log('Unsupported source');
throw new Error('Unsupported source');
}
await this.remote.send('SOURCE');
await new Promise(r => setTimeout(r, 200));
let i = 0;
while (current_input !== identifier) {
if (i++ > this.tv_sources.length + 5) throw new Error('???');
current_input++;
await this.remote.send('SOURCE');
await new Promise(r => setTimeout(r, 50));
if (current_input > this.tv_sources.length) current_input = 0;
this.log('Now at input ' + current_input);
this.last_input_switched = current_input;
}
this.last_input_switched_time = Date.now();
await new Promise(r => setTimeout(r, 200));
await this.remote.send('SELECT');
} catch (err) {
this.last_input_switched_time = undefined;
this.last_input_switched = identifier;
}
throw err;
} finally {
this._setting_input_identifier = false;
if (this._setting_input_identifier_callbacks && this._setting_input_identifier_callbacks[0]) this._setting_input_identifier_callbacks[0]();
if (service.config.picture_zoom) {
await new Promise(r => setTimeout(r, 1000));
await this.followtv.setPictureZoom(service.config.picture_zoom);
}
}
......@@ -550,7 +505,7 @@ class TVAccessory {
}
addInputSourceService(id, config, type, subtype) {
const input_service = new this.constructor.hap.Service.InputSource(id, id);
const input_service = new this.constructor.hap.Service.InputSource(config.name, config.name || id);
if (!type) {
if (typeof config === 'object') type = config.input_source_type;
......@@ -591,6 +546,7 @@ class TVAccessory {
});
this.input_source_services.push(input_service);
this.tv_service.addLinkedService(input_service);
return input_service;
}
......
......@@ -58,6 +58,10 @@ for (let service of services) {
accessory.addService(service);
}
if (accessory_instance.identify) {
accessory.on('identify', (paired, callback) => accessory_instance.identify(callback));
}
storage.initSync({
dir: path.resolve(__dirname, '..', 'persist'),
stringify: data => JSON.stringify(data, undefined, 4),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment