Skip to content

Commit

Permalink
better defs
Browse files Browse the repository at this point in the history
  • Loading branch information
jessedp committed Apr 19, 2020
1 parent 2d50375 commit 2d87084
Show file tree
Hide file tree
Showing 8 changed files with 598 additions and 32 deletions.
18 changes: 18 additions & 0 deletions src/Device.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default interface Device {
board: string;
private_ip: string;
server_id: string;
via: string;
dev_type: string;
host?: string;
public_ip?: string;
slip?: number;
http?: number;
ssl?: number;
inserted?: string;
modified?: string;
lastSeen?: string;
server_version?: string;
name?: string;
roku?: number;
}
2 changes: 2 additions & 0 deletions src/Device.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"use strict";
exports.__esModule = true;
20 changes: 20 additions & 0 deletions src/ServerInfo.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export default interface ServerInfo {
server_id: string;
name: string;
timezone: string;
version: string;
local_address: string;
setup_completed: boolean;
build_number: number;
model: Model;
availability: string;
cache_key: string;
checked: string;
}
export interface Model {
wifi: boolean;
tuners: number;
type: string;
name: string;
device: string;
}
2 changes: 2 additions & 0 deletions src/ServerInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"use strict";
exports.__esModule = true;
16 changes: 16 additions & 0 deletions src/discovery.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Device from "./Device";
export default class Discover {
private readonly sendPort;
private readonly recvPort;
private readonly discoveryUrl;
private watcher;
/**
* Attempt discovery via UDP broadcast. Will only return a single device.
*/
broadcast(): Promise<[Device]>;
/**
* Attempt discovery via HTTP broadcast using Tablo discovery service
*/
http(): Promise<Device[]>;
}
export declare const discovery: Discover;
166 changes: 166 additions & 0 deletions src/discovery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
exports.__esModule = true;
var axios_1 = require("axios");
var bytes = require("byte-data");
var Debug = require("debug");
var dgram = require("dgram");
var debug = Debug('discovery');
var Discover = /** @class */ (function () {
function Discover() {
this.sendPort = 8881;
this.recvPort = 8882;
this.discoveryUrl = 'https://api.tablotv.com/assocserver/getipinfo/';
}
/**
* Attempt discovery via UDP broadcast. Will only return a single device.
*/
Discover.prototype.broadcast = function () {
return __awaiter(this, void 0, void 0, function () {
var server, outerDevice;
var _this = this;
return __generator(this, function (_a) {
server = dgram.createSocket('udp4');
server.on('error', function (error) {
debug('Bcast Error: ', error);
clearTimeout(_this.watcher);
server.close();
});
server.on('listening', function () {
// once the server is listening, broadcast the discovery packet...
var client = dgram.createSocket('udp4');
client.bind(_this.sendPort, function () {
client.setBroadcast(true);
});
var data = Buffer.from('BnGr');
var to = '255.255.255.255';
client.send(data, _this.sendPort, to, function (error) {
if (error) {
debug('Bcast Error: %s', error);
}
else {
debug('Bcast request sent');
}
client.close();
});
});
server.on('message', function (msg, info) {
if (msg.length !== 140) {
debug("UNK: Received " + msg.length + " of 140 required bytes from " + info.address + ":" + info.port);
debug(msg);
server.close();
return false;
}
// this is the proper format string let s = struct('>4s64s32s20s10s10s')
// s = struct.format('b').unpack()
var trunc = function (txt) { return txt.split('\0', 1)[0]; };
var device = {
host: trunc(bytes.unpackString(msg, 4, 68)),
private_ip: trunc(bytes.unpackString(msg, 68, 100)),
// resp_code: trunc(bytes.unpackString(msg, 0, 4)),
server_id: trunc(bytes.unpackString(msg, 100, 120)),
dev_type: trunc(bytes.unpackString(msg, 120, 130)),
board: trunc(bytes.unpackString(msg, 130, 140)),
via: 'broadcast'
};
clearTimeout(_this.watcher);
server.close();
debug('server.on.message received:');
debug(device);
// I feel like this shouldn't work, but...
outerDevice = device;
// eslint wanted a return, this works but may be wrong
return device;
});
server.bind(this.recvPort);
this.watcher = setTimeout(function () {
server.close();
}, 250); // should be 250
// this is easily grosser and more wronger than it looks
return [2 /*return*/, new Promise(function (resolve) {
server.on('close', function () {
debug('broadcast : close');
debug('broadcast, data:');
debug(outerDevice);
resolve([outerDevice]);
});
})];
});
});
};
/**
* Attempt discovery via HTTP broadcast using Tablo discovery service
*/
Discover.prototype.http = function () {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
var data, response, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, axios_1["default"].get(this.discoveryUrl)];
case 1:
response = _a.sent();
data = response.data.cpes;
data.forEach(function (part, index, arr) {
if (arr[index]) {
arr[index].via = 'http';
}
}, data); // use arr as this
return [3 /*break*/, 3];
case 2:
error_1 = _a.sent();
debug('Http Error:', error_1);
reject(new Error("Http Error: " + error_1));
return [3 /*break*/, 3];
case 3:
resolve(data);
return [2 /*return*/];
}
});
}); })];
});
});
};
return Discover;
}());
exports["default"] = Discover;
exports.discovery = new Discover();
77 changes: 45 additions & 32 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@
// import { Tablo } from "./index"
import Device from "./Device";
import ServerInfo from "./ServerInfo";

/**
Simple API for accessing Tablo devices
*/
export class Tablo {
constructor(...args: any[]);

batch(...args: any[]): void;

delete(...args: any[]): void;

discover(...args: any[]): Device;

get(...args: any[]): void;

getRecordings(...args: any[]): void;

getRecordingsCount(...args: any[]): void;

getServerInfo(...args: any[]): ServerInfo;

getUrl(...args: any[]): void;

isReady(...args: any[]): void;

post(...args: any[]): void;

}

import Device from "./Device";
import ServerInfo from "./ServerInfo";
export default class Tablo {
private devices;
private airingsCache;
private device;
/**
* Utilizes HTTP discovery with UDP broadcast fallback to find local Tablo devices
*/
discover(): Promise<Device[]>;
/**
* Pre-flight check
* @throws Error when no device has been selected
*/
private isReady;
/**
* Returns server info reported by the Tablo
*/
getServerInfo(): Promise<ServerInfo>;
/**
* Returns a count of the Recordings on the Tablo
* @param force whether or not to force reloading from the device or use cached airings
*/
getRecordingsCount(force?: boolean): Promise<0>;
/**
* Retrieves all Recordings from the Tablo
* @param force whether or not to force reloading from the device or use cached airings
* @param progressCallback function to receive a count of records processed
*/
getRecordings(force: boolean, progressCallback: (num: number) => void): Promise<unknown[]>;
/**
* Deletes a
* @param path
*/
delete(path: string): Promise<import("axios").AxiosResponse<any>>;
/**
* Try to receive data from a specified path
* @param path
*/
get<T>(path: string): Promise<T>;
private getUrl;
private batch;
post<T>(path?: string, strArray?: string[]): Promise<T[]>;
}
export { Tablo };
Loading

0 comments on commit 2d87084

Please sign in to comment.