2020-01-03 16:26:55 -05:00

294 lines
9.4 KiB
TypeScript

import * as Config from "../Models/Config"
import { IAccessory } from './IAccessory';
import callbackify from '../Util/Callbackify';
import HarmonyDataProvider from '../DataProviders/HarmonyDataProvider';
let Service: HAPNodeJS.Service;
let Characteristic: HAPNodeJS.Characteristic;
let Api: any;
let homebridge: any;
/**
* Enum describing remote key presses from homebridge.
*/
export enum RemoteKey {
REWIND = 0,
FAST_FORWARD = 1,
NEXT_TRACK = 2,
PREVIOUS_TRACK = 3,
ARROW_UP = 4,
ARROW_DOWN = 5,
ARROW_LEFT = 6,
ARROW_RIGHT = 7,
SELECT = 8,
BACK = 9,
EXIT = 10,
PLAY_PAUSE = 11,
INFORMATION = 15,
}
export interface IControlUnitProps {
dataProvider: HarmonyDataProvider,
displayName: string,
activities: Array<Config.IActivity>,
api: any,
log: any,
homebridge: any,
}
/**
* ControlUnit accessory
*/
export class ControlUnit implements IAccessory {
//fields
private log: any = {};
private displayName: string = "";
//Service fields
private _televisionService: HAPNodeJS.Service | undefined;
private _televisionSpeakerService: HAPNodeJS.Service | undefined;
private _infoService: HAPNodeJS.Service;
private _inputServices: Array<HAPNodeJS.Service | undefined> = [];
//Harmony fields
private _activities: Array<Config.IActivity> = [];
private _dataProvider: HarmonyDataProvider;
/**
* Constructor
* @param props Input properties
*/
constructor(props: IControlUnitProps) {
//Assign class variables
this.log = props.log;
Api = props.api;
Service = props.api.hap.Service;
Characteristic = props.api.hap.Characteristic;
this.name = props.displayName;
this.displayName = props.displayName;
this._activities = props.activities;
this._dataProvider = props.dataProvider;
homebridge = props.homebridge;
this.platformAccessory = new homebridge.platformAccessory(this.displayName, this.generateUUID(), homebridge.hap.Accessory.Categories.TELEVISION);
//@ts-ignore
this._infoService = new Service.AccessoryInformation();
this._infoService.setCharacteristic(Characteristic.Manufacturer, "Brandon Watson")
this._infoService.setCharacteristic(Characteristic.Model, "Matrix Output Television")
this._infoService.setCharacteristic(Characteristic.SerialNumber, "123-456-789");
//Configure services
this.configureTvService();
this.configureTvSpeakerService();
// this.configureAccessoryInformation();
this.configureInputSourceService();
//Configure external services
this.getServices().forEach(service => {
try {
this.platformAccessory.addService(service);
} catch (error) { }
//@ts-ignore
if (service.linked) {
//@ts-ignore
this._televisionService!.addLinkedService(service);
}
});
}
//Required by homebridge
name: string = "";
public platformAccessory: any;
/*************
*
* Tv Service
*
*************/
/**
* Configure television service
*/
private configureTvService(): void {
this._televisionService = new Service.Television(
this.displayName,
'tvService'
)
this._televisionService.setCharacteristic(Characteristic.ConfiguredName, this.displayName);
//@ts-ignore
this._televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE);
this._televisionService.setCharacteristic(Characteristic.ActiveIdentifier, 1);
this._televisionService.setCharacteristic(Characteristic.Active, false);
//setup listeners
this._televisionService.getCharacteristic(Characteristic.Active)
//@ts-ignore
.on("set", callbackify(this.onSetAccessoryActive))
//@ts-ignore
.on("get", callbackify(this.onGetAccessoryActive));
//Set remote characteristics if is external
this._televisionService.getCharacteristic(Characteristic.RemoteKey)
//@ts-ignore
.on("set", callbackify(this.onSetRemoteKey));
this._televisionService.getCharacteristic(Characteristic.ActiveIdentifier)
//@ts-ignore
.on("set", callbackify(this.onSetActiveIdentifier))
//@ts-ignore
.on("get", callbackify(this.onGetActiveIdentifier));
}
/**
* Event handler for SET active characteristic
*/
private onSetAccessoryActive = async (value: any) => {
switch (value) {
case 0: this._dataProvider.powerOff(this.name); break;
//Turn on with first activity
case 1: this._dataProvider.powerOn(this.name, this._activities[0]); break;
}
}
/**
* Event handler for GET active characteristic
*/
private onGetAccessoryActive = async () => {
//@ts-ignore
return this._dataProvider.getIsActive(this.name) ? Characteristic.Active.Active : Characteristic.Active.Inactive
}
/**
* Event handler for SET remote key
*/
private onSetRemoteKey = async (key: any) => {
this._dataProvider.sendKeyPress(this.name, key);
}
/**
* Event handler for SET active identifier characteristic
*/
private onSetActiveIdentifier = async (identifier: any) => {
this._dataProvider.startActivity(this.name, this._activities[identifier]);
}
/**
* Event handler for GET active identifier characteristic
*/
private onGetActiveIdentifier = async () => {
let currentActivity: Config.IActivity = this._dataProvider.getIsActive(this.name)!;
let identifier: number = 0;
if (currentActivity) {
identifier = this._activities.findIndex(e => e.DisplayName === currentActivity.DisplayName);
}
return identifier;
}
/******************
*
* Speaker Service
*
*****************/
/**
* Configure Speaker Service
*/
private configureTvSpeakerService(): void {
this._televisionSpeakerService = new Service.TelevisionSpeaker(
this.displayName,
'tvSpeakerService'
);
this._televisionSpeakerService.setCharacteristic(Characteristic.Name, this.displayName);
//@ts-ignore
this._televisionSpeakerService.setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE);
//@ts-ignore
this._televisionSpeakerService.setCharacteristic(Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE);
this._televisionSpeakerService.subtype = this.displayName + "Volume";
if (this._televisionService) {
//@ts-ignore
this._televisionService.addLinkedService(this._televisionSpeakerService);
}
//Setup listeners
this._televisionSpeakerService.getCharacteristic(Characteristic.VolumeSelector)
//@ts-ignore
.on("set", callbackify(this.onSetVolumeSelector));
}
/**
* Event handler for SET volume characteristic
*/
private onSetVolumeSelector = async (value: any) => {
switch (value) {
case 0: this._dataProvider.volumeUp(this.name); break;
case 1: this._dataProvider.volumeDown(this.name); break;
}
}
/*****************
*
* Input services
*
*****************/
/**
* Configure input service
*/
private configureInputSourceService(): void {
let inputs: Array<HAPNodeJS.Service> = [];
this._activities.forEach((activity: Config.IActivity, index: number) => {
let inputService = new Service.InputSource(activity.DisplayName, 'activity' + activity.DisplayName);
inputService
.setCharacteristic(Characteristic.Identifier, index)
.setCharacteristic(
Characteristic.ConfiguredName,
activity.DisplayName)
.setCharacteristic(
Characteristic.IsConfigured,
//@ts-ignore
Characteristic.IsConfigured.CONFIGURED
)
//@ts-ignore
.setCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.HDMI);
//@ts-ignore
this._televisionService!.addLinkedService(inputService);
inputs.push(inputService);
});
this._inputServices = inputs;
}
private generateUUID(): string { // Public Domain/MIT
var d = new Date().getTime();
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
d += performance.now(); //use high-precision timer if available
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
/**
* Called by homebridge to gather services for this accessory.
*/
getServices(): Array<HAPNodeJS.Service> {
let services: Array<HAPNodeJS.Service> = [this._infoService, this._televisionService!, this._televisionSpeakerService!];
this._inputServices.forEach((service: HAPNodeJS.Service | undefined) => {
services.push(service!);
});
return (services);
}
}