homebridge-sonybravia-platform
Version:
homebridge-sonybravia-platform
982 lines (737 loc) • 45.9 kB
JavaScript
var http = require("http"),
async = require("async");
var TV_Accessory = require('./accessories/TV.js'),
VOLUME_Accessory = require('./accessories/Volume.js'),
APP_Accessory = require('./accessories/Apps.js'),
CHANNEL_Accessory = require('./accessories/Channels.js'),
SOURCE_Accessory = require('./accessories/Inputs.js'),
EXTRAS_Accessory = require('./accessories/Extras.js'),
REMOTE_Accessory = require('./accessories/Remote.js');
var Accessory,
Service,
Characteristic;
module.exports = function(homebridge) {
Accessory = homebridge.platformAccessory;
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerPlatform("homebridge-sonybravia-platform", "SonyBravia", SonyBraviaPlatform);
}
function SonyBraviaPlatform(log, config, api) {
var platform = this;
//HB
this.api = api;
this.config = config;
this.log = log;
//Base
this.name = config["name"] || "Sony";
this.psk = encodeURIComponent(config["psk"]);
if (!this.psk) throw new Error("PSK is required!");
this.ipadress = config["ipadress"];
if (!this.ipadress) throw new Error("IP Adress is required!");
this.port = config["port"] || 80;
this.interval = (config['interval'] * 1000) || 10000;
if ((this.interval / 1000) < 10) {
this.log("Critical interval value! Setting interval to 10 seconds");
this.interval = 10000;
}
this.inputsEnabled = config["inputsEnabled"] === true;
this.offState = config["offState"] || "HOME";
// Modes: HOME, CHANNEL, OFF
//Volume
this.volumeEnabled = config["volumeEnabled"] === true;
this.maxVolume = config["maxVolume"] || 35;
//Extra Inputs
this.extraInputs = config["extraInputs"] || false;
//Apps
this.appsEnabled = config["appsEnabled"] === true;
this.homeapp = config["homeapp"];
//Channels
this.channelsEnabled = config["channelsEnabled"] || false;
this.channelSource = config["channelSource"] || "tv:dvbt";
this.favChannel = config["favChannel"];
//CECs
this.detectCEC = config["detectCEC"] === true;
this.cecDevices = config["cecDevices"];
if (!this.inputsEnabled) {
this.detectCEC = false
}
//Remote Control
this.remoteControl = config["remoteControl"] || false;
this.controlMode = config["controlMode"] || "BASIC";
//Modes: BASIC, ADVANCED
//COUNT
this.counthdmi = 0;
this.countcec = 0;
this.countapps = 0;
this.countextras = 0;
this.countchannels = 0;
//STORAGE
this.storage = require('node-persist');
this.storage.initSync({
dir: this.api.user.persistPath()
});
//PROMISE
this.getContent = function(setPath, setMethod, setParams, setVersion) {
return new Promise((resolve, reject) => {
var options = {
host: platform.ipadress,
port: platform.port,
family: 4,
path: setPath,
method: 'POST',
headers: {
'X-Auth-PSK': platform.psk
}
};
var post_data = {
"method": setMethod,
"params": [setParams],
"id": 1,
"version": setVersion
};
var req = http.request(options, function(res) {
if (res.statusCode < 200 || res.statusCode > 299) {
reject(new Error('Failed to load data, status code: ' + res.statusCode));
}
const body = []
res.on('data', (chunk) => body.push(chunk));
res.on('end', () => resolve(body.join('')));
});
req.on('error', (err) => reject(err))
req.write(JSON.stringify(post_data));
req.end();
})
};
}
SonyBraviaPlatform.prototype = {
accessories: function(callback) {
var self = this;
var accessoriesArray = []
async.waterfall([
function(next) {
var tvConfig = {
uri: self.uri,
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
interval: self.interval,
homeapp: self.homeapp,
port: self.port
}
var tvAccessory = new TV_Accessory(self.log, tvConfig, self.api)
accessoriesArray.push(tvAccessory);
next();
},
function(next) {
if (self.remoteControl) {
self.log("Getting remote control..");
var remoteConfig = {
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
port: self.port,
controlMode: self.controlMode
}
var remoteAccessory = new REMOTE_Accessory(self.log, remoteConfig, self.api)
accessoriesArray.push(remoteAccessory);
}
next();
},
function(next) {
function fetchAppService(next) {
if (self.appsEnabled) {
self.log("Getting apps...");
if (self.storage.getItem("Sony_Apps")) {
self.log("Apps found in storage.");
self.log("Note: If you want to refresh the app list, please use the 'Applist' button within the EVE app or delete 'Sony_Apps' file in your persist folder!");
var response = self.storage.getItem("Sony_Apps");
var result = response.result[0];
self.countapps = response.result[0].length;
if (!self.homeapp && !self.storage.getItem("Sony_HomeApp")) {
self.log("No home app found in config and storage. Setting home app to " + result[0].title + ". The Home App can also be changed within the EVE app!");
self.homeapp = result[0].uri;
self.favappname = result[0].title;
} else if (self.storage.getItem("Sony_HomeApp")) {
self.homeapp = self.storage.getItem("Sony_HomeApp");
for (var i = 0; i < result.length; i++) {
if (self.storage.getItem("Sony_HomeApp") == result[i].uri) {
self.favappname = result[i].title;
}
}
self.log("Home App found in storage. Home App is: " + self.favappname);
} else {
for (var i = 0; i < result.length; i++) {
if (self.homeapp == result[i].uri) {
self.favappname = result[i].title;
}
}
self.log("Home App found in config. Home App is: " + self.favappname);
}
var appListConfig = {
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
maxApps: response.result[0].length,
port: self.port,
interval: self.interval,
homeapp: self.homeapp,
favappname: self.favappname,
offState: self.offState,
favChannel: self.favChannel,
channelSource: self.channelSource
}
var appListAccessory = new APP_Accessory(self.log, appListConfig, self.api, result)
accessoriesArray.push(appListAccessory);
next();
} else {
self.log("No apps in storage. Start requesting.")
self.getContent("/sony/appControl", "getApplicationList", "1.0", "1.0")
.then((data) => {
var response = JSON.parse(data);
if ("error" in response) {
self.log("An error occured by getting application list!");
if (response.error[0] == 7 || response.error[0] == 40005) {
self.log("Please turn on the TV! Trying again...");
} else if (response.error[0] == 3 || response.error[0] == 5) {
self.log("Illegal argument!");
} else {
self.log("ERROR: " + JSON.stringify(response));
}
setTimeout(function() {
fetchAppService(next);
}, 10000)
} else {
var result = response.result[0];
self.countapps = response.result[0].length;
self.log("Caching apps.")
self.storage.setItem("Sony_Apps", response)
if (!self.homeapp && !self.storage.getItem("Sony_HomeApp")) {
self.log("No home app found in config and storage. Setting home app to " + result[0].title + ". The Home App can also be changed within the EVE app!");
self.homeapp = result[0].uri;
self.favappname = result[0].title;
} else if (self.storage.getItem("Sony_HomeApp")) {
self.homeapp = self.storage.getItem("Sony_HomeApp");
for (var i = 0; i < result.length; i++) {
if (self.storage.getItem("Sony_HomeApp") == result[i].uri) {
self.favappname = result[i].title;
}
}
self.log("Home App found in storage. Home App is: " + self.favappname);
} else {
for (var i = 0; i < result.length; i++) {
if (self.homeapp == result[i].uri) {
self.favappname = result[i].title;
}
}
self.log("Home App found in config. Home App is: " + self.favappname);
}
var appListConfig = {
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
maxApps: response.result[0].length,
port: self.port,
interval: self.interval,
homeapp: self.homeapp,
favappname: self.favappname,
offState: self.offState,
favChannel: self.favChannel,
channelSource: self.channelSource
}
var appListAccessory = new APP_Accessory(self.log, appListConfig, self.api, result)
accessoriesArray.push(appListAccessory);
next();
}
})
.catch((err) => {
self.log("Apps: " + err + " - Trying again");
setTimeout(function() {
fetchAppService(next);
}, 10000)
});
}
} else {
self.log("Apps not enabled. Skipping discovery.")
next();
}
}
fetchAppService(next)
},
function(next) {
function fetchChannels(next) {
if (self.channelsEnabled) {
self.log("Getting channels...");
if (!self.favChannel && !self.storage.getItem("Sony_FavChannel")) {
self.getContent("/sony/avContent", "getContentList", {
"source": self.channelSource,
"stIdx": 0
}, "1.2")
.then((data) => {
var response = JSON.parse(data);
if ("error" in response) {
self.log("An error occured by setting favourite channel!");
if (response.error[0] == 7 || response.error[0] == 40005) {
self.log("Please turn on the TV! Trying again...");
} else if (response.error[0] == 3 || response.error[0] == 5) {
self.log("Illegal argument!");
} else {
self.log("ERROR: " + JSON.stringify(response));
}
setTimeout(function() {
fetchChannels(next);
}, 10000)
} else {
var name = response.result[0];
for (var i = 0; i <= name.length; i++) {
switch (i) {
case 0:
self.favChannel = name[0].uri;
self.favchannelname = name[0].title;
self.channelname = name[0].title;
break;
}
}
self.log("No favourite channel found in config and storage. Setting channel to " + self.favchannelname + ". The channel can also be changed within the EVE app!");
}
})
.catch((err) => {
self.log("Favourite Channel: " + err + " - Trying again");
setTimeout(function() {
fetchChannels(next);
}, 10000)
});
} else if (self.storage.getItem("Sony_FavChannel")) {
self.favChannel = self.storage.getItem("Sony_FavChannel");
self.favchannelname = self.favChannel.split("Name=").pop();
self.channelname = "";
self.log("Favourite channel found in storage. Channel is: " + self.favChannel.split("Name=").pop());
} else {
self.favchannelname = self.favChannel.split("Name=").pop();
self.channelname = "";
self.log("Favourite channel found in config. Channel is: " + self.favchannelname);
}
self.getContent("/sony/avContent", "getContentCount", {
"source": self.channelSource
}, "1.0")
.then((data) => {
var response = JSON.parse(data);
if ("error" in response) {
self.log("An error occured by getting channel list!");
if (response.error[0] == 7 || response.error[0] == 40005) {
self.log("Please turn on the TV! Trying again...");
} else if (response.error[0] == 3 || response.error[0] == 5) {
self.log("Illegal argument!");
} else {
self.log("ERROR: " + JSON.stringify(response));
}
setTimeout(function() {
fetchChannels(next);
}, 10000)
} else {
var channelListConfig = {
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
maxChannels: response.result[0].count - 1,
port: self.port,
interval: self.interval,
channelSource: self.channelSource,
homeapp: self.homeapp,
favChannel: self.favChannel,
favchannelname: self.favchannelname,
channelname: self.channelname,
offState: self.offState
}
self.countchannels = channelListConfig.maxChannels;
var channelListAccessory = new CHANNEL_Accessory(self.log, channelListConfig, self.api)
accessoriesArray.push(channelListAccessory);
next();
}
})
.catch((err) => {
self.log("Channel: " + err + " - Trying again");
setTimeout(function() {
fetchChannels(next);
}, 10000)
});
} else {
self.log("Channels not enabled. Skipping discovery.")
next();
}
}
fetchChannels(next)
},
function(next) {
function fetchSources(next) {
if (self.inputsEnabled) {
self.log("Getting inputs...")
if (self.storage.getItem("Sony_Inputs")) {
self.log("HDMI inputs found in storage.");
var response = self.storage.getItem("Sony_Inputs");
var result = response.result[0];
self.counthdmi = 0;
self.countcec = 0;
var hdmiArray = []
for (var i = 0; i < result.length; i++) {
var toConfig = {
psk: self.psk,
ipadress: self.ipadress,
interval: self.interval,
homeapp: self.homeapp,
port: self.port,
offState: self.offState,
favChannel: self.favChannel,
channelSource: self.channelSource
}
if (result[i].icon == "meta:hdmi") {
self.counthdmi += 1;
toConfig["name"] = self.name + " " + result[i].title;
toConfig["title"] = result[i].title;
toConfig["uri"] = result[i].uri;
toConfig["meta"] = result[i].icon;
hdmiArray.push(toConfig);
}
}
next(null, hdmiArray)
} else {
self.log("No HDMI inputs in storage. Start requesting.")
self.getContent("/sony/avContent", "getCurrentExternalInputsStatus", "1.0", "1.0")
.then((data) => {
var response = JSON.parse(data);
if ("error" in response) {
self.log("An error occured by getting inputs!");
if (response.error[0] == 7 || response.error[0] == 40005) {
self.log("Please turn on the TV! Trying again...");
} else if (response.error[0] == 3 || response.error[0] == 5) {
self.log("Illegal argument!");
} else {
self.log("ERROR: " + JSON.stringify(response));
}
setTimeout(function() {
fetchSources(next);
}, 10000)
} else {
var result = response.result[0];
self.log("Caching HDMI inputs.")
self.storage.setItem("Sony_Inputs", response)
self.counthdmi = 0;
self.countcec = 0;
var hdmiArray = []
for (var i = 0; i < result.length; i++) {
var toConfig = {
psk: self.psk,
ipadress: self.ipadress,
interval: self.interval,
homeapp: self.homeapp,
port: self.port,
offState: self.offState,
favChannel: self.favChannel,
channelSource: self.channelSource
}
if (result[i].icon == "meta:hdmi") {
self.counthdmi += 1;
toConfig["name"] = self.name + " " + result[i].title;
toConfig["title"] = result[i].title;
toConfig["uri"] = result[i].uri;
toConfig["meta"] = result[i].icon;
hdmiArray.push(toConfig);
}
}
next(null, hdmiArray)
}
})
.catch((err) => {
self.log("Inputs: " + err + " - Trying again");
setTimeout(function() {
fetchSources(next);
}, 10000)
});
}
} else {
var hdmiArray;
self.log("Inputs not enabled. Skipping discovery.")
next(null, hdmiArray);
}
}
fetchSources(next)
},
function(hdmiArray, next) {
function fetchCEC(next) {
if (self.detectCEC) {
self.log("CEC detection is enabled.")
if (self.storage.getItem("Sony_CEC")) {
self.log("CEC devices found in storage.");
var response = self.storage.getItem("Sony_CEC");
var result = response.result[0];
for (var i = 0; i < result.length; i++) {
if (result[i].icon == "meta:playbackdevice") {
self.countcec += 1;
var uri = result[i].uri;
if (self.cecDevices) {
for (var l = 0; l < self.cecDevices.length; l++) {
if (self.cecDevices[l].title == result[i].title) {
var port = self.cecDevices[l].hdmiport;
var customuri = "extInput:hdmi?port=" + port;
}
}
} else {
if (uri.match("logicalAddr")) {
var port = uri.split("port=")[1].split("&logicalAddr=")[0];
} else {
var port = uri.split("port=")[1];
}
}
var newuri = "extInput:hdmi?port=" + port;
for (var j = 0; j < hdmiArray.length; j++) {
if (hdmiArray[j].uri == newuri) {
hdmiArray[j].name = self.name + " " + result[i].title;
hdmiArray[j].title = result[i].title;
hdmiArray[j].uri = result[i].uri;
hdmiArray[j].meta = result[i].icon;
if (self.cecDevices) {
hdmiArray[j]["customuri"] = customuri;
}
}
}
}
}
next(null, hdmiArray)
} else {
self.log("No CEC devices in storage. Start requesting.")
self.getContent("/sony/system", "getPowerStatus", "1.0", "1.0")
.then((tvdata) => {
var tvresponse = JSON.parse(tvdata);
if ("error" in tvresponse) {
self.log("CEC: An error occured by getting TV state");
if (response.error[0] == 7 || response.error[0] == 40005) {
self.log("Please turn on the TV! Trying again...");
} else if (response.error[0] == 3 || response.error[0] == 5) {
self.log("Illegal argument!");
} else {
self.log("ERROR: " + JSON.stringify(tvresponse));
}
} else {
var status = tvresponse.result[0].status;
if (status == "active") {
self.log("TV is ON. Getting CEC devices...");
self.getContent("/sony/avContent", "getCurrentExternalInputsStatus", "1.0", "1.0")
.then((data) => {
var response = JSON.parse(data);
if ("error" in response) {
self.log("An error occured by getting cec inputs!");
if (response.error[0] == 7 || response.error[0] == 40005) {
self.log("Please turn on the TV! Trying again...");
} else if (response.error[0] == 3 || response.error[0] == 5) {
self.log("Illegal argument!");
} else {
self.log("ERROR: " + JSON.stringify(response));
}
setTimeout(function() {
fetchCEC(next);
}, 10000)
} else {
var result = response.result[0];
self.log("Caching CEC devices.")
self.storage.setItem("Sony_CEC", response)
for (var i = 0; i < result.length; i++) {
if (result[i].icon == "meta:playbackdevice") {
self.countcec += 1;
var uri = result[i].uri;
if (uri.match("logicalAddr")) {
var port = uri.split("port=")[1].split("&logicalAddr=")[0];
} else {
var port = uri.split("port=")[1];
}
var newuri = "extInput:hdmi?port=" + port;
for (var j = 0; j < hdmiArray.length; j++) {
if (hdmiArray[j].uri == newuri) {
hdmiArray[j].name = self.name + " " + result[i].title;
hdmiArray[j].title = result[i].title;
hdmiArray[j].uri = result[i].uri;
hdmiArray[j].meta = result[i].icon;
}
}
}
}
next(null, hdmiArray)
}
})
.catch((err) => {
self.log("CEC: " + err + " - Trying again");
setTimeout(function() {
fetchCEC(next);
}, 10000)
});
} else if (status == "standby") {
self.log("TV seems to be off. TV needs to be turned on for getting CEC devices! Trying again...");
setTimeout(function() {
fetchCEC(next);
}, 10000)
} else {
self.log("CEC: An error occured by getting TV state. Error: " + JSON.stringify(tvresponse));
setTimeout(function() {
fetchCEC(next);
}, 10000)
}
}
})
.catch((tverr) => {
self.log("CEC TV state: " + tverr + " - Trying again");
setTimeout(function() {
fetchCEC(next);
}, 10000)
});
}
} else {
self.log("CEC detection not enabled. Skipping discovery.")
next(null, hdmiArray);
}
}
fetchCEC(next)
},
function(hdmiArray, next) {
async.forEachOf(hdmiArray, function(zone, key, step) {
function pushMyAccessories(step) {
var hdmiAccessory = new SOURCE_Accessory(self.log, zone, self.api)
accessoriesArray.push(hdmiAccessory);
step()
}
pushMyAccessories(step)
}, function(err) {
if (err) next(err)
else next()
})
},
function(next) {
function fetchExtras(next) {
if (self.extraInputs) {
self.log("Getting extra inputs...")
if (self.storage.getItem("Sony_Inputs")) {
self.log("Extra inputs found in storage.");
var response = self.storage.getItem("Sony_Inputs");
var result = response.result[0];
var extraArray = []
for (var i = 0; i < result.length; i++) {
if (result[i].icon == "meta:scart" || result[i].icon == "meta:composite" || result[i].icon == "meta:wifidisplay") {
self.countextras += 1;
var extraConfig = {
uri: result[i].uri,
extraname: result[i].title,
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
interval: self.interval,
homeapp: self.homeapp,
port: self.port,
offState: self.offState,
favChannel: self.favChannel,
channelSource: self.channelSource
}
extraArray.push(extraConfig);
}
}
next(null, extraArray)
} else {
self.log("No extra inputs in storage. Start requesting.")
self.getContent("/sony/avContent", "getCurrentExternalInputsStatus", "1.0", "1.0")
.then((data) => {
var response = JSON.parse(data);
if ("error" in response) {
self.log("An error occured by getting extra inputs!");
if (response.error[0] == 7 || response.error[0] == 40005) {
self.log("Please turn on the TV! Trying again...");
} else if (response.error[0] == 3 || response.error[0] == 5) {
self.log("Illegal argument!");
} else {
self.log("ERROR: " + JSON.stringify(response));
}
setTimeout(function() {
fetchExtras(next);
}, 10000)
} else {
var result = response.result[0];
var extraArray = []
for (var i = 0; i < result.length; i++) {
if (result[i].icon == "meta:scart" || result[i].icon == "meta:composite" || result[i].icon == "meta:wifidisplay") {
self.countextras += 1;
var extraConfig = {
uri: result[i].uri,
extraname: result[i].title,
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
interval: self.interval,
homeapp: self.homeapp,
port: self.port,
offState: self.offState,
favChannel: self.favChannel,
channelSource: self.channelSource
}
extraArray.push(extraConfig);
}
}
next(null, extraArray)
}
})
.catch((err) => {
self.log("Extra Inputs: " + err + " - Trying again");
setTimeout(function() {
fetchExtras(next);
}, 10000)
});
}
} else {
self.log("Extra Inputs not enabled. Skipping discovery.")
var extraArray;
next(null, extraArray)
}
}
fetchExtras(next)
},
function(extraArray, next) {
if (self.extraInputs) {
async.forEachOf(extraArray, function(zone, key, step) {
function pushMyExtraAccessories(step) {
var extraAccessory = new EXTRAS_Accessory(self.log, zone, self.api)
accessoriesArray.push(extraAccessory);
step()
}
pushMyExtraAccessories(step)
}, function(err) {
if (err) next(err)
else next()
})
} else {
next();
}
},
function(next) {
if (self.volumeEnabled) {
var volConfig = {
uri: self.uri,
name: self.name,
psk: self.psk,
ipadress: self.ipadress,
interval: self.interval,
maxVolume: self.maxVolume,
port: self.port
}
var volAccessory = new VOLUME_Accessory(self.log, volConfig, self.api)
accessoriesArray.push(volAccessory);
} else {
self.log("Volume not enabled. Skipping discovery.")
}
next();
}
],
function(err, result) {
if (err) {
callback(err)
} else {
self.log("Found " + self.counthdmi + " HDMI input(s) with " + self.countcec + " CEC device(s), " + self.countextras + " extra input(s), " + self.countapps + " app(s) and " + self.countchannels + " channel(s)");
callback(accessoriesArray);
}
}
)
}
}