104 lines
3.1 KiB
JavaScript
104 lines
3.1 KiB
JavaScript
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
|