androidtv-remote-slstn
Version:
AndroidTV Remote
275 lines (256 loc) • 10 kB
JavaScript
import tls from "tls";
import { remoteMessageManager } from "./RemoteMessageManager.js";
import EventEmitter from "events";
class RemoteManager extends EventEmitter {
constructor(host, port, certs) {
super();
this.host = host;
this.port = port;
this.certs = certs;
this.chunks = Buffer.from([]);
this.error = null;
}
async start() {
let isEmitedError = false;
return new Promise((resolve, reject) => {
let options = {
key: this.certs.key,
cert: this.certs.cert,
port: this.port,
host: this.host,
rejectUnauthorized: false,
};
console.debug("Start Remote Connect");
this.client = tls.connect(options, () => {
//console.debug("Remote connected")
});
this.client.on("timeout", () => {
this.client.destroy();
if (!isEmitedError) {
this.emit("error", { message: "Connection timeout" });
isEmitedError = true;
}
resolve({ state: "error", message: "Connection timeout" });
});
// Le ping est reçu toutes les 5 secondes
this.client.setTimeout(10000);
this.client.on("secureConnect", () => {
console.debug(this.host + " Remote secureConnect");
resolve(true);
});
this.client.on("data", (data) => {
let buffer = Buffer.from(data);
this.chunks = Buffer.concat([this.chunks, buffer]);
if (
this.chunks.length > 0 &&
this.chunks.readInt8(0) === this.chunks.length - 1
) {
let message = remoteMessageManager.parse(this.chunks);
if (!message.remotePingRequest) {
//console.debug(this.host + " Receive : " + Array.from(this.chunks));
console.debug(
this.host + " Receive : " + JSON.stringify(message.toJSON())
);
}
if (message.remoteConfigure) {
this.client.write(
remoteMessageManager.createRemoteConfigure(
622,
"Build.MODEL",
"Build.MANUFACTURER",
1,
"Build.VERSION.RELEASE"
)
);
this.emit("ready");
} else if (message.remoteSetActive) {
this.client.write(remoteMessageManager.createRemoteSetActive(622));
} else if (message.remotePingRequest) {
this.client.write(
remoteMessageManager.createRemotePingResponse(
message.remotePingRequest.val1
)
);
} else if (message.remoteImeKeyInject) {
this.emit(
"current_app",
message.remoteImeKeyInject.appInfo.appPackage
);
} else if (message.remoteImeBatchEdit) {
console.debug(
"Receive IME BATCH EDIT" + message.remoteImeBatchEdit
);
} else if (message.remoteImeShowRequest) {
console.debug(
"Receive IME SHOW REQUEST" + message.remoteImeShowRequest
);
} else if (message.remoteVoiceBegin) {
//console.debug("Receive VOICE BEGIN" + message.remoteVoiceBegin);
} else if (message.remoteVoicePayload) {
//console.debug("Receive VOICE PAYLOAD" + message.remoteVoicePayload);
} else if (message.remoteVoiceEnd) {
//console.debug("Receive VOICE END" + message.remoteVoiceEnd);
} else if (message.remoteStart) {
this.emit("powered", message.remoteStart.started);
} else if (message.remoteSetVolumeLevel) {
this.emit("volume", {
level: message.remoteSetVolumeLevel.volumeLevel,
maximum: message.remoteSetVolumeLevel.volumeMax,
muted: message.remoteSetVolumeLevel.volumeMuted,
});
//console.debug("Receive SET VOLUME LEVEL" + message.remoteSetVolumeLevel.toJSON().toString());
} else if (message.remoteSetPreferredAudioDevice) {
//console.debug("Receive SET PREFERRED AUDIO DEVICE" + message.remoteSetPreferredAudioDevice);
} else if (message.remoteError) {
//console.debug("Receive REMOTE ERROR");
if (!isEmitedError) {
this.emit("error", { message: message.remoteError });
isEmitedError = true;
}
resolve({ state: "error", message: message.remoteError });
} else {
console.log("What else ?");
if (!isEmitedError) {
this.emit("error", this.error);
isEmitedError = true;
}
resolve({ state: "error", message: "Unknown error" });
}
this.chunks = Buffer.from([]);
}
});
this.client.on("close", async (hasError) => {
console.info(this.host + " Remote Connection closed ", hasError);
if (hasError) {
reject(this.error.code);
if (this.error.code === "ECONNRESET") {
this.emit("unpaired");
resolve({ state: "error", message: "Device is not paired" });
} else if (this.error.code === "ECONNREFUSED") {
// L'appareil n'est pas encore prêt : on relance
// await new Promise(resolve => setTimeout(resolve, 1000));
// await this.start().catch((error) => {
// console.error(error);
// });
if (!isEmitedError) {
this.emit("error", { message: "Connection refused" });
isEmitedError = true;
}
resolve({ state: "error", message: "Connection refused" });
} else if (this.error.code === "EHOSTDOWN") {
// L'appareil est down, on ne fait rien
if (!isEmitedError) {
this.emit("error", { message: "Host is down" });
isEmitedError = true;
}
resolve({ state: "error", message: "Host is down" });
} else if (
this.error.code === "ERR_SSL_SSLV3_ALERT_CERTIFICATE_UNKNOWN"
) {
if (!isEmitedError) {
this.emit("error", { message: "Certificate is revoked" });
isEmitedError = true;
}
resolve({
state: "error",
message: "Certificate is revoked",
});
} else {
// Dans le doute on redémarre
// await new Promise(resolve => setTimeout(resolve, 1000));
// await this.start().catch((error) => {
// console.error(error);
// });
if (!isEmitedError) {
this.emit("error", this.error);
isEmitedError = true;
}
resolve({ state: "error", message: "Unknown error" });
}
} else {
// Si pas d'erreur on relance. Si elle s'est éteinte alors une erreur empéchera de relancer encore
// await new Promise((resolve) => setTimeout(resolve, 1000));
// await this.start().catch((error) => {
// console.error(error);
// });
if (this.error) {
if (this.error.code === "ECONNRESET") {
this.emit("unpaired");
resolve({ state: "error", message: "Device is not paired" });
} else if (this.error.code === "ECONNREFUSED") {
// L'appareil n'est pas encore prêt : on relance
// await new Promise(resolve => setTimeout(resolve, 1000));
// await this.start().catch((error) => {
// console.error(error);
// });
if (!isEmitedError) {
this.emit("error", { message: "Connection refused" });
isEmitedError = true;
}
resolve({ state: "error", message: "Connection refused" });
} else if (this.error.code === "EHOSTDOWN") {
// L'appareil est down, on ne fait rien
if (!isEmitedError) {
this.emit("error", { message: "Host is down" });
isEmitedError = true;
}
resolve({ state: "error", message: "Host is down" });
} else if (
this.error.code === "ERR_SSL_SSLV3_ALERT_CERTIFICATE_UNKNOWN"
) {
if (!isEmitedError) {
this.emit("error", { message: "Certificate is revoked" });
isEmitedError = true;
}
resolve({
state: "error",
message: "Certificate is revoked",
});
} else {
// Dans le doute on redémarre
// await new Promise(resolve => setTimeout(resolve, 1000));
// await this.start().catch((error) => {
// console.error(error);
// });
if (!isEmitedError) {
this.emit("error", this.error);
isEmitedError = true;
}
resolve({ state: "error", message: this.error.message });
}
} else {
console.log("Has no error");
resolve({ state: "success", message: "Has no error" });
}
}
});
this.client.on("error", (error) => {
console.error(this.host, error);
this.error = error;
resolve({ state: "error", message: error.message });
});
});
}
sendPower() {
this.client.write(
remoteMessageManager.createRemoteKeyInject(
remoteMessageManager.RemoteDirection.SHORT,
remoteMessageManager.RemoteKeyCode.KEYCODE_POWER
)
);
}
sendKey(key, direction) {
this.client.write(
remoteMessageManager.createRemoteKeyInject(direction, key)
);
}
sendAppLink(app_link) {
this.client.write(
remoteMessageManager.createRemoteRemoteAppLinkLaunchRequest(app_link)
);
}
stop() {
this.client.destroy();
}
}
export { RemoteManager };