UNPKG

tizen-remote

Version:

Samsung smart tv 2016+ models remote control

158 lines (129 loc) 5.24 kB
const EventEmitter = require('events'); const WebSocket = require('ws'); const {WS_PORT, WS_OPEN_TIMEOUT, WS_CHECK_TIMEOUT, WS_CHECK_INTERVAL} = require('./constants'); class SocketAdater extends EventEmitter { constructor(ip, serviceName, token = null, logger = null) { super(); this._ip = ip; this._serviceName = serviceName; this._token = token; this._logger = logger; this._ws = null; this._connectionPromise = null; this._pingTimeout = () => { if (!this._ws) return; if (this._logger) { this._logger.warn('Socket ping timeout. Closing socket...'); } this._ws.close(); this._ws = null; clearTimeout(this._pingTimer); }; } connect() { if (this._ws && !this._connectionPromise) { return Promise.resolve(); } if (!this._connectionPromise) { this._connectionPromise = new Promise(async (resolve, reject) => { const onConnectionError = (reason) => { if (this._connectionPromise) { this._connectionPromise = null; clearTimeout(openTimer); if (this._ws) { this._ws.close(); } reject(reason); } }; const openTimer = setTimeout(() => onConnectionError('Socket opening timeout'), WS_OPEN_TIMEOUT); // Start connection const name = Buffer.from(this._serviceName).toString('base64'); let wsEndpoint = `wss://${this._ip}:${WS_PORT}/api/v2/channels/samsung.remote.control?name=${name}`; if (this._token) { wsEndpoint += `&token=${this._token}`; } this._ws = new WebSocket(wsEndpoint, { rejectUnauthorized: false }); // When the socket has an error this._ws.on('error', onConnectionError); // When the socket is closed this._ws.on('close', () => { if (this._ws) { this._ws = null; clearTimeout(this._pingTimer); this.emit('disconnected'); } }); // When the socket is open this._ws.on('message', data => { // Parse response const response = JSON.parse(data); // We are connected if (response.event === 'ms.channel.connect') { this._clientId = response.data.id; if (this._logger) { this._logger.info('connected: ',this._clientId); } this.emit('connected', this._clientId); // Save token if updated if (response.data.token) { this._token = response.data.token; this.emit('tokenUpdate', this._token); } this._connectionPromise = null; this._ping(); clearTimeout(openTimer); resolve(); } else { this.emit(response.event, response); // Other response if (response.event !== '_ping' && this._logger) { this._logger.info('WS event:\n'+data); } } }); }); } return this._connectionPromise; } disconnect() { if (this._ws) { this._ws.close(); clearTimeout(this._pingTimer); clearTimeout(this._connectionTimer); } } _ping() { if (!this._ws) return; this._connectionTimer = setTimeout(this._pingTimeout, WS_CHECK_TIMEOUT); this.once('_ping', (event) => { if (event.from === this._clientId) { clearTimeout(this._connectionTimer); this._pingTimer = setTimeout(() => this._ping(), WS_CHECK_INTERVAL); } }); this._sendWS({ method:'ms.channel.emit', params: { event: '_ping', data: new Date().toISOString(), to: this._clientId } }); } _sendWS(data) { return this.connect() .then(() => new Promise((resolve, reject) => { this._ws.send(JSON.stringify(data), error => { if (error) { reject(error); return; } resolve(); }) })); } } module.exports = {SocketAdapter: SocketAdater};