import { Client as WebSocket } from 'faye-websocket' import delay from 'delay' import to from 'await-to-js' import logger from '@uci-utils/logger' let log = logger({ name: 'HomeAssistant:createSocket'}) const MSG_TYPE_AUTH_REQUIRED = 'auth_required' const MSG_TYPE_AUTH_INVALID = 'auth_invalid' const MSG_TYPE_AUTH_OK = 'auth_ok' function createSocket (url,opts) { // includes authentification return new Promise(async (resolve, reject) => { let [errws,socket] = await to(connectAndAuthenticate(url,opts)) if (errws) { log.debug({msg:'error in establishing connection to socket', error:errws, retries:opts.retriesLeft}) if (opts.retriesLeft === 0) throw reject(errws) log.debug(`retrying initial connection in ${opts.retryTimeout/1000} secs`) opts.retriesLeft-- if (opts.retriesLeft >-1) log.debug(`${opts.retriesLeft} connection attemps remaining before aborting`) else log.debug(`${-(opts.retriesLeft+1)} connection attempts`) await delay(opts.retryTimeout) resolve(await createSocket(url,opts)) } resolve(socket) }) // end promise } // end createSocket export default createSocket function connectAndAuthenticate (url,opts) { return new Promise((resolve, reject) => { const auth = JSON.stringify({ type: 'auth', access_token: opts.access_token, api_password: opts.password }) log.debug({msg:'[Auth Phase] Initializing', url:url}) let ws = new WebSocket(url,opts.ws) const closing = reason => { log.debug(`[Auth Phase] cleaning up and closing socket: ${reason}`) cleanup() ws.close(1000,reason) reject(`socket closed, ${reason}`) } const cleanup = () => { log.debug('[Auth Phase] removing authorization listeners') ws.removeListener('message',authorize) ws.removeListener('error', error) clearTimeout(authTimeout) } const authTimeout = setTimeout( handleTimeout.bind(ws,opts.timeout || 5000), opts.timeout || 5000) function handleTimeout(timeout) { closing(`Unable to Authorize in ${timeout} seconds`) } ws.on('error', error) ws.on('message',authorize) function error(err) { log.debug({msg:'[Auth Phase] socket error', error: err.message}) closing(`error occured on socket..aborting\n${err.message}`) } function authorize(event) { let message = JSON.parse(event.data) log.debug(`[Auth Phase] Message Received ${message}`) switch (message.type) { case MSG_TYPE_AUTH_REQUIRED: try { log.debug({msg:'[Auth Phase] sending authorization',auth:auth}) ws.send(auth) } catch (err) { log.debug({msg:'sending auth error', error:err}) closing(`error when sending authorization: ${err}`) } break case MSG_TYPE_AUTH_OK: cleanup() resolve(ws) break case MSG_TYPE_AUTH_INVALID: closing(`bad token or password = ${MSG_TYPE_AUTH_INVALID}`) break default: log.debug({msg:'[Auth Phase] Unhandled message - ignoring', message:message}) } } // end authorize }) } //end authenticate