mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
File diff suppressed because one or more lines are too long
@@ -10,7 +10,14 @@ mkdirSync(artifactDir, { recursive: true });
|
||||
|
||||
const referenceAsset = (theme, file, coreSize) => {
|
||||
let source = readFileSync(resolve('demo/srv/reference/device-icons', theme, file), 'utf8');
|
||||
if (theme === 'Dark' && file === 'Unlock.svg') source = source.replaceAll('#1DC21D', '#F0A00C');
|
||||
if (file === 'Lock.svg') {
|
||||
const old = theme === 'Dark' ? '#252525' : 'black';
|
||||
source = source.replaceAll(old, '#66D17A');
|
||||
if (theme === 'Dark') source = source.replaceAll('fill="white"', 'fill="#252525"');
|
||||
}
|
||||
if (file === 'Unlock.svg') {
|
||||
source = source.replaceAll(theme === 'Dark' ? '#1DC21D' : '#F0A00C', '#F0410C');
|
||||
}
|
||||
const nativeWidth = Number(source.match(/<svg[^>]*width="([\d.]+)"/)?.[1] || 127);
|
||||
return {
|
||||
url: `data:image/svg+xml;base64,${Buffer.from(source).toString('base64')}`,
|
||||
|
||||
@@ -242,6 +242,7 @@ export const GOLDEN_SCENARIOS = Object.freeze([
|
||||
'golden-light-three': ['lock-unlocked'],
|
||||
'golden-presence': ['virtual'],
|
||||
'golden-climate': ['alarm', 'sel'],
|
||||
'golden-right-linkquality': ['open'],
|
||||
},
|
||||
focusDevice: 'golden-climate',
|
||||
hoverDevice: 'golden-presence',
|
||||
|
||||
@@ -55,13 +55,20 @@ const reference = {
|
||||
alert: svgState('Dark', 'Alert Value.svg'),
|
||||
},
|
||||
};
|
||||
// Owner decision in #179 overrides the stale green Dark/Unlock asset.
|
||||
reference.dark.unlock = {
|
||||
...reference.dark.unlock,
|
||||
core: reference.light.unlock.core,
|
||||
glyph: reference.dark.active.glyph,
|
||||
shell: reference.light.unlock.shell,
|
||||
};
|
||||
// Owner decision in #219 supersedes the package paint while retaining its
|
||||
// geometry: a closed lock is green, an open lock is red, and the glyph follows
|
||||
// the same Light/Dark foreground as every other coloured core.
|
||||
for (const theme of ['light', 'dark']) {
|
||||
const glyph = theme === 'light' ? browserColor('#FFFFFF') : browserColor('#252525');
|
||||
reference[theme].lock = {
|
||||
...reference[theme].lock,
|
||||
core: browserColor('#66D17A'), glyph, shell: browserColor('#66D17A'),
|
||||
};
|
||||
reference[theme].unlock = {
|
||||
...reference[theme].unlock,
|
||||
core: browserColor('#F0410C'), glyph, shell: browserColor('#F0410C'),
|
||||
};
|
||||
}
|
||||
|
||||
const { page, browser } = await launch(
|
||||
{ width: 1120, height: 900 }, 1, [], { colorScheme: 'dark' },
|
||||
@@ -320,6 +327,7 @@ const result = await page.evaluate(async ({ beforeUnavailable, afterUnavailable,
|
||||
const stateMatrix = Object.fromEntries(['light', 'dark'].map((theme) => [theme, {
|
||||
default: sampleState(theme),
|
||||
active: sampleState(theme, ['on']),
|
||||
open: sampleState(theme, ['open']),
|
||||
lock: sampleState(theme, ['lock-locked']),
|
||||
unlock: sampleState(theme, ['lock-unlocked']),
|
||||
alert: sampleState(theme, ['alarm']),
|
||||
@@ -329,6 +337,22 @@ const result = await page.evaluate(async ({ beforeUnavailable, afterUnavailable,
|
||||
lockNode.className = originalLockClasses;
|
||||
lockNode.setAttribute('style', originalLockStyle);
|
||||
|
||||
const openingProbe = document.createElement('div');
|
||||
openingProbe.innerHTML = '<span class="oplock-shell"><span class="oplock-core">'
|
||||
+ '<ha-icon icon="mdi:lock"></ha-icon></span></span>';
|
||||
sr().append(openingProbe);
|
||||
const sampleOpeningLock = (theme, state) => {
|
||||
openingProbe.className = `oplock theme-${theme} ${state}`;
|
||||
const core = getComputedStyle(openingProbe.querySelector('.oplock-core'));
|
||||
const shell = getComputedStyle(openingProbe.querySelector('.oplock-shell'));
|
||||
return { core: core.backgroundColor, glyph: core.color, shell: shell.borderColor };
|
||||
};
|
||||
const openingLockMatrix = Object.fromEntries(['light', 'dark'].map((theme) => [theme, {
|
||||
lock: sampleOpeningLock(theme, 'locked'),
|
||||
unlock: sampleOpeningLock(theme, 'unlocked'),
|
||||
}]));
|
||||
openingProbe.remove();
|
||||
|
||||
const originalLightStyle = light.getAttribute('style') || '';
|
||||
const originalDoubleStyle = double.getAttribute('style') || '';
|
||||
const originalTextStyle = text.getAttribute('style') || '';
|
||||
@@ -490,6 +514,7 @@ const result = await page.evaluate(async ({ beforeUnavailable, afterUnavailable,
|
||||
defaultShell,
|
||||
focusCoreDecoration,
|
||||
stateMatrix,
|
||||
openingLockMatrix,
|
||||
sizeMatrix,
|
||||
textShellSizeMatrix,
|
||||
textFrameRadius,
|
||||
@@ -520,6 +545,15 @@ const result = await page.evaluate(async ({ beforeUnavailable, afterUnavailable,
|
||||
&& stateMatrix[theme][state].shell === reference[theme][state].shell
|
||||
&& Math.abs(stateMatrix[theme][state].shellWidth
|
||||
- Math.max(1, Math.floor(reference[theme][state].shellWidth * 50.4))) <= 0.5)),
|
||||
openingLockPaletteMatches: ['light', 'dark'].every((theme) =>
|
||||
['lock', 'unlock'].every((state) =>
|
||||
openingLockMatrix[theme][state].core === reference[theme][state].core
|
||||
&& openingLockMatrix[theme][state].glyph === reference[theme][state].glyph
|
||||
&& openingLockMatrix[theme][state].shell === reference[theme][state].shell)),
|
||||
orangeGlyphsFollowTheme: stateMatrix.light.active.glyph === 'rgb(255, 255, 255)'
|
||||
&& stateMatrix.light.open.glyph === 'rgb(255, 255, 255)'
|
||||
&& stateMatrix.dark.active.glyph === 'rgb(37, 37, 37)'
|
||||
&& stateMatrix.dark.open.glyph === 'rgb(37, 37, 37)',
|
||||
referenceHoverMatches: lightHover.core === reference.light.hover.core
|
||||
&& lightHover.glyph === reference.light.hover.glyph
|
||||
&& lightHover.shell === reference.light.default.shell
|
||||
@@ -575,9 +609,11 @@ const result = await page.evaluate(async ({ beforeUnavailable, afterUnavailable,
|
||||
keyboardActionFeedback,
|
||||
informationalActionHasNoFeedback,
|
||||
lightThemeCoreIsWhite: lightThemeCore === 'rgb(255, 255, 255)',
|
||||
darkThemeCoreIs252525: darkThemeClassProjected && darkThemeCore === 'rgb(37, 37, 37)',
|
||||
darkLockUsesWhiteGlyphAndDarkShell: darkLockGlyph === 'rgb(255, 255, 255)'
|
||||
&& darkLockShell === 'rgb(37, 37, 37)',
|
||||
darkThemeCoreIs252525: darkThemeClassProjected
|
||||
&& stateMatrix.dark.default.core === 'rgb(37, 37, 37)',
|
||||
darkLockUsesGreenCoreAndDarkGlyph: darkThemeCore === 'rgb(102, 209, 122)'
|
||||
&& darkLockGlyph === 'rgb(37, 37, 37)'
|
||||
&& darkLockShell === 'rgb(102, 209, 122)',
|
||||
hitAreaAtLeast44: parseFloat(pseudo.width) >= 44 && parseFloat(pseudo.height) >= 44,
|
||||
presenceContinuousGreen: presence?.querySelector('.device-pulse.continuous.reason-presence')
|
||||
&& getComputedStyle(presence.querySelector('.device-pulse i')).borderColor === 'rgb(29, 194, 29)'
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,8 +7,11 @@ for issues #179 and #211. They do not ship in the production bundle.
|
||||
- Export: 2026-08-19
|
||||
- Source archive SHA-256:
|
||||
`63670C73E25D1E59DDAF1BE236F3D7F2FAC827B9B5D6DD4B77125EA9BC012025`
|
||||
- Owner override: Dark Unlock is amber, not the stale green paint in
|
||||
`Dark/Unlock.svg`.
|
||||
- Owner override #219: package geometry remains normative, but Lock/Unlock
|
||||
paint is projected at runtime as closed green `#66D17A` and open red
|
||||
`#F0410C`; glyphs are white in Light and `#252525` in Dark. The SVG files stay
|
||||
byte-identical source evidence, while the smoke and comparison capture apply
|
||||
this explicit product override.
|
||||
|
||||
`demo/smoke_device_icon_design.mjs` reads the SVG attributes directly for the
|
||||
computed-style contract. `demo/capture_device_icon_reference.mjs` builds a
|
||||
|
||||
Vendored
+18
-24
File diff suppressed because one or more lines are too long
@@ -2,6 +2,10 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Door and gate locks now use green for locked and red for unlocked on both
|
||||
compact opening badges and ordinary device markers. Every glyph on an orange
|
||||
device core is now white in the light theme and dark in the dark theme
|
||||
([#219](https://github.com/Matysh/houseplan-card/issues/219)).
|
||||
- Fixed Glow disappearing from an entire space when harmless floating-point
|
||||
tails or one malformed room outline upset floor clipping. Boolean inputs are
|
||||
now stabilised at render time, and a residual failure is isolated to the
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Замки дверей и ворот теперь зелёные в закрытом состоянии и красные в
|
||||
открытом — и на компактном значке проёма, и на обычном маркере устройства.
|
||||
Все иконки на оранжевой подложке устройства теперь белые в светлой теме и
|
||||
тёмные в тёмной
|
||||
([#219](https://github.com/Matysh/houseplan-card/issues/219)).
|
||||
- Исправлено исчезновение Glow во всём пространстве из-за безвредных хвостов
|
||||
вычислений с плавающей точкой или одного некорректного контура комнаты.
|
||||
Входные данные для геометрических операций теперь стабилизируются только при
|
||||
|
||||
+31
-13
@@ -361,7 +361,8 @@ public binding-status hook.
|
||||
The independent reference subset under
|
||||
`demo/srv/reference/device-icons/` comes directly from designer package 1.1.1;
|
||||
it is not generated from production CSS. The package archive hash and the
|
||||
owner's amber Dark Unlock override are recorded in that directory's README.
|
||||
owner's #219 red/green Lock/Unlock paint override are recorded in that
|
||||
directory's README.
|
||||
|
||||
- [ ] `node demo/smoke_device_icon_design.mjs` reads the SVG colors and stroke
|
||||
widths, then compares them with fresh computed styles. It also measures
|
||||
@@ -403,6 +404,20 @@ owner's amber Dark Unlock override are recorded in that directory's README.
|
||||
- [ ] `node demo/capture_device_icon_reference.mjs` includes an additional
|
||||
96 px Text row beside the normative Light/Dark `Text Default.svg`.
|
||||
|
||||
## Device lock and orange foreground palette (#219)
|
||||
|
||||
- [ ] Closed/`locked` is green `#66D17A`; open/`unlocked` is red `#F0410C`.
|
||||
The same core/stroke palette is used by ordinary lock markers and compact
|
||||
door/gate lock badges; glyph shape remains closed/open/question.
|
||||
- [ ] Every device glyph on an orange core (`on`/working and physical `open`)
|
||||
is white in Light and `#252525` in Dark. `device-icon-state-table-light`
|
||||
and `device-icon-state-table-dark` show `on` and `open` together, plus
|
||||
both lock states [unit: device-marker-polish-contract; auto:
|
||||
smoke_device_icon_design; golden: device-icon-state-table-*].
|
||||
- [ ] Alarm, hover, focus, selected, unavailable, virtual, press feedback,
|
||||
pulse, hit-area and lock actions retain their existing priority and
|
||||
behaviour [unit: device presentation/polish/pointer; visual source review].
|
||||
|
||||
## Touch support and release gates
|
||||
|
||||
The product contract is defined in `docs/TOUCH-SUPPORT.md`:
|
||||
@@ -667,10 +682,11 @@ separately promised workflows:
|
||||
- [ ] Drag anywhere (no edit mode), snaps to grid, persists after reload, per space
|
||||
- [ ] ↺ reset restores auto layout after confirm
|
||||
- [ ] Temperature badge on thermometers; LQI value under zigbee icons with red→green color
|
||||
- [ ] Unified live states (dev, owner 2026-08-05): actual work is yellow;
|
||||
open door/window, unlocked lock and open valve are orange; covers stay
|
||||
neutral and morph their icon; unavailable is faded. The plate and the
|
||||
activity effect come from the same semantic resolver
|
||||
- [ ] Unified live states (dev, owner 2026-08-05; lock palette #219): actual
|
||||
work is yellow; open door/window and open valve are orange; unlocked lock
|
||||
is red and locked lock is green; covers stay neutral and morph their icon;
|
||||
unavailable is faded. The plate and the activity effect come from the same
|
||||
semantic resolver
|
||||
- [ ] State icons (v1.26.0): auto icons morph with state — door/window/garage open↔closed,
|
||||
lock locked↔unlocked, bulb on; custom icons and unavailable states never morph [manual]
|
||||
- [ ] display "Value instead of an icon": the marker shows the measurement (°/%/unit)
|
||||
@@ -764,10 +780,10 @@ separately promised workflows:
|
||||
auto-icon family (mdi:roller-shade, mdi:garage-variant), a hand-picked
|
||||
icon morphs only inside the pair it was picked from, and an
|
||||
unknown/unavailable state morphs nothing. NOT touched: an open door /
|
||||
window binary sensor, an unlocked lock and an open valve still wear the
|
||||
orange «открыто» frame (a valve has no icon pair, so the frame is all it
|
||||
has) [auto: smoke_cover_no_plate + unit stateIcon «every class, both
|
||||
ways»]
|
||||
window binary sensor and an open valve still wear the orange «открыто»
|
||||
frame (a valve has no icon pair, so the frame is all it has); lock keeps
|
||||
its separate red-unlocked/green-locked palette [auto:
|
||||
smoke_cover_no_plate + unit stateIcon «every class, both ways»]
|
||||
- [ ] Cover target and indication parity: on a device where a hidden functional
|
||||
`cover.*` competes with an auxiliary option switch, the shared resolver
|
||||
selects the cover, calls only it and supplies the same entity to icon
|
||||
@@ -1608,8 +1624,9 @@ require hands on real hardware — they remain for the human pass.
|
||||
resolved media entities fade only when none is available and powered
|
||||
- [ ] Controls aggregate their targets: any working target drives both the
|
||||
yellow plate and the running effect
|
||||
- [ ] Open contact/unlocked lock/open valve are orange; an open cover stays
|
||||
neutral because its icon morph carries that state
|
||||
- [ ] Open contact/open valve are orange; unlocked lock is red and locked lock
|
||||
is green; an open cover stays neutral because its icon morph carries that
|
||||
state
|
||||
- [ ] Unavailable suppresses ordinary activity. Alarm outranks all dynamic
|
||||
presentation, including when ordinary live states are off; `static_icon`
|
||||
deliberately hides alarm paint without suppressing service-call errors
|
||||
@@ -1633,8 +1650,9 @@ require hands on real hardware — they remain for the human pass.
|
||||
- [ ] Bind a contact sensor: open → leaf swings and the arc draws on in the accent colour;
|
||||
closed → leaf lies along the wall, arc hidden; invert flips this
|
||||
- [ ] Sensor unavailable → the opening freezes at its static default (door open / window closed)
|
||||
- [ ] A door with a lock shows the package padlock badge: black/dark locked,
|
||||
amber unlocked and neutral unknown in both themes
|
||||
- [ ] A door with a lock shows the compact padlock badge: green locked, red
|
||||
unlocked and neutral unknown; coloured glyphs are white in Light and
|
||||
`#252525` in Dark
|
||||
- [ ] A gate defaults to 300 cm, has two equal leaves, no swing arc and opens
|
||||
10° outwards; contact, inversion, lock, drag and resize anchoring match a door
|
||||
- [ ] Clicking an opening (or the padlock) in view mode opens the info card with both states;
|
||||
|
||||
+5
-4
@@ -310,8 +310,8 @@ near-end openings remain visible and are not moved until their geometry is
|
||||
edited.
|
||||
|
||||
An opening may bind a contact; doors and gates may also bind a lock. View paints
|
||||
the moving leaf and state. A lock badge uses the package's black/dark locked
|
||||
state and amber unlocked state. A plan tap never toggles a lock. The opening
|
||||
the moving leaf and state. A lock badge is green when locked and red when
|
||||
unlocked. A plan tap never toggles a lock. The opening
|
||||
card provides a labelled
|
||||
lock/unlock control, with confirmation before unlocking.
|
||||
|
||||
@@ -408,8 +408,9 @@ stable core, icon or value, and optional activity pulse. Visual priority is
|
||||
| State | Meaning | Examples |
|
||||
|---|---|---|
|
||||
| Red alarm | Critical condition, even with live states disabled | Smoke, gas, CO, leak, tamper/problem/safety, triggered alarm |
|
||||
| Yellow | Device is doing its main job or a lock is unlocked | Light/switch/fan on, active climate, vacuum cleaning, known appliance work, unlocked lock |
|
||||
| Black lock glyph | Lock is secured | Locked lock |
|
||||
| Yellow | Device is doing its main job | Light/switch/fan on, active climate, vacuum cleaning, known appliance work |
|
||||
| Red lock | Lock is unsecured | Unlocked/open lock |
|
||||
| Green lock | Lock is secured | Locked lock |
|
||||
| Orange | Physically open | Door/window contact, opening valve |
|
||||
| Faded | Data unavailable | All relevant entities unknown, unavailable or absent |
|
||||
| Neutral | No alarm, work or open condition | Off, closed, idle, standby, docked |
|
||||
|
||||
@@ -523,8 +523,8 @@ YAML-сущность без `unique_id` и строки в Entity Registry: п
|
||||
|
||||
### Замок
|
||||
|
||||
- `locked` показывается как закрытый чёрный/тёмный замок из дизайн-пакета;
|
||||
- `unlocked`/`open` — как открытый, с жёлтой подложкой дизайн-пакета;
|
||||
- `locked` показывается как закрытый замок на зелёной подложке;
|
||||
- `unlocked`/`open` — как открытый замок на красной подложке;
|
||||
- неизвестное состояние получает нейтральное/неизвестное оформление;
|
||||
- клик по значку открывает карточку проёма;
|
||||
- закрытие выполняется без дополнительного подтверждения;
|
||||
@@ -775,8 +775,9 @@ Hover включается только после события от наст
|
||||
| Вид | Что означает | Примеры |
|
||||
|---|---|---|
|
||||
| Красная авария | Критическое событие; показывается даже при выключенных живых состояниях | Дым, газ, CO, протечка, tamper/problem/safety, сирена `on`, охранная панель `triggered` |
|
||||
| Жёлтая подложка | Устройство сейчас выполняет работу либо замок разблокирован | Свет/реле/вентилятор/увлажнитель `on`; climate с распознанным action `heating`, `cooling`, `preheating` или `defrosting`; при отсутствии распознанного action — включённый HVAC-режим как лучшее доступное приближение; vacuum cleaning/returning; script on; техника washing/drying/etc.; lock unlocked/open |
|
||||
| Чёрный значок замка | Замок заблокирован | lock locked |
|
||||
| Жёлтая подложка | Устройство сейчас выполняет работу | Свет/реле/вентилятор/увлажнитель `on`; climate с распознанным action `heating`, `cooling`, `preheating` или `defrosting`; при отсутствии распознанного action — включённый HVAC-режим как лучшее доступное приближение; vacuum cleaning/returning; script on; техника washing/drying/etc. |
|
||||
| Красная подложка замка | Замок разблокирован | lock unlocked/open |
|
||||
| Зелёная подложка замка | Замок заблокирован | lock locked |
|
||||
| Оранжевая подложка | Физически открыто | Датчик двери/окна/гаража `on`, клапан open/opening/closing |
|
||||
| Бледный маркер | Данные недоступны либо медиаплеер выключен | Все рабочие сущности маркера `unknown`, `unavailable` или отсутствуют; `media_player.* = off` |
|
||||
| Нейтральный | Нет аварии, работы и открытого состояния | Выключено, закрыто, idle/standby/docked и т. п. |
|
||||
@@ -894,7 +895,7 @@ marker на плане при этом не меняются.
|
||||
| Door/window/garage contact | Оранжевый, пока `on` | Короткое событие при открытии | Закрыто/открыто для известных классов | Внутренняя карточка |
|
||||
| `cover.*` штор/жалюзи | Нейтральный | Переход во время opening/closing либо короткий fallback | Закрыто/открыто | Внутренняя карточка; **Переключить состояние** автоматически делает open/close/stop |
|
||||
| `cover.*` garage/door/gate | Нейтральный | Переход во время движения | Закрыто/открыто | **Переключить состояние** можно сохранить, но оно явно показывает защитный no-op и не выполняет service call |
|
||||
| `lock.*` | Оранжевый при unlocked/open | Переход при locking/unlocking | Замкнут/открыт | Карточка; Toggle запрещён |
|
||||
| `lock.*` | Красный при unlocked/open, зелёный при locked | Переход при locking/unlocking | Замкнут/открыт | Карточка; Toggle запрещён |
|
||||
| `valve.*` | Оранжевый при open/opening/closing | Переход при opening/closing | Обычно нет | Карточка; Toggle можно выбрать явно |
|
||||
| `climate.*` | Жёлтый при реальном heating/cooling/working; если интеграция не отдаёт action — пока включён HVAC-режим | Работа по `hvac_action`/аналогичному атрибуту, иначе по режиму из `hvac_modes` | Нет | Внутренняя карточка |
|
||||
| `media_player.*` | Нейтральный при любом включённом/транспортном состоянии; приглушённый при `off`, как при `unknown`/`unavailable` | Нет: `playing`, вспомогательная подсветка и служебные переключатели не считаются работой | Нет | Внутренняя карточка |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"fixture": "synthetic-only",
|
||||
"sourceFingerprint": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceFingerprint": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"captureScriptSha256": "34f2219790d46efd8250e7a1bd829cb8fc0b0547e1260635fefa52407551b41b",
|
||||
"command": "npm run build && node demo/docs/capture.mjs",
|
||||
"scenarios": {
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "2885f96e348b15ab7c696e56e99bddcd9bb2ee94218a883e5a2155f45f5042aa"
|
||||
},
|
||||
"view-touch": {
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "f62d8af3617c00a5e99511bd765980d2e27badf25047a1be34046b64195abe6c"
|
||||
},
|
||||
"space-create": {
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "c53db2e5c642a5549c13f3c93a5b359fed69bdb2621bf71a243a877ffcb95e6b"
|
||||
},
|
||||
"room-contour-close": {
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "2f13ea645af306eee9cc7c52699c928377b72f412b08c1a3db6beb848ace1b51"
|
||||
},
|
||||
"plan-context-tray": {
|
||||
@@ -57,7 +57,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "a6c526fede11bc3503fd2384bd4a3f6afa008f481c5c47578f06cae6c81c6f9b"
|
||||
},
|
||||
"device-editor": {
|
||||
@@ -68,7 +68,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "9585b59add4d35b5a6f028ce5b720ef1b77a3f8b19436d192cd1a0e6fc637264"
|
||||
},
|
||||
"device-display-preview": {
|
||||
@@ -79,7 +79,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "cfc317da4628d079a116ff71311fbf06b1eb3b181928a5ea7ba888ab936a5e8b"
|
||||
},
|
||||
"background-editor": {
|
||||
@@ -90,7 +90,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "d7cfe70551d9260169df8efd832e32ed7df99c7f60b55d1fd4a8322bd4c47175"
|
||||
},
|
||||
"room-card": {
|
||||
@@ -101,7 +101,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
|
||||
},
|
||||
"device-info": {
|
||||
@@ -112,7 +112,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "54e06c66d6c97a941c5b5850da6b71f433ec2f93e75100363c0c943c06e56fce",
|
||||
"sourceSha256": "9c09c069c89daab3481acea8378351def1a80b52790425944cd55b0f1ff621e7",
|
||||
"imageSha256": "a06cbf83f09e2f67b3566d7c0b10e973db060c3d20786f26f74ada6a21937c3e"
|
||||
}
|
||||
}
|
||||
|
||||
+17
-23
@@ -831,23 +831,19 @@ export const cardStyles = css`
|
||||
line-height: 0;
|
||||
}
|
||||
.oplock.locked {
|
||||
--oplock-core-bg: light-dark(#000, #252525);
|
||||
--oplock-core-fg: #fff;
|
||||
--oplock-shell-stroke: light-dark(#000, #252525);
|
||||
}
|
||||
.oplock.theme-light.locked {
|
||||
--oplock-core-bg: #000;
|
||||
--oplock-shell-stroke: #000;
|
||||
--oplock-core-bg: #66D17A;
|
||||
--oplock-core-fg: light-dark(#fff, #252525);
|
||||
--oplock-shell-stroke: #66D17A;
|
||||
}
|
||||
.oplock.theme-light.locked { --oplock-core-fg: #fff; }
|
||||
.oplock.theme-dark.locked {
|
||||
--oplock-core-bg: #252525;
|
||||
--oplock-shell-stroke: #252525;
|
||||
--oplock-core-fg: #252525;
|
||||
--oplock-stroke-ratio: .025;
|
||||
}
|
||||
.oplock.unlocked {
|
||||
--oplock-core-bg: #F0A00C;
|
||||
--oplock-core-bg: #F0410C;
|
||||
--oplock-core-fg: light-dark(#fff, #252525);
|
||||
--oplock-shell-stroke: #F0A00C;
|
||||
--oplock-shell-stroke: #F0410C;
|
||||
}
|
||||
.oplock.theme-light.unlocked { --oplock-core-fg: #fff; }
|
||||
.oplock.theme-dark.unlocked {
|
||||
@@ -2142,26 +2138,24 @@ export const cardStyles = css`
|
||||
}
|
||||
.dev.open {
|
||||
--device-face-bg: var(--hp-open);
|
||||
--device-face-fg: #4a2800;
|
||||
--device-face-fg: light-dark(#fff, #252525);
|
||||
}
|
||||
.dev.theme-light.open { --device-face-fg: #fff; }
|
||||
.dev.theme-dark.open { --device-face-fg: #252525; }
|
||||
.dev.lock-locked {
|
||||
--device-face-bg: light-dark(#000, #252525);
|
||||
--device-face-fg: #fff;
|
||||
--device-shell-stroke: light-dark(#000, #252525);
|
||||
}
|
||||
.dev.theme-light.lock-locked {
|
||||
--device-face-bg: #000;
|
||||
--device-shell-stroke: #000;
|
||||
--device-face-bg: #66D17A;
|
||||
--device-face-fg: light-dark(#fff, #252525);
|
||||
--device-shell-stroke: #66D17A;
|
||||
}
|
||||
.dev.theme-light.lock-locked { --device-face-fg: #fff; }
|
||||
.dev.theme-dark.lock-locked {
|
||||
--device-face-bg: #252525;
|
||||
--device-shell-stroke: #252525;
|
||||
--device-face-fg: #252525;
|
||||
--device-shell-stroke-ratio: .025;
|
||||
}
|
||||
.dev.lock-unlocked {
|
||||
--device-face-bg: #F0A00C;
|
||||
--device-face-bg: #F0410C;
|
||||
--device-face-fg: light-dark(#fff, #252525);
|
||||
--device-shell-stroke: #F0A00C;
|
||||
--device-shell-stroke: #F0410C;
|
||||
}
|
||||
.dev.theme-light.lock-unlocked { --device-face-fg: #fff; }
|
||||
.dev.theme-dark.lock-unlocked {
|
||||
|
||||
@@ -34,12 +34,45 @@ test('issue 213 projects opening locks through the compact package layers', () =
|
||||
assert.match(styles, /--oplock-size:\s*calc\(var\(--icon-size,\s*2\.5cqw\)\s*\*\s*0\.62\)/);
|
||||
assert.match(styles, /--oplock-core-size:\s*calc\(var\(--oplock-size\)\s*\/\s*1\.26875\)/);
|
||||
assert.match(styles, /\.oplock ha-icon\s*\{[\s\S]*--mdc-icon-size:[^;]*\*\s*0\.55/);
|
||||
assert.match(styles, /\.oplock\.unlocked\s*\{[\s\S]*--oplock-core-bg:\s*#F0A00C/);
|
||||
assert.doesNotMatch(styles, /\.oplock\.locked\s*\{[^}]*#66d17a/i);
|
||||
assert.match(card, /class="oplock \$\{deviceThemeClass\(this\._renderPlanHass\)\}/);
|
||||
assert.match(card, /class="oplock-shell"[\s\S]*class="oplock-core"/);
|
||||
});
|
||||
|
||||
test('issue 219 gives locks red-open green-closed and themes every colored glyph', () => {
|
||||
const styles = source('styles.ts');
|
||||
const card = source('houseplan-card.ts');
|
||||
for (const [selector, background] of [
|
||||
['oplock\\.locked', '#66D17A'],
|
||||
['oplock\\.unlocked', '#F0410C'],
|
||||
['dev\\.lock-locked', '#66D17A'],
|
||||
['dev\\.lock-unlocked', '#F0410C'],
|
||||
]) {
|
||||
const block = styles.match(new RegExp(`\\.${selector}\\s*\\{([\\s\\S]*?)\\}`))?.[1] || '';
|
||||
assert.match(block, new RegExp(`(?:core|face)-bg:\\s*${background}`));
|
||||
assert.match(block, new RegExp(`shell-stroke:\\s*${background}`));
|
||||
assert.match(block, /(?:core|face)-fg:\s*light-dark\(#fff, #252525\)/);
|
||||
}
|
||||
|
||||
const open = styles.match(/\.dev\.open\s*\{([\s\S]*?)\}/)?.[1] || '';
|
||||
assert.match(open, /--device-face-bg:\s*var\(--hp-open\)/);
|
||||
assert.match(open, /--device-face-fg:\s*light-dark\(#fff, #252525\)/);
|
||||
assert.doesNotMatch(open, /#4a2800/i);
|
||||
|
||||
for (const state of ['on', 'open', 'lock-locked', 'lock-unlocked']) {
|
||||
assert.match(styles, new RegExp(`\\.dev\\.theme-light\\.${state.replace('-', '\\-')}[^}]*#fff`));
|
||||
assert.match(styles, new RegExp(`\\.dev\\.theme-dark\\.${state.replace('-', '\\-')}[\\s\\S]*?--device-face-fg:\\s*#252525`));
|
||||
}
|
||||
for (const state of ['locked', 'unlocked']) {
|
||||
assert.match(styles, new RegExp(`\\.oplock\\.theme-light\\.${state}[^}]*#fff`));
|
||||
assert.match(styles, new RegExp(`\\.oplock\\.theme-dark\\.${state}[\\s\\S]*?--oplock-core-fg:\\s*#252525`));
|
||||
}
|
||||
|
||||
assert.match(card, /locked \? 'mdi:lock' : known \? 'mdi:lock-open-variant'/);
|
||||
for (const name of ['houseplan-card.ts', 'hp-device-preview.ts', 'space-card.ts']) {
|
||||
assert.match(source(name), /cardStyles/, `${name} must keep the shared state palette`);
|
||||
}
|
||||
});
|
||||
|
||||
test('issue 212 Text value uses a stadium radius based on height', () => {
|
||||
const styles = source('styles.ts');
|
||||
const rule = styles.match(/\.dev\.valonly \.device-core\s*\{([\s\S]*?)\}/)?.[1] || '';
|
||||
|
||||
@@ -321,6 +321,7 @@ test('device icon state-table goldens cover both themes and design facets', () =
|
||||
assert.deepEqual(scenario.deviceClassOverrides['golden-presence'], ['virtual']);
|
||||
assert.deepEqual(scenario.deviceClassOverrides['golden-light-two'], ['lock-locked']);
|
||||
assert.deepEqual(scenario.deviceClassOverrides['golden-light-three'], ['lock-unlocked']);
|
||||
assert.deepEqual(scenario.deviceClassOverrides['golden-right-linkquality'], ['open']);
|
||||
const fixture = prepareGoldenFixture(scenario);
|
||||
assert.equal(fixture.states['light.golden_light_one'].attributes.lqi, 40);
|
||||
assert.equal(fixture.states['light.golden_light_two'].attributes.lqi, 41);
|
||||
|
||||
Reference in New Issue
Block a user