Skip to content

Instantly share code, notes, and snippets.

@simonesestito
Last active January 4, 2020 17:27
Show Gist options
  • Save simonesestito/b3db9ff1c02053f2259806debf8ac72f to your computer and use it in GitHub Desktop.
Save simonesestito/b3db9ff1c02053f2259806debf8ac72f to your computer and use it in GitHub Desktop.
PC Shutdown with Alexa and IFTTT

PC Shutdown

  • ifttt-api.js:

Node server, must be hosted on a provider.

It exposes a TCP Server to send commands to the client, and an HTTP Server to receive commands from IFTTT.

  • pc-client.js:

Node client, must be run on the target PC (on startup).

It connects to the TCP Server to receive commands. It reconnects in case of failure.

How To Use

Create a service on IFTTT Platform

  • Go to platform.ifttt.com and create a new service
  • Go to "API" > "General"
  • Type the URL where the server is hosted
  • Copy the Secret Key and paste it in ifttt-api.js (SECRET_IFTTT_KEY)
  • Go to "API" > "Actions"
  • Click on "New action"
  • Fulfill the form, typing shutdown_pc in Endpoint field

Setup the server

  • Run ifttt-api.js with PM2 on a server
  • Open ports indicated in ifttt-api.js if necessary on the server

Setup the client on PC

  • Open pc-client.js and set the value of TCP_ADDR
  • Run pc-client.js on PC, better if at startup

Create an applet

Create an Alexa routine

  • Open the Alexa app
  • Go to Routines
  • Create a new routine
  • When a phrase of your choice is detected, run the IFTTT applet created before
const net = require('net');
const http = require('http');
const HTTP_PORT = 56744;
const TCP_PORT = 62710;
const TCP_KEEP_ALIVE = 60000; // 1 min
const IFTTT_ACTIONS = {
'shutdown_pc': shutdown
};
const IFTTT_URL_PREFIX = '/ifttt/v1/actions/';
const IFTTT_HEADER = 'ifttt-service-key';
const SECRET_IFTTT_KEY = '<IFTTT SERVICE KEY>';
const clients = [];
http.createServer((req, res) => {
if (req.headers[IFTTT_HEADER] !== SECRET_IFTTT_KEY) {
res.statusCode = 401;
res.end();
return;
}
if (!req.url.startsWith(IFTTT_URL_PREFIX)) {
res.statusCode = 404;
res.end();
return;
}
const actionName = req.url.replace(IFTTT_URL_PREFIX, '');
if (!IFTTT_ACTIONS[actionName]) {
res.statusCode = 404;
res.end();
return;
}
if (req.method !== 'POST') {
res.statusCode = 405;
res.end();
return;
}
IFTTT_ACTIONS[actionName]();
res.end();
}).listen(HTTP_PORT, () => {
console.log(`HTTP ready on ${HTTP_PORT}`);
});
function shutdown() {
clients.forEach(c => c.write('shutdown\n'));
}
// TCP Socket Client
const server = net.createServer(conn => {
clients.push(conn);
conn.on('close', () => {
clients.splice(clients.indexOf(conn), 1);
});
});
setInterval(() => {
clients.forEach(c => c.write('keep-alive\n'));
}, TCP_KEEP_ALIVE);
server.listen(TCP_PORT, () => {
console.log(`TCP server ready on ${TCP_PORT}`);
});
const net = require('net');
const { execSync } = require('child_process');
const TCP_PORT = 62710;
const TCP_ADDR = '<DOMAIN NAME / IP>';
const RECONN_TIMEOUT = 5000; // 5 secs
const socket = new net.Socket();
socket.setEncoding('utf8');
let connected = false;
socket.on('connect', () => {
connected = true;
});
socket.on('close', () => {
connected = false;
reconnect();
});
socket.on('error', reconnect);
let dataBuff = '';
socket.on('data', d => {
dataBuff += d;
if (dataBuff.indexOf('\n')) {
const cmds = dataBuff.split('\n');
dataBuff = cmds.pop();
cmds.forEach(onCommand);
}
});
function onCommand(cmd) {
switch (cmd) {
case 'shutdown':
shutdown();
break;
case 'keep-alive':
// No action required.
break;
default:
console.error(`Unknown command: ${cmd}`);
}
}
function shutdown() {
execSync('cmd.exe /C "shutdown /s /hybrid /t 0"');
}
function connect() {
if (!socket.connecting && !connected)
socket.connect(TCP_PORT, TCP_ADDR);
}
function reconnect() {
setTimeout(connect, RECONN_TIMEOUT);
}
connect();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment