Add HA Lovelace cards replicating Luma glass UI
Custom card bundle (luma-card, luma-weather-card) with slide-in panels for light/climate/cover/media/vacuum, matching the TV launcher's design tokens. Includes theme, dashboard YAML for all 8 rooms, and deployment handoff doc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
bde779d759
commit
89ff17a3bc
6 files changed
+1766
No files matched your search
@@ -0,0 +1,112 @@
|
|||||||
|
# Luma HA Cards — Deployment Handoff
|
||||||
|
|
||||||
|
## What this is
|
||||||
|
|
||||||
|
Custom Lovelace card bundle that replicates the Luma TV launcher's glass UI as a
|
||||||
|
Home Assistant dashboard. Built in a single session on 2026-10-27.
|
||||||
|
|
||||||
|
## Files in `ha-cards/`
|
||||||
|
|
||||||
|
| File | Size | What it does |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `luma-cards.js` | 43 KB | Main card element (`<luma-card>`) — glass cards with accent glow per domain, brightness bars, curtain SVGs, media artwork. Click opens a slide-in panel (420px, right edge) with full controls for each domain. |
|
||||||
|
| `luma-weather-card.js` | 7 KB | Weather card (`<luma-weather-card>`) — current conditions + 5-day forecast row. |
|
||||||
|
| `luma-theme.yaml` | 2.5 KB | HA theme (`luma_glass`) — dark glass palette, gold accent, Inter font. |
|
||||||
|
| `dashboard.yaml` | 7 KB | Full dashboard YAML — all 8 rooms from `ha-home/config.json`, native HA grid cards, no HACS dependencies. |
|
||||||
|
| `README.md` | 3 KB | Installation steps (also summarised below). |
|
||||||
|
|
||||||
|
## Slide-in panel controls per domain
|
||||||
|
|
||||||
|
| Domain | Panel sections |
|
||||||
|
|--------|---------------|
|
||||||
|
| **light** | Brightness slider + presets (Off/25/50/100%) + colour temp + colour swatches + effects grid |
|
||||||
|
| **climate** | Target temp + HVAC mode icons + fan auto (Auto/Quiet/Turbo) + fan manual slider + airflow buttons |
|
||||||
|
| **cover** | Position slider + Open/Stop/Close + presets (Open/60/45/30/Closed) |
|
||||||
|
| **media_player** | Now playing (art + title + artist) + progress bar + transport (prev/play-pause/next) + volume |
|
||||||
|
| **vacuum** | Status + battery + fan mode buttons + Start/Pause/Dock |
|
||||||
|
| **switch/scene/script** | No panel — click toggles directly |
|
||||||
|
|
||||||
|
## Design token mapping
|
||||||
|
|
||||||
|
All colours, radii, shadows, and sizes come from `design-system/tokens.json`.
|
||||||
|
Key values hardcoded in the JS `T` object:
|
||||||
|
|
||||||
|
- Card fill: `rgba(34,38,46,0.88)`, border `rgba(255,255,255,0.10)`, radius 22
|
||||||
|
- Panel fill: `rgba(30,34,42,0.98)`, width 420, radius 28
|
||||||
|
- Accents: light `#F5B85A`, heat `#F06A4A`, cool `#4C9BF0`, curtain `#DCD2B4`, media `#9B7DDB`, vacuum `#60C090`
|
||||||
|
|
||||||
|
## Deployment steps (on Unraid SSH → HA)
|
||||||
|
|
||||||
|
### 1. Copy JS + theme files to HA
|
||||||
|
|
||||||
|
From Unraid (which has the repo or can pull from Gitea):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone or pull on Unraid
|
||||||
|
cd /tmp && git clone https://jazzymc:<password>@git.xtrm-lab.org/jazzymc/luma-home.git 2>/dev/null || (cd /tmp/luma-home && git pull)
|
||||||
|
|
||||||
|
# SCP to HA (needs HA SSH addon password or key)
|
||||||
|
scp -P 22 /tmp/luma-home/ha-cards/luma-cards.js /tmp/luma-home/ha-cards/luma-weather-card.js root@192.168.10.102:/config/www/luma/
|
||||||
|
|
||||||
|
# Or if the HA Terminal addon is used, from inside HA:
|
||||||
|
mkdir -p /config/www/luma
|
||||||
|
# then pull files via scp from Unraid
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Register Lovelace resources
|
||||||
|
|
||||||
|
In HA → **Settings → Dashboards → Resources** (top-right ⋮ menu), add:
|
||||||
|
|
||||||
|
| URL | Type |
|
||||||
|
|-----|------|
|
||||||
|
| `/local/luma/luma-cards.js` | JavaScript Module |
|
||||||
|
| `/local/luma/luma-weather-card.js` | JavaScript Module |
|
||||||
|
|
||||||
|
Or in `configuration.yaml`:
|
||||||
|
```yaml
|
||||||
|
lovelace:
|
||||||
|
resources:
|
||||||
|
- url: /local/luma/luma-cards.js
|
||||||
|
type: module
|
||||||
|
- url: /local/luma/luma-weather-card.js
|
||||||
|
type: module
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Install theme
|
||||||
|
|
||||||
|
Copy theme file:
|
||||||
|
```bash
|
||||||
|
mkdir -p /config/themes
|
||||||
|
cp /tmp/luma-home/ha-cards/luma-theme.yaml /config/themes/luma_glass.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Or merge into existing themes config. Then reload themes in **Developer Tools → YAML → Themes**.
|
||||||
|
|
||||||
|
### 4. Create dashboard
|
||||||
|
|
||||||
|
**Settings → Dashboards → Add Dashboard** → set title "Luma Home", icon `mdi:television`.
|
||||||
|
Switch to YAML mode and paste `dashboard.yaml` contents.
|
||||||
|
|
||||||
|
Or add to `configuration.yaml`:
|
||||||
|
```yaml
|
||||||
|
lovelace:
|
||||||
|
dashboards:
|
||||||
|
luma:
|
||||||
|
mode: yaml
|
||||||
|
title: Luma Home
|
||||||
|
icon: mdi:television
|
||||||
|
show_in_sidebar: true
|
||||||
|
filename: dashboards/luma.yaml
|
||||||
|
```
|
||||||
|
Then `cp ha-cards/dashboard.yaml /config/dashboards/luma.yaml`.
|
||||||
|
|
||||||
|
### 5. Activate theme
|
||||||
|
|
||||||
|
Profile (avatar) → Theme → **Luma Glass**.
|
||||||
|
|
||||||
|
## What's NOT deployed yet (TV binary)
|
||||||
|
|
||||||
|
The Luma TV binary with all 11 fixes from this session was compiled and is at
|
||||||
|
`/mnt/user/appdata/luma-build/target/armv7-unknown-linux-gnueabi/release/luma` on Unraid
|
||||||
|
and `/tmp/luma-deploy` on Mac. The TV (192.168.30.124) was offline during this session.
|
||||||
|
Deploy when it comes back online — see the main project README for the deploy process.
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# Luma Home — HA Lovelace Cards
|
||||||
|
|
||||||
|
Custom Lovelace cards that replicate the Luma TV launcher design for Home Assistant.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `luma-cards.js` | Main card bundle — `luma-card` element with slide-in panels |
|
||||||
|
| `luma-weather-card.js` | Weather card with 5-day forecast |
|
||||||
|
| `luma-theme.yaml` | HA theme matching the Luma glass aesthetic |
|
||||||
|
| `dashboard.yaml` | Full dashboard config with all rooms |
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Copy card JS files
|
||||||
|
|
||||||
|
Copy both JS files to your HA `www/` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp ha-cards/luma-cards.js ha-cards/luma-weather-card.js \
|
||||||
|
root@192.168.10.102:/config/www/luma/
|
||||||
|
```
|
||||||
|
|
||||||
|
Or via the File Editor addon, create `/config/www/luma/` and upload both files.
|
||||||
|
|
||||||
|
### 2. Register as resources
|
||||||
|
|
||||||
|
Go to **Settings → Dashboards → Resources** (or add to `configuration.yaml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# configuration.yaml
|
||||||
|
lovelace:
|
||||||
|
resources:
|
||||||
|
- url: /local/luma/luma-cards.js
|
||||||
|
type: module
|
||||||
|
- url: /local/luma/luma-weather-card.js
|
||||||
|
type: module
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Install the theme
|
||||||
|
|
||||||
|
Add to your `themes:` section in `configuration.yaml`, or merge into an existing themes file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# configuration.yaml
|
||||||
|
frontend:
|
||||||
|
themes: !include_dir_merge_named themes/
|
||||||
|
```
|
||||||
|
|
||||||
|
Then copy `luma-theme.yaml` to `/config/themes/luma_glass.yaml`.
|
||||||
|
|
||||||
|
### 4. Import the dashboard
|
||||||
|
|
||||||
|
Go to **Settings → Dashboards → Add Dashboard**, then switch to YAML mode and paste the contents of `dashboard.yaml`.
|
||||||
|
|
||||||
|
Or add it as a separate dashboard in `configuration.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
lovelace:
|
||||||
|
mode: storage
|
||||||
|
dashboards:
|
||||||
|
luma:
|
||||||
|
mode: yaml
|
||||||
|
title: Luma Home
|
||||||
|
icon: mdi:television
|
||||||
|
show_in_sidebar: true
|
||||||
|
filename: dashboards/luma.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Then copy `dashboard.yaml` to `/config/dashboards/luma.yaml`.
|
||||||
|
|
||||||
|
### 5. Activate the theme
|
||||||
|
|
||||||
|
Go to your **Profile** (click your avatar) → **Theme** → select **Luma Glass**.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
None — uses only native HA grid cards and custom elements. No HACS cards required.
|
||||||
|
|
||||||
|
## Card types
|
||||||
|
|
||||||
|
### `luma-card`
|
||||||
|
|
||||||
|
General-purpose card for lights, climate, covers, switches, scenes, media, and vacuum.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
type: custom:luma-card
|
||||||
|
entity: light.living_room
|
||||||
|
name: Living Room # optional, defaults to friendly_name
|
||||||
|
icon: mdi:lightbulb # optional, auto-detected from domain
|
||||||
|
type_hint: range # optional, helps with icon/accent selection
|
||||||
|
```
|
||||||
|
|
||||||
|
Click opens a slide-in panel with full controls (brightness, color temp, color swatches, presets, effects for lights; target temp, mode, fan speed, airflow for climate; position/presets for covers; transport/volume for media; rooms/mode/controls for vacuum).
|
||||||
|
|
||||||
|
Toggle-only entities (switches, scenes, scripts, lights without color modes) toggle on click instead of opening a panel.
|
||||||
|
|
||||||
|
### `luma-weather-card`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
type: custom:luma-weather-card
|
||||||
|
entity: weather.forecast_home
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
All colors are defined in the JS files' `T` (tokens) object and in the theme YAML. Adjust there to match your preference.
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
title: Luma Home
|
||||||
|
views:
|
||||||
|
# ── Home ──────────────────────────────────────────────
|
||||||
|
- title: Home
|
||||||
|
path: home
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-weather-card
|
||||||
|
entity: weather.forecast_home
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: media_player.music_assistant
|
||||||
|
name: Music
|
||||||
|
icon: mdi:music
|
||||||
|
type_hint: media
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: vacuum.living_room_roborock_s7_pro_ultra
|
||||||
|
name: Roborock
|
||||||
|
icon: mdi:robot-vacuum
|
||||||
|
type_hint: vacuum
|
||||||
|
|
||||||
|
# ── Living Area ───────────────────────────────────────
|
||||||
|
- title: Living Area
|
||||||
|
path: living-area
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
# Climate
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: climate.living_room_thermostat
|
||||||
|
name: Thermostat
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: climate.living_room_radiator
|
||||||
|
name: Radiator
|
||||||
|
|
||||||
|
# Lights
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.living_room_light
|
||||||
|
name: Ceiling Light
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.tv_lights
|
||||||
|
name: TV Lights
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.rgbic_strip_lights_z1
|
||||||
|
name: LED Strip 1
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.rgbic_strip_lights_z2
|
||||||
|
name: LED Strip 2
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.picture_frame_lamp
|
||||||
|
name: Picture Lamp
|
||||||
|
|
||||||
|
# Curtain
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: cover.living_room_curtain
|
||||||
|
name: Curtain
|
||||||
|
icon: mdi:curtains
|
||||||
|
type_hint: curtain
|
||||||
|
|
||||||
|
# More lights
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.dining_room_light
|
||||||
|
name: Dining Light
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.entrance_light_1
|
||||||
|
name: Entrance
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.entrance_mirror_light
|
||||||
|
name: Mirror
|
||||||
|
|
||||||
|
# Scenes & Actions row
|
||||||
|
- type: grid
|
||||||
|
columns: 4
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: scene.tv_mode
|
||||||
|
name: TV Mode
|
||||||
|
icon: mdi:television
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: scene.living_room_100
|
||||||
|
name: "100%"
|
||||||
|
icon: mdi:brightness-7
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: scene.living_room_lights_10
|
||||||
|
name: "10%"
|
||||||
|
icon: mdi:brightness-4
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: script.tv_ambient_on
|
||||||
|
name: Ambient On
|
||||||
|
icon: mdi:led-strip-variant
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: script.tv_ambient_off
|
||||||
|
name: Ambient Off
|
||||||
|
icon: mdi:led-strip-variant
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: script.living_dining_all_off
|
||||||
|
name: All Off
|
||||||
|
icon: mdi:lightbulb-off
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: scene.curtains_close
|
||||||
|
name: Close Curtains
|
||||||
|
icon: mdi:curtains-closed
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
# ── Kitchen ───────────────────────────────────────────
|
||||||
|
- title: Kitchen
|
||||||
|
path: kitchen
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: climate.kitchen_termostat
|
||||||
|
name: Thermostat
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: switch.dishwasher_power
|
||||||
|
name: Dishwasher
|
||||||
|
icon: mdi:dishwasher
|
||||||
|
|
||||||
|
# ── Dad's Bedroom ─────────────────────────────────────
|
||||||
|
- title: Dad's Bedroom
|
||||||
|
path: bedroom
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: climate.main_bedroom_radiator
|
||||||
|
name: Radiator
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.bedroom_ceiling_light
|
||||||
|
name: Ceiling
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.bedroom_desk_lights
|
||||||
|
name: Desk
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.bedroom_led_strip
|
||||||
|
name: LED Strip
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: scene.desk_mac
|
||||||
|
name: "Desk: Mac"
|
||||||
|
icon: mdi:apple
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: scene.desk_pc
|
||||||
|
name: "Desk: PC"
|
||||||
|
icon: mdi:desktop-tower-monitor
|
||||||
|
type_hint: action
|
||||||
|
|
||||||
|
# ── Dari's Bedroom ────────────────────────────────────
|
||||||
|
- title: Dari's Bedroom
|
||||||
|
path: dari-bedroom
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: climate.girls_bedroom_radiator
|
||||||
|
name: Radiator
|
||||||
|
|
||||||
|
# ── Teo's Bedroom ─────────────────────────────────────
|
||||||
|
- title: Teo's Bedroom
|
||||||
|
path: teo-bedroom
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.boys_smartlight
|
||||||
|
name: Light
|
||||||
|
|
||||||
|
# ── Main Bathroom ─────────────────────────────────────
|
||||||
|
- title: Main Bathroom
|
||||||
|
path: main-bathroom
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.bathroom_light_1
|
||||||
|
name: Light 1
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.bathroom_light_2
|
||||||
|
name: Light 2
|
||||||
|
|
||||||
|
# ── Kids Bathroom ─────────────────────────────────────
|
||||||
|
- title: Kids Bathroom
|
||||||
|
path: kids-bathroom
|
||||||
|
cards:
|
||||||
|
- type: grid
|
||||||
|
columns: 3
|
||||||
|
square: false
|
||||||
|
cards:
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.kids_bathroom_light_1
|
||||||
|
name: Light 1
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: light.kids_bathroom_light_2
|
||||||
|
name: Light 2
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: switch.washing_machine_power
|
||||||
|
name: Washing Machine
|
||||||
|
icon: mdi:washing-machine
|
||||||
|
|
||||||
|
- type: custom:luma-card
|
||||||
|
entity: switch.dryer_power
|
||||||
|
name: Dryer
|
||||||
|
icon: mdi:tumble-dryer
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,78 @@
|
|||||||
|
luma_glass:
|
||||||
|
# ── Background ──
|
||||||
|
lovelace-background: "linear-gradient(135deg, #080B12 0%, #11141C 50%, #080B12 100%)"
|
||||||
|
primary-background-color: "#0D1017"
|
||||||
|
secondary-background-color: "#11141C"
|
||||||
|
card-background-color: "rgba(34,38,46,0.88)"
|
||||||
|
|
||||||
|
# ── Text ──
|
||||||
|
primary-text-color: "#FFFFFF"
|
||||||
|
secondary-text-color: "rgba(255,255,255,0.72)"
|
||||||
|
text-primary-color: "#FFFFFF"
|
||||||
|
disabled-text-color: "rgba(255,255,255,0.40)"
|
||||||
|
|
||||||
|
# ── Accent ──
|
||||||
|
primary-color: "#F5B85A"
|
||||||
|
accent-color: "#F5B85A"
|
||||||
|
light-primary-color: "rgba(245,184,90,0.24)"
|
||||||
|
|
||||||
|
# ── Cards ──
|
||||||
|
ha-card-background: "rgba(34,38,46,0.88)"
|
||||||
|
ha-card-border-color: "rgba(255,255,255,0.10)"
|
||||||
|
ha-card-border-radius: "22px"
|
||||||
|
ha-card-box-shadow: "0 2px 8px rgba(0,0,0,0.25)"
|
||||||
|
ha-card-header-color: "#FFFFFF"
|
||||||
|
ha-card-header-font-size: "19px"
|
||||||
|
|
||||||
|
# ── Sidebar ──
|
||||||
|
sidebar-background-color: "#0D1017"
|
||||||
|
sidebar-text-color: "#FFFFFF"
|
||||||
|
sidebar-icon-color: "rgba(255,255,255,0.60)"
|
||||||
|
sidebar-selected-icon-color: "#F5B85A"
|
||||||
|
sidebar-selected-text-color: "#F5B85A"
|
||||||
|
|
||||||
|
# ── Header ──
|
||||||
|
app-header-background-color: "rgba(13,16,23,0.95)"
|
||||||
|
app-header-text-color: "#FFFFFF"
|
||||||
|
|
||||||
|
# ── Dividers ──
|
||||||
|
divider-color: "rgba(255,255,255,0.10)"
|
||||||
|
|
||||||
|
# ── Inputs / controls ──
|
||||||
|
input-fill-color: "rgba(255,255,255,0.06)"
|
||||||
|
input-ink-color: "#FFFFFF"
|
||||||
|
input-label-ink-color: "rgba(255,255,255,0.60)"
|
||||||
|
input-idle-line-color: "rgba(255,255,255,0.20)"
|
||||||
|
input-hover-line-color: "rgba(255,255,255,0.40)"
|
||||||
|
input-dropdown-icon-color: "rgba(255,255,255,0.60)"
|
||||||
|
|
||||||
|
# ── Toggles ──
|
||||||
|
switch-checked-color: "#F5B85A"
|
||||||
|
switch-checked-track-color: "rgba(245,184,90,0.40)"
|
||||||
|
switch-unchecked-button-color: "rgba(255,255,255,0.60)"
|
||||||
|
switch-unchecked-track-color: "rgba(255,255,255,0.20)"
|
||||||
|
|
||||||
|
# ── Sliders ──
|
||||||
|
paper-slider-knob-color: "#F5B85A"
|
||||||
|
paper-slider-active-color: "#F5B85A"
|
||||||
|
paper-slider-container-color: "rgba(255,255,255,0.14)"
|
||||||
|
|
||||||
|
# ── Badges / chips ──
|
||||||
|
label-badge-background-color: "#1E222A"
|
||||||
|
label-badge-text-color: "#FFFFFF"
|
||||||
|
|
||||||
|
# ── Scrollbar ──
|
||||||
|
scrollbar-thumb-color: "rgba(255,255,255,0.20)"
|
||||||
|
|
||||||
|
# ── Dialogs / popups ──
|
||||||
|
dialog-backdrop-color: "rgba(0,0,0,0.45)"
|
||||||
|
|
||||||
|
# ── Misc ──
|
||||||
|
state-icon-color: "rgba(255,255,255,0.60)"
|
||||||
|
state-icon-active-color: "#F5B85A"
|
||||||
|
paper-item-icon-color: "rgba(255,255,255,0.60)"
|
||||||
|
paper-item-icon-active-color: "#F5B85A"
|
||||||
|
mdc-theme-primary: "#F5B85A"
|
||||||
|
|
||||||
|
# ── Font ──
|
||||||
|
primary-font-family: "'Inter', system-ui, -apple-system, sans-serif"
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/**
|
||||||
|
* Luma Weather Card — custom Lovelace card
|
||||||
|
* Shows current conditions + 5-day forecast in the Luma glass style.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const WEATHER_VERSION = '1.0.0';
|
||||||
|
|
||||||
|
const WEATHER_ICONS = {
|
||||||
|
'clear-night': 'mdi:weather-night',
|
||||||
|
'cloudy': 'mdi:weather-cloudy',
|
||||||
|
'fog': 'mdi:weather-fog',
|
||||||
|
'hail': 'mdi:weather-hail',
|
||||||
|
'lightning': 'mdi:weather-lightning',
|
||||||
|
'lightning-rainy': 'mdi:weather-lightning-rainy',
|
||||||
|
'partlycloudy': 'mdi:weather-partly-cloudy',
|
||||||
|
'pouring': 'mdi:weather-pouring',
|
||||||
|
'rainy': 'mdi:weather-rainy',
|
||||||
|
'snowy': 'mdi:weather-snowy',
|
||||||
|
'snowy-rainy': 'mdi:weather-snowy-rainy',
|
||||||
|
'sunny': 'mdi:weather-sunny',
|
||||||
|
'windy': 'mdi:weather-windy',
|
||||||
|
'windy-variant': 'mdi:weather-windy-variant',
|
||||||
|
'exceptional': 'mdi:alert-circle-outline',
|
||||||
|
};
|
||||||
|
|
||||||
|
const CONDITION_LABELS = {
|
||||||
|
'clear-night': 'Clear Night',
|
||||||
|
'cloudy': 'Cloudy',
|
||||||
|
'fog': 'Foggy',
|
||||||
|
'hail': 'Hail',
|
||||||
|
'lightning': 'Thunderstorm',
|
||||||
|
'lightning-rainy': 'Thunderstorm',
|
||||||
|
'partlycloudy': 'Partly Cloudy',
|
||||||
|
'pouring': 'Heavy Rain',
|
||||||
|
'rainy': 'Rainy',
|
||||||
|
'snowy': 'Snowy',
|
||||||
|
'snowy-rainy': 'Sleet',
|
||||||
|
'sunny': 'Sunny',
|
||||||
|
'windy': 'Windy',
|
||||||
|
'windy-variant': 'Windy',
|
||||||
|
'exceptional': 'Exceptional',
|
||||||
|
};
|
||||||
|
|
||||||
|
class LumaWeatherCard extends HTMLElement {
|
||||||
|
set hass(hass) {
|
||||||
|
this._hass = hass;
|
||||||
|
if (!this._config) return;
|
||||||
|
this._render();
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfig(config) {
|
||||||
|
if (!config.entity) throw new Error('Missing entity');
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
_render() {
|
||||||
|
const hass = this._hass;
|
||||||
|
const config = this._config;
|
||||||
|
const stateObj = hass.states[config.entity];
|
||||||
|
if (!stateObj) {
|
||||||
|
this.innerHTML = `<ha-card><div style="padding:16px;color:#888">Entity not found: ${config.entity}</div></ha-card>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const condition = stateObj.state;
|
||||||
|
const temp = stateObj.attributes.temperature;
|
||||||
|
const unit = stateObj.attributes.temperature_unit || '°C';
|
||||||
|
const humidity = stateObj.attributes.humidity;
|
||||||
|
const windSpeed = stateObj.attributes.wind_speed;
|
||||||
|
const windUnit = stateObj.attributes.wind_speed_unit || 'km/h';
|
||||||
|
const icon = WEATHER_ICONS[condition] || 'mdi:weather-partly-cloudy';
|
||||||
|
const label = CONDITION_LABELS[condition] || condition;
|
||||||
|
const forecast = stateObj.attributes.forecast || [];
|
||||||
|
|
||||||
|
if (!this.shadowRoot) {
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = WEATHER_STYLES;
|
||||||
|
this.shadowRoot.appendChild(style);
|
||||||
|
const wrap = document.createElement('div');
|
||||||
|
wrap.className = 'lw-wrap';
|
||||||
|
this.shadowRoot.appendChild(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||||
|
const forecastDays = forecast.slice(0, 5);
|
||||||
|
|
||||||
|
this.shadowRoot.querySelector('.lw-wrap').innerHTML = `
|
||||||
|
<div class="lw-card">
|
||||||
|
<div class="lw-now">
|
||||||
|
<div class="lw-icon-wrap">
|
||||||
|
<ha-icon icon="${icon}" style="--mdc-icon-size:56px;color:#F5B85A"></ha-icon>
|
||||||
|
</div>
|
||||||
|
<div class="lw-now-info">
|
||||||
|
<div class="lw-temp">${temp !== undefined ? Math.round(temp) : '–'}°</div>
|
||||||
|
<div class="lw-condition">${label}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="lw-details">
|
||||||
|
<div class="lw-detail">
|
||||||
|
<ha-icon icon="mdi:water-percent" style="--mdc-icon-size:20px;color:rgba(255,255,255,0.60)"></ha-icon>
|
||||||
|
<span>${humidity !== undefined ? humidity + '%' : '–'}</span>
|
||||||
|
</div>
|
||||||
|
<div class="lw-detail">
|
||||||
|
<ha-icon icon="mdi:weather-windy" style="--mdc-icon-size:20px;color:rgba(255,255,255,0.60)"></ha-icon>
|
||||||
|
<span>${windSpeed !== undefined ? windSpeed + ' ' + windUnit : '–'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${forecastDays.length > 0 ? `
|
||||||
|
<div class="lw-divider"></div>
|
||||||
|
<div class="lw-forecast">
|
||||||
|
${forecastDays.map(f => {
|
||||||
|
const d = new Date(f.datetime);
|
||||||
|
const day = days[d.getDay()];
|
||||||
|
const fIcon = WEATHER_ICONS[f.condition] || 'mdi:weather-partly-cloudy';
|
||||||
|
const lo = f.templow !== undefined ? Math.round(f.templow) : '–';
|
||||||
|
const hi = f.temperature !== undefined ? Math.round(f.temperature) : '–';
|
||||||
|
return `
|
||||||
|
<div class="lw-day">
|
||||||
|
<div class="lw-day-name">${day}</div>
|
||||||
|
<ha-icon icon="${fIcon}" style="--mdc-icon-size:24px;color:rgba(255,255,255,0.72)"></ha-icon>
|
||||||
|
<div class="lw-day-range">
|
||||||
|
<span class="lw-hi">${hi}°</span>
|
||||||
|
<span class="lw-lo">${lo}°</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('')}
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCardSize() { return 4; }
|
||||||
|
static getStubConfig() { return { entity: 'weather.forecast_home' }; }
|
||||||
|
}
|
||||||
|
|
||||||
|
const WEATHER_STYLES = `
|
||||||
|
:host { display: block; }
|
||||||
|
.lw-card {
|
||||||
|
background: rgba(34,38,46,0.88);
|
||||||
|
border: 2px solid rgba(255,255,255,0.10);
|
||||||
|
border-radius: 22px;
|
||||||
|
padding: 22px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.lw-now {
|
||||||
|
display: flex; align-items: center; gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.lw-icon-wrap {
|
||||||
|
width: 72px; height: 72px; border-radius: 50%;
|
||||||
|
background: rgba(245,184,90,0.18);
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.lw-now-info { flex: 1; }
|
||||||
|
.lw-temp {
|
||||||
|
font-size: 48px; font-weight: 700; color: #fff;
|
||||||
|
line-height: 1; letter-spacing: -0.03em;
|
||||||
|
}
|
||||||
|
.lw-condition {
|
||||||
|
font-size: 17px; font-weight: 500; color: rgba(255,255,255,0.72);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
.lw-details {
|
||||||
|
display: flex; gap: 20px; margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.lw-detail {
|
||||||
|
display: flex; align-items: center; gap: 6px;
|
||||||
|
font-size: 15px; font-weight: 500; color: rgba(255,255,255,0.72);
|
||||||
|
}
|
||||||
|
.lw-divider {
|
||||||
|
height: 1px; background: rgba(255,255,255,0.10);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.lw-forecast {
|
||||||
|
display: flex; justify-content: space-between;
|
||||||
|
}
|
||||||
|
.lw-day {
|
||||||
|
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.lw-day-name {
|
||||||
|
font-size: 13px; font-weight: 600; color: rgba(255,255,255,0.60);
|
||||||
|
text-transform: uppercase; letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
.lw-day-range {
|
||||||
|
display: flex; flex-direction: column; align-items: center; gap: 2px;
|
||||||
|
}
|
||||||
|
.lw-hi { font-size: 16px; font-weight: 700; color: #fff; }
|
||||||
|
.lw-lo { font-size: 13px; font-weight: 500; color: rgba(255,255,255,0.50); }
|
||||||
|
`;
|
||||||
|
|
||||||
|
customElements.define('luma-weather-card', LumaWeatherCard);
|
||||||
|
|
||||||
|
window.customCards = window.customCards || [];
|
||||||
|
window.customCards.push({
|
||||||
|
type: 'luma-weather-card',
|
||||||
|
name: 'Luma Weather Card',
|
||||||
|
description: 'Weather card with 5-day forecast in the Luma glass design.',
|
||||||
|
preview: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.info(`%c LUMA-WEATHER %c v${WEATHER_VERSION} `, 'background:#F5B85A;color:#15181E;font-weight:700', 'background:#22262E;color:#F5B85A');
|
||||||
Reference in new issue
Block a user