Skip to main content

Demo Code

WS Client

This JavaScript code depends on ws library and shows how to connect to a shelly cloud instance for receiving device statuses and some basic device control

const WebSocket = require('ws');
const { TOKEN, HOST, ID } = process.env;

const wss = `wss://${HOST}:6113/shelly/wss/hk_sock`;
const sock = new WebSocket(`${wss}?t=${TOKEN}`);

sock.on('open', () => {
console.log(`| WS Open @ ${wss} |\n`);

//When connection is established, send some example commands
//to device with id as provided with environment variable ID
const id = Number(ID);

if (isNaN(id)) {
throw new Error("ID must be of type Number!");
}

//N.B. "event" is a function holder object defined below
sendThroughWSS([
event.ActionRequest(id),

// Relay example:
event.CommandRequest_relay('on', id),
event.CommandRequest_relay('off', id),

// Light example:
// event.CommandRequest_light('off', ID)
]);

}).on('message', (message) => {
console.log("<<-- RECEIVED\n\t" + message);

}).on('error', (error) => {
console.error("Error: " + error.message);
});

// ------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------

const sleep = (s) => new Promise(r => setTimeout(r, s * 1000));
const event = {

ActionRequest(id) {
return JSON.stringify({
event: "Integrator:ActionRequest",
trid: Math.floor(Math.random() * 999),
data: { action: 'DeviceVerify', id }
});
},

CommandRequest_relay(turn = "on", id, timeout) {
return JSON.stringify({
event: "Shelly:CommandRequest",
trid: Math.floor(Math.random() * 999),
deviceId: id,
data: {
cmd: "relay",

// NOTE: settings timeout, will revert
// the state after the timeout seconds
params: { id: 0, turn, timeout },
}
});
},

CommandRequest_light(turn = "on", id) {
return JSON.stringify({
event: "Shelly:CommandRequest",
trid: Math.floor(Math.random() * 999),
deviceId: id,
data: {
cmd: "light",

// NOTE: for full list of supported parameters see:
// https://shelly-api-docs.shelly.cloud/gen1/#shelly-bulb-light-0
params: { id: 0, turn, mode: 'white', temp: 4000 },
}
});
}
};

async function sendThroughWSS(commands) {
for (let command of commands) {
sock.send(command);
console.log("-->> SEND\n\t" + command);
await sleep(5);
}
}

Checking Callback URL Security token

As explained here all user consent callbacks can be verified to be coming from shelly cloud instance by the token send via SCL-Trust header. Here is some example code that verifies such token provided as command-line argument

In JavaScript depending on jsonwebtoken:

const pubkey=`
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3Kx+6C/0ZbnelYUgucUo4/X4xt1NCmEL
coyLpgkuLHume4VLZnQjtXeYgzr2FUdsO/ip8SzssSu3CEU9ArvB+yGIlW7l1yLt
wHVs/2zXrL0riL++7jdoQCpTGanFVzpM
-----END PUBLIC KEY-----
`;
const check=process.argv[2];
if (check==undefined) {
console.log("a JWT to check must be provided as parameter");
process.exit(-1);
}
const JWT=require('jsonwebtoken');
try {
console.log(JWT.verify(check,pubkey,['ES384']));
} catch(e1) {
console.log("Verify failed!");
const decoded=JWT.decode(check);
if (decoded!=null) {
console.log ("INALIDATED msg: ",decoded);
} else {
console.log ("failed to decode the param as JWT!");
}
}

Similar example in Java:

Full maven project here

public class App 
{
static {
Security.addProvider(new BouncyCastleProvider());
}

static PublicKey GetAlltercoPubKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
StringReader src=new StringReader(
"-----BEGIN PUBLIC KEY-----\n"+
"MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3Kx+6C/0ZbnelYUgucUo4/X4xt1NCmEL\n"+
"coyLpgkuLHume4VLZnQjtXeYgzr2FUdsO/ip8SzssSu3CEU9ArvB+yGIlW7l1yLt\n"+
"wHVs/2zXrL0riL++7jdoQCpTGanFVzpM\n"+
"-----END PUBLIC KEY-----\n"
);
PemReader pr=new PemReader(src);
PemObject o = pr.readPemObject();
byte[] content = o.getContent();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
KeyFactory factory = KeyFactory.getInstance("ECDSA");
return factory.generatePublic(pubKeySpec);

}

static JWTVerifier getES384Verifier(ECPublicKey pubkey) {
return JWT.require(Algorithm.ECDSA384(pubkey,null))
.acceptLeeway(5) //sec for nbf and iat
.acceptExpiresAt(30) //30 secs for exp
.build();

}
public static void main( String[] args )
{
if (args.length==0) {
System.out.println("a JWT to check must be provided as parameter");
System.exit(-1);
}

String toCheck = args[0];
System.out.println("Must check "+toCheck);

PublicKey pubkey=null;
try {
pubkey = GetAlltercoPubKey();
} catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
System.out.println("Failed to transfigure Allterco public key!");
System.exit(-1);
}
if (! (pubkey instanceof ECPublicKey)) {
System.out.println("Obtained Allterco public key is not a ECPublicKey?!");
System.exit(-1);
}

JWTVerifier verify = getES384Verifier((ECPublicKey)pubkey);
DecodedJWT decoded=null;
try {
decoded = verify.verify(toCheck);
} catch (Throwable e) {
System.out.println("failed to verify! e:"+e);
System.exit(-1);
}

System.out.println("Verified: "+decoded.getClaims());

}
}