Complete System Example
Table of Contents
Overview
This example demonstrates a complete RCAMS API integration, including a class-based architecture for managing authentication, resources, and controlling lights. The code is organized into a cohesive client library that you can adapt for your specific integration needs.
Note: This example combines all the concepts from the previous examples into a comprehensive client library. It provides a foundation that you can extend and customize for your specific use case.
Complete RCAMS API Client
The following code implements a complete client library for the RCAMS API, organized into specialized manager classes for different aspects of the system. Click the "View Full Code" button to see the entire implementation, or click on individual class cards below to view and copy specific classes.
Individual Classes
The RCAMS API client is composed of the following specialized classes that you can use individually or together:
RcamsApiClient
Main client class that orchestrates all managers and handles API requests.
AuthManager
Handles authentication operations including login, and logout.
DistrictManager
Manages district operations including listing, creating, updating, and deleting districts.
SchoolManager
Manages school operations including listing, creating, updating, and deleting schools.
GroupManager
Manages group operations including listing, creating, updating, and deleting groups.
UserManager
Manages user operations including listing, creating, updating, and deleting users.
WifiManager
Manages WiFi settings including listing, creating, updating, and deleting WiFi configurations.
LightManager
Manages light operations including listing, retrieving, updating, and deleting lights.
CommandManager
Handles sending commands to lights, groups, schools, and districts.
Light Command Constants
Constants for light commands (e.g., lockdown, evacuate, etc.)
Usage Example
Example demonstrating how to use the RCAMS API Client.
Note: This example provides a comprehensive framework that you can adapt to your specific needs. You may want to extend it with additional features such as error handling, logging, and retry mechanisms for production use.
Complete RCAMS API Client Code
// RCAMS API Client - Complete System Example
// Light Command Constants
const LIGHT_COMMANDS = {
OFF: 'MODE:0', // Turn off the light
LOCKDOWN: 'MODE:1', // RED - Lockdown
SECURE: 'MODE:2', // BLUE - Secure
TEAM_ASSIST: 'MODE:3', // WHITE - Team Assist
EVACUATE: 'MODE:4', // GREEN - Evacuate
SHELTER: 'MODE:5', // ORANGE - Shelter
HOLD: 'MODE:6' // PURPLE - Hold
};
// RCAMS API Client - Main class for interacting with the RCAMS API
class RcamsApiClient {
// Create a new RCAMS API client
// @param {string} apiUsername - API username provided by RCAMS administrator
// @param {string} apiPassword - API password provided by RCAMS administrator
// @param {string} baseUrl - Base URL for the API (optional)
constructor(apiUsername, apiPassword, baseUrl = 'https://rcamsapi.spheronomics.com/api/v2') {
this.apiUsername = apiUsername;
this.apiPassword = apiPassword;
this.baseUrl = baseUrl;
// Create sub-managers
this.auth = new AuthManager(this);
this.districts = new DistrictManager(this);
this.schools = new SchoolManager(this);
this.groups = new GroupManager(this);
this.users = new UserManager(this);
this.wifi = new WifiManager(this);
this.lights = new LightManager(this);
this.commands = new CommandManager(this);
}
// Make an API request
// @param {string} endpoint - API endpoint to call
// @param {Object} params - Parameters to include in the request
// @returns {Promise} API response as JSON
async makeRequest(endpoint, params = {}) {
// Create json data from params
const json = {};
for (const [key, value] of Object.entries(params)) {
json[key] = value;
}
const jsonString = JSON.stringify(json);
try {
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa(`${this.apiUsername}:${this.apiPassword}`)
},
body: jsonString
});
return await response.json();
} catch (error) {
console.error(`API request to ${endpoint} failed:`, error);
throw error;
}
}
}
// Authentication Manager
class AuthManager {
constructor(client) {
this.client = client;
}
// Log in to the RCAMS API
// @param {string} username - User's email address
// @param {string} password - User's password
// @returns {Promise} Login success status
async login(username, password) {
try {
const response = await this.client.makeRequest('login.php', {
user: username,
pass: password
});
if (response.status === 'OK' ) {
return true;
} else {
console.error('Login failed:', response.message);
return false;
}
} catch (error) {
console.error('Login error:', error);
return false;
}
}
// Log out from the RCAMS API
// @returns {Promise} Logout success status
async logout() {
if (!this.client.status) {
console.log('Not logged in.');
return true;
}
try {
const response = await this.client.makeRequest('logout.php', {});
if (response.status === 'ok' || response.status === 'OK') {
this.client.status = null;
return true;
} else {
console.error('Logout failed:', response.message);
return false;
}
} catch (error) {
console.error('Logout error:', error);
return false;
}
}
// Check if currently logged in
// @returns {boolean} Login status
isLoggedIn() {
return this.client.status !== null;
}
}
// District Manager
class DistrictManager {
constructor(client) {
this.client = client;
}
// List all districts
// @returns {Promise} List of districts
async list() {
const response = await this.client.makeRequest('district.php', {
action: 'list'
});
if (response.status !== 'OK') {
throw new Error(`Failed to list districts: ${response.message}`);
}
return response.districts || [];
}
// Get district details
// @param {string} districtId - District ID
// @returns {Promise} District details
async get(districtId) {
const response = await this.client.makeRequest('district.php', {
action: 'get',
district_id: districtId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get district: ${response.message}`);
}
return {
district_id: response.district_id,
district_name: response.district_name
};
}
// Create a new district
// @param {string} name - District name
// @returns {Promise} New district ID
async create(name) {
const response = await this.client.makeRequest('district.php', {
action: 'insert',
district_name: name
});
if (response.status !== 'OK') {
throw new Error(`Failed to create district: ${response.message}`);
}
return response.district_id;
}
// Update a district
// @param {string} districtId - District ID
// @param {string} name - New district name
// @returns {Promise} Update success status
async update(districtId, name) {
const response = await this.client.makeRequest('district.php', {
action: 'update',
district_id: districtId,
district_name: name
});
return response.status === 'OK';
}
// Delete a district
// @param {string} districtId - District ID
// @returns {Promise} Delete success status
async delete(districtId) {
const response = await this.client.makeRequest('district.php', {
action: 'remove',
district_id: districtId
});
return response.status === 'OK';
}
}
// School Manager
class SchoolManager {
constructor(client) {
this.client = client;
}
// List all schools in a district
// @param {string} districtId - District ID
// @returns {Promise} List of schools
async list(districtId) {
const response = await this.client.makeRequest('school.php', {
action: 'list',
district_id: districtId
});
if (response.status !== 'OK') {
throw new Error(`Failed to list schools: ${response.message}`);
}
return response.school_list || [];
}
// Get school details
// @param {string} schoolId - School ID
// @returns {Promise<Object>} School details
async get(schoolId) {
const response = await this.client.makeRequest('school.php', {
action: 'get',
school_id: schoolId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get school: ${response.message}`);
}
return response;
}
// Create a new school
// @param {string} districtId - District ID
// @param {string} name - School name
// @param {Object} options - Additional school details (phone, email, address)
// @returns {Promise} New school ID
async create(districtId, name, options = {}) {
const params = {
action: 'insert',
district_id: districtId,
school_name: name
};
if (options.phone) params.school_phone = options.phone;
if (options.email) params.school_email = options.email;
if (options.address) params.school_address = options.address;
const response = await this.client.makeRequest('school.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to create school: ${response.message}`);
}
return response.school_id || response.district_id; // Handle API inconsistency
}
// Update a school
// @param {string} schoolId - School ID
// @param {string} districtId - District ID
// @param {string} name - New school name
// @param {Object} options - Additional school details (phone, email, address)
// @returns {Promise} Update success status
async update(schoolId, districtId, name, options = {}) {
const params = {
action: 'update',
school_id: schoolId,
district_id: districtId,
school_name: name
};
if (options.phone) params.school_phone = options.phone;
if (options.email) params.school_email = options.email;
if (options.address) params.school_address = options.address;
const response = await this.client.makeRequest('school.php', params);
return response.status === 'OK';
}
// Delete a school
// @param {string} schoolId - School ID
// @returns {Promise} Delete success status
async delete(schoolId) {
const response = await this.client.makeRequest('school.php', {
action: 'remove',
school_id: schoolId
});
return response.status === 'OK';
}
}
// Group Manager
class GroupManager {
constructor(client) {
this.client = client;
}
// List all groups in a school
// @param {string} schoolId - School ID
// @returns {Promise} List of groups
async list(schoolId) {
const response = await this.client.makeRequest('group.php', {
action: 'list',
school_id: schoolId
});
if (response.status !== 'OK') {
throw new Error(`Failed to list groups: ${response.message}`);
}
return response.groups || [];
}
// Get group details
// @param {string} groupId - Group ID
// @returns {Promise<Object>} Group details
async get(groupId) {
const response = await this.client.makeRequest('group.php', {
action: 'get',
group_id: groupId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get group: ${response.message}`);
}
return {
group_id: response.group_id,
group_name: response.group_name,
status: response.status
};
}
// Create a new group
// @param {string} schoolId - School ID
// @param {string} name - Group name
// @returns {Promise} New group ID
async create(schoolId, name) {
const response = await this.client.makeRequest('group.php', {
action: 'insert',
school_id: schoolId,
group_name: name
});
if (response.status !== 'OK') {
throw new Error(`Failed to create group: ${response.message}`);
}
return response.group_id;
}
// Update a group
// @param {string} groupId - Group ID
// @param {string} name - New group name
// @returns {Promise} Update success status
async update(groupId, name) {
const response = await this.client.makeRequest('group.php', {
action: 'update',
group_id: groupId,
group_name: name
});
return response.status === 'OK';
}
// Delete a group
// @param {string} groupId - Group ID
// @returns {Promise} Delete success status
async delete(groupId) {
const response = await this.client.makeRequest('group.php', {
action: 'remove',
group_id: groupId
});
return response.status === 'OK';
}
}
// User Manager
class UserManager {
constructor(client) {
this.client = client;
}
// List all users
// @returns {Promise} List of users
async list() {
const response = await this.client.makeRequest('user.php', {
action: 'list'
});
if (response.status !== 'OK') {
throw new Error(`Failed to list users: ${response.message}`);
}
return response.users || [];
}
// Get user details
// @param {string} userTokenId - User token ID
// @returns {Promise<Object>} User details
async get(userTokenId) {
const response = await this.client.makeRequest('user.php', {
action: 'get',
usr_tokenid: userTokenId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get user: ${response.message}`);
}
return response;
}
// Create a new user
// @param {Object} userData - User data (first_name, last_name, user_name, email, password, etc.)
// @returns {Promise} New user token
async create(userData) {
const params = {
action: 'insert',
first_name: userData.firstName,
last_name: userData.lastName,
user_name: userData.userName,
email: userData.email,
password: userData.password
};
if (userData.phone) params.phone = userData.phone;
if (userData.pictureUrl) params.picture_url = userData.pictureUrl;
if (userData.notes) params.notes = userData.notes;
if (userData.loginNoExpired !== undefined) params.login_no_expired = userData.loginNoExpired ? '1' : '0';
const response = await this.client.makeRequest('user.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to create user: ${response.message}`);
}
return response.user_token;
}
// Update a user
// @param {string} userTokenId - User token ID
// @param {Object} userData - User data to update
// @returns {Promise} Update success status
async update(userTokenId, userData) {
const params = {
action: 'update',
usr_tokenid: userTokenId,
first_name: userData.firstName,
last_name: userData.lastName
};
if (userData.phone) params.phone = userData.phone;
if (userData.pictureUrl) params.picture_url = userData.pictureUrl;
if (userData.notes) params.notes = userData.notes;
if (userData.loginNoExpired !== undefined) params.login_no_expired = userData.loginNoExpired ? '1' : '0';
const response = await this.client.makeRequest('user.php', params);
return response.status === 'OK';
}
// Delete a user
// @param {string} userTokenId - User token ID
// @returns {Promise} Delete success status
async delete(userTokenId) {
const response = await this.client.makeRequest('user.php', {
action: 'remove',
usr_tokenid: userTokenId
});
return response.status === 'OK';
}
// Assign districts to a user
// @param {string} userTokenId - User token ID
// @param {Array} districtIds - Array of district IDs
// @returns {Promise<Object>} Assignment results
async assignDistricts(userTokenId, districtIds) {
const response = await this.client.makeRequest('userdistrict.php', {
usr_tokenid: userTokenId,
district_list: districtIds.join(',')
});
if (response.status !== 'OK') {
throw new Error(`Failed to assign districts: ${response.message}`);
}
return response.result || [];
}
// Get user data (districts, schools, groups, etc.)
// @returns {Promise<Object>} User data structure
async getUserData() {
const response = await this.client.makeRequest('userdata.php', {});
if (response.status !== 'OK') {
throw new Error(`Failed to get user data: ${response.message}`);
}
return response.districts || [];
}
}
// WiFi Manager
class WifiManager {
constructor(client) {
this.client = client;
}
// List all WiFi settings for a school
// @param {string} schoolId - School ID
// @returns {Promise} List of WiFi settings
async list(schoolId) {
const response = await this.client.makeRequest('wifi.php', {
action: 'list',
school_id: schoolId
});
if (response.status !== 'OK') {
throw new Error(`Failed to list WiFi settings: ${response.message}`);
}
return response.wifi_list || [];
}
// Get WiFi setting details
// @param {string} ssidId - SSID ID
// @returns {Promise<Object>} WiFi setting details
async get(ssidId) {
const response = await this.client.makeRequest('wifi.php', {
action: 'get',
ssid_id: ssidId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get WiFi setting: ${response.message}`);
}
return response;
}
// Create a new WiFi setting
// @param {string} schoolId - School ID
// @param {string} ssidName - SSID name
// @param {string} ssidPass - SSID password
// @param {string} ssidDesc - SSID description (optional)
// @returns {Promise} New SSID ID
async create(schoolId, ssidName, ssidPass, ssidDesc = null) {
const params = {
action: 'insert',
school_id: schoolId,
ssid_name: ssidName,
ssid_pass: ssidPass
};
if (ssidDesc) params.ssid_desc = ssidDesc;
const response = await this.client.makeRequest('wifi.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to create WiFi setting: ${response.message}`);
}
return response.wifi_id;
}
// Update a WiFi setting
// @param {string} ssidId - SSID ID
// @param {string} ssidName - New SSID name
// @param {string} ssidPass - New SSID password
// @param {string} ssidDesc - New SSID description (optional)
// @returns {Promise} Update success status
async update(ssidId, ssidName, ssidPass, ssidDesc = null) {
const params = {
action: 'update',
ssid_id: ssidId,
ssid_name: ssidName,
ssid_pass: ssidPass
};
if (ssidDesc) params.ssid_desc = ssidDesc;
const response = await this.client.makeRequest('wifi.php', params);
return response.status === 'OK';
}
// Delete a WiFi setting
// @param {string} ssidId - SSID ID
// @returns {Promise} Delete success status
async delete(ssidId) {
const response = await this.client.makeRequest('wifi.php', {
action: 'remove',
ssid_id: ssidId
});
return response.status === 'OK';
}
}
// Light Manager
class LightManager {
constructor(client) {
this.client = client;
}
// List lights in a school or group
// @param {Object} filters - Filters (school_id, group_id)
// @returns {Promise} List of lights
async list(filters) {
// At least one filter is required
if (!filters.schoolId && !filters.groupId) {
throw new Error('At least one filter (schoolId or groupId) is required.');
}
const params = {};
if (filters.schoolId) params.school_id = filters.schoolId;
if (filters.groupId) params.group_id = filters.groupId;
const response = await this.client.makeRequest('lightlist.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to list lights: ${response.message}`);
}
return response.lights || [];
}
// Get light details
// @param {string} lightMac - Light MAC address
// @returns {Promise<Object>} Light details
async get(lightMac) {
const response = await this.client.makeRequest('light.php', {
action: 'get',
light_mac: lightMac
});
if (response.status !== 'OK') {
throw new Error(`Failed to get light: ${response.message}`);
}
return response.data || {};
}
// Update a light
// @param {string} lightMac - Light MAC address
// @param {string} lightName - New light name
// @param {string} groupId - New group ID
// @returns {Promise} Update success status
async update(lightMac, lightName, groupId) {
const response = await this.client.makeRequest('light.php', {
action: 'update',
light_mac: lightMac,
light_name: lightName,
group_id: groupId
});
return response.status === 'OK';
}
// Delete a light
// @param {string} lightMac - Light MAC address
// @returns {Promise} Delete success status
async delete(lightMac) {
const response = await this.client.makeRequest('light.php', {
action: 'remove',
light_mac: lightMac
});
return response.status === 'OK';
}
// Get light history
// @param {Object} filters - Filters (light_mac, school_id, group_id)
// @returns {Promise<Object>} Light history
async getHistory(filters = {}) {
const params = {};
if (filters.lightMac) params.light_mac = filters.lightMac;
if (filters.schoolId) params.school_id = filters.schoolId;
if (filters.groupId) params.group_id = filters.groupId;
const response = await this.client.makeRequest('lighthistory.php', params);
if (response.status !== 'OK' && response.satus !== 'OK') { // Handle both spelling variations
throw new Error(`Failed to get light history: ${response.message}`);
}
return response.data || {};
}
// Get comprehensive light data
// @returns {Promise<Object>} Light data structure
async getLightData() {
const response = await this.client.makeRequest('lightdata.php', {
usr_token: this.client.token
});
if (response.status !== 'OK') {
throw new Error(`Failed to get light data: ${response.message}`);
}
return response.districts || [];
}
}
// Command Manager
class CommandManager {
constructor(client) {
this.client = client;
this.commands = LIGHT_COMMANDS;
}
// Send a command to an individual light
// @param {string} lightMac - Light MAC address
// @param {string} command - Command to send
// @returns {Promise<Object>} Command result
async sendToLight(lightMac, command) {
const response = await this.client.makeRequest('lightcommands.php', {
light_mac: lightMac,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to light: ${response.message}`);
}
return response;
}
// Send a command to a group of lights
// @param {string} groupId - Group ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendToGroup(groupId, command) {
const response = await this.client.makeRequest('lightcommandsgroup.php', {
group_id: groupId,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to group: ${response.message}`);
}
return response;
}
// Send a command to all lights in a school
// @param {string} schoolId - School ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendToSchool(schoolId, command) {
const response = await this.client.makeRequest('lightcommandsclient.php', {
school_id: schoolId,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to school: ${response.message}`);
}
return response;
}
// Send a command to all lights in a district
// @param {string} districtId - District ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendToDistrict(districtId, command) {
const response = await this.client.makeRequest('lightcommandsdistrict.php', {
district_id: districtId,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to district: ${response.message}`);
}
return response;
}
// Helper methods for specific commands
async turnOff(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.OFF);
}
async sendLockdown(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.LOCKDOWN);
}
async sendSecure(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.SECURE);
}
async sendTeamAssist(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.TEAM_ASSIST);
}
async sendEvacuate(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.EVACUATE);
}
async sendShelter(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.SHELTER);
}
async sendHold(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.HOLD);
}
// Generic command dispatcher
// @param {string} target - Target type (light, group, school, district)
// @param {string} targetId - Target ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendCommand(target, targetId, command) {
switch (target.toLowerCase()) {
case 'light':
return await this.sendToLight(targetId, command);
case 'group':
return await this.sendToGroup(targetId, command);
case 'school':
return await this.sendToSchool(targetId, command);
case 'district':
return await this.sendToDistrict(targetId, command);
default:
throw new Error(`Invalid target type: ${target}. Must be 'light', 'group', 'school', or 'district'.`);
}
}
}
// Example usage of the RCAMS API Client
async function rcamsClientExample() {
// Create client with API credentials
const client = new RcamsApiClient('api_username', 'api_password');
try {
// Login
const loginSuccess = await client.auth.login('user@example.com', 'password123');
if (!loginSuccess) return;
console.log('Authentication successful.');
// Get user's organizational data
const userData = await client.users.getUserData();
console.log(`User has access to ${userData.length} districts.`);
// For demonstration, we'll use the first district, school, and group
if (userData.length > 0) {
const district = userData[0];
console.log(`Working with district: ${district.district_name} (ID: ${district.district_id})`);
if (district.schools && district.schools.length > 0) {
const school = district.schools[0];
console.log(`Working with school: ${school.school_name} (ID: ${school.school_id})`);
if (school.groups && school.groups.length > 0) {
const group = school.groups[0];
console.log(`Working with group: ${group.group_name} (ID: ${group.group_id})`);
// List lights in the group
const lights = await client.lights.list({
schoolId: school.school_id,
groupId: group.group_id
});
console.log(`Found ${lights.length} lights in the group.`);
if (lights.length > 0) {
const light = lights[0];
console.log(`Working with light: ${light.light_name} (MAC: ${light.light_mac})`);
// Send a command to the light
console.log('Sending SECURE command to the light...');
await client.commands.sendSecure('light', light.light_mac);
// Wait 2 seconds
await new Promise(resolve => setTimeout(resolve, 2000));
// Send a command to the group
console.log('Sending EVACUATE command to the group...');
await client.commands.sendEvacuate('group', group.group_id);
// Wait 2 seconds
await new Promise(resolve => setTimeout(resolve, 2000));
// Send a command to the school
console.log('Sending ALL CLEAR command to the school...');
await client.commands.turnOff('school', school.school_id);
// Get light history
console.log('Getting light history...');
const history = await client.lights.getHistory({
schoolId: school.school_id,
groupId: group.group_id
});
console.log('Light history retrieved successfully.');
}
}
}
}
// Logout
await client.auth.logout();
console.log('RCAMS client example completed successfully.');
} catch (error) {
console.error('RCAMS client example error:', error);
}
}
// Run the RCAMS client example
// rcamsClientExample();
RcamsApiClient Class
// RCAMS API Client - Main class for interacting with the RCAMS API
class RcamsApiClient {
// Create a new RCAMS API client
// @param {string} apiUsername - API username provided by RCAMS administrator
// @param {string} apiPassword - API password provided by RCAMS administrator
// @param {string} baseUrl - Base URL for the API (optional)
constructor(apiUsername, apiPassword, baseUrl = 'https://rcamsapi.spheronomics.com/api/v2') {
this.apiUsername = apiUsername;
this.apiPassword = apiPassword;
this.baseUrl = baseUrl;
// Create sub-managers
this.auth = new AuthManager(this);
this.districts = new DistrictManager(this);
this.schools = new SchoolManager(this);
this.groups = new GroupManager(this);
this.users = new UserManager(this);
this.wifi = new WifiManager(this);
this.lights = new LightManager(this);
this.commands = new CommandManager(this);
}
// Make an API request
// @param {string} endpoint - API endpoint to call
// @param {Object} params - Parameters to include in the request
// @param {boolean} requiresToken - Whether the request requires an authentication token
// @returns {Promise<Object>} API response as JSON
async makeRequest(endpoint, params = {}) {
// Create json data from params
const json = {};
for (const [key, value] of Object.entries(params)) {
json[key] = value;
}
const jsonString = JSON.stringify(json);
try {
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa(`${this.apiUsername}:${this.apiPassword}`)
},
body: jsonString
});
return await response.json();
} catch (error) {
console.error(`API request to ${endpoint} failed:`, error);
throw error;
}
}
}
AuthManager Class
// Authentication Manager
class AuthManager {
constructor(client) {
this.client = client;
}
// Log in to the RCAMS API
// @param {string} username - User's email address
// @param {string} password - User's password
// @returns {Promise} Login success status
async login(username, password) {
try {
const response = await this.client.makeRequest('login.php', {
user: username,
pass: password
});
if (response.status === 'OK') {
return true;
} else {
console.error('Login failed:', response.message);
return false;
}
} catch (error) {
console.error('Login error:', error);
return false;
}
}
// Log out from the RCAMS API
// @returns {Promise} Logout success status
async logout() {
if (!this.client.status) {
console.log('Not logged in.');
return true;
}
try {
const response = await this.client.makeRequest('logout.php', {});
if (response.status === 'ok' || response.status === 'OK') {
this.client.status = null;
return true;
} else {
console.error('Logout failed:', response.message);
return false;
}
} catch (error) {
console.error('Logout error:', error);
return false;
}
}
// Check if currently logged in
// @returns {boolean} Login status
isLoggedIn() {
return this.client.status !== null;
}
}
DistrictManager Class
// District Manager
class DistrictManager {
constructor(client) {
this.client = client;
}
// List all districts
// @returns {Promise} List of districts
async list() {
const response = await this.client.makeRequest('district.php', {
action: 'list'
});
if (response.status !== 'OK') {
throw new Error(`Failed to list districts: ${response.message}`);
}
return response.districts || [];
}
// Get district details
// @param {string} districtId - District ID
// @returns {Promise<Object>} District details
async get(districtId) {
const response = await this.client.makeRequest('district.php', {
action: 'get',
district_id: districtId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get district: ${response.message}`);
}
return {
district_id: response.district_id,
district_name: response.district_name
};
}
// Create a new district
// @param {string} name - District name
// @returns {Promise} New district ID
async create(name) {
const response = await this.client.makeRequest('district.php', {
action: 'insert',
district_name: name
});
if (response.status !== 'OK') {
throw new Error(`Failed to create district: ${response.message}`);
}
return response.district_id;
}
// Update a district
// @param {string} districtId - District ID
// @param {string} name - New district name
// @returns {Promise} Update success status
async update(districtId, name) {
const response = await this.client.makeRequest('district.php', {
action: 'update',
district_id: districtId,
district_name: name
});
return response.status === 'OK';
}
// Delete a district
// @param {string} districtId - District ID
// @returns {Promise} Delete success status
async delete(districtId) {
const response = await this.client.makeRequest('district.php', {
action: 'remove',
district_id: districtId
});
return response.status === 'OK';
}
}
SchoolManager Class
// School Manager
class SchoolManager {
constructor(client) {
this.client = client;
}
// List all schools in a district
// @param {string} districtId - District ID
// @returns {Promise} List of schools
async list(districtId) {
const response = await this.client.makeRequest('school.php', {
action: 'list',
district_id: districtId
});
if (response.status !== 'OK') {
throw new Error(`Failed to list schools: ${response.message}`);
}
return response.school_list || [];
}
// Get school details
// @param {string} schoolId - School ID
// @returns {Promise<Object>} School details
async get(schoolId) {
const response = await this.client.makeRequest('school.php', {
action: 'get',
school_id: schoolId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get school: ${response.message}`);
}
return response;
}
// Create a new school
// @param {string} districtId - District ID
// @param {string} name - School name
// @param {Object} options - Additional school details (phone, email, address)
// @returns {Promise} New school ID
async create(districtId, name, options = {}) {
const params = {
action: 'insert',
district_id: districtId,
school_name: name
};
if (options.phone) params.school_phone = options.phone;
if (options.email) params.school_email = options.email;
if (options.address) params.school_address = options.address;
const response = await this.client.makeRequest('school.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to create school: ${response.message}`);
}
return response.school_id || response.district_id; // Handle API inconsistency
}
// Update a school
// @param {string} schoolId - School ID
// @param {string} districtId - District ID
// @param {string} name - New school name
// @param {Object} options - Additional school details (phone, email, address)
// @returns {Promise} Update success status
async update(schoolId, districtId, name, options = {}) {
const params = {
action: 'update',
school_id: schoolId,
district_id: districtId,
school_name: name
};
if (options.phone) params.school_phone = options.phone;
if (options.email) params.school_email = options.email;
if (options.address) params.school_address = options.address;
const response = await this.client.makeRequest('school.php', params);
return response.status === 'OK';
}
// Delete a school
// @param {string} schoolId - School ID
// @returns {Promise} Delete success status
async delete(schoolId) {
const response = await this.client.makeRequest('school.php', {
action: 'remove',
school_id: schoolId
});
return response.status === 'OK';
}
}
GroupManager Class
// Group Manager
class GroupManager {
constructor(client) {
this.client = client;
}
// List all groups in a school
// @param {string} schoolId - School ID
// @returns {Promise} List of groups
async list(schoolId) {
const response = await this.client.makeRequest('group.php', {
action: 'list',
school_id: schoolId
});
if (response.status !== 'OK') {
throw new Error(`Failed to list groups: ${response.message}`);
}
return response.groups || [];
}
// Get group details
// @param {string} groupId - Group ID
// @returns {Promise<Object>} Group details
async get(groupId) {
const response = await this.client.makeRequest('group.php', {
action: 'get',
group_id: groupId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get group: ${response.message}`);
}
return {
group_id: response.group_id,
group_name: response.group_name,
status: response.status
};
}
// Create a new group
// @param {string} schoolId - School ID
// @param {string} name - Group name
// @returns {Promise} New group ID
async create(schoolId, name) {
const response = await this.client.makeRequest('group.php', {
action: 'insert',
school_id: schoolId,
group_name: name
});
if (response.status !== 'OK') {
throw new Error(`Failed to create group: ${response.message}`);
}
return response.group_id;
}
// Update a group
// @param {string} groupId - Group ID
// @param {string} name - New group name
// @returns {Promise} Update success status
async update(groupId, name) {
const response = await this.client.makeRequest('group.php', {
action: 'update',
group_id: groupId,
group_name: name
});
return response.status === 'OK';
}
// Delete a group
// @param {string} groupId - Group ID
// @returns {Promise} Delete success status
async delete(groupId) {
const response = await this.client.makeRequest('group.php', {
action: 'remove',
group_id: groupId
});
return response.status === 'OK';
}
}
UserManager Class
// User Manager
class UserManager {
constructor(client) {
this.client = client;
}
// List all users
// @returns {Promise} List of users
async list() {
const response = await this.client.makeRequest('user.php', {
action: 'list'
});
if (response.status !== 'OK') {
throw new Error(`Failed to list users: ${response.message}`);
}
return response.users || [];
}
// Get user details
// @param {string} userTokenId - User token ID
// @returns {Promise<Object>} User details
async get(userTokenId) {
const response = await this.client.makeRequest('user.php', {
action: 'get',
usr_tokenid: userTokenId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get user: ${response.message}`);
}
return response;
}
// Create a new user
// @param {Object} userData - User data (first_name, last_name, user_name, email, password, etc.)
// @returns {Promise} New user token
async create(userData) {
const params = {
action: 'insert',
first_name: userData.firstName,
last_name: userData.lastName,
user_name: userData.userName,
email: userData.email,
password: userData.password
};
if (userData.phone) params.phone = userData.phone;
if (userData.pictureUrl) params.picture_url = userData.pictureUrl;
if (userData.notes) params.notes = userData.notes;
if (userData.loginNoExpired !== undefined) params.login_no_expired = userData.loginNoExpired ? '1' : '0';
const response = await this.client.makeRequest('user.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to create user: ${response.message}`);
}
return response.user_token;
}
// Update a user
// @param {string} userTokenId - User token ID
// @param {Object} userData - User data to update
// @returns {Promise} Update success status
async update(userTokenId, userData) {
const params = {
action: 'update',
usr_tokenid: userTokenId,
first_name: userData.firstName,
last_name: userData.lastName
};
if (userData.phone) params.phone = userData.phone;
if (userData.pictureUrl) params.picture_url = userData.pictureUrl;
if (userData.notes) params.notes = userData.notes;
if (userData.loginNoExpired !== undefined) params.login_no_expired = userData.loginNoExpired ? '1' : '0';
const response = await this.client.makeRequest('user.php', params);
return response.status === 'OK';
}
// Delete a user
// @param {string} userTokenId - User token ID
// @returns {Promise} Delete success status
async delete(userTokenId) {
const response = await this.client.makeRequest('user.php', {
action: 'remove',
usr_tokenid: userTokenId
});
return response.status === 'OK';
}
// Assign districts to a user
// @param {string} userTokenId - User token ID
// @param {Array} districtIds - Array of district IDs
// @returns {Promise<Object>} Assignment results
async assignDistricts(userTokenId, districtIds) {
const response = await this.client.makeRequest('userdistrict.php', {
usr_tokenid: userTokenId,
district_list: districtIds.join(',')
});
if (response.status !== 'OK') {
throw new Error(`Failed to assign districts: ${response.message}`);
}
return response.result || [];
}
// Get user data (districts, schools, groups, etc.)
// @returns {Promise<Object>} User data structure
async getUserData() {
const response = await this.client.makeRequest('userdata.php', {});
if (response.status !== 'OK') {
throw new Error(`Failed to get user data: ${response.message}`);
}
return response.districts || [];
}
}
WifiManager Class
// WiFi Manager
class WifiManager {
constructor(client) {
this.client = client;
}
// List all WiFi settings for a school
// @param {string} schoolId - School ID
// @returns {Promise} List of WiFi settings
async list(schoolId) {
const response = await this.client.makeRequest('wifi.php', {
action: 'list',
school_id: schoolId
});
if (response.status !== 'OK') {
throw new Error(`Failed to list WiFi settings: ${response.message}`);
}
return response.wifi_list || [];
}
// Get WiFi setting details
// @param {string} ssidId - SSID ID
// @returns {Promise<Object>} WiFi setting details
async get(ssidId) {
const response = await this.client.makeRequest('wifi.php', {
action: 'get',
ssid_id: ssidId
});
if (response.status !== 'OK') {
throw new Error(`Failed to get WiFi setting: ${response.message}`);
}
return response;
}
// Create a new WiFi setting
// @param {string} schoolId - School ID
// @param {string} ssidName - SSID name
// @param {string} ssidPass - SSID password
// @param {string} ssidDesc - SSID description (optional)
// @returns {Promise} New SSID ID
async create(schoolId, ssidName, ssidPass, ssidDesc = null) {
const params = {
action: 'insert',
school_id: schoolId,
ssid_name: ssidName,
ssid_pass: ssidPass
};
if (ssidDesc) params.ssid_desc = ssidDesc;
const response = await this.client.makeRequest('wifi.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to create WiFi setting: ${response.message}`);
}
return response.wifi_id;
}
// Update a WiFi setting
// @param {string} ssidId - SSID ID
// @param {string} ssidName - New SSID name
// @param {string} ssidPass - New SSID password
// @param {string} ssidDesc - New SSID description (optional)
// @returns {Promise} Update success status
async update(ssidId, ssidName, ssidPass, ssidDesc = null) {
const params = {
action: 'update',
ssid_id: ssidId,
ssid_name: ssidName,
ssid_pass: ssidPass
};
if (ssidDesc) params.ssid_desc = ssidDesc;
const response = await this.client.makeRequest('wifi.php', params);
return response.status === 'OK';
}
// Delete a WiFi setting
// @param {string} ssidId - SSID ID
// @returns {Promise} Delete success status
async delete(ssidId) {
const response = await this.client.makeRequest('wifi.php', {
action: 'remove',
ssid_id: ssidId
});
return response.status === 'OK';
}
}
LightManager Class
// Light Manager
class LightManager {
constructor(client) {
this.client = client;
}
// List lights in a school or group
// @param {Object} filters - Filters (school_id, group_id)
// @returns {Promise} List of lights
async list(filters) {
// At least one filter is required
if (!filters.schoolId && !filters.groupId) {
throw new Error('At least one filter (schoolId or groupId) is required.');
}
const params = {};
if (filters.schoolId) params.school_id = filters.schoolId;
if (filters.groupId) params.group_id = filters.groupId;
const response = await this.client.makeRequest('lightlist.php', params);
if (response.status !== 'OK') {
throw new Error(`Failed to list lights: ${response.message}`);
}
return response.lights || [];
}
// Get light details
// @param {string} lightMac - Light MAC address
// @returns {Promise<Object>} Light details
async get(lightMac) {
const response = await this.client.makeRequest('light.php', {
action: 'get',
light_mac: lightMac
});
if (response.status !== 'OK') {
throw new Error(`Failed to get light: ${response.message}`);
}
return response.data || {};
}
// Update a light
// @param {string} lightMac - Light MAC address
// @param {string} lightName - New light name
// @param {string} groupId - New group ID
// @returns {Promise} Update success status
async update(lightMac, lightName, groupId) {
const response = await this.client.makeRequest('light.php', {
action: 'update',
light_mac: lightMac,
light_name: lightName,
group_id: groupId
});
return response.status === 'OK';
}
// Delete a light
// @param {string} lightMac - Light MAC address
// @returns {Promise} Delete success status
async delete(lightMac) {
const response = await this.client.makeRequest('light.php', {
action: 'remove',
light_mac: lightMac
});
return response.status === 'OK';
}
// Get light history
// @param {Object} filters - Filters (light_mac, school_id, group_id)
// @returns {Promise<Object>} Light history
async getHistory(filters = {}) {
const params = {};
if (filters.lightMac) params.light_mac = filters.lightMac;
if (filters.schoolId) params.school_id = filters.schoolId;
if (filters.groupId) params.group_id = filters.groupId;
const response = await this.client.makeRequest('lighthistory.php', params);
if (response.status !== 'OK' && response.satus !== 'OK') { // Handle both spelling variations
throw new Error(`Failed to get light history: ${response.message}`);
}
return response.data || {};
}
// Get comprehensive light data
// @returns {Promise<Object>} Light data structure
async getLightData() {
const response = await this.client.makeRequest('lightdata.php', {
usr_token: this.client.token
});
if (response.status !== 'OK') {
throw new Error(`Failed to get light data: ${response.message}`);
}
return response.districts || [];
}
}
CommandManager Class
// Command Manager
class CommandManager {
constructor(client) {
this.client = client;
this.commands = LIGHT_COMMANDS;
}
// Send a command to an individual light
// @param {string} lightMac - Light MAC address
// @param {string} command - Command to send
// @returns {Promise<Object>} Command result
async sendToLight(lightMac, command) {
const response = await this.client.makeRequest('lightcommands.php', {
light_mac: lightMac,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to light: ${response.message}`);
}
return response;
}
// Send a command to a group of lights
// @param {string} groupId - Group ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendToGroup(groupId, command) {
const response = await this.client.makeRequest('lightcommandsgroup.php', {
group_id: groupId,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to group: ${response.message}`);
}
return response;
}
// Send a command to all lights in a school
// @param {string} schoolId - School ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendToSchool(schoolId, command) {
const response = await this.client.makeRequest('lightcommandsclient.php', {
school_id: schoolId,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to school: ${response.message}`);
}
return response;
}
// Send a command to all lights in a district
// @param {string} districtId - District ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendToDistrict(districtId, command) {
const response = await this.client.makeRequest('lightcommandsdistrict.php', {
district_id: districtId,
message: command
});
if (response.status !== 'OK') {
throw new Error(`Failed to send command to district: ${response.message}`);
}
return response;
}
// Helper methods for specific commands
async turnOff(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.OFF);
}
async sendLockdown(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.LOCKDOWN);
}
async sendSecure(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.SECURE);
}
async sendTeamAssist(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.TEAM_ASSIST);
}
async sendEvacuate(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.EVACUATE);
}
async sendShelter(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.SHELTER);
}
async sendHold(target, targetId) {
return await this.sendCommand(target, targetId, this.commands.HOLD);
}
// Generic command dispatcher
// @param {string} target - Target type (light, group, school, district)
// @param {string} targetId - Target ID
// @param {string} command - Command to send
// @returns {Promise<Object>} Command results
async sendCommand(target, targetId, command) {
switch (target.toLowerCase()) {
case 'light':
return await this.sendToLight(targetId, command);
case 'group':
return await this.sendToGroup(targetId, command);
case 'school':
return await this.sendToSchool(targetId, command);
case 'district':
return await this.sendToDistrict(targetId, command);
default:
throw new Error(`Invalid target type: ${target}. Must be 'light', 'group', 'school', or 'district'.`);
}
}
}
Light Command Constants
// Light Command Constants
const LIGHT_COMMANDS = {
OFF: 'MODE:0', // Turn off the light
LOCKDOWN: 'MODE:1', // RED - Lockdown
SECURE: 'MODE:2', // BLUE - Secure
TEAM_ASSIST: 'MODE:3', // WHITE - Team Assist
EVACUATE: 'MODE:4', // GREEN - Evacuate
SHELTER: 'MODE:5', // ORANGE - Shelter
HOLD: 'MODE:6' // PURPLE - Hold
};
Usage Example
// Example usage of the RCAMS API Client
async function rcamsClientExample() {
// Create client with API credentials
const client = new RcamsApiClient('api_username', 'api_password');
try {
// Login
const loginSuccess = await client.auth.login('user@example.com', 'password123');
if (!loginSuccess) return;
console.log('Authentication successful.');
// Get user's organizational data
const userData = await client.users.getUserData();
console.log(`User has access to ${userData.length} districts.`);
// For demonstration, we'll use the first district, school, and group
if (userData.length > 0) {
const district = userData[0];
console.log(`Working with district: ${district.district_name} (ID: ${district.district_id})`);
if (district.schools && district.schools.length > 0) {
const school = district.schools[0];
console.log(`Working with school: ${school.school_name} (ID: ${school.school_id})`);
if (school.groups && school.groups.length > 0) {
const group = school.groups[0];
console.log(`Working with group: ${group.group_name} (ID: ${group.group_id})`);
// List lights in the group
const lights = await client.lights.list({
schoolId: school.school_id,
groupId: group.group_id
});
console.log(`Found ${lights.length} lights in the group.`);
if (lights.length > 0) {
const light = lights[0];
console.log(`Working with light: ${light.light_name} (MAC: ${light.light_mac})`);
// Send a command to the light
console.log('Sending SECURE command to the light...');
await client.commands.sendSecure('light', light.light_mac);
// Wait 2 seconds
await new Promise(resolve => setTimeout(resolve, 2000));
// Send a command to the group
console.log('Sending EVACUATE command to the group...');
await client.commands.sendEvacuate('group', group.group_id);
// Wait 2 seconds
await new Promise(resolve => setTimeout(resolve, 2000));
// Send a command to the school
console.log('Sending ALL CLEAR command to the school...');
await client.commands.turnOff('school', school.school_id);
// Get light history
console.log('Getting light history...');
const history = await client.lights.getHistory({
schoolId: school.school_id,
groupId: group.group_id
});
console.log('Light history retrieved successfully.');
}
}
}
}
// Logout
await client.auth.logout();
console.log('RCAMS client example completed successfully.');
} catch (error) {
console.error('RCAMS client example error:', error);
}
}
// Run the RCAMS client example
// rcamsClientExample();