mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-06 22:49:16 +00:00
fix: quantise the light aperture amount of moving covers (#366)
A gate or door bound to a position-reporting cover fed current_position into the light-barrier signature at toFixed(3) precision: every percent of movement produced a new fingerprint, a full physicalBodyParts recompute and a recut — up to ~100 heavy passes per gate cycle, plus LRU churn. The light pipeline now consumes one quantised amount (OPENING_LIGHT_AMOUNT_QUANTUM = 0.05, exact 0 and 1 nodes) at the single point that feeds BOTH the signature and the cut geometry, so the cache key and the drawn aperture agree by construction and a full sweep costs at most 21 recomputes. The door LEAF animation stays smooth — _openingAmt is quantised only for the light pipeline, nowhere else. Binary contact doors are byte-identical to the previous behaviour (pinned by unit). Assumption recorded in the spec: the 5% visual step of the light cut is indistinguishable on real plans; if field impressions disagree, the quantum is a one-constant change (0.02 => <=51 recomputes) or the decision falls back to a debounce. LIGHT.md §Caching documents the grid. Issue: #366 User-Visible: yes
This commit is contained in:
+37
-1
@@ -24,7 +24,9 @@ import {
|
||||
normalizeDeviceDisplay, isAlarmCapable,
|
||||
liveText, liveTextValue, liveTextReference, liveTextToken,
|
||||
hassValue, valueWithUnit, decorTextScale, decorTextLines,
|
||||
LIVE_TEXT_DASH, LIVE_TEXT_VALUE_MAX, DECOR_TEXT_SCALE_MIN, DECOR_TEXT_SCALE_MAX } from '../test-build/logic.js';
|
||||
LIVE_TEXT_DASH, LIVE_TEXT_VALUE_MAX, DECOR_TEXT_SCALE_MIN, DECOR_TEXT_SCALE_MAX,
|
||||
quantizeOpeningLightAmount,
|
||||
} from '../test-build/logic.js';
|
||||
import {
|
||||
iconFor, compileIconRules, isValidPattern, iconFromDeviceClasses,
|
||||
} from '../test-build/rules.js';
|
||||
@@ -1530,3 +1532,37 @@ test('liveTextValue: a formatter that omits the unit still gets one (the demo st
|
||||
assert.equal(liveTextValue(h, { entity: 'sensor.tank' }), '68 %',
|
||||
'formatEntityState returning a bare state must not cost the label its unit');
|
||||
});
|
||||
|
||||
test('квант amount света: границы, узлы и ≤21 сигнатура за полный ход (#366)', () => {
|
||||
// AC1: точные узлы и зажимы
|
||||
assert.equal(quantizeOpeningLightAmount(0), 0);
|
||||
assert.equal(quantizeOpeningLightAmount(1), 1);
|
||||
assert.equal(quantizeOpeningLightAmount(NaN), 0);
|
||||
assert.equal(quantizeOpeningLightAmount(-1), 0);
|
||||
assert.equal(quantizeOpeningLightAmount(2), 1);
|
||||
assert.equal(quantizeOpeningLightAmount(0.30), quantizeOpeningLightAmount(0.31), 'один узел');
|
||||
assert.notEqual(quantizeOpeningLightAmount(0.30), quantizeOpeningLightAmount(0.33), 'разные узлы');
|
||||
|
||||
// AC2: свип 0..1 шагом 0.001 через сигнатуру — не больше 21 различной
|
||||
const signatures = new Set();
|
||||
for (let step = 0; step <= 1000; step++) {
|
||||
const amount = quantizeOpeningLightAmount(step / 1000);
|
||||
signatures.add(openingLightStateSignature([
|
||||
{ id: 'gate', type: 'gate', contact: 'cover.gate', amount },
|
||||
]));
|
||||
}
|
||||
assert.ok(signatures.size <= 21, `сигнатур ${signatures.size}, ожидалось ≤21`);
|
||||
|
||||
// конечные положения совпадают с неквантованными (binary-регресс, AC3)
|
||||
const closed = openingLightStateSignature([
|
||||
{ id: 'door', type: 'door', contact: 'binary_sensor.d', amount: 0 },
|
||||
]);
|
||||
const open = openingLightStateSignature([
|
||||
{ id: 'door', type: 'door', contact: 'binary_sensor.d', amount: 1 },
|
||||
]);
|
||||
assert.equal(closed, 'door:0.000');
|
||||
assert.equal(open, 'door:1.000');
|
||||
assert.equal(openingLightStateSignature([
|
||||
{ id: 'door', type: 'door', contact: 'binary_sensor.d', amount: quantizeOpeningLightAmount(1) },
|
||||
]), open, 'квант единицы байт-в-байт');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user