feat(scene): refactor scene automation into modular trigger-action system
- Support manual one-tap commands and automatic condition-based rules - Add modular triggers (time, temperature, humidity, device state) - Add modular conditions (environment thresholds, socket state, time range) - Upgrade actions to support virtual remotes, sockets, delays, and notifications - Support sequential/parallel concurrency mode and error execution strategies - Refactor SceneListScreen with filter tabs, quick execution, and enable switches - Refactor SceneEditorScreen with modular builders and preset pickers - Enhance SceneExecutionScreen with step timing, skips, and rerun capability - Add comprehensive domain and UI ViewModel unit tests
This commit is contained in:
+20
-8
@@ -181,12 +181,6 @@ class FakeSceneRepository : SceneRepository {
|
||||
|
||||
/**
|
||||
* Fake [SceneExecutor] that drives the existing [SocketGateway] and [InfraredGateway] fakes.
|
||||
*
|
||||
* Behaviour matches the project_plan.md §4.7 contract:
|
||||
* - actions run sequentially in [Scene.actions] order;
|
||||
* - a single action failure does not abort subsequent actions;
|
||||
* - the returned [SceneExecutionResult] contains one entry per action;
|
||||
* - re-entering execute() while a scene is running returns [DomainError.OperationFailed].
|
||||
*/
|
||||
class FakeSceneExecutor(
|
||||
private val socketGateway: SocketGateway,
|
||||
@@ -203,9 +197,23 @@ class FakeSceneExecutor(
|
||||
),
|
||||
)
|
||||
}
|
||||
val startTime = System.currentTimeMillis()
|
||||
val results = mutableListOf<SceneActionExecutionResult>()
|
||||
return try {
|
||||
val results = scene.actions.map { action -> executeAction(scene.profileId, action) }
|
||||
DomainResult.Success(SceneExecutionResult(scene = scene, results = results))
|
||||
for (action in scene.actions) {
|
||||
val actionStart = System.currentTimeMillis()
|
||||
val execResult = executeAction(scene.profileId, action)
|
||||
val duration = System.currentTimeMillis() - actionStart
|
||||
results.add(execResult.copy(executionDurationMillis = duration))
|
||||
}
|
||||
val totalDuration = System.currentTimeMillis() - startTime
|
||||
DomainResult.Success(
|
||||
SceneExecutionResult(
|
||||
scene = scene,
|
||||
results = results,
|
||||
totalDurationMillis = totalDuration,
|
||||
),
|
||||
)
|
||||
} finally {
|
||||
running.remove(scene.id)
|
||||
}
|
||||
@@ -227,6 +235,10 @@ class FakeSceneExecutor(
|
||||
deviceName = action.deviceName,
|
||||
key = action.key,
|
||||
)
|
||||
|
||||
is SceneAction.VirtualRemoteButton,
|
||||
is SceneAction.Delay,
|
||||
is SceneAction.Notification -> DomainResult.Success(Unit)
|
||||
}
|
||||
return when (result) {
|
||||
is DomainResult.Success -> SceneActionExecutionResult(
|
||||
|
||||
Reference in New Issue
Block a user