energy-manager-iot
Version:
Library for energy management in IoT devices via MQTT protocol. Documentation: https://jonhvmp.github.io/energy-manager-iot-docs/
396 lines (395 loc) • 14.6 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeviceRegistry = void 0;
const validators_1 = require("../utils/validators");
const error_handler_1 = require("../utils/error-handler");
const logger_1 = __importDefault(require("../utils/logger"));
/**
* Manages IoT device registration and grouping
*
* This class provides a centralized registry for all IoT devices,
* with features for device management, grouping, and status updates.
* It enforces validation rules for device IDs and configurations.
*
* @remarks
* The registry maintains device-group relationships in both directions
* for efficient querying and management.
*/
class DeviceRegistry {
devices;
groups;
logger = logger_1.default.child('DeviceRegistry');
constructor() {
this.devices = new Map();
this.groups = new Map();
}
/**
* Registers a new device in the system
*
* @param id - Unique identifier for the device
* @param name - Human-readable device name
* @param type - Type of device from the DeviceType enum
* @param config - Optional device configuration parameters
* @param groups - Optional initial groups to assign the device to
* @returns The newly registered device object
* @throws {EnergyManagerError} If device ID is invalid or already exists
*
* @example
* ```ts
* registry.registerDevice('temp01', 'Kitchen Temperature', DeviceType.SENSOR);
* ```
*/
registerDevice(id, name, type, config = {}, groups = []) {
// Validate ID
if (!(0, validators_1.validateDeviceId)(id)) {
throw new error_handler_1.EnergyManagerError(`Invalid device ID: ${id}`, error_handler_1.ErrorType.VALIDATION);
}
// Check if device already exists
if (this.devices.has(id)) {
throw new error_handler_1.EnergyManagerError(`Device with ID ${id} already exists`, error_handler_1.ErrorType.VALIDATION);
}
// Validate configuration
if (!(0, validators_1.validateDeviceConfig)(config)) {
throw new error_handler_1.EnergyManagerError(`Invalid configuration for device ${id}`, error_handler_1.ErrorType.VALIDATION);
}
// Create device
const now = Date.now();
const device = {
id,
name,
type,
groups: [],
config,
createdAt: now,
updatedAt: now
};
// Add to specified groups
if (groups.length > 0) {
for (const groupName of groups) {
this.addDeviceToGroup(id, groupName, device);
}
} // Store device
this.devices.set(id, device);
this.logger.info(`Device registered successfully`, {
deviceId: id,
deviceName: name,
deviceType: type,
groupCount: groups.length
});
return device;
}
/**
* Updates an existing device's properties
*
* @param id - ID of the device to update
* @param updates - Object containing properties to update
* @returns The updated device object
* @throws {EnergyManagerError} If device not found or configuration is invalid
*/
updateDevice(id, updates) {
const device = this.getDevice(id);
// Update properties
if (updates.name) {
device.name = updates.name;
}
if (updates.type) {
device.type = updates.type;
}
if (updates.config) {
// Validate new configuration
if (!(0, validators_1.validateDeviceConfig)(updates.config)) {
throw new error_handler_1.EnergyManagerError(`Invalid configuration for device ${id}`, error_handler_1.ErrorType.VALIDATION);
}
device.config = { ...device.config, ...updates.config };
}
// Update timestamp
device.updatedAt = Date.now(); // Update in map
this.devices.set(id, device);
this.logger.info(`Device updated`, {
deviceId: id,
updatedFields: Object.keys(updates),
timestamp: device.updatedAt
});
return device;
}
/**
* Updates a device's status information
*
* @param id - ID of the device to update
* @param status - New status information
* @returns The updated device object
* @throws {EnergyManagerError} If device not found
*/
updateDeviceStatus(id, status) {
const device = this.getDevice(id);
// Update status and timestamp
device.status = status;
device.updatedAt = Date.now(); // Update in map
this.devices.set(id, device);
this.logger.debug(`Device status updated`, {
deviceId: id,
connectionStatus: status.connectionStatus,
timestamp: device.updatedAt
});
return device;
}
/**
* Removes a device from the registry and all its groups
*
* @param id - ID of the device to remove
* @returns True if device was found and removed, false otherwise
*/
removeDevice(id) {
if (!this.devices.has(id)) {
return false;
}
// Get device's groups
const device = this.devices.get(id);
// Remove from all groups
for (const groupName of device.groups) {
const group = this.groups.get(groupName);
if (group) {
group.delete(id);
}
} // Remove from registry
this.devices.delete(id);
this.logger.info(`Device removed from registry`, {
deviceId: id,
deviceName: device.name,
removedFromGroups: device.groups
});
return true;
}
/**
* Retrieves a device by its ID
*
* @param id - ID of the device to retrieve
* @returns The device object
* @throws {EnergyManagerError} If device not found
*/ getDevice(id) {
const device = this.devices.get(id);
if (!device) {
this.logger.warn(`Device lookup failed - device not found`, { deviceId: id });
throw new error_handler_1.EnergyManagerError(`Device not found: ${id}`, error_handler_1.ErrorType.DEVICE_NOT_FOUND);
}
// Add trace log for device retrieval
this.logger.trace(`Device retrieved`, { deviceId: id });
return device;
}
/**
* Checks if a device exists in the registry
*
* @param id - ID of the device to check
* @returns True if device exists, false otherwise
*/
hasDevice(id) {
return this.devices.has(id);
}
/**
* Creates a new device group
*
* @param name - Name for the new group
* @returns True if group was created, false if it already exists
* @throws {EnergyManagerError} If group name is invalid
*/
createGroup(name) {
// Validate group name
if (!(0, validators_1.validateGroupName)(name)) {
throw new error_handler_1.EnergyManagerError(`Invalid group name: ${name}`, error_handler_1.ErrorType.VALIDATION);
}
// Check if group already exists
if (this.groups.has(name)) {
return false;
} // Create empty group
this.groups.set(name, new Set());
this.logger.info(`Group created`, {
groupName: name,
timestamp: Date.now()
});
return true;
}
/**
* Adds a device to a group
*
* @param deviceId - ID of the device to add
* @param groupName - Name of the group to add the device to
* @param device - Optional device object (to avoid lookup if already available)
* @returns True if the device was added to the group
* @throws {EnergyManagerError} If group name is invalid or device not found
*/
addDeviceToGroup(deviceId, groupName, device) {
// Validate group name
if (!(0, validators_1.validateGroupName)(groupName)) {
throw new error_handler_1.EnergyManagerError(`Invalid group name: ${groupName}`, error_handler_1.ErrorType.VALIDATION);
}
// Get or create the group
if (!this.groups.has(groupName)) {
this.createGroup(groupName);
}
// Device reference
const deviceRef = device || this.getDevice(deviceId);
// Add to group
const group = this.groups.get(groupName);
group.add(deviceId);
// Add group to device if not already there
if (!deviceRef.groups.includes(groupName)) {
deviceRef.groups.push(groupName);
deviceRef.updatedAt = Date.now();
this.devices.set(deviceId, deviceRef);
} // Log with device correlation ID
this.logger.withCorrelationId(deviceId).debug(`Device added to group`, {
deviceId,
groupName,
deviceGroups: deviceRef.groups,
groupSize: group.size
});
return true;
}
/**
* Removes a device from a group
*
* @param deviceId - ID of the device to remove
* @param groupName - Name of the group to remove the device from
* @returns True if the device was removed from the group
* @throws {EnergyManagerError} If group not found
*/
removeDeviceFromGroup(deviceId, groupName) {
// Check if group exists
if (!this.groups.has(groupName)) {
throw new error_handler_1.EnergyManagerError(`Group not found: ${groupName}`, error_handler_1.ErrorType.GROUP_NOT_FOUND);
}
// Remove from group
const group = this.groups.get(groupName);
const removed = group.delete(deviceId);
if (removed) {
// Remove group from device's group list
const device = this.devices.get(deviceId);
if (device) {
device.groups = device.groups.filter(g => g !== groupName);
device.updatedAt = Date.now();
this.devices.set(deviceId, device);
this.logger.withCorrelationId(deviceId).debug(`Device removed from group`, {
deviceId,
groupName,
remainingGroups: device.groups.length
});
}
}
return removed;
}
/**
* Removes a group and disassociates all devices from it
*
* @param name - Name of the group to remove
* @returns True if group was found and removed
*/
removeGroup(name) {
// Check if group exists
if (!this.groups.has(name)) {
return false;
}
// Get devices in group
const group = this.groups.get(name);
// Remove group from each device
for (const deviceId of group) {
const device = this.devices.get(deviceId);
if (device) {
device.groups = device.groups.filter(g => g !== name);
device.updatedAt = Date.now();
this.devices.set(deviceId, device);
}
} // Remove the group
this.groups.delete(name);
this.logger.info(`Group removed`, {
groupName: name,
deviceCount: group.size,
timestamp: Date.now()
});
return true;
}
/**
* Retrieves all devices in a group
*
* @param groupName - Name of the group to query
* @returns Array of devices in the group
* @throws {EnergyManagerError} If group not found
*/ getDevicesInGroup(groupName) {
// Check if group exists
if (!this.groups.has(groupName)) {
this.logger.warn(`Group lookup failed - group not found`, { groupName });
throw new error_handler_1.EnergyManagerError(`Group not found: ${groupName}`, error_handler_1.ErrorType.GROUP_NOT_FOUND);
}
const group = this.groups.get(groupName);
const devices = []; // Collect all devices in the group
for (const deviceId of group) {
const device = this.devices.get(deviceId);
if (device) {
devices.push(device);
}
else {
// Log inconsistency in data
this.logger.warn(`Data inconsistency detected: Device in group not found in registry`, {
groupName,
deviceId,
timestamp: Date.now()
});
}
}
this.logger.debug(`Retrieved devices in group`, {
groupName,
deviceCount: devices.length,
totalMembersInGroup: group.size
});
return devices;
}
/**
* Retrieves IDs of all devices in a group
*
* @param groupName - Name of the group to query
* @returns Array of device IDs in the group
* @throws {EnergyManagerError} If group not found
*/ getDeviceIdsInGroup(groupName) {
// Check if group exists
if (!this.groups.has(groupName)) {
this.logger.warn(`Group lookup failed - group not found`, { groupName });
throw new error_handler_1.EnergyManagerError(`Group not found: ${groupName}`, error_handler_1.ErrorType.GROUP_NOT_FOUND);
}
const deviceIds = Array.from(this.groups.get(groupName));
this.logger.trace(`Retrieved device IDs from group`, {
groupName,
deviceCount: deviceIds.length
});
return deviceIds;
}
/**
* Retrieves all existing group names
*
* @returns Array of group names
*/ getAllGroups() {
const groups = Array.from(this.groups.keys());
this.logger.trace(`Retrieved all groups`, { groupCount: groups.length });
return groups;
}
/**
* Retrieves all devices in the registry
*
* @returns Array of all devices
*/ getAllDevices() {
const devices = Array.from(this.devices.values());
this.logger.trace(`Retrieved all devices`, { deviceCount: devices.length });
return devices;
}
/**
* Retrieves all device IDs in the registry
*
* @returns Array of all device IDs
*/ getAllDeviceIds() {
const deviceIds = Array.from(this.devices.keys());
this.logger.trace(`Retrieved all device IDs`, { deviceCount: deviceIds.length });
return deviceIds;
}
}
exports.DeviceRegistry = DeviceRegistry;