Fixed linting errors (Not tested)

This commit is contained in:
watsonb8 2019-09-07 22:06:45 -04:00
parent b836178356
commit 423be484b2
10 changed files with 279 additions and 259 deletions

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
**/HAPNodeJS.d.ts

View File

@ -6,81 +6,84 @@
// Borrowed and heaviliy modifed from https://github.com/miguelmota/http-message-parser // Borrowed and heaviliy modifed from https://github.com/miguelmota/http-message-parser
import { Buffer } from 'buffer'; import { Buffer } from 'buffer';
import { Dictionary } from '../../../Types/types'; import { Dictionary } from '../../Types/types';
interface httpResult { interface IHttpResult {
protocol?: string, protocol?: string;
httpVersion?: number, httpVersion?: number;
statusCode?: number, statusCode?: number;
statusMessage?: string, statusMessage?: string;
method?: string, method?: string;
url?: string, url?: string;
headers?: Dictionary<string | number>, headers?: Dictionary<string | number>;
body?: any, //eslint-disable-next-line
boundary?: any, body?: any;
multipart?: any, //eslint-disable-next-line
additional?: any, boundary?: any;
meta?: any //eslint-disable-next-line
multipart?: any;
//eslint-disable-next-line
additional?: any;
//eslint-disable-next-line
meta?: any;
} }
function httpMessageParser(message: any) { export default class HttpMessageParser {
const result: httpResult = {} private static requestLineRegex = /(HTTP|EVENT)\/(1\.0|1\.1|2\.0)\s+(\d+)\s+([\w\s-_]+)/i;
private static responseLineRegex = /(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|TRACE|CONNECT)\s+(.*)\s+HTTP\/(1\.0|1\.1|2\.0)/i;
private static headerNewlineRegex = /^[\r\n]+/gim;
private static boundaryRegex = /(\n|\r\n)+--[\w-]+(\n|\r\n)+/g;
let messageString = ''; //eslint-disable-next-line
let headerNewlineIndex = 0; public static parse(message: any): IHttpResult {
let fullBoundary: any = null; const result: IHttpResult = {};
if (httpMessageParser._isBuffer(message)) { let messageString = '';
messageString = message.toString(); let headerNewlineIndex = 0;
} else if (typeof message === 'string') { //eslint-disable-next-line
messageString = message; let fullBoundary: any = null;
message = new Buffer(messageString);
} else {
return result;
}
/* if (this.isBuffer(message)) {
* Strip extra return characters
*/
messageString = messageString.replace(/\r\n/gim, '\n');
/*
* Trim leading whitespace
*/
(function () {
const firstNonWhitespaceRegex = /[\w-]+/gim;
const firstNonWhitespaceIndex = messageString.search(firstNonWhitespaceRegex);
if (firstNonWhitespaceIndex > 0) {
message = message.slice(firstNonWhitespaceIndex, message.length);
messageString = message.toString(); messageString = message.toString();
} else if (typeof message === 'string') {
messageString = message;
message = new Buffer(messageString);
} else {
return result;
} }
})();
/* Parse request line /*
*/ * Strip extra return characters
(function () { */
messageString = messageString.replace(/\r\n/gim, '\n');
messageString = this.trim(messageString);
/* Parse request line
*/
const possibleRequestLine = messageString.split(/\n|\r\n/)[0]; const possibleRequestLine = messageString.split(/\n|\r\n/)[0];
const requestLineMatch = possibleRequestLine.match(httpMessageParser._requestLineRegex); const requestLineMatch = possibleRequestLine.match(this.requestLineRegex);
/* Parse request line
*/
if (Array.isArray(requestLineMatch) && requestLineMatch.length > 1) { if (Array.isArray(requestLineMatch) && requestLineMatch.length > 1) {
result.protocol = requestLineMatch[1]; result.protocol = requestLineMatch[1];
result.httpVersion = parseFloat(requestLineMatch[2]); result.httpVersion = parseFloat(requestLineMatch[2]);
result.statusCode = parseInt(requestLineMatch[3], 10); result.statusCode = parseInt(requestLineMatch[3], 10);
result.statusMessage = requestLineMatch[4]; result.statusMessage = requestLineMatch[4];
} else { } else {
const responseLineMath = possibleRequestLine.match(httpMessageParser._responseLineRegex); const responseLineMath = possibleRequestLine.match(this.responseLineRegex);
if (Array.isArray(responseLineMath) && responseLineMath.length > 1) { if (Array.isArray(responseLineMath) && responseLineMath.length > 1) {
result.method = responseLineMath[1]; result.method = responseLineMath[1];
result.url = responseLineMath[2]; result.url = responseLineMath[2];
result.httpVersion = parseFloat(responseLineMath[3]); result.httpVersion = parseFloat(responseLineMath[3]);
} }
} }
})();
/* Parse headers /* Parse headers
*/ */
(function () {
headerNewlineIndex = messageString.search(httpMessageParser._headerNewlineRegex); headerNewlineIndex = messageString.search(this.headerNewlineRegex);
if (headerNewlineIndex > -1) { if (headerNewlineIndex > -1) {
headerNewlineIndex = headerNewlineIndex + 1; // 1 for newline length headerNewlineIndex = headerNewlineIndex + 1; // 1 for newline length
} else { } else {
@ -93,20 +96,18 @@ function httpMessageParser(message: any) {
} }
const headersString = messageString.substr(0, headerNewlineIndex); const headersString = messageString.substr(0, headerNewlineIndex);
const headers = httpMessageParser._parseHeaders(headersString); const headers = this.parseHeaders(headersString);
if (Object.keys(headers).length > 0) { if (Object.keys(headers).length > 0) {
result.headers = headers; result.headers = headers;
// TOOD: extract boundary. // TOOD: extract boundary.
} }
})();
/* Try to get boundary if no boundary header /* Try to get boundary if no boundary header
*/ */
(function () {
if (!result.boundary) { if (!result.boundary) {
const boundaryMatch = messageString.match(httpMessageParser._boundaryRegex); const boundaryMatch = messageString.match(this.boundaryRegex);
if (Array.isArray(boundaryMatch) && boundaryMatch.length) { if (Array.isArray(boundaryMatch) && boundaryMatch.length) {
fullBoundary = boundaryMatch[0].replace(/[\r\n]+/gi, ''); fullBoundary = boundaryMatch[0].replace(/[\r\n]+/gi, '');
@ -114,13 +115,11 @@ function httpMessageParser(message: any) {
result.boundary = boundary; result.boundary = boundary;
} }
} }
})();
/* Parse body /* Parse body
*/ */
(function () {
let start = headerNewlineIndex; let start = headerNewlineIndex;
let contentLength: number = result.headers!['Content-Length'] as number; const contentLength: number = result.headers!['Content-Length'] as number;
let end = (result.headers && result.headers['Content-Length'] && contentLength ? contentLength + start : messageString.length); let end = (result.headers && result.headers['Content-Length'] && contentLength ? contentLength + start : messageString.length);
@ -154,11 +153,9 @@ function httpMessageParser(message: any) {
} }
} }
} }
})();
/* Parse multipart sections /* Parse multipart sections
*/ */
(function () {
if (result.boundary) { if (result.boundary) {
const multipartStart = messageString.indexOf(fullBoundary) + fullBoundary.length; const multipartStart = messageString.indexOf(fullBoundary) + fullBoundary.length;
const multipartEnd = messageString.lastIndexOf(fullBoundary); const multipartEnd = messageString.lastIndexOf(fullBoundary);
@ -166,9 +163,9 @@ function httpMessageParser(message: any) {
const splitRegex = new RegExp('^' + fullBoundary + '.*[\n\r]?$', 'gm'); const splitRegex = new RegExp('^' + fullBoundary + '.*[\n\r]?$', 'gm');
const parts = multipartBody.split(splitRegex); const parts = multipartBody.split(splitRegex);
result.multipart = parts.filter(httpMessageParser._isTruthy).map(function (part, i) { result.multipart = parts.filter(this.isTruthy).map(function (part, i) {
// tslint:disable-next-line: no-shadowed-variable // tslint:disable-next-line: no-shadowed-variable
const result: httpResult = {}; const result: IHttpResult = {};
const newlineRegex = /\n\n|\r\n\r\n/gim; const newlineRegex = /\n\n|\r\n\r\n/gim;
let newlineIndex = 0; let newlineIndex = 0;
@ -191,7 +188,7 @@ function httpMessageParser(message: any) {
let endOffset = null; let endOffset = null;
if (newlineIndex > -1) { if (newlineIndex > -1) {
const headers = httpMessageParser._parseHeaders(possibleHeadersString); const headers = HttpMessageParser.parseHeaders(possibleHeadersString);
if (Object.keys(headers).length > 0) { if (Object.keys(headers).length > 0) {
result.headers = headers; result.headers = headers;
@ -231,68 +228,73 @@ function httpMessageParser(message: any) {
return result; return result;
}); });
} }
})();
return result; return result;
}
httpMessageParser._isTruthy = function _isTruthy(value: any) {
return !!value;
};
httpMessageParser._isNumeric = function _isNumeric(value: any) {
if (typeof value === 'number' && !isNaN(value)) {
return true;
} }
value = (value || '').toString().trim(); private static trim(str: string): string {
const firstNonWhitespaceRegex = /[\w-]+/gim;
if (!value) { const firstNonWhitespaceIndex = str.search(firstNonWhitespaceRegex);
return false; let tmpStr = "";
} if (firstNonWhitespaceIndex > 0) {
tmpStr = str.slice(firstNonWhitespaceIndex, str.length);
return !isNaN(value); str = tmpStr.toString();
};
httpMessageParser._isBuffer = function (item: any) {
return ((httpMessageParser._isNodeBufferSupported() &&
typeof global === 'object' &&
global.Buffer.isBuffer(item)) ||
(item instanceof Object &&
item._isBuffer));
};
httpMessageParser._isNodeBufferSupported = function () {
return (typeof global === 'object' &&
typeof global.Buffer === 'function' &&
typeof global.Buffer.isBuffer === 'function');
};
httpMessageParser._parseHeaders = function _parseHeaders(body: any) {
const headers: Dictionary<string | number> = {};
if (typeof body !== 'string') {
return headers;
}
body.split(/[\r\n]/).forEach(function (string) {
const match = string.match(/([\w-]+):\s*(.*)/i);
if (Array.isArray(match) && match.length === 3) {
const key = match[1];
const value = match[2];
headers[key] = httpMessageParser._isNumeric(value) ? Number(value) : value;
} }
});
return headers; return str;
}; }
httpMessageParser._requestLineRegex = /(HTTP|EVENT)\/(1\.0|1\.1|2\.0)\s+(\d+)\s+([\w\s-_]+)/i; private static isTruthy = function _isTruthy(value: any): boolean {
httpMessageParser._responseLineRegex = /(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|TRACE|CONNECT)\s+(.*)\s+HTTP\/(1\.0|1\.1|2\.0)/i; return !!value;
// httpMessageParser._headerNewlineRegex = /^[\r\n]+/gim; };
httpMessageParser._headerNewlineRegex = /^[\r\n]+/gim;
httpMessageParser._boundaryRegex = /(\n|\r\n)+--[\w-]+(\n|\r\n)+/g;
export default httpMessageParser; private static isNumeric = function _isNumeric(value: any): boolean {
if (typeof value === 'number' && !isNaN(value)) {
return true;
}
value = (value || '').toString().trim();
if (!value) {
return false;
}
return !isNaN(value);
};
private static isBuffer = (item: any): boolean => {
return ((HttpMessageParser.isNodeBufferSupported() &&
typeof global === 'object' &&
global.Buffer.isBuffer(item)) ||
(item instanceof Object &&
item._isBuffer));
};
private static isNodeBufferSupported = (): boolean => {
return (typeof global === 'object' &&
typeof global.Buffer === 'function' &&
typeof global.Buffer.isBuffer === 'function');
};
private static parseHeaders = (body: any): Dictionary<string | number> => {
const headers: Dictionary<string | number> = {};
if (typeof body !== 'string') {
return headers;
}
body.split(/[\r\n]/).forEach(function (string) {
const match = string.match(/([\w-]+):\s*(.*)/i);
if (Array.isArray(match) && match.length === 3) {
const key = match[1];
const value = match[2];
headers[key] = HttpMessageParser.isNumeric(value) ? Number(value) : value;
}
});
return headers;
};
}

View File

@ -5,8 +5,8 @@
import * as net from 'net'; import * as net from 'net';
import * as url from 'url'; import * as url from 'url';
import httpMessageParser from './/httpParser'; import HttpMessageParser from './httpParser';
import { Dictionary } from '../../../Types/types'; import { Dictionary } from '../../Types/types';
interface IRequest { interface IRequest {
method: 'PUT' | 'POST' | 'GET' | 'DELETE' | 'PATCH'; method: 'PUT' | 'POST' | 'GET' | 'DELETE' | 'PATCH';
@ -16,7 +16,34 @@ interface IRequest {
body: string; body: string;
} }
export const parseMessage = httpMessageParser; export const parseMessage = HttpMessageParser.parse;
function _headersToString(headers: Dictionary<string>): string {
let response = '';
for (const header of Object.keys(headers)) {
response = response + header + ': ' + headers[header] + '\r\n';
}
return (response);
}
function _buildMessage(request: IRequest): string {
const context = url.parse(request.url);
let message;
message = request.method + ' ' + context.pathname;
if (context.search) {
message = message + context.search;
}
message = message + ' HTTP/1.1\r\nHost: ' + context.host + '\r\n' + _headersToString(request.headers);
if (request.body) {
message = message + 'Content-Length: ' + request.body.length + '\r\n\r\n' + request.body + '\r\n\r\n';
} else {
message = message + '\r\n\r\n';
}
// debug("Message ->", message);
return (message);
}
/** /**
* Create a socket connection. * Create a socket connection.
@ -24,7 +51,7 @@ export const parseMessage = httpMessageParser;
* @param pin The authorization token * @param pin The authorization token
* @param body The connection body * @param body The connection body
*/ */
export function createConnection(instance: { ipAddress: string, port: number }, pin: string, body: any): net.Socket { export function createConnection(instance: { ipAddress: string; port: number }, pin: string, body: unknown): net.Socket {
const client: net.Socket = net.createConnection({ const client: net.Socket = net.createConnection({
host: instance.ipAddress, host: instance.ipAddress,
port: instance.port, port: instance.port,
@ -44,30 +71,3 @@ export function createConnection(instance: { ipAddress: string, port: number },
return client; return client;
} }
function _headersToString(headers: Dictionary<string>) {
let response = '';
for (const header of Object.keys(headers)) {
response = response + header + ': ' + headers[header] + '\r\n';
}
return (response);
}
function _buildMessage(request: IRequest) {
const context = url.parse(request.url);
let message;
message = request.method + ' ' + context.pathname;
if (context.search) {
message = message + context.search;
}
message = message + ' HTTP/1.1\r\nHost: ' + context.host + '\r\n' + _headersToString(request.headers);
if (request.body) {
message = message + 'Content-Length: ' + request.body.length + '\r\n\r\n' + request.body + '\r\n\r\n';
} else {
message = message + '\r\n\r\n';
}
// debug("Message ->", message);
return (message);
}

View File

@ -1,4 +1,4 @@
import { Dictionary } from "../../Types/types"; import { Dictionary } from "../Types/types";
/* This file is automatically generated */ /* This file is automatically generated */

View File

@ -2,35 +2,37 @@ import 'source-map-support/register';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import decamelize from 'decamelize'; import decamelize from 'decamelize';
import * as inflection from 'inflection'; import * as inflection from 'inflection';
import Bonjour, { Service } from 'bonjour'; import Bonjour from 'bonjour';
import { EventEmitter } from 'events';
import { get, put } from 'request-promise-native'; import { get, put } from 'request-promise-native';
import { Services, Characteristics } from './hap-types'; import { Services, Characteristics } from './hap-types';
import { HapMonitor } from './monitor'; import { HapMonitor } from './hapMonitor';
import { IHapAccessoriesRespType, IServiceType, ICharacteristicType, IHapInstance, createDefaultCharacteristicType, IAccessoryResp } from './interfaces'; import { IHapAccessoriesRespType, IServiceType, ICharacteristicType, IHapInstance, createDefaultCharacteristicType, IAccessoryResp } from './interfaces';
import { log, Dictionary } from '../Types/types';
export type HapAccessoriesRespType = IHapAccessoriesRespType; export type HapAccessoriesRespType = IHapAccessoriesRespType;
export type ServiceType = IServiceType; export type ServiceType = IServiceType;
export type CharacteristicType = ICharacteristicType; export type CharacteristicType = ICharacteristicType;
export interface IDevice { export interface IDevice {
addresses: Array<string> addresses: Array<string>;
fqdn: string, fqdn: string;
host: string, host: string;
name: string, name: string;
port: number, port: number;
protocol: "tcp" | "udp", protocol: "tcp" | "udp";
rawTxt: Buffer, rawTxt: Buffer;
referer: { referer: {
address: string, address: string;
family: "IPv4" | "IPv6", family: "IPv4" | "IPv6";
port: number, port: number;
size: number, size: number;
}, };
subtypes: Array<any>, // eslint-disable-next-line
txt: any, subtypes: Array<any>;
type: string // eslint-disable-next-line
txt: any;
type: string;
} }
interface IConfig { interface IConfig {
@ -63,8 +65,8 @@ export class HapClient {
constructor(opts: { constructor(opts: {
pin: string; pin: string;
logger?: any; logger: log;
config: any; config: IConfig;
}) { }) {
this.pin = opts.pin; this.pin = opts.pin;
this.log = opts.logger; this.log = opts.logger;
@ -72,19 +74,19 @@ export class HapClient {
this.config = opts.config; this.config = opts.config;
} }
private debug(msg: any) { private debug(msg: string): void {
if (this.debugEnabled) { if (this.debugEnabled) {
this.log(msg); this.log(msg);
} }
} }
private debugErr(msg: string, err: Error) { private debugErr(msg: string, err: Error): void {
if (this.debugEnabled) { if (this.debugEnabled) {
this.log(`${msg}: ${err.message}\n ${err.stack}`); this.log(`${msg}: ${err.message}\n ${err.stack}`);
} }
} }
public refreshInstances() { public refreshInstances(): void {
if (!this.discoveryInProgress) { if (!this.discoveryInProgress) {
this.discover(); this.discover();
} else { } else {
@ -99,7 +101,7 @@ export class HapClient {
} }
public async discover(): Promise<void> { public async discover(): Promise<void> {
return new Promise((resolve) => { return new Promise((resolve): void => {
this.discoveryInProgress = true; this.discoveryInProgress = true;
this.browser = this.bonjour.find({ this.browser = this.bonjour.find({
@ -111,8 +113,9 @@ export class HapClient {
this.debug(`[HapClient] Discovery :: Started`); this.debug(`[HapClient] Discovery :: Started`);
// service found // service found
this.browser.on('up', async (service: any) => { this.browser.on('up', async (service: Bonjour.Service) => {
let device = service as IDevice; const unknownService: unknown = service as unknown;
const device = unknownService as IDevice;
if (!device || !device.txt) { if (!device || !device.txt) {
this.debug(`[HapClient] Discovery :: Ignoring device that contains no txt records. ${JSON.stringify(device)}`); this.debug(`[HapClient] Discovery :: Ignoring device that contains no txt records. ${JSON.stringify(device)}`);
return; return;
@ -124,7 +127,7 @@ export class HapClient {
name: device.txt.md, name: device.txt.md,
username: device.txt.id, username: device.txt.id,
port: device.port, port: device.port,
} };
this.debug(`[HapClient] Discovery :: Found HAP device ${instance.displayName} with username ${instance.username}`); this.debug(`[HapClient] Discovery :: Found HAP device ${instance.displayName} with username ${instance.username}`);
@ -194,7 +197,7 @@ export class HapClient {
}); });
} }
private humanizeString(string: string) { private humanizeString(string: string): string {
return inflection.titleize(decamelize(string)); return inflection.titleize(decamelize(string));
} }
@ -210,7 +213,7 @@ export class HapClient {
resp.accessories && resp.accessories.forEach((accessory: IAccessoryResp) => { resp.accessories && resp.accessories.forEach((accessory: IAccessoryResp) => {
accessory.instance = instance; accessory.instance = instance;
accessories.push(accessory); accessories.push(accessory);
}) });
} catch (e) { } catch (e) {
if (this.log) { if (this.log) {
this.debugErr(`[HapClient] [${instance.displayName} - ${instance.ipAddress}:${instance.port} (${instance.username})] Failed to connect`, e); this.debugErr(`[HapClient] [${instance.displayName} - ${instance.ipAddress}:${instance.port} (${instance.username})] Failed to connect`, e);
@ -236,7 +239,7 @@ export class HapClient {
/* Parse Accessory Information */ /* Parse Accessory Information */
const accessoryInformationService = accessory.services.find(x => x.type === Services.AccessoryInformation); const accessoryInformationService = accessory.services.find(x => x.type === Services.AccessoryInformation);
const accessoryInformation: { [key: string]: any } = {}; const accessoryInformation: Dictionary<unknown> = {};
if (accessoryInformationService && accessoryInformationService.characteristics) { if (accessoryInformationService && accessoryInformationService.characteristics) {
accessoryInformationService.characteristics.forEach((c) => { accessoryInformationService.characteristics.forEach((c) => {
@ -272,7 +275,7 @@ export class HapClient {
uuid: c.type, uuid: c.type,
type: Characteristics[c.type], type: Characteristics[c.type],
serviceType: Services[s.type], serviceType: Services[s.type],
serviceName: serviceName!.value.toString(), serviceName: serviceName && serviceName.value ? serviceName.value.toString() : "Service name not found",
description: c.description, description: c.description,
value: c.value, value: c.value,
format: c.format, format: c.format,
@ -307,7 +310,7 @@ export class HapClient {
.digest('hex'); .digest('hex');
/* Helper function to trigger a call to the accessory to get all the characteristic values */ /* Helper function to trigger a call to the accessory to get all the characteristic values */
service.refreshCharacteristics = () => { service.refreshCharacteristics = (): Promise<IServiceType> => {
return this.refreshServiceCharacteristics.bind(this)(service); return this.refreshServiceCharacteristics.bind(this)(service);
}; };
@ -318,7 +321,7 @@ export class HapClient {
/* Helper function to returns a characteristic by it's type name */ /* Helper function to returns a characteristic by it's type name */
service.getCharacteristic = (type: string): ICharacteristicType => { service.getCharacteristic = (type: string): ICharacteristicType => {
let characteristic = service.serviceCharacteristics.find(c => c.type === type); const characteristic = service.serviceCharacteristics.find(c => c.type === type);
return characteristic ? characteristic : createDefaultCharacteristicType(); return characteristic ? characteristic : createDefaultCharacteristicType();
}; };
@ -345,7 +348,7 @@ export class HapClient {
} }
public getService(iid: number): Promise<IServiceType | undefined> { public getService(iid: number): Promise<IServiceType | undefined> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject): Promise<void> => {
try { try {
const services = await this.getAllServices(); const services = await this.getAllServices();
return resolve(services.find(x => x.iid === iid)); return resolve(services.find(x => x.iid === iid));
@ -357,7 +360,7 @@ export class HapClient {
} }
public async getServiceByName(serviceName: string): Promise<IServiceType | undefined> { public async getServiceByName(serviceName: string): Promise<IServiceType | undefined> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject): Promise<void> => {
try { try {
const services = await this.getAllServices(); const services = await this.getAllServices();
return resolve(services.find(x => x.serviceName === serviceName)); return resolve(services.find(x => x.serviceName === serviceName));
@ -379,7 +382,9 @@ export class HapClient {
resp.characteristics.forEach((charType: ICharacteristicType) => { resp.characteristics.forEach((charType: ICharacteristicType) => {
const characteristic = service.serviceCharacteristics.find(x => x.iid === charType.iid && x.aid === service.aid); const characteristic = service.serviceCharacteristics.find(x => x.iid === charType.iid && x.aid === service.aid);
characteristic!.value = charType.value; if (characteristic) {
characteristic.value = charType.value;
}
}); });
return service; return service;
@ -435,6 +440,6 @@ export class HapClient {
} }
} }
return characteristic return characteristic;
} }
} }

View File

@ -2,15 +2,16 @@ import { EventEmitter } from 'events';
import { IServiceType, IHapEvInstance } from './interfaces'; import { IServiceType, IHapEvInstance } from './interfaces';
import { createConnection, parseMessage } from './eventedHttpClient'; import { createConnection, parseMessage } from './eventedHttpClient';
import { CharacteristicType } from './hapClient'; import { CharacteristicType } from './hapClient';
import { log } from '../Types/types';
export class HapMonitor extends EventEmitter { export class HapMonitor extends EventEmitter {
private pin: string; private pin: string;
private evInstances: IHapEvInstance[]; private evInstances: IHapEvInstance[];
private services: IServiceType[]; private services: IServiceType[];
private logger: any; private logger: log;
private debug: any; private debug: log;
constructor(logger: any, debug: any, pin: string, services: IServiceType[]) { constructor(logger: log, debug: log, pin: string, services: IServiceType[]) {
super(); super();
this.logger = logger; this.logger = logger;
this.debug = debug; this.debug = debug;
@ -28,7 +29,7 @@ export class HapMonitor extends EventEmitter {
/** /**
* Start monitoring * Start monitoring
*/ */
public start() { public start(): void {
for (const instance of this.evInstances) { for (const instance of this.evInstances) {
instance.socket = createConnection(instance, this.pin, { characteristics: instance.evCharacteristics }); instance.socket = createConnection(instance, this.pin, { characteristics: instance.evCharacteristics });
@ -40,7 +41,7 @@ export class HapMonitor extends EventEmitter {
if (message.statusCode === 401) { if (message.statusCode === 401) {
if (this.logger) { if (this.logger) {
this.logger.warn(`[HapClient] [${instance.ipAddress}:${instance.port} (${instance.username})] ` + this.logger(`[HapClient] [${instance.ipAddress}:${instance.port} (${instance.username})] ` +
`${message.statusCode} ${message.statusMessage} - make sure Homebridge pin for this instance is set to ${this.pin}.`); `${message.statusCode} ${message.statusMessage} - make sure Homebridge pin for this instance is set to ${this.pin}.`);
} }
} }
@ -71,7 +72,7 @@ export class HapMonitor extends EventEmitter {
}); });
// push update to listeners // push update to listeners
this.emit('service-update', response.filter((x: any) => x)); this.emit('service-update', response.filter((x: unknown) => x));
} }
} catch (e) { } catch (e) {
// do nothing // do nothing
@ -84,7 +85,7 @@ export class HapMonitor extends EventEmitter {
/** /**
* Stop monitoring. * Stop monitoring.
*/ */
public stop() { public stop(): void {
for (const instance of this.evInstances) { for (const instance of this.evInstances) {
if (instance.socket) { if (instance.socket) {
try { try {
@ -97,7 +98,7 @@ export class HapMonitor extends EventEmitter {
} }
} }
private parseServices() { private parseServices(): void {
// get a list of characteristics we can watch for each instance // get a list of characteristics we can watch for each instance
for (const service of this.services) { for (const service of this.services) {
const evCharacteristics = service.serviceCharacteristics.filter(x => x.perms.includes('ev')); const evCharacteristics = service.serviceCharacteristics.filter(x => x.perms.includes('ev'));
@ -113,8 +114,10 @@ export class HapMonitor extends EventEmitter {
const instance = this.evInstances.find(x => x.username === service.instance.username); const instance = this.evInstances.find(x => x.username === service.instance.username);
for (const evCharacteristic of evCharacteristics) { for (const evCharacteristic of evCharacteristics) {
if (!instance!.evCharacteristics!.find(x => x.aid === service.aid && x.iid === evCharacteristic.iid)) { if (instance &&
instance!.evCharacteristics!.push({ aid: service.aid, iid: evCharacteristic.iid, ev: true }); instance.evCharacteristics &&
!instance.evCharacteristics.find(x => x.aid === service.aid && x.iid === evCharacteristic.iid)) {
instance.evCharacteristics.push({ aid: service.aid, iid: evCharacteristic.iid, ev: true });
} }
} }
} }

View File

@ -1 +1,14 @@
export type Dictionary<T> = { [key: string]: T }; import { EventEmitter } from "events";
export type Dictionary<T> = { [key: string]: T };
export type log = (msg: string) => void;
export type Homebridge = {
registerPlatform: (
platformName: string,
platformExtension: string,
platformClass: new (log: log, config: {}, api: EventEmitter) => void,
someBool: boolean
) => void;
}

View File

@ -1,65 +1,37 @@
import { HapClient } from "./3rdParty/HapClient/hapClient"; import { log, Homebridge } from './Types/types';
import { HapMonitor } from "./3rdParty/HapClient/monitor"; import { EventEmitter } from 'events';
let Accessory: any; class AutomationPlatform {
let Homebridge: any; private log: log;
private config: {} = {};
private api: EventEmitter;
constructor(log: log, config: {}, api: EventEmitter) {
this.log = log;
this.config = config;
this.api = api;
this.log('INFO - Registering automation platform');
this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this));
}
/**
* Handler for didFinishLaunching
* Happens after constructor
*/
private didFinishLaunching(): void {
//No external accessories to publish
}
}
/** /**
* Main entry. * Main entry.
* @param homebridge * @param homebridge
*/ */
export default function (homebridge: any) { export default function (homebridge: Homebridge): void {
Homebridge = homebridge;
Accessory = homebridge.platformAccessory;
homebridge.registerPlatform( homebridge.registerPlatform(
'ml-automation', 'ml-automation',
'automation', 'automation',
AutomationPlatform, AutomationPlatform,
true true
); );
}; };
class AutomationPlatform {
log: any = {};
config: any = {};
api: any;
client: HapClient;
constructor(log: any, config: any, api: any) {
this.log = log;
this.config = config;
this.api = api;
this.log('INFO - Registering automation platform');
this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this));
this.client = new HapClient({
pin: "031-45-154",
logger: log,
config: config
});
this.client.discover().then(async () => {
let asdf = await this.client.getAccessories();
let asdff = "asdf";
})
}
/**
* Handler for didFinishLaunching
* Happens after constructor
*/
didFinishLaunching() {
//No external accessories to publish
}
/**
* Called by homebridge to gather accessories.
* @param callback
*/
accessories(callback: (accessories: Array<any>) => void) {
}
}

24
src/services/monitor.ts Normal file
View File

@ -0,0 +1,24 @@
import { HapClient } from "../HapClient/hapClient";
import { log } from '../Types/types';
import { singleton } from 'tsyringe';
export interface IMonitorProps {
log: log;
}
@singleton()
export class Monitor {
private log: log;
private api: any;
private client: HapClient;
constructor(props: IMonitorProps) {
this.log = props.log;
this.client = new HapClient({
pin: "031-45-154",
logger: this.log,
config: {}
});
}
}