bhyve-api
Version:
Unofficial interface to Orbit B-hyve API for Node.js
191 lines (167 loc) • 4.9 kB
JavaScript
// index.js
import axios from 'axios';
import { EventEmitter } from 'events';
import debugLib from 'debug';
import OrbitError from './OrbitError.js';
import WebSocketManager from './WebSocketManager.js';
import ConfigManager from './ConfigManager.js';
const debug = debugLib('bhyve-api');
/**
* Client class that manages connections and interactions with the Orbit API.
*/
class Client extends EventEmitter {
#token;
#userId;
#deviceId;
#stream;
#restConfig;
configManager;
/**
* Initializes a new Client instance with optional custom configuration.
* @param {Object} customConfig Custom configuration settings for the client.
*/
constructor(customConfig = {}) {
super();
this.configManager = new ConfigManager(customConfig);
}
/**
* Authenticates and connects to the Orbit API using the provided configuration settings.
* @param {Object} newConfig Configuration to update before connecting.
*/
async connect(newConfig = {}) {
debug('connect');
if (Object.keys(newConfig).length > 0) {
this.configManager.updateConfig(newConfig);
}
const {
baseURL,
timeout,
email,
password,
maxContentLength,
maxBodyLength,
} =
this.configManager.getConfig();
try {
const response = await axios
.create({
baseURL,
timeout,
maxContentLength,
maxBodyLength,
})
.post('/v1/session', {
session: {
email,
password,
},
});
this.#token = response.data.orbit_session_token;
this.#userId = response.data.user_id;
this.#restConfig = {
baseURL,
timeout,
maxContentLength,
maxBodyLength,
headers: { 'orbit-session-token': this.#token },
};
debug(`connect response.data: ${JSON.stringify(response.data)}`);
this.emit('authenticated', true);
this.emit('user_id', this.#userId);
} catch (err) {
const error = new OrbitError('Failed to connect to Orbit API', {
originalError: err,
});
debug(`connect error ${JSON.stringify(error)}`);
this.emit('error', error);
}
}
/**
* Fetches and emits the list of devices associated with the authenticated user.
*/
async devices() {
try {
const response = await axios
.create(this.#restConfig)
.get(`/v1/devices?user_id=${this.#userId}`);
debug(`devices response.data: ${JSON.stringify(response.data)}`);
this.#deviceId = response.data[0].id;
this.emit('devices', response.data);
this.emit('device_id', this.#deviceId);
} catch (err) {
debug(`devices error: ${err}`);
const error = new OrbitError('Failed to fetch devices', {
originalError: err,
});
this.emit('error', error);
}
}
/**
* Sets up and manages the WebSocket connection for real-time communication.
*/
connectStream() {
debug('connectStream');
const config = this.configManager.getConfig();
this.#stream = new WebSocketManager(
config.wssURL,
{
handshakeTimeout: config.wsTimeout,
},
debug,
);
// Handle open event with custom logic
this.#stream.on('open', () => {
const message = {
event: 'app_connection',
orbit_session_token: this.#token,
};
debug(`stream.on open: authenticate message: ${JSON.stringify(message)}`);
this.#stream.send(message);
});
// Handle message events
this.#stream.on('message', (data) => {
this.emit('message', JSON.parse(data));
});
// Handle errors
this.#stream.on('error', (err) => {
const error = new OrbitError('WebSocket error occurred', {
originalError: err,
});
this.emit('error', error);
});
// Handle close events
this.#stream.on('close', (num, reason) => {
debug(`stream.on close: ${num} reason: ${reason}`);
});
// Handle unexpected response
this.#stream.on('unexpected-response', (request, response) => {
debug(
`stream.on unexpected-response / request: ${JSON.stringify(
request,
)} response: ${JSON.stringify(response)}`,
);
const error = new OrbitError('Unexpected response from WebSocket', {
request: JSON.stringify(request),
response: JSON.stringify(response),
});
this.emit('error', error);
});
}
/**
* Sends a message via the WebSocket connection.
* @param {Object} message The message object to send.
*/
send(message) {
debug(`send: ${JSON.stringify(message)}`);
try {
this.#stream.send(message);
} catch (err) {
this.emit('error', err);
throw new OrbitError('Sending message failed', {
originalError: err,
message,
});
}
}
}
export default Client;