homebridge-platform-orbit
Version:
Orbit Irrigation System platform plugin for [HomeBridge](https://github.com/nfarina/homebridge).
211 lines (176 loc) • 6.5 kB
text/typescript
import { Logger } from "homebridge";
import bent from 'bent';
import WS from 'ws';
import ReconnectingWebSocket from 'reconnecting-websocket';
const endpoint = 'https://api.orbitbhyve.com/v1';
const ws_endpoint = 'wss://api.orbitbhyve.com/v1';
const WSpingINTERVAL = 25000 // Websocket get's timed out after 30s, so ping every 25s
export class OrbitAPI {
private readonly log: Logger;
private readonly email: string;
private readonly password: string;
private token: string;
constructor(log: Logger, email: string, password: string) {
this.log = log;
this.email = email;
this.password = password;
this.token = "";
}
async login() {
// Log in
try {
const postJSON = bent('POST', 'json');
let response = await postJSON(endpoint + "/session",
{
"session": {
"email": this.email,
"password": this.password
}
},
{
"orbit-app-id": "Orbit Support Dashboard",
"orbit-api-key": "null"
});
this.token = response['orbit_api_key'];
} catch (error) {
throw error;
}
}
async getDevices(): Promise<OrbitDevice[]> {
let devices: OrbitDevice[] = [];
// Get the device details
try {
const getJSON = bent('GET', 'json');
let response = await getJSON(endpoint + "/devices", {},
{
"orbit-app-id": "Orbit Support Dashboard",
"orbit-api-key": this.token
});
response.forEach((result: any) => {
if (result['type'] == "sprinkler_timer") {
// Create the device
let device = new OrbitDevice(this.log, this.token, result['id'], result['name'], result['hardware_version'], result['firmware_version'], result['is_connected']);
// Create zones
result['zones'].forEach((zone: any) => {
device.addZone(zone['station'], zone['name']);
});
devices.push(device);
}
});
return devices;
} catch (error) {
throw error;
}
}
}
export class OrbitDevice {
private readonly log: Logger;
private readonly token: string;
public readonly id: string;
public readonly name: string;
public readonly hardware_version: string;
public readonly firmware_version: string;
public readonly is_connected: boolean;
public readonly zones: {}[];
private messageQueue: string[];
private rws: ReconnectingWebSocket;
constructor(log: Logger, token: string, id: string, name: string, hardware_version: string, firmware_version: string, is_connected: boolean) {
this.log = log;
this.token = token;
this.id = id;
this.name = name;
this.hardware_version = hardware_version;
this.firmware_version = firmware_version;
this.is_connected = is_connected;
this.zones = [];
this.messageQueue = []
// Create the Reconnecting Web Socket
this.rws = new ReconnectingWebSocket(`${ws_endpoint}/events`, [], {
WebSocket: WS,
connectionTimeout: 10000,
maxReconnectionDelay: 64000,
minReconnectionDelay: 2000,
reconnectionDelayGrowFactor: 2
});
// Intercept send events for logging and queuing
const origSend = this.rws.send.bind(this.rws);
this.rws.send = (data: string) => {
if (this.rws.readyState === WS.OPEN) {
this.log.debug('TX', data);
origSend(data);
}
else {
this.messageQueue.push(data);
}
};
// Ping
setInterval(() => {
this.rws.send(JSON.stringify({ event: 'ping' }));
}, WSpingINTERVAL);
// On Open, process any queued messages
this.rws.onopen = (openEvent: WS.OpenEvent) => {
this.log.debug('WebSocket', openEvent.type);
while (this.messageQueue.length > 0) {
let data: string = this.messageQueue.shift()!;
this.log.debug('TX', data);
origSend(data);
}
};
// On Close
this.rws.onclose = (closeEvent: WS.CloseEvent) => {
this.log.debug('WebSocket', closeEvent.type);
};
// On Error
this.rws.onerror = (errorEvent: WS.ErrorEvent) => {
this.log.error('WebSocket Error', errorEvent);
this.rws.close();
};
}
addZone(station: string, name: string) {
this.zones.push({ "station": station, "name": name });
}
openConnection() {
this.log.debug('openConnection');
this.rws.send(JSON.stringify({
event: "app_connection",
orbit_session_token: this.token,
subscribe_device_id: this.id
}));
}
onMessage(listner: Function) {
this.log.debug('onMessage');
this.rws.onmessage = (messageEvent: MessageEvent) => {
this.log.debug('RX', messageEvent.data);
listner(messageEvent.data);
};
}
sync() {
this.log.debug('sync');
this.rws.send(JSON.stringify({
event: "sync",
device_id: this.id
}));
}
startZone(station: number, run_time: number) {
this.log.debug('startZone', station, run_time);
this.rws.send(JSON.stringify({
event: "change_mode",
mode: "manual",
device_id: this.id,
timestamp: new Date().toISOString(),
stations: [
{ "station": station, "run_time": run_time }
]
}));
}
stopZone() {
this.log.debug('stopZone');
this.rws.send(JSON.stringify({
event: "change_mode",
mode: "manual",
device_id: this.id,
timestamp: new Date().toISOString(),
stations: []
}));
}
}