Fix nested fixture transition draining
This commit is contained in:
@@ -35,10 +35,13 @@ Singleton {
|
|||||||
property bool fixtureTransitionDraining: false
|
property bool fixtureTransitionDraining: false
|
||||||
property bool fixtureTransitionStreamFinished: false
|
property bool fixtureTransitionStreamFinished: false
|
||||||
property bool fixtureTransitionExited: false
|
property bool fixtureTransitionExited: false
|
||||||
|
property var pendingFixtureTarget: null
|
||||||
|
property int delayedFixtureExitCode: 0
|
||||||
|
|
||||||
readonly property var visibleEntities: root.selectedEntities.slice(0, 4)
|
readonly property var visibleEntities: root.selectedEntities.slice(0, 4)
|
||||||
readonly property int discoveredCount: root.catalog.length
|
readonly property int discoveredCount: root.catalog.length
|
||||||
readonly property int configuredCount: root.selectedEntities.length
|
readonly property int configuredCount: root.selectedEntities.length
|
||||||
|
readonly property bool actionProcessRunning: actionProc.running
|
||||||
|
|
||||||
// Temporary aliases keep the current Home controls usable until the shelf
|
// Temporary aliases keep the current Home controls usable until the shelf
|
||||||
// switches to the selected-entity and per-entity action interfaces.
|
// switches to the selected-entity and per-entity action interfaces.
|
||||||
@@ -219,10 +222,14 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fixtureActionCommand(action: var): var {
|
function fixtureActionCommand(action: var): var {
|
||||||
|
if (root.fixtureProcessMode === "delayed-exit")
|
||||||
|
return ["/usr/bin/sh", "-c", "printf '%s' '{\"ok\":true}'; exit 7"];
|
||||||
if (root.fixtureProcessMode === "no-output"
|
if (root.fixtureProcessMode === "no-output"
|
||||||
&& action.entityId === "light.fixture_kitchen") {
|
&& action.entityId === "light.fixture_kitchen") {
|
||||||
return ["/usr/bin/sh", "-c", "sleep 0.5; exit 7"];
|
return ["/usr/bin/sh", "-c", "sleep 0.5; exit 7"];
|
||||||
}
|
}
|
||||||
|
if (root.fixtureProcessMode === "slow-success")
|
||||||
|
return ["/usr/bin/sh", "-c", "sleep 2; printf '%s' '{\"ok\":true}'"];
|
||||||
return ["/usr/bin/sh", "-c", "sleep 0.5; printf '%s' '{\"ok\":true}'"];
|
return ["/usr/bin/sh", "-c", "sleep 0.5; printf '%s' '{\"ok\":true}'"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +247,20 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleActionExited(exitCode: int): void {
|
function handleActionExited(exitCode: int): void {
|
||||||
|
// This fixture-only delay gives the contract a deterministic window
|
||||||
|
// where the process stopped but its exit bookkeeping is still pending.
|
||||||
|
if (root.fixtureMode
|
||||||
|
&& root.fixtureProcessMode === "delayed-exit"
|
||||||
|
&& root.activeAction !== null
|
||||||
|
&& !root.fixtureTransitionDraining) {
|
||||||
|
root.delayedFixtureExitCode = exitCode;
|
||||||
|
delayedFixtureExitTimer.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.recordActionExited(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordActionExited(exitCode: int): void {
|
||||||
if (root.fixtureTransitionDraining) {
|
if (root.fixtureTransitionDraining) {
|
||||||
root.fixtureTransitionExited = true;
|
root.fixtureTransitionExited = true;
|
||||||
fixtureTransitionTimer.restart();
|
fixtureTransitionTimer.restart();
|
||||||
@@ -368,6 +389,9 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function beginFixtureTransition(): void {
|
function beginFixtureTransition(): void {
|
||||||
|
if (root.fixtureTransitionDraining)
|
||||||
|
return;
|
||||||
|
|
||||||
const shouldDrain = root.fixtureMode
|
const shouldDrain = root.fixtureMode
|
||||||
&& root.fixtureProcessMode !== ""
|
&& root.fixtureProcessMode !== ""
|
||||||
&& (actionProc.running || root.activeAction !== null);
|
&& (actionProc.running || root.activeAction !== null);
|
||||||
@@ -397,22 +421,59 @@ Singleton {
|
|||||||
if (!root.fixtureTransitionStreamFinished || !root.fixtureTransitionExited)
|
if (!root.fixtureTransitionStreamFinished || !root.fixtureTransitionExited)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const target = root.pendingFixtureTarget;
|
||||||
|
root.pendingFixtureTarget = null;
|
||||||
root.fixtureTransitionDraining = false;
|
root.fixtureTransitionDraining = false;
|
||||||
root.fixtureTransitionStreamFinished = false;
|
root.fixtureTransitionStreamFinished = false;
|
||||||
root.fixtureTransitionExited = false;
|
root.fixtureTransitionExited = false;
|
||||||
|
if (target !== null)
|
||||||
|
root.installFixtureTarget(target);
|
||||||
queueAdvanceTimer.restart();
|
queueAdvanceTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyFixture(name: string): void {
|
function applyFixture(name: string): void {
|
||||||
if (["ready", "stale", "unavailable", "missing-selected", "action-error",
|
if (["ready", "stale", "unavailable", "missing-selected", "action-error",
|
||||||
"process-actions", "process-no-output"].indexOf(name) < 0)
|
"process-actions", "process-no-output", "process-delayed-exit",
|
||||||
|
"process-slow-actions"].indexOf(name) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
root.requestFixtureTarget({ fixture: true, name });
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestFixtureTarget(target: var): void {
|
||||||
|
if (root.fixtureTransitionDraining) {
|
||||||
|
// Preserve the old process's callback guard and keep only the
|
||||||
|
// latest replacement requested during that drain.
|
||||||
|
root.pendingFixtureTarget = target;
|
||||||
|
root.resetActionState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.pendingFixtureTarget = target;
|
||||||
root.beginFixtureTransition();
|
root.beginFixtureTransition();
|
||||||
|
if (root.fixtureTransitionDraining)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.pendingFixtureTarget = null;
|
||||||
|
root.installFixtureTarget(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
function installFixtureTarget(target: var): void {
|
||||||
|
if (target.fixture)
|
||||||
|
root.installFixture(target.name);
|
||||||
|
else
|
||||||
|
root.installLiveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function installFixture(name: string): void {
|
||||||
root.fixtureMode = true;
|
root.fixtureMode = true;
|
||||||
root.fixtureProcessMode = name === "process-actions"
|
root.fixtureProcessMode = name === "process-actions"
|
||||||
? "success"
|
? "success"
|
||||||
: (name === "process-no-output" ? "no-output" : "");
|
: (name === "process-no-output"
|
||||||
|
? "no-output"
|
||||||
|
: (name === "process-delayed-exit"
|
||||||
|
? "delayed-exit"
|
||||||
|
: (name === "process-slow-actions" ? "slow-success" : "")));
|
||||||
if (name === "unavailable") {
|
if (name === "unavailable") {
|
||||||
root.catalog = [];
|
root.catalog = [];
|
||||||
root.fixtureFavorites = [];
|
root.fixtureFavorites = [];
|
||||||
@@ -442,7 +503,10 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clearFixture(): void {
|
function clearFixture(): void {
|
||||||
root.beginFixtureTransition();
|
root.requestFixtureTarget({ fixture: false, name: "" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function installLiveState(): void {
|
||||||
root.fixtureMode = false;
|
root.fixtureMode = false;
|
||||||
root.fixtureProcessMode = "";
|
root.fixtureProcessMode = "";
|
||||||
root.phase = "loading";
|
root.phase = "loading";
|
||||||
@@ -496,6 +560,12 @@ Singleton {
|
|||||||
onTriggered: root.tryFinishFixtureTransition()
|
onTriggered: root.tryFinishFixtureTransition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: delayedFixtureExitTimer
|
||||||
|
interval: 1000
|
||||||
|
onTriggered: root.recordActionExited(root.delayedFixtureExitCode)
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
interval: 60000
|
interval: 60000
|
||||||
repeat: true
|
repeat: true
|
||||||
|
|||||||
@@ -274,6 +274,8 @@ ShellRoot {
|
|||||||
busyEntityIds: HomeAssistant.busyEntityIds,
|
busyEntityIds: HomeAssistant.busyEntityIds,
|
||||||
pendingBrightness: HomeAssistant.pendingBrightness,
|
pendingBrightness: HomeAssistant.pendingBrightness,
|
||||||
entityErrors: HomeAssistant.entityErrors,
|
entityErrors: HomeAssistant.entityErrors,
|
||||||
|
actionProcessRunning: HomeAssistant.actionProcessRunning,
|
||||||
|
actionStreamFinished: HomeAssistant.actionStreamFinished,
|
||||||
lastError: HomeAssistant.lastError
|
lastError: HomeAssistant.lastError
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,6 +209,31 @@ wait_for_home_status '
|
|||||||
(.entities[] | select(.id == "light.fixture_hall") | .active == true)
|
(.entities[] | select(.id == "light.fixture_hall") | .active == true)
|
||||||
' 'switching process fixtures stranded or misattributed the new action'
|
' 'switching process fixtures stranded or misattributed the new action'
|
||||||
|
|
||||||
|
qs_for_test ipc call home-assistant fixture process-delayed-exit >/dev/null
|
||||||
|
qs_for_test ipc call home-assistant brightness light.fixture_living 88 >/dev/null
|
||||||
|
wait_for_home_status '
|
||||||
|
.busyEntityIds == ["light.fixture_living"] and
|
||||||
|
.pendingBrightness["light.fixture_living"] == 88 and
|
||||||
|
.actionProcessRunning == false and
|
||||||
|
.actionStreamFinished == true
|
||||||
|
' 'nested fixture transition setup did not reach its delayed-exit window'
|
||||||
|
qs_for_test ipc call home-assistant fixture process-no-output >/dev/null
|
||||||
|
qs_for_test ipc call home-assistant fixture process-slow-actions >/dev/null
|
||||||
|
qs_for_test ipc call home-assistant brightness light.fixture_desk 57 >/dev/null
|
||||||
|
wait_for_home_status '
|
||||||
|
.phase == "ready" and
|
||||||
|
.busy == false and
|
||||||
|
.busyEntityIds == [] and
|
||||||
|
.pendingBrightness == {} and
|
||||||
|
.entityErrors == {} and
|
||||||
|
(.entities[] |
|
||||||
|
select(.id == "light.fixture_living") |
|
||||||
|
.active == false and .brightnessPct == 36) and
|
||||||
|
(.entities[] |
|
||||||
|
select(.id == "light.fixture_desk") |
|
||||||
|
.active == true and .brightnessPct == 57)
|
||||||
|
' 'nested fixture transitions released the drain or corrupted the latest action'
|
||||||
|
|
||||||
qs_for_test ipc call home-assistant fixture missing-selected >/dev/null
|
qs_for_test ipc call home-assistant fixture missing-selected >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
|
|||||||
Reference in New Issue
Block a user