First Commit
This commit is contained in:
commit
cddc7096a5
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
# Samsung TV Web Browser Controller
|
||||||
|
|
||||||
|
Currently this project comes with an API to control your Samsung Smart TV which has the means to generate authenication tokens and the following functions:
|
||||||
|
|
||||||
|
CURRENT COMMAND SHORTCUTS:
|
||||||
|
|
||||||
|
HOME
|
||||||
|
|
||||||
|
UP
|
||||||
|
|
||||||
|
LEFT
|
||||||
|
|
||||||
|
DOWN
|
||||||
|
|
||||||
|
RIGHT
|
||||||
|
|
||||||
|
Back
|
||||||
|
|
||||||
|
Volume Up
|
||||||
|
|
||||||
|
Volume Down
|
||||||
|
|
||||||
|
MUTE
|
||||||
|
|
||||||
|
Enter
|
||||||
|
|
||||||
|
Picture Mode
|
||||||
|
|
||||||
|
New Updates will also provide a GUI for the web app, currently its only using keyboard events.
|
201
client.html
Normal file
201
client.html
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<!--Hello! You have found my TV Controller. The API provided in this source is behind a firewall and will be of no use to you. Move along!-->
|
||||||
|
<head></head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||||
|
<title>Scott Household TV Controller</title>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
|
<center>
|
||||||
|
<div id="view"></div>
|
||||||
|
<center>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #101010;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
let rootURL = "https://grwh.work:3031"
|
||||||
|
let pass = prompt("Enter the password!", "");
|
||||||
|
let text;
|
||||||
|
fetch(rootURL + "/checkPass?pass=" + pass)
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == "1") {
|
||||||
|
if (pass == null || pass == "") {
|
||||||
|
document.getElementById("view").innerHTML = "<meta http-equiv=\"refresh\" content=0;>"
|
||||||
|
} else {
|
||||||
|
document.getElementById("view").innerHTML = "CURRENT COMMAND SHORTCUTS: <BR>HOME: H<BR> UP: W<BR> LEFT: A<BR> DOWN: S <BR> RIGHT: D<BR> Back: X<BR> Volume Up: P<BR> Volume Down: O<BR>MUTE: M<BR> Enter: Enter<BR>Picture Mode: P<BR><BR>Not Working? Issue a new Token using: r";
|
||||||
|
|
||||||
|
|
||||||
|
function addEvent(element, eventName, callback) {
|
||||||
|
if (element.addEventListener) {
|
||||||
|
element.addEventListener(eventName, callback, false);
|
||||||
|
} else if (element.attachEvent) {
|
||||||
|
element.attachEvent("on" + eventName, callback);
|
||||||
|
} else {
|
||||||
|
element["on" + eventName] = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addEvent(document, "keypress", function (e) {
|
||||||
|
console.log(e)
|
||||||
|
|
||||||
|
e = e || window.event;
|
||||||
|
// Handle
|
||||||
|
if (e.key == "w") {
|
||||||
|
fetch(rootURL + "/up")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: UP - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle
|
||||||
|
if (e.key == "a") {
|
||||||
|
fetch(rootURL + "/left")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: LEFT - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Handle
|
||||||
|
if (e.key == "s") {
|
||||||
|
fetch(rootURL + "/down")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: DOWN - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Handle
|
||||||
|
if (e.key == "d") {
|
||||||
|
fetch(rootURL + "/right")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: RIGHT - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key == "Enter") {
|
||||||
|
fetch(rootURL + "/enter")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: ENTER - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (e.key == "x") {
|
||||||
|
fetch(rootURL + "/back")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: BACK - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key == "p") {
|
||||||
|
fetch(rootURL + "/volumeUp")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: BACK - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key == "o") {
|
||||||
|
fetch(rootURL + "/volumeDown")
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: BACK - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key == "r") {
|
||||||
|
fetch(rootURL + "/newToken")
|
||||||
|
.then(function (response) {
|
||||||
|
commandData
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: New Token - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (e.key == "m") {
|
||||||
|
fetch(rootURL + "/mute")
|
||||||
|
.then(function (response) {
|
||||||
|
commandData
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: MUTE - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key == "p") {
|
||||||
|
fetch(rootURL + "/pmode")
|
||||||
|
.then(function (response) {
|
||||||
|
commandData
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: PMODE - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key == "h") {
|
||||||
|
fetch(rootURL + "/home")
|
||||||
|
.then(function (response) {
|
||||||
|
commandData
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (data == 1) return console.log("Command: HOME - SENT!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById("view").innerHTML = "The password you provided was incorrect!"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
9
package.json
Normal file
9
package.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"jsonfile": "^6.1.0",
|
||||||
|
"samsung-tv-control": "^1.13.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
205
tv_api.js
Normal file
205
tv_api.js
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
var fs = require('fs');
|
||||||
|
const express = require('express')
|
||||||
|
const { Samsung, KEYS, APPS } = require('samsung-tv-control')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const file = '/home/token.json'
|
||||||
|
var https = require('https');
|
||||||
|
var privateKey = fs.readFileSync('/cert/grwh.work.key', 'utf8');
|
||||||
|
var certificate = fs.readFileSync('/cert/grwh.work.cert.combined', 'utf8');
|
||||||
|
var credentials = {key: privateKey, cert: certificate};
|
||||||
|
|
||||||
|
var cors = require('cors')
|
||||||
|
const corsOptions ={
|
||||||
|
origin:'*',
|
||||||
|
credentials:true,
|
||||||
|
optionSuccessStatus:200,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonfile.readFile(file, function (err, obj) {
|
||||||
|
if (err) console.error(err)
|
||||||
|
console.log("token: " + obj.data)
|
||||||
|
const config = {
|
||||||
|
debug: false, // Default: false
|
||||||
|
ip: '192.168.0.2',
|
||||||
|
mac: 'A4:30:7A:08:57:6A',
|
||||||
|
nameApp: 'OurControlApp', // Default: NodeJS
|
||||||
|
port: 8002, // Default: 8002
|
||||||
|
token: obj.data,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const control = new Samsung(config)
|
||||||
|
// Get all installed apps from TV
|
||||||
|
control.getAppsFromTV((err, res) => {
|
||||||
|
console.log(err)
|
||||||
|
if (!err) {
|
||||||
|
console.log('# Response getAppsFromTV', res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function sendKey(key) {
|
||||||
|
// Send key to TV
|
||||||
|
control.sendKey(key, function (err, res) {
|
||||||
|
if (!err) {
|
||||||
|
console.log("Sending: " + key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
var httpsServer = https.createServer(credentials, app);
|
||||||
|
|
||||||
|
const port = 3031
|
||||||
|
app.use(cors(corsOptions))
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.end('Welcome to the TV API!')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/home', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_HOME)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/right', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_RIGHT)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/left', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_LEFT)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/up', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_UP)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/down', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_DOWN)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/enter', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_ENTER)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/back', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_RETURN)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/volumeDown', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_VOLDOWN)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/volumeUp', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_VOLUP)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/mute', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_MUTE)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/pmode', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_PMODE)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/off', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
await sendKey(KEYS.KEY_POWEROFF)
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/newToken', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
control
|
||||||
|
.isAvailable()
|
||||||
|
.then(() => {
|
||||||
|
// Get token for API
|
||||||
|
control.getToken((token) => {
|
||||||
|
console.info('# Response getToken:', token)
|
||||||
|
const obj = { data: token }
|
||||||
|
if (token == "null") return
|
||||||
|
jsonfile.writeFile(file, obj, function (err) {
|
||||||
|
if (err) console.error(err)
|
||||||
|
console.log("new key, exiting to refresh")
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/openNetFlix', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
// Open app by appId which you can get from getAppsFromTV
|
||||||
|
control.openApp(APPS.Netflix, (err, res) => {
|
||||||
|
if (!err) {
|
||||||
|
console.log('# Response openApp', res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/OpenYouTube', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
// Open app by appId which you can get from getAppsFromTV
|
||||||
|
control.openApp(APPS.Netflix, (err, res) => {
|
||||||
|
if (!err) {
|
||||||
|
console.log('# Response openApp', res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
res.end('1')
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/checkPass', (req, res) => {
|
||||||
|
(async () => {
|
||||||
|
let pass = "YourPassHere"
|
||||||
|
let sent = req.query.pass
|
||||||
|
if (pass == sent){
|
||||||
|
console.log("User Authed Properly!")
|
||||||
|
res.end('1')
|
||||||
|
} else {
|
||||||
|
res.end('0')
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
|
||||||
|
httpsServer.listen(port, () => {
|
||||||
|
console.log(`TV Control API listening on port ${port}`)
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user