UNPKG

matterbridge-roborock-vacuum-plugin

Version:
255 lines 11.5 kB
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