Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ Usage
-----

```javascript
const SamsungRemote = require('samsung-remote');
const remote = new SamsungRemote({
ip: '192.168.1.13' // required: IP address of your Samsung Smart TV
var SamsungRemote = require('samsung-remote');
var remote = new SamsungRemote({
ip: '192.168.1.13' // required: IP address of your Samsung Smart TV or '0.0.0.0' for auto discovery
});

remote.send('KEY_VOLUP', (err) => {
Expand Down
53 changes: 42 additions & 11 deletions lib/samsung-remote.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
const net = require("net");

const url = require("url");
const isIp = require("is-ip");
const ping = require("ping");

const ssdp = require("node-upnp-ssdp");
const EventEmitter = require("events").EventEmitter;

const { base64Encode } = require("./helper");

const chr = String.fromCharCode;

const SAMSUNG_TV_URN = "urn:samsung.com:device:RemoteControlReceiver:1";

class Remote {
constructor(config) {
this.config = Object.assign({
Expand All @@ -25,14 +30,36 @@ class Remote {
}, this.config.host);

this.validateConfig();

this.availability = new EventEmitter();

// Auto discover TV with UPnP
if (config.ip === "0.0.0.0") {
ssdp.on(`DeviceUnavailable:${SAMSUNG_TV_URN}`, this.upnpUpdate.bind(this, false));
ssdp.on(`DeviceAvailable:${SAMSUNG_TV_URN}`, this.upnpUpdate.bind(this, true));
ssdp.on(`DeviceUpdate:${SAMSUNG_TV_URN}`, this.upnpUpdate.bind(this, true));
const that = this;
const deviceFound = function(device) { if (device.st === SAMSUNG_TV_URN) that.upnpUpdate(true, device); };
ssdp.on("DeviceFound", deviceFound);
ssdp.mSearch(SAMSUNG_TV_URN);
} else {
this.isAvailable = true; // IP address supposed to be valid
}
}

upnpUpdate(available, device) {
// On UPnP update, retrieve IP and emit availability event
if (available) this.config.ip = url.parse(device.location).hostname;
if (this.isAvailable !== available) this.availability.emit("change", available);
this.isAvailable = available;
}

validateConfig() {
if (!this.config.ip) throw new Error("TV IP address is required");
if (!isIp(this.config.ip)) throw new Error("IP address format is wrong");
if (!isIp(this.config.host.ip)) throw new Error("Host IP format is incorrect");
}

_socketChunkOne() {
const ipEncoded = base64Encode(this.config.host.ip);
const macEncoded = base64Encode(this.config.host.mac);
Expand Down Expand Up @@ -77,6 +104,7 @@ class Remote {

send(command, done) {
if (!command) throw new Error("Missing command");
if (!this.isAvailable) return done("Device not available");

const socket = net.connect(this.config.port, this.config.ip);

Expand All @@ -89,7 +117,7 @@ class Remote {
socket.destroy();
done(false);
});

if (this.config.showDisconnectedLog) {
socket.on("close", () => {
console.log(`Samsung Remote Client: disconnected from ${this.config.ip}:${this.config.port}`);
Expand All @@ -104,7 +132,6 @@ class Remote {
} else {
errorMsg = `Samsung Remote Client: ${error.code}`;
}

done(errorMsg);
});

Expand All @@ -114,13 +141,17 @@ class Remote {
}

isAlive(done) {
ping.sys.probe(this.config.ip, (isAlive) => {
if (isAlive) {
done(0);
} else {
done(1);
}
});
if (!this.isAvailable) { // No valid IP address
done("Device not available");
} else {
ping.sys.probe(this.config.ip, (isAlive) => {
if (isAlive) {
done(0);
} else {
done(1);
}
});
}
}
}

Expand Down
Loading