Skip to content

Commit

Permalink
Merge pull request #11 from MrAsterisco/feature/ssl-improvements
Browse files Browse the repository at this point in the history
Implement SSL improvements
  • Loading branch information
MrAsterisco authored May 8, 2021
2 parents b808410 + 7ea81ff commit 5f52927
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 5 deletions.
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,34 @@ You can use the Homebridge UI to make changes to the plugin configuration. You m

Repeat the configuration for each TV you want to use, then restart Homebridge.

### SSL mode

Since version 1.1.0, this plugin also supports connecting to HiSense TVs that require different SSL modes than the default one. For example, some models have an unrecognized certificate and some other models need a specific encryption to be used.

To change how the plugin connects to your TV, use the `sslmode` config key. See below in the config example for more info.

*If your TV needs a specific encryption key and certificate, you can find the most common ones [here](https://github.com/MrAsterisco/hisensetv/tree/master/cert). Choose the appropriate one and download it onto the machine that executes Homebridge.*

### Config example

```json
{
"platform": "HiSenseTV",
"ifname": "ens33",
"devices": [
{
"id": "A unique identifier (such as your TV S/N)",
"name": "A name to display in the Home app",
"ipaddress": "Your TV IP address",
"macaddress": "Your TV MAC Address",
"sslmode": "default (most common)|disabled (no SSL)|custom (use cert and key below)",
"sslcertificate": "/absolute/path/to/the/ssl/certificate.cer",
"sslprivatekey": "/absolute/path/to/the/ssl/privateKey.pkcs8"
}
]
}
```

## Add the TV to Home

Once Homebridge is ready, look for a log line in the Homebridge log that looks like this one:
Expand All @@ -89,7 +117,7 @@ This plugin is **under active development**, but most of the features are ready
- The input list might not be fetched correctly if the TV is turned off while adding the accessory or after restarting Homebridge. To fix this, force close your Home app and open it again.
- Switching input to "TV" might not work properly. Home will not display any error, but the next TV state refresh will bring the input back to the previous one (which is also the one displayed on the TV).
- Making changes to the TV state (turning on/off, changing input) while the Home app is opened will not trigger a live update. *This is theoretically supported by the plugin, but it seems to not work properly.*. Just switching to another app and then going back to Home will trigger a refresh.
- Some HiSense TVs have a self-signed certificate which will make any connection fail. Support for this is **planned** and it will be released in a future update, as it is already supported by the base script. If you have some free time and you'd like to contribute, please look at the Contribution section below.
- ~~Some HiSense TVs have a self-signed certificate which will make any connection fail. Support for this is **planned** and it will be released in a future update, as it is already supported by the base script. If you have some free time and you'd like to contribute, please look at the Contribution section below.~~ This is now supported starting from version 1.1.0.

# Contributions
All contributions to expand the library are welcome. Fork the repo, make the changes you want, and open a Pull Request.
Expand All @@ -101,5 +129,7 @@ This plugin makes use of a modified version of the [hisensetv](https://github.co

The code structure and style is heavily inspired by the [homebridge-smartglass plugin](https://github.com/unknownskl/homebridge-smartglass), written by [UnknownSKL](https://github.com/unknownskl).

SSL Support has been implemented with a lot of help from [chinedu40](https://github.com/chinedu40) and [ryanshand](https://github.com/ryanshand).

# License
This plugin is distributed under the MIT license. See LICENSE for details.
13 changes: 13 additions & 0 deletions bin/hisensetv/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import json
import logging
import ssl

from . import HisenseTv


Expand Down Expand Up @@ -83,6 +84,12 @@ def main():
action="store_true",
help="Do not connect with SSL (required for some models).",
)
parser.add_argument("--certfile", help="Absolute path to the .cer file (required for some models). "
"Works only when --keyfile is also specified. "
"Will be ignored if --no-ssl is specified.")
parser.add_argument("--keyfile", help="Absolute path to the .pkcs8 file (required for some models). "
"Works only when --certfile is also specified. "
"Will be ignored if --no-ssl is specified.")
parser.add_argument(
"-v", "--verbose", action="count", default=0, help="Logging verbosity."
)
Expand All @@ -105,9 +112,15 @@ def main():
logger = logging.getLogger(__name__)

if args.no_ssl:
logger.info("No SSL context specified.")
ssl_context = None
elif args.certfile is not None and args.keyfile is not None:
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain(certfile=args.certfile, keyfile=args.keyfile)
logger.info("SSL context created with cert file (" + args.certfile + ") and private key (" + args.keyfile + ")")
else:
ssl_context = ssl._create_unverified_context()
logger.info("Unverified SSL context created.")

tv = HisenseTv(
args.hostname, enable_client_logger=args.verbose >= 2, ssl_context=ssl_context, network_interface=args.ifname
Expand Down
Binary file modified bin/hisensetv/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file modified bin/hisensetv/__pycache__/__main__.cpython-38.pyc
Binary file not shown.
26 changes: 26 additions & 0 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@
"required": true,
"placeholder": "00:XX:00:XX:00:XX",
"description": "In order to be able to turn on the TV when it's off, insert your TV's MAC Address. Make sure to use the MAC Address of the network interface (WiFi or Ethernet) you're using and also turn on 'Wake on LAN' and 'Wake on WiFi' in your TV settings."
},
"sslmode": {
"title": "SSL mode",
"type": "string",
"default": "default",
"oneOf": [
{ "title": "Default", "enum": ["default"] },
{ "title": "Disabled", "enum": ["disabled"] },
{ "title": "Custom", "enum": ["custom"] }
],
"required": true,
"description": "Depending on your TV model, you might need to change the SSL mode. The Default mode uses a normal SSL context and works with TVs that have a valid certificate onboard; if your TV does not have a valid certificate, you can try to disable it. Certain models need specific certificates and private keys: in this case, select Custom and specify the URL to them, making sure they are reachable from the machine that is running Homebridge."
},
"sslcertificate": {
"title": "SSL Certificate path",
"type": "string",
"required": false,
"placeholder": "/absolute/path/to/rcm_certchain_pem.cer",
"description": "The absolute path to the Certificate (.cer) file. This value is only read when the SSL mode is set to Custom."
},
"sslprivatekey": {
"title": "SSL Private key path",
"type": "string",
"required": false,
"placeholder": "/absolute/path/to/rcm_pem_privkey.pkcs8",
"description": "The absolute path to the Private key (.pkcs8) file. This value is only read when the SSL mode is set to Custom."
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"displayName": "HiSense TV",
"name": "homebridge-hisense-tv-remotenow",
"version": "1.0.0",
"version": "1.1.0",
"description": "Control RemoteNow-enabled HiSense TVs.",
"main": "dist/index.js",
"license": "MIT",
Expand Down
60 changes: 59 additions & 1 deletion src/platformAccessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,21 @@ export class HiSenseTVAccessory {
this.platform.log.debug('Set Characteristic On ->', value);

if (value === 1) {
if (this.deviceState.isConnected) {
// The device is already turned on.
callback(null);
return;
}
wol.wake(this.accessory.context.device.macaddress, (err, res) => {
this.platform.log.debug('Sent magic packet, response: ' + res + 'error: ' + err);
callback(err);
});
} else {
if (!this.deviceState.isConnected) {
// The device is already turned off.
callback(null);
return;
}
this.sendCommand(['--key', 'power'], (err) => {
this.platform.log.debug('Sent power off, error: ' + err);
callback(err);
Expand Down Expand Up @@ -397,7 +407,55 @@ export class HiSenseTVAccessory {
* @param callback A callback to call with an error and the output of the script.
*/
sendCommand(args: string[], callback: (err?: Error, output?: any) => void) {
PythonShell.run(path.resolve(__dirname, '../bin/hisensetv.py'), {args: args.concat([this.accessory.context.device.ipaddress, '--ifname', this.platform.config.ifname])}, callback);
const sslParameter = this.getSslArgument();

const pythonScript = path.resolve(__dirname, '../bin/hisensetv.py');

let pythonArgs = args.concat([
this.accessory.context.device.ipaddress,
'--ifname',
this.platform.config.ifname,
]);
if (sslParameter !== null) {
pythonArgs = pythonArgs.concat(sslParameter);
}

this.platform.log.debug('Run Python command: ' + pythonScript + ' ' + pythonArgs.join(' '));

PythonShell.run(pythonScript, {args: pythonArgs}, (err, output) => {
if (err === null) {
this.platform.log.debug('Received Python command response: ' + output);
} else {
this.platform.log.debug('Received Python command error: ' + err);
}

callback(err, output);
});
}

/**
* Compute the SSL argument to pass to the underlying script,
* based on the current device configuration.
*
* @returns The SSL parameter to pass or null.
*/
getSslArgument(): string[] {
let sslParameter: string[] = [];
switch (this.accessory.context.device.sslmode) {
case 'disabled':
sslParameter = ['--no-ssl'];
break;
case 'custom':
sslParameter = [
'--certfile',
this.accessory.context.device.sslcertificate.trim(),
'--keyfile',
this.accessory.context.device.sslprivatekey.trim(),
];
break;
}

return sslParameter;
}

// #endregion
Expand Down

0 comments on commit 5f52927

Please sign in to comment.