matterbridge-roborock-vacuum-plugin
Version:
Matterbridge Roborock Vacuum Plugin
294 lines • 13.7 kB
JavaScript
import { RvcOperationalState, RvcRunMode } from 'matterbridge/matter/clusters';
import { OperationStatusCode } from '../roborockCommunication/enums/operationStatusCode.js';
import { state_to_matter_operational_status, state_to_matter_state } from './function.js';
/**
* Resolves the robot's state based on status code and modifier flags.
*
* Implements a 48-row state resolution matrix with three priority levels:
*
* 1. **Status Override Rules** (Highest Priority)
* - Certain status codes ignore ALL modifier flags
* - Examples: Sleeping (2), Idle (3), InError (12), WashingTheMop (23), GoingToWashTheMop (26), etc.
* - Sleeping (2): inCleaning=true → Cleaning+Paused; isExploring=true → Mapping+Paused; base → Idle+Paused
*
* 2. **Cleaning Status Special Overrides**
* - When status = 5 (Cleaning), modifiers apply in explicit priority order:
* a. isLocating/isExploring (Highest) - Sets UpdatingMaps operational state
* b. inWarmup (Second) - Sets CleaningMop operational state
* c. inReturning (Third) - Sets SeekingCharger operational state
* d. inFreshState (N/A) - Not applicable to Cleaning status
*
* 3. **Modifier Priority Chain**
* - For all other cases, modifiers apply in priority order:
* a. inReturning (High) - Sets SeekingCharger operational state (ignored for Paused, ReturnToDock, ReturningDock)
* b. isExploring (Medium) - Changes run mode to Mapping (blocked on Charging)
* c. inFreshState (Low) - No longer used (redundant with Charging base state)
*
* @param message StatusChangeMessage containing status code and modifier flags
* @returns ResolvedState with runMode and operationalState
*
* @see misc/state_resolution_matrix.md - Complete 47-row matrix documentation
*/
export function resolveDeviceState(message) {
const status = message.status;
// ═══════════════════════════════════════════════════════════════════════════
// Priority 0: Status Override Rules (Highest Priority)
// ═══════════════════════════════════════════════════════════════════════════
// These status codes ignore ALL modifier flags and always return fixed states
// Sleeping Status Override - Rows 8-10
if (status === OperationStatusCode.Sleeping) {
// Row 8: inCleaning=true overrides all other flags → Cleaning + Paused
if (message.inCleaning === true) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.Paused,
};
}
// Row 9: isExploring=true (without inCleaning) → Mapping + Paused
if (message.isExploring === true) {
return {
runMode: RvcRunMode.ModeTag.Mapping,
operationalState: RvcOperationalState.OperationalState.Paused,
};
}
// Row 10: Base → Idle + Paused
return {
runMode: RvcRunMode.ModeTag.Idle,
operationalState: RvcOperationalState.OperationalState.Paused,
};
}
// Idle Status Override - Row 11
if (status === OperationStatusCode.Idle) {
return {
runMode: RvcRunMode.ModeTag.Idle,
operationalState: RvcOperationalState.OperationalState.Docked,
};
}
// InError Status Override - Rows 31-33
if (status === OperationStatusCode.InError) {
if (message.inCleaning === true) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.Error,
};
}
if (message.isExploring === true) {
return {
runMode: RvcRunMode.ModeTag.Mapping,
operationalState: RvcOperationalState.OperationalState.Error,
};
}
return {
runMode: RvcRunMode.ModeTag.Idle,
operationalState: RvcOperationalState.OperationalState.Error,
};
}
// EmptyingDustContainer Status Override - Row 44
if (status === OperationStatusCode.EmptyingDustContainer) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.EmptyingDustBin,
};
}
// WashingTheMop Status Override - Row 45
if (status === OperationStatusCode.WashingTheMop) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.CleaningMop,
};
}
// GoingToWashTheMop Status Override - Row 46
if (status === OperationStatusCode.GoingToWashTheMop) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.CleaningMop,
};
}
// Mapping Status Override - Row 47
if (status === OperationStatusCode.Mapping) {
return {
runMode: RvcRunMode.ModeTag.Mapping,
operationalState: RvcOperationalState.OperationalState.Running,
};
}
// ═══════════════════════════════════════════════════════════════════════════
// Priority 1: Cleaning Status Special Overrides
// ═══════════════════════════════════════════════════════════════════════════
// When status = 5 (Cleaning), apply modifiers in explicit priority order
if (status === OperationStatusCode.Cleaning ||
status === OperationStatusCode.ManualMode ||
status === OperationStatusCode.RoomClean ||
status === OperationStatusCode.ZoneClean ||
status === OperationStatusCode.SpotCleaning ||
status === OperationStatusCode.CleanMopCleaning ||
status === OperationStatusCode.CleanMopMopping ||
status === OperationStatusCode.RoomCleanMopCleaning ||
status === OperationStatusCode.RoomCleanMopMopping ||
status === OperationStatusCode.ZoneCleanMopCleaning ||
status === OperationStatusCode.ZoneCleanMopMopping ||
status === OperationStatusCode.RoomMopping ||
status === OperationStatusCode.ZoneMopping) {
// Priority 1: isLocating/isExploring (Highest) - Rows 14-15
// Map update operations override all other flags during cleaning
if (message.isLocating === true || message.isExploring === true) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.UpdatingMaps,
};
}
// Priority 2: inWarmup (Second) - Row 13
if (message.inWarmup === true) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.CleaningMop,
};
}
// Priority 3: inReturning (Third) - Rows 16-19
if (message.inReturning === true) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.SeekingCharger,
};
}
// Priority 4: inFreshState not applicable to Cleaning status
// Falls through to base state (Cleaning + Running) - Row 12
}
// ═══════════════════════════════════════════════════════════════════════════
// Priority 2: Modifier Priority Chain
// ═══════════════════════════════════════════════════════════════════════════
// Apply modifiers in priority order for all other statuses
// Get base state from status code
let state = getBaseState(message);
// Priority 2a: inReturning modifier (High Priority)
state = applyInReturningModifier(state, message);
// Priority 2b: isExploring modifier (Medium Priority)
state = applyIsExploringModifier(state, message);
// Priority 2c: inFreshState modifier (Low Priority)
state = applyInFreshStateModifier(state, message);
return state;
}
/**
* Gets the base state from status code using legacy mapping functions.
* Falls back to Idle/Docked if status code is not recognized.
*
* @param message StatusChangeMessage
* @returns ResolvedState with base runMode and operationalState
*/
function getBaseState(message) {
// Special case: Unknown status (0) should return Idle/Docked, not Idle/Error
if (message.status === OperationStatusCode.Unknown) {
return {
runMode: RvcRunMode.ModeTag.Idle,
operationalState: RvcOperationalState.OperationalState.Docked,
};
}
if (message.status === OperationStatusCode.Paused && message.isExploring === true) {
return {
runMode: RvcRunMode.ModeTag.Mapping,
operationalState: RvcOperationalState.OperationalState.Paused,
};
}
// Special case: Paused status (10) should always return Cleaning/Paused
if (message.status === OperationStatusCode.Paused) {
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.Paused,
};
}
const runMode = state_to_matter_state(message.status);
const operationalState = state_to_matter_operational_status(message.status);
if (runMode === undefined || operationalState === undefined) {
// Unknown status code - default to Idle/Docked
return {
runMode: RvcRunMode.ModeTag.Idle,
operationalState: RvcOperationalState.OperationalState.Docked,
};
}
return { runMode, operationalState };
}
/**
* Applies inReturning modifier (High Priority).
*
* When inReturning=true, robot is seeking charger regardless of other flags.
* Sets runMode to Cleaning and operationalState to SeekingCharger.
*
* Special Cases: For certain statuses (Paused, ReturnToDock, ReturningDock),
* the operational state is fixed by the status itself, so inReturning is ignored entirely.
* These statuses only use isExploring to determine run mode (Cleaning vs Mapping).
*
* Matrix Coverage: Rows 2, 4, 6, 16, 17, 21, 28, 36, 39, 42
*
* @param state Current resolved state
* @param message StatusChangeMessage
* @returns Updated state with inReturning applied
*/
function applyInReturningModifier(state, message) {
if (message.inReturning === true) {
// Skip inReturning for statuses with fixed operational states
if (message.status === OperationStatusCode.Paused ||
message.status === OperationStatusCode.ReturnToDock ||
message.status === OperationStatusCode.ReturningDock) {
return state; // Keep current state unchanged
}
return {
runMode: RvcRunMode.ModeTag.Cleaning,
operationalState: RvcOperationalState.OperationalState.SeekingCharger,
};
}
return state;
}
/**
* Applies isExploring modifier (Medium Priority).
*
* When isExploring=true and inReturning is not true, robot is in mapping mode.
* Changes runMode to Mapping while preserving operationalState.
*
* Note: This modifier is skipped if inReturning was already applied (higher priority).
* Note: This modifier is blocked when status is Charging (8) as the vacuum
* cannot charge and explore simultaneously.
*
* Matrix Coverage: Rows 3, 7, 9, 19, 22, 26, 32, 34, 37, 40, 43
*
* @param state Current resolved state
* @param message StatusChangeMessage
* @returns Updated state with isExploring applied
*/
function applyIsExploringModifier(state, message) {
// Block isExploring when status is Charging (8) - invalid state
const isCharging = message.status === OperationStatusCode.Charging;
if (message.isExploring === true && message.inReturning !== true && !isCharging) {
return {
...state,
runMode: RvcRunMode.ModeTag.Mapping,
};
}
return state;
}
/**
* Applies inFreshState modifier (Low Priority).
*
* When inFreshState=true and status is Charging (8),
* robot has just completed cleaning and transitions to Idle/Docked.
*
* Note: This modifier only applies to status 8 (Charging).
* For other statuses, including Cleaning (5), inFreshState has no effect
* as the vacuum does not go to fresh state during active cleaning.
*
* Matrix Coverage: None (inFreshState is redundant and no longer used in matrix)
*
* @param state Current resolved state
* @param message StatusChangeMessage
* @returns Updated state with inFreshState applied
*/
function applyInFreshStateModifier(state, message) {
if (message.inFreshState === true &&
message.status === OperationStatusCode.Charging &&
message.inReturning !== true) {
return {
runMode: RvcRunMode.ModeTag.Idle,
operationalState: RvcOperationalState.OperationalState.Docked,
};
}
return state;
}
//# sourceMappingURL=stateResolver.js.map