matterbridge-roborock-vacuum-plugin
Version:
Matterbridge Roborock Vacuum Plugin
255 lines • 11.5 kB
JavaScript
import { SCENE_AREA_ID_MIN } from '../constants/index.js';
import { ServiceContainer, } from '../services/index.js';
/** Facade coordinating Auth, Device, Area, and Message services via ServiceContainer. */
export class RoborockService {
logger;
configManager;
container;
authCoordinator;
deviceService;
areaService;
messageRoutingService;
pollingService;
connectionService;
toastMessage;
deviceNotify;
constructor(params, logger, configManager) {
this.logger = logger;
this.configManager = configManager;
if (params.container) {
// Use injected container (for testing)
this.container = params.container;
}
else {
// Create service container with configuration (production)
const config = {
baseUrl: params.baseUrl,
refreshInterval: params.refreshInterval,
authenticateApiFactory: params.authenticateApiFactory,
iotApiFactory: params.iotApiFactory,
persist: params.persist,
configManager: params.configManager,
toastMessage: params.toastMessage,
};
this.container = new ServiceContainer(logger, config);
}
// Get service instances
this.authCoordinator = this.container.getAuthenticationCoordinator();
this.deviceService = this.container.getDeviceManagementService();
this.areaService = this.container.getAreaManagementService();
this.messageRoutingService = this.container.getMessageRoutingService();
this.pollingService = this.container.getPollingService();
this.connectionService = this.container.getConnectionService();
this.toastMessage = params.toastMessage;
}
// ============================================================================
// Authentication Methods (delegate to AuthenticationCoordinator)
// ============================================================================
async authenticate() {
if (!this.configManager) {
throw new Error('PlatformConfigManager not provided. Cannot authenticate.');
}
const username = this.configManager.username;
const password = this.configManager.password;
const verificationCode = this.configManager.verificationCode;
const method = this.configManager.authenticationMethod;
this.logger.debug(`Authenticating method: ${method},
Username: ${username},
Password: ${password ? '******' : '<not provided>'},
Verification Code: ${verificationCode ? '******' : '<not provided>'}`);
let userData;
try {
userData = await this.authCoordinator.authenticate(method, {
username,
password,
verificationCode,
});
}
catch (error) {
this.logger.error(`Authentication failed: ${error.message}`);
this.toastMessage(`Authentication failed: ${error.message}`, 5000, 'error');
return { userData: undefined, shouldContinue: false, isSuccess: false };
}
if (!userData) {
this.logger.info('Authentication incomplete. Further action required (e.g., 2FA).');
this.toastMessage('Authentication incomplete. Further action required (e.g., 2FA).', 5000, 'warning');
return { userData: undefined, shouldContinue: false, isSuccess: false };
}
this.logger.info(`Authentication successful for user: ${userData.nickname} (${userData.username})`);
this.toastMessage(`Authentication successful for user: ${userData.username}`, 5000, 'success');
this.container.setUserData(userData);
return { userData, shouldContinue: true, isSuccess: true };
}
// ============================================================================
// Device Management Methods (delegate to DeviceManagementService)
// ============================================================================
/** List all devices for the user's account. */
async listDevices() {
return this.deviceService.listDevices();
}
async getSerialNumber(duid) {
return this.messageRoutingService.getSerialNumber(duid);
}
/** Get home data for periodic updates. */
async getHomeDataForUpdating(homeid) {
return this.deviceService.getHomeDataForUpdating(homeid);
}
/** Initialize MQTT client for cloud communication. */
async initializeMessageClient(device, userdata) {
await this.connectionService.initializeMessageClient(device, userdata);
this.container.synchronizeMessageClients();
}
/** Initialize local network connection for a device. */
async initializeMessageClientForLocal(device) {
const result = await this.connectionService.initializeMessageClientForLocal(device);
this.container.synchronizeMessageClients();
return result;
}
/** Send a test email to verify email notification settings. */
async sendTestEmailNotification() {
await this.connectionService.sendTestEmailNotification();
}
/** Set callback for device status notifications. */
setDeviceNotify(callback) {
this.deviceNotify = callback;
this.connectionService.setDeviceNotify(callback);
}
/** Start polling device status via local network. */
activateDeviceNotify(device) {
this.pollingService.activateDeviceNotifyOverLocal(device);
}
/** Trigger a one-shot local/MQTT status request for a device. */
async requestDeviceStatusOnce(duid) {
await this.pollingService.requestStatusOnce(duid);
}
/** Stop service and clean up resources. */
stopService() {
this.container.destroy();
}
// ============================================================================
// Area Management Methods (delegate to AreaManagementService)
// ============================================================================
/** Set selected cleaning areas for a device. */
setSelectedAreas(duid, selectedAreas) {
this.areaService.setSelectedAreas(duid, selectedAreas);
}
/** Get selected cleaning areas for a device. */
getSelectedAreas(duid) {
return this.areaService.getSelectedAreas(duid);
}
/** Set supported cleaning areas (rooms) for a device. */
setSupportedAreas(duid, supportedAreas) {
this.areaService.setSupportedAreas(duid, supportedAreas);
}
/** Set area-to-room index mapping for a device. */
setSupportedAreaIndexMap(duid, indexMap) {
this.areaService.setSupportedAreaIndexMap(duid, indexMap);
}
/** Set supported cleaning routines/scenes for a device. */
setSupportedRoutines(duid, routineAsRooms) {
this.areaService.setSupportedRoutines(duid, routineAsRooms);
}
/** Get supported cleaning areas for a device. */
getSupportedAreas(duid) {
return this.areaService.getSupportedAreas(duid);
}
/** Get area index map for a device. */
getSupportedAreasIndexMap(duid) {
return this.areaService.getSupportedAreasIndexMap(duid);
}
/** Get map information for a device. */
async getMapInfo(duid) {
return this.areaService.getMapInfo(duid);
}
/** Get room mapping for a device. */
async getRoomMap(duid, activeMap) {
return this.areaService.getRoomMap(duid, activeMap);
}
/** Get all scenes for a home. */
async getScenes(homeId) {
return this.areaService.getScenes(homeId);
}
/** Start a cleaning scene/routine. */
async startScene(sceneId) {
return this.areaService.startScene(sceneId);
}
// ============================================================================
// Message Routing Methods (delegate to MessageRoutingService)
// ============================================================================
/** Get current cleaning mode settings. */
async getCleanModeData(duid) {
return this.messageRoutingService.getCleanModeData(duid);
}
/** Get vacuum's current room from map. */
async getRoomIdFromMap(duid) {
return this.messageRoutingService.getRoomIdFromMap(duid);
}
/** Change cleaning mode settings. */
async changeCleanMode(duid, settings) {
return this.messageRoutingService.changeCleanMode(duid, settings);
}
/** Start cleaning with selected areas. */
async startClean(duid) {
const command = this.buildCleanCommand(duid);
return this.messageRoutingService.startClean(duid, command);
}
/** Pause cleaning. */
async pauseClean(duid) {
return this.messageRoutingService.pauseClean(duid);
}
/** Stop cleaning and return to dock. */
async stopAndGoHome(duid) {
return this.messageRoutingService.stopAndGoHome(duid);
}
/** Resume paused cleaning. */
async resumeClean(duid) {
return this.messageRoutingService.resumeClean(duid);
}
async stopClean(duid) {
return this.messageRoutingService.stopClean(duid);
}
/** Play sound to locate vacuum. */
async playSoundToLocate(duid) {
return this.messageRoutingService.playSoundToLocate(duid);
}
/** Execute custom GET request to device. */
async customGet(duid, request) {
return this.messageRoutingService.customGet(duid, request);
}
/** Send custom command to device (fire-and-forget). */
async customSend(duid, request) {
return this.messageRoutingService.customSend(duid, request);
}
/** Execute custom API GET request. */
async getCustomAPI(url) {
const iotApi = this.container.getIotApi();
if (!iotApi) {
throw new Error('IoT API not initialized. Please login first.');
}
return iotApi.getCustom(url);
}
buildCleanCommand(duid) {
const selectedAreaIds = this.areaService.getSelectedAreas(duid);
const supportedRooms = this.areaService.getSupportedAreas(duid) ?? [];
const supportedRoutines = this.areaService.getSupportedRoutines(duid) ?? [];
const indexMap = this.areaService.getSupportedAreasIndexMap(duid);
const selectedRoutines = selectedAreaIds
.map((areaId) => supportedRoutines.find((r) => r.areaId === areaId))
.filter((area) => area !== undefined)
.sort((a, b) => (a.areaInfo.locationInfo?.locationName ?? '').localeCompare(b.areaInfo.locationInfo?.locationName ?? ''));
if (selectedRoutines.length > 0) {
return { type: 'routine', routineId: selectedRoutines[0].areaId - SCENE_AREA_ID_MIN };
}
const activeMapId = supportedRooms.find((r) => selectedAreaIds.includes(r.areaId))?.mapId;
const activeMapRooms = activeMapId != null ? supportedRooms.filter((r) => r.mapId === activeMapId) : supportedRooms;
const roomIds = selectedAreaIds
.filter((areaId) => supportedRooms.some((r) => r.areaId === areaId))
.map((areaId) => indexMap?.getRoomId(areaId))
.filter((id) => id !== undefined);
if (roomIds.length === 0 || roomIds.length === activeMapRooms.length || activeMapRooms.length === 0) {
return { type: 'global' };
}
return { type: 'room', roomIds };
}
}
//# sourceMappingURL=roborockService.js.map