Fixed linting errors (Not tested)
This commit is contained in:
parent
b836178356
commit
423be484b2
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@ -0,0 +1 @@
|
||||
**/HAPNodeJS.d.ts
|
@ -6,81 +6,84 @@
|
||||
// Borrowed and heaviliy modifed from https://github.com/miguelmota/http-message-parser
|
||||
|
||||
import { Buffer } from 'buffer';
|
||||
import { Dictionary } from '../../../Types/types';
|
||||
import { Dictionary } from '../../Types/types';
|
||||
|
||||
interface httpResult {
|
||||
protocol?: string,
|
||||
httpVersion?: number,
|
||||
statusCode?: number,
|
||||
statusMessage?: string,
|
||||
method?: string,
|
||||
url?: string,
|
||||
headers?: Dictionary<string | number>,
|
||||
body?: any,
|
||||
boundary?: any,
|
||||
multipart?: any,
|
||||
additional?: any,
|
||||
meta?: any
|
||||
interface IHttpResult {
|
||||
protocol?: string;
|
||||
httpVersion?: number;
|
||||
statusCode?: number;
|
||||
statusMessage?: string;
|
||||
method?: string;
|
||||
url?: string;
|
||||
headers?: Dictionary<string | number>;
|
||||
//eslint-disable-next-line
|
||||
body?: any;
|
||||
//eslint-disable-next-line
|
||||
boundary?: any;
|
||||
//eslint-disable-next-line
|
||||
multipart?: any;
|
||||
//eslint-disable-next-line
|
||||
additional?: any;
|
||||
//eslint-disable-next-line
|
||||
meta?: any;
|
||||
}
|
||||
|
||||
function httpMessageParser(message: any) {
|
||||
const result: httpResult = {}
|
||||
export default class HttpMessageParser {
|
||||
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 = '';
|
||||
let headerNewlineIndex = 0;
|
||||
let fullBoundary: any = null;
|
||||
//eslint-disable-next-line
|
||||
public static parse(message: any): IHttpResult {
|
||||
const result: IHttpResult = {};
|
||||
|
||||
if (httpMessageParser._isBuffer(message)) {
|
||||
messageString = message.toString();
|
||||
} else if (typeof message === 'string') {
|
||||
messageString = message;
|
||||
message = new Buffer(messageString);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
let messageString = '';
|
||||
let headerNewlineIndex = 0;
|
||||
//eslint-disable-next-line
|
||||
let fullBoundary: any = null;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (this.isBuffer(message)) {
|
||||
messageString = message.toString();
|
||||
} else if (typeof message === 'string') {
|
||||
messageString = message;
|
||||
message = new Buffer(messageString);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
})();
|
||||
|
||||
/* Parse request line
|
||||
*/
|
||||
(function () {
|
||||
/*
|
||||
* Strip extra return characters
|
||||
*/
|
||||
messageString = messageString.replace(/\r\n/gim, '\n');
|
||||
|
||||
messageString = this.trim(messageString);
|
||||
|
||||
/* Parse request line
|
||||
*/
|
||||
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) {
|
||||
result.protocol = requestLineMatch[1];
|
||||
result.httpVersion = parseFloat(requestLineMatch[2]);
|
||||
result.statusCode = parseInt(requestLineMatch[3], 10);
|
||||
result.statusMessage = requestLineMatch[4];
|
||||
} else {
|
||||
const responseLineMath = possibleRequestLine.match(httpMessageParser._responseLineRegex);
|
||||
const responseLineMath = possibleRequestLine.match(this.responseLineRegex);
|
||||
if (Array.isArray(responseLineMath) && responseLineMath.length > 1) {
|
||||
result.method = responseLineMath[1];
|
||||
result.url = responseLineMath[2];
|
||||
result.httpVersion = parseFloat(responseLineMath[3]);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
/* Parse headers
|
||||
*/
|
||||
(function () {
|
||||
headerNewlineIndex = messageString.search(httpMessageParser._headerNewlineRegex);
|
||||
/* Parse headers
|
||||
*/
|
||||
|
||||
headerNewlineIndex = messageString.search(this.headerNewlineRegex);
|
||||
if (headerNewlineIndex > -1) {
|
||||
headerNewlineIndex = headerNewlineIndex + 1; // 1 for newline length
|
||||
} else {
|
||||
@ -93,20 +96,18 @@ function httpMessageParser(message: any) {
|
||||
}
|
||||
|
||||
const headersString = messageString.substr(0, headerNewlineIndex);
|
||||
const headers = httpMessageParser._parseHeaders(headersString);
|
||||
const headers = this.parseHeaders(headersString);
|
||||
|
||||
if (Object.keys(headers).length > 0) {
|
||||
result.headers = headers;
|
||||
|
||||
// TOOD: extract boundary.
|
||||
}
|
||||
})();
|
||||
|
||||
/* Try to get boundary if no boundary header
|
||||
*/
|
||||
(function () {
|
||||
/* Try to get boundary if no boundary header
|
||||
*/
|
||||
if (!result.boundary) {
|
||||
const boundaryMatch = messageString.match(httpMessageParser._boundaryRegex);
|
||||
const boundaryMatch = messageString.match(this.boundaryRegex);
|
||||
|
||||
if (Array.isArray(boundaryMatch) && boundaryMatch.length) {
|
||||
fullBoundary = boundaryMatch[0].replace(/[\r\n]+/gi, '');
|
||||
@ -114,13 +115,11 @@ function httpMessageParser(message: any) {
|
||||
result.boundary = boundary;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
/* Parse body
|
||||
*/
|
||||
(function () {
|
||||
/* Parse body
|
||||
*/
|
||||
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);
|
||||
|
||||
@ -154,11 +153,9 @@ function httpMessageParser(message: any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
/* Parse multipart sections
|
||||
*/
|
||||
(function () {
|
||||
/* Parse multipart sections
|
||||
*/
|
||||
if (result.boundary) {
|
||||
const multipartStart = messageString.indexOf(fullBoundary) + fullBoundary.length;
|
||||
const multipartEnd = messageString.lastIndexOf(fullBoundary);
|
||||
@ -166,9 +163,9 @@ function httpMessageParser(message: any) {
|
||||
const splitRegex = new RegExp('^' + fullBoundary + '.*[\n\r]?$', 'gm');
|
||||
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
|
||||
const result: httpResult = {};
|
||||
const result: IHttpResult = {};
|
||||
|
||||
const newlineRegex = /\n\n|\r\n\r\n/gim;
|
||||
let newlineIndex = 0;
|
||||
@ -191,7 +188,7 @@ function httpMessageParser(message: any) {
|
||||
let endOffset = null;
|
||||
|
||||
if (newlineIndex > -1) {
|
||||
const headers = httpMessageParser._parseHeaders(possibleHeadersString);
|
||||
const headers = HttpMessageParser.parseHeaders(possibleHeadersString);
|
||||
if (Object.keys(headers).length > 0) {
|
||||
result.headers = headers;
|
||||
|
||||
@ -231,68 +228,73 @@ function httpMessageParser(message: any) {
|
||||
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();
|
||||
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isNaN(value);
|
||||
};
|
||||
|
||||
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;
|
||||
private static trim(str: string): string {
|
||||
const firstNonWhitespaceRegex = /[\w-]+/gim;
|
||||
const firstNonWhitespaceIndex = str.search(firstNonWhitespaceRegex);
|
||||
let tmpStr = "";
|
||||
if (firstNonWhitespaceIndex > 0) {
|
||||
tmpStr = str.slice(firstNonWhitespaceIndex, str.length);
|
||||
str = tmpStr.toString();
|
||||
}
|
||||
});
|
||||
|
||||
return headers;
|
||||
};
|
||||
return str;
|
||||
}
|
||||
|
||||
httpMessageParser._requestLineRegex = /(HTTP|EVENT)\/(1\.0|1\.1|2\.0)\s+(\d+)\s+([\w\s-_]+)/i;
|
||||
httpMessageParser._responseLineRegex = /(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|TRACE|CONNECT)\s+(.*)\s+HTTP\/(1\.0|1\.1|2\.0)/i;
|
||||
// httpMessageParser._headerNewlineRegex = /^[\r\n]+/gim;
|
||||
httpMessageParser._headerNewlineRegex = /^[\r\n]+/gim;
|
||||
httpMessageParser._boundaryRegex = /(\n|\r\n)+--[\w-]+(\n|\r\n)+/g;
|
||||
private static isTruthy = function _isTruthy(value: any): boolean {
|
||||
return !!value;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
@ -5,8 +5,8 @@
|
||||
|
||||
import * as net from 'net';
|
||||
import * as url from 'url';
|
||||
import httpMessageParser from './/httpParser';
|
||||
import { Dictionary } from '../../../Types/types';
|
||||
import HttpMessageParser from './httpParser';
|
||||
import { Dictionary } from '../../Types/types';
|
||||
|
||||
interface IRequest {
|
||||
method: 'PUT' | 'POST' | 'GET' | 'DELETE' | 'PATCH';
|
||||
@ -16,7 +16,34 @@ interface IRequest {
|
||||
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.
|
||||
@ -24,7 +51,7 @@ export const parseMessage = httpMessageParser;
|
||||
* @param pin The authorization token
|
||||
* @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({
|
||||
host: instance.ipAddress,
|
||||
port: instance.port,
|
||||
@ -44,30 +71,3 @@ export function createConnection(instance: { ipAddress: string, port: number },
|
||||
|
||||
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);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Dictionary } from "../../Types/types";
|
||||
import { Dictionary } from "../Types/types";
|
||||
|
||||
/* This file is automatically generated */
|
||||
|
@ -2,35 +2,37 @@ import 'source-map-support/register';
|
||||
import * as crypto from 'crypto';
|
||||
import decamelize from 'decamelize';
|
||||
import * as inflection from 'inflection';
|
||||
import Bonjour, { Service } from 'bonjour';
|
||||
import { EventEmitter } from 'events';
|
||||
import Bonjour from 'bonjour';
|
||||
import { get, put } from 'request-promise-native';
|
||||
|
||||
import { Services, Characteristics } from './hap-types';
|
||||
import { HapMonitor } from './monitor';
|
||||
import { HapMonitor } from './hapMonitor';
|
||||
import { IHapAccessoriesRespType, IServiceType, ICharacteristicType, IHapInstance, createDefaultCharacteristicType, IAccessoryResp } from './interfaces';
|
||||
import { log, Dictionary } from '../Types/types';
|
||||
|
||||
export type HapAccessoriesRespType = IHapAccessoriesRespType;
|
||||
export type ServiceType = IServiceType;
|
||||
export type CharacteristicType = ICharacteristicType;
|
||||
|
||||
export interface IDevice {
|
||||
addresses: Array<string>
|
||||
fqdn: string,
|
||||
host: string,
|
||||
name: string,
|
||||
port: number,
|
||||
protocol: "tcp" | "udp",
|
||||
rawTxt: Buffer,
|
||||
addresses: Array<string>;
|
||||
fqdn: string;
|
||||
host: string;
|
||||
name: string;
|
||||
port: number;
|
||||
protocol: "tcp" | "udp";
|
||||
rawTxt: Buffer;
|
||||
referer: {
|
||||
address: string,
|
||||
family: "IPv4" | "IPv6",
|
||||
port: number,
|
||||
size: number,
|
||||
},
|
||||
subtypes: Array<any>,
|
||||
txt: any,
|
||||
type: string
|
||||
address: string;
|
||||
family: "IPv4" | "IPv6";
|
||||
port: number;
|
||||
size: number;
|
||||
};
|
||||
// eslint-disable-next-line
|
||||
subtypes: Array<any>;
|
||||
// eslint-disable-next-line
|
||||
txt: any;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface IConfig {
|
||||
@ -63,8 +65,8 @@ export class HapClient {
|
||||
|
||||
constructor(opts: {
|
||||
pin: string;
|
||||
logger?: any;
|
||||
config: any;
|
||||
logger: log;
|
||||
config: IConfig;
|
||||
}) {
|
||||
this.pin = opts.pin;
|
||||
this.log = opts.logger;
|
||||
@ -72,19 +74,19 @@ export class HapClient {
|
||||
this.config = opts.config;
|
||||
}
|
||||
|
||||
private debug(msg: any) {
|
||||
private debug(msg: string): void {
|
||||
if (this.debugEnabled) {
|
||||
this.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private debugErr(msg: string, err: Error) {
|
||||
private debugErr(msg: string, err: Error): void {
|
||||
if (this.debugEnabled) {
|
||||
this.log(`${msg}: ${err.message}\n ${err.stack}`);
|
||||
}
|
||||
}
|
||||
|
||||
public refreshInstances() {
|
||||
public refreshInstances(): void {
|
||||
if (!this.discoveryInProgress) {
|
||||
this.discover();
|
||||
} else {
|
||||
@ -99,7 +101,7 @@ export class HapClient {
|
||||
}
|
||||
|
||||
public async discover(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise((resolve): void => {
|
||||
this.discoveryInProgress = true;
|
||||
|
||||
this.browser = this.bonjour.find({
|
||||
@ -111,8 +113,9 @@ export class HapClient {
|
||||
this.debug(`[HapClient] Discovery :: Started`);
|
||||
|
||||
// service found
|
||||
this.browser.on('up', async (service: any) => {
|
||||
let device = service as IDevice;
|
||||
this.browser.on('up', async (service: Bonjour.Service) => {
|
||||
const unknownService: unknown = service as unknown;
|
||||
const device = unknownService as IDevice;
|
||||
if (!device || !device.txt) {
|
||||
this.debug(`[HapClient] Discovery :: Ignoring device that contains no txt records. ${JSON.stringify(device)}`);
|
||||
return;
|
||||
@ -124,7 +127,7 @@ export class HapClient {
|
||||
name: device.txt.md,
|
||||
username: device.txt.id,
|
||||
port: device.port,
|
||||
}
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@ -210,7 +213,7 @@ export class HapClient {
|
||||
resp.accessories && resp.accessories.forEach((accessory: IAccessoryResp) => {
|
||||
accessory.instance = instance;
|
||||
accessories.push(accessory);
|
||||
})
|
||||
});
|
||||
} catch (e) {
|
||||
if (this.log) {
|
||||
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 */
|
||||
const accessoryInformationService = accessory.services.find(x => x.type === Services.AccessoryInformation);
|
||||
const accessoryInformation: { [key: string]: any } = {};
|
||||
const accessoryInformation: Dictionary<unknown> = {};
|
||||
|
||||
if (accessoryInformationService && accessoryInformationService.characteristics) {
|
||||
accessoryInformationService.characteristics.forEach((c) => {
|
||||
@ -272,7 +275,7 @@ export class HapClient {
|
||||
uuid: c.type,
|
||||
type: Characteristics[c.type],
|
||||
serviceType: Services[s.type],
|
||||
serviceName: serviceName!.value.toString(),
|
||||
serviceName: serviceName && serviceName.value ? serviceName.value.toString() : "Service name not found",
|
||||
description: c.description,
|
||||
value: c.value,
|
||||
format: c.format,
|
||||
@ -307,7 +310,7 @@ export class HapClient {
|
||||
.digest('hex');
|
||||
|
||||
/* 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);
|
||||
};
|
||||
|
||||
@ -318,7 +321,7 @@ export class HapClient {
|
||||
|
||||
/* Helper function to returns a characteristic by it's type name */
|
||||
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();
|
||||
};
|
||||
|
||||
@ -345,7 +348,7 @@ export class HapClient {
|
||||
}
|
||||
|
||||
public getService(iid: number): Promise<IServiceType | undefined> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
return new Promise(async (resolve, reject): Promise<void> => {
|
||||
try {
|
||||
const services = await this.getAllServices();
|
||||
return resolve(services.find(x => x.iid === iid));
|
||||
@ -357,7 +360,7 @@ export class HapClient {
|
||||
}
|
||||
|
||||
public async getServiceByName(serviceName: string): Promise<IServiceType | undefined> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
return new Promise(async (resolve, reject): Promise<void> => {
|
||||
try {
|
||||
const services = await this.getAllServices();
|
||||
return resolve(services.find(x => x.serviceName === serviceName));
|
||||
@ -379,7 +382,9 @@ export class HapClient {
|
||||
|
||||
resp.characteristics.forEach((charType: ICharacteristicType) => {
|
||||
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;
|
||||
@ -435,6 +440,6 @@ export class HapClient {
|
||||
}
|
||||
}
|
||||
|
||||
return characteristic
|
||||
return characteristic;
|
||||
}
|
||||
}
|
@ -2,15 +2,16 @@ import { EventEmitter } from 'events';
|
||||
import { IServiceType, IHapEvInstance } from './interfaces';
|
||||
import { createConnection, parseMessage } from './eventedHttpClient';
|
||||
import { CharacteristicType } from './hapClient';
|
||||
import { log } from '../Types/types';
|
||||
|
||||
export class HapMonitor extends EventEmitter {
|
||||
private pin: string;
|
||||
private evInstances: IHapEvInstance[];
|
||||
private services: IServiceType[];
|
||||
private logger: any;
|
||||
private debug: any;
|
||||
private logger: log;
|
||||
private debug: log;
|
||||
|
||||
constructor(logger: any, debug: any, pin: string, services: IServiceType[]) {
|
||||
constructor(logger: log, debug: log, pin: string, services: IServiceType[]) {
|
||||
super();
|
||||
this.logger = logger;
|
||||
this.debug = debug;
|
||||
@ -28,7 +29,7 @@ export class HapMonitor extends EventEmitter {
|
||||
/**
|
||||
* Start monitoring
|
||||
*/
|
||||
public start() {
|
||||
public start(): void {
|
||||
for (const instance of this.evInstances) {
|
||||
instance.socket = createConnection(instance, this.pin, { characteristics: instance.evCharacteristics });
|
||||
|
||||
@ -40,7 +41,7 @@ export class HapMonitor extends EventEmitter {
|
||||
|
||||
if (message.statusCode === 401) {
|
||||
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}.`);
|
||||
}
|
||||
}
|
||||
@ -71,7 +72,7 @@ export class HapMonitor extends EventEmitter {
|
||||
});
|
||||
|
||||
// push update to listeners
|
||||
this.emit('service-update', response.filter((x: any) => x));
|
||||
this.emit('service-update', response.filter((x: unknown) => x));
|
||||
}
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
@ -84,7 +85,7 @@ export class HapMonitor extends EventEmitter {
|
||||
/**
|
||||
* Stop monitoring.
|
||||
*/
|
||||
public stop() {
|
||||
public stop(): void {
|
||||
for (const instance of this.evInstances) {
|
||||
if (instance.socket) {
|
||||
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
|
||||
for (const service of this.services) {
|
||||
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);
|
||||
|
||||
for (const evCharacteristic of evCharacteristics) {
|
||||
if (!instance!.evCharacteristics!.find(x => x.aid === service.aid && x.iid === evCharacteristic.iid)) {
|
||||
instance!.evCharacteristics!.push({ aid: service.aid, iid: evCharacteristic.iid, ev: true });
|
||||
if (instance &&
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
78
src/index.ts
78
src/index.ts
@ -1,65 +1,37 @@
|
||||
import { HapClient } from "./3rdParty/HapClient/hapClient";
|
||||
import { HapMonitor } from "./3rdParty/HapClient/monitor";
|
||||
import { log, Homebridge } from './Types/types';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
let Accessory: any;
|
||||
let Homebridge: any;
|
||||
class AutomationPlatform {
|
||||
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.
|
||||
* @param homebridge
|
||||
*/
|
||||
export default function (homebridge: any) {
|
||||
Homebridge = homebridge;
|
||||
Accessory = homebridge.platformAccessory;
|
||||
export default function (homebridge: Homebridge): void {
|
||||
homebridge.registerPlatform(
|
||||
'ml-automation',
|
||||
'automation',
|
||||
AutomationPlatform,
|
||||
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
24
src/services/monitor.ts
Normal 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: {}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user