Fix Home action completion and test isolation
This commit is contained in:
@@ -21,12 +21,17 @@ Singleton {
|
||||
property string lastError: ""
|
||||
property bool fixtureMode: false
|
||||
property var fixtureFavorites: []
|
||||
property string fixtureProcessMode: ""
|
||||
|
||||
property var actionQueue: []
|
||||
property var activeAction: null
|
||||
property var busyEntityIds: []
|
||||
property var pendingBrightness: ({})
|
||||
property var entityErrors: ({})
|
||||
property string actionResponseText: ""
|
||||
property bool actionStreamFinished: false
|
||||
property bool actionExited: false
|
||||
property int actionExitCode: 0
|
||||
|
||||
readonly property var visibleEntities: root.selectedEntities.slice(0, 4)
|
||||
readonly property int discoveredCount: root.catalog.length
|
||||
@@ -177,7 +182,7 @@ Singleton {
|
||||
root.pendingBrightness = nextPending;
|
||||
}
|
||||
|
||||
if (root.fixtureMode) {
|
||||
if (root.fixtureMode && root.fixtureProcessMode === "") {
|
||||
root.applyFixtureAction(action);
|
||||
root.finishActionState(action.entityId);
|
||||
return;
|
||||
@@ -192,27 +197,73 @@ Singleton {
|
||||
return;
|
||||
|
||||
root.activeAction = root.actionQueue[0];
|
||||
actionProc.command = root.activeAction.kind === "brightness"
|
||||
? [root.helperPath, "brightness", root.activeAction.entityId, String(root.activeAction.percent)]
|
||||
: [root.helperPath, "toggle", root.activeAction.entityId];
|
||||
root.actionResponseText = "";
|
||||
root.actionStreamFinished = false;
|
||||
root.actionExited = false;
|
||||
root.actionExitCode = 0;
|
||||
if (root.fixtureProcessMode !== "") {
|
||||
actionProc.command = root.fixtureActionCommand(root.activeAction);
|
||||
} else {
|
||||
actionProc.command = root.activeAction.kind === "brightness"
|
||||
? [root.helperPath, "brightness", root.activeAction.entityId, String(root.activeAction.percent)]
|
||||
: [root.helperPath, "toggle", root.activeAction.entityId];
|
||||
}
|
||||
actionProc.running = true;
|
||||
}
|
||||
|
||||
function consumeAction(text: string): void {
|
||||
function fixtureActionCommand(action: var): var {
|
||||
if (root.fixtureProcessMode === "no-output"
|
||||
&& action.entityId === "light.fixture_kitchen") {
|
||||
return ["/usr/bin/sh", "-c", "sleep 0.5; exit 7"];
|
||||
}
|
||||
return ["/usr/bin/sh", "-c", "sleep 0.5; printf '%s' '{\"ok\":true}'"];
|
||||
}
|
||||
|
||||
function handleActionStreamFinished(text: string): void {
|
||||
if (root.activeAction === null || root.actionStreamFinished)
|
||||
return;
|
||||
root.actionResponseText = text;
|
||||
root.actionStreamFinished = true;
|
||||
actionCompletionTimer.restart();
|
||||
}
|
||||
|
||||
function handleActionExited(exitCode: int): void {
|
||||
if (root.activeAction === null || root.actionExited)
|
||||
return;
|
||||
root.actionExited = true;
|
||||
root.actionExitCode = exitCode;
|
||||
actionCompletionTimer.restart();
|
||||
}
|
||||
|
||||
function tryCompleteAction(): void {
|
||||
if (root.activeAction === null)
|
||||
return;
|
||||
if (actionProc.running) {
|
||||
actionCompletionTimer.restart();
|
||||
return;
|
||||
}
|
||||
if (!root.actionExited || !root.actionStreamFinished)
|
||||
return;
|
||||
root.consumeAction(root.actionResponseText, root.actionExitCode);
|
||||
}
|
||||
|
||||
function consumeAction(text: string, exitCode: int): void {
|
||||
if (root.activeAction === null)
|
||||
return;
|
||||
|
||||
const completedAction = root.activeAction;
|
||||
let ok = false;
|
||||
let errorCode = "action-failed";
|
||||
let errorCode = text === "" ? "action-failed" : "invalid-response";
|
||||
try {
|
||||
const result = JSON.parse(text);
|
||||
ok = result.ok === true;
|
||||
errorCode = String(result.error || errorCode);
|
||||
ok = exitCode === 0 && result.ok === true;
|
||||
errorCode = String(result.error || "action-failed");
|
||||
} catch (error) {
|
||||
errorCode = "invalid-response";
|
||||
// Empty output from a failed process is an action failure, while
|
||||
// malformed non-empty output remains an invalid response.
|
||||
}
|
||||
|
||||
actionCompletionTimer.stop();
|
||||
root.actionQueue = root.actionQueue.slice(1);
|
||||
root.activeAction = null;
|
||||
root.finishActionState(completedAction.entityId);
|
||||
@@ -220,12 +271,15 @@ Singleton {
|
||||
const nextErrors = Object.assign({}, root.entityErrors);
|
||||
if (ok) {
|
||||
delete nextErrors[completedAction.entityId];
|
||||
refreshDelay.restart();
|
||||
if (root.fixtureMode && root.fixtureProcessMode !== "")
|
||||
root.applyFixtureAction(completedAction);
|
||||
else
|
||||
refreshDelay.restart();
|
||||
} else {
|
||||
nextErrors[completedAction.entityId] = errorCode;
|
||||
}
|
||||
root.entityErrors = nextErrors;
|
||||
root.startNextAction();
|
||||
queueAdvanceTimer.restart();
|
||||
}
|
||||
|
||||
function finishActionState(entityId: string): void {
|
||||
@@ -283,19 +337,29 @@ Singleton {
|
||||
}
|
||||
|
||||
function resetActionState(): void {
|
||||
actionCompletionTimer.stop();
|
||||
queueAdvanceTimer.stop();
|
||||
root.actionQueue = [];
|
||||
root.activeAction = null;
|
||||
root.busyEntityIds = [];
|
||||
root.pendingBrightness = {};
|
||||
root.entityErrors = {};
|
||||
root.actionResponseText = "";
|
||||
root.actionStreamFinished = false;
|
||||
root.actionExited = false;
|
||||
root.actionExitCode = 0;
|
||||
}
|
||||
|
||||
function applyFixture(name: string): void {
|
||||
if (["ready", "stale", "unavailable", "missing-selected", "action-error"].indexOf(name) < 0)
|
||||
if (["ready", "stale", "unavailable", "missing-selected", "action-error",
|
||||
"process-actions", "process-no-output"].indexOf(name) < 0)
|
||||
return;
|
||||
|
||||
root.fixtureMode = true;
|
||||
root.resetActionState();
|
||||
root.fixtureProcessMode = name === "process-actions"
|
||||
? "success"
|
||||
: (name === "process-no-output" ? "no-output" : "");
|
||||
if (name === "unavailable") {
|
||||
root.catalog = [];
|
||||
root.fixtureFavorites = [];
|
||||
@@ -326,6 +390,7 @@ Singleton {
|
||||
|
||||
function clearFixture(): void {
|
||||
root.fixtureMode = false;
|
||||
root.fixtureProcessMode = "";
|
||||
root.phase = "loading";
|
||||
root.catalog = [];
|
||||
root.selectedEntities = [];
|
||||
@@ -347,9 +412,27 @@ Singleton {
|
||||
Process {
|
||||
id: actionProc
|
||||
stdout: StdioCollector {
|
||||
id: actionOutput
|
||||
onStreamFinished: root.handleActionStreamFinished(this.text)
|
||||
}
|
||||
onExited: (code, status) => root.handleActionExited(code)
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: actionCompletionTimer
|
||||
interval: 0
|
||||
onTriggered: root.tryCompleteAction()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: queueAdvanceTimer
|
||||
interval: 0
|
||||
onTriggered: {
|
||||
if (actionProc.running) {
|
||||
queueAdvanceTimer.restart();
|
||||
return;
|
||||
}
|
||||
root.startNextAction();
|
||||
}
|
||||
onExited: (code, status) => root.consumeAction(actionOutput.text)
|
||||
}
|
||||
|
||||
Timer {
|
||||
|
||||
@@ -270,6 +270,7 @@ ShellRoot {
|
||||
selectedIds: HomeAssistant.selectedEntities.map(entity => entity.id),
|
||||
entities: HomeAssistant.selectedEntities,
|
||||
stale: HomeAssistant.stale,
|
||||
busy: HomeAssistant.busyEntityIds.length > 0,
|
||||
busyEntityIds: HomeAssistant.busyEntityIds,
|
||||
pendingBrightness: HomeAssistant.pendingBrightness,
|
||||
entityErrors: HomeAssistant.entityErrors,
|
||||
|
||||
Reference in New Issue
Block a user