GUIDE_dashboard_json_EN - dckiller51/astrion-custom-dashboard GitHub Wiki
Language: Italiano · English · Français · Español · Deutsch
Reference: repository
dckiller51/astrion-custom-dashboard, branchmain, commitd73ec52(app 1.1.4). Every option described here was checked against the Kotlin source (config/DashboardLoader.kt,config/AppConfig.kt,cards/impl/*.kt). Later versions may change.All examples use fictitious entities (
light.soggiorno,media_player.salotto, …) shared across all language versions: replace them with your own.
- What it is (and what it isn't)
- Where the file lives and how to upload it
- Root structure
- Pages
- Cards: common rules
- Cards without an editor form
- Cards with an editor form
- Physical keys (
hotkeys/longHotkeys) - Harmony, local IR and Activities
- Theme
- Complete example
- Troubleshooting
Astrion Custom Dashboard is an Android app that replaces the launcher of the Sanytron Astrion HA100 remote. It talks to Home Assistant over WebSocket.
- It is not a Lovelace dashboard and is not installed from HACS.
- Its "cards" are native app components, not Lovelace cards: the syntax is JSON, not YAML.
- Many option names mimic Mushroom cards, but only the options listed here are read by the app.
| Item | Path on the remote |
|---|---|
| Configuration | /sdcard/astrion/dashboard.json |
| PNG icons | /sdcard/astrion/icons/ |
| IR database (optional) | /sdcard/astrion/ir-database/ |
Ways to edit it:
- Online editor — https://dckiller51.github.io/astrion-custom-dashboard/: forms for pages, cards and keys; download the generated JSON.
-
Local page —
http://<remote-ip>:8080(address shown in the Settings panel): upload/downloaddashboard.json, upload icons. Uploading reloads the dashboard without a restart. -
adb —
adb push dashboard.json /sdcard/astrion/dashboard.json, then reopen the app.
⚠️ If the JSON is invalid the app does not crash: it loads its built-in layout and showsdashboard.json invalid (…) — using built-in defaults. Always download a backup before uploading.
⚠️ In the online editor, reopening a card and saving it through the simple form rewrites its options from scratch: JSON-only fields (section 6) are lost. For those cards always use the "Options (raw JSON)" field.
The root is a JSON object. An array of cards is also accepted: it becomes a single page named Main.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
pages |
array | yes (non-empty) | — | Pages, left to right |
startPage |
integer | no | 0 |
Index (0-based) of the page shown at launch |
hotkeys |
array | no | [] |
Physical keys, short press |
longHotkeys |
array | no | [] |
Physical keys, long press (~500 ms) |
irDevices |
array | no | [] |
Local IR devices (section 9) |
activities |
array | no | [] |
Composed Activities (section 9) |
theme |
object | no | dark theme | Colours (section 10) |
{
"startPage": 0,
"pages": [
{ "name": "Home", "cards": [] }
],
"hotkeys": [],
"longHotkeys": []
}| Key | Type | Default | Description |
|---|---|---|---|
name |
string | "Page" |
Name; used for navigation (case-insensitive) |
cards |
array | [] |
Page cards, top to bottom |
hotkeys / longHotkeys
|
array | [] |
Keys active only on this page; they override global ones with the same key
|
parent |
string | — | Parent page: the parentKey key returns to it |
parentKey |
string | "BACK" |
Physical key that returns to parent
|
linkedPage |
string | — | Page opened by swiping up on the page indicator |
hiddenUnlessActivity |
string | — | Activity id: the page dot shows only while that Activity is active (the page stays reachable) |
openWhenEntity |
string | — | HA entity: when it turns on the page opens by itself; when it leaves on it closes |
ℹ️ The online editor can also write
openWhenState/closeWhenState, but in the analysed commitDashboardLoader.ktdoes not read them: the open state is alwayson. For an entity with other states, create a templatebinary_sensorin HA.
Example — doorbell page that opens when the bell rings and closes with BACK:
{
"startPage": 0,
"pages": [
{ "name": "Casa", "cards": [] },
{
"name": "Citofono",
"parent": "Casa",
"openWhenEntity": "binary_sensor.campanello",
"cards": [
{ "type": "camera", "options": { "entity_id": "camera.ingresso" } }
]
}
]
}Every card has this shape:
{ "type": "light", "options": { "entity_id": "light.soggiorno" } }-
typeis required;optionsis optional. - An unregistered
typeis not a parse error: the card is simply not drawn. -
"pin": "bottom"insideoptions(any card) pins it to the bottom of the page, outside the scroll area. -
PNG icons: absolute path, e.g.
/sdcard/astrion/icons/netflix.png. -
Colours:
#RRGGBBor#AARRGGBB(alpha first). Exception:switch.on_coloronly accepts#AARRGGBB(see 7.2).
Registered types (21) and online editor support:
| Type | Editor | Type | Editor |
|---|---|---|---|
title |
form (+ JSON-only fields) | media_player |
form |
light |
form | camera |
form |
switch |
form | clock_weather |
form |
cover |
form | vacuum |
form |
fan |
form | plex |
form |
climate |
form | speaker_group |
JSON only |
source_select |
form | monitor |
JSON only |
select |
form | picture_elements |
JSON only |
button_grid |
form | row |
JSON only |
scene_grid |
form | apple_tv_remote |
form |
tv_remote |
form |
In the online editor pick the type (group Advanced (raw options)) and paste only the options object into Options (raw JSON). The examples below show the whole card: copy what is inside "options".
One row per speaker with: tick = group member, draggable volume bar, mute, vol−/vol+. The first row is the master.
| Option | Type | Req. | Description |
|---|---|---|---|
master |
string | yes | Group coordinator media_player. Without it the card is not shown |
name |
string | no | Name shown for the master |
speakers |
array | no | List of { "entity_id", "name" } of groupable speakers |
Behaviour:
- Ticking →
media_player.joinon the master withgroup_members: [speaker], immediately. - Unticking →
media_player.unjoinon the speaker. - "In group" state is read from the speaker's
group_membersattribute (it must contain the master). - Volume:
media_player.volume_set,volume_up,volume_down,volume_mute.
{
"type": "speaker_group",
"options": {
"master": "media_player.salotto",
"name": "Salotto",
"speakers": [
{ "entity_id": "media_player.cucina", "name": "Cucina" },
{ "entity_id": "media_player.camera", "name": "Camera" },
{ "entity_id": "media_player.bagno", "name": "Bagno" }
]
}
}Read-only list: name, current value, unit_of_measurement. No history graph. unknown / unavailable are shown as —.
| Option | Type | Req. | Description |
|---|---|---|---|
title |
string | no | Header |
entities |
array | yes |
{ "entity_id", "name" }; name optional (default: friendly name) |
{
"type": "monitor",
"options": {
"title": "Ambiente",
"entities": [
{ "entity_id": "sensor.temperatura_soggiorno", "name": "Temperatura" },
{ "entity_id": "sensor.umidita_soggiorno", "name": "Umidità" },
{ "entity_id": "sensor.co2_soggiorno" },
{ "entity_id": "sensor.potenza_casa", "name": "Consumo" }
]
}
}Horizontal container: child cards get equal width. Each child is a full card (type + options). Any card is allowed, including another row.
| Option | Type | Req. | Description |
|---|---|---|---|
cards |
array | yes | Child cards |
{
"type": "row",
"options": {
"cards": [
{ "type": "cover", "options": { "entity_id": "cover.tapparella_sx", "name": "Sinistra", "layout": "vertical" } },
{ "type": "cover", "options": { "entity_id": "cover.tapparella_dx", "name": "Destra", "layout": "vertical" } }
]
}
}💡 Inside a
rowprefer"layout": "vertical"(light/cover/select): horizontal space is limited.
Background image with icons placed by percentage. It can also show mmWave radar targets and the robot vacuum.
| Option | Type | Default | Description |
|---|---|---|---|
image |
string | /sdcard/astrion/floorplan.png |
PNG/JPG path on the remote |
aspect |
number | 1.3 |
W/H ratio used until the image is loaded (then the real one wins) |
elements |
array | [] |
Icons (table below) |
radar |
object | — | Radar overlay (6.4.1) |
vacuum |
object | — | Vacuum overlay (6.4.2) |
Fields of each element:
| Field | Type | Default | Description |
|---|---|---|---|
left / top
|
number | 50 |
Icon centre, % of width / height |
entity_id |
string | — | Tap → toggle the entity; amber icon when on
|
service |
string | — | Used only if entity_id is missing: domain.service
|
targets |
array | — | Entities service is applied to, one call per entity |
icon |
string | bulb | Only alternative value: "power"
|
{
"type": "picture_elements",
"options": {
"image": "/sdcard/astrion/planimetria.png",
"aspect": 1.5,
"elements": [
{ "entity_id": "light.soggiorno", "left": 25, "top": 35 },
{ "entity_id": "light.cucina", "left": 70, "top": 30 },
{ "entity_id": "switch.lampada_studio", "left": 80, "top": 75 },
{ "service": "light.turn_off", "targets": ["light.soggiorno", "light.cucina"], "icon": "power", "left": 95, "top": 6 }
]
}
}💡 Finding coordinates: open the image in an image editor, read X/Y in pixels and compute
left = X / width × 100,top = Y / height × 100.
Draws up to targets numbered dots. Reads entities <prefix>_<n>_x and <prefix>_<n>_y with n = 1…targets. If the entity is missing or not numeric, the dot is not shown.
| Field | Default | Description |
|---|---|---|
prefix |
— (req.) | Sensor prefix, e.g. sensor.radar_soggiorno_target
|
targets |
3 |
Number of targets |
origin_left / origin_top
|
50 / 10
|
Sensor position on the image (%) |
scale_x / scale_y
|
8 / 8
|
% of image per unit of the sensor value |
scale_x_right |
= scale_x
|
X scale for values ≥ 0 (right side) |
top_offset_left |
0 |
Extra vertical offset (%) for the left side |
rotation |
0 |
Rotation in degrees |
flip_x / flip_y
|
false |
Mirror the axes |
blend |
"overlay" |
none, multiply, screen, softlight, hardlight, difference, overlay
|
Applied formula (to understand calibration): left = origin_left + rotated_x × x_scale, top = origin_top + rotated_y × scale_y. Result clamped to 0–100 %.
{
"type": "picture_elements",
"options": {
"image": "/sdcard/astrion/planimetria.png",
"elements": [],
"radar": {
"prefix": "sensor.radar_soggiorno_target",
"targets": 3,
"origin_left": 50,
"origin_top": 5,
"scale_x": 10,
"scale_y": 10,
"rotation": 0,
"flip_x": false,
"flip_y": false,
"blend": "overlay"
}
}
}💡 Calibration: stand at a known spot, see where the dot appears, fix
origin_*first, thenscale_*, finallyrotation/flip_*. The unit ofscale_*depends on how your firmware exposes x/y (mm, cm or m).
Robot icon placed per room (HA exposes no robot X/Y, only the current room). Tap → popup with all the vacuum card controls (7.12). Accepts the same options as vacuum plus:
| Field | Description |
|---|---|
entity_id |
vacuum.* entity (req.) |
room_entity |
Sensor holding the current room name |
room_positions |
Map "RoomName": [left, top] in % |
dock_position |
[left, top] of the dock |
{
"type": "picture_elements",
"options": {
"image": "/sdcard/astrion/planimetria.png",
"elements": [],
"vacuum": {
"entity_id": "vacuum.robot",
"name": "Robot",
"map_image": "image.robot_mappa",
"rooms": [
{ "name": "Cucina", "id": 16 },
{ "name": "Soggiorno", "id": 17 }
],
"room_entity": "sensor.robot_stanza_corrente",
"room_positions": { "Cucina": [70, 30], "Soggiorno": [25, 40] },
"dock_position": [5, 60]
}
}
}The title card has a form, but tappable title and subtitle can only be set in JSON. It uses the same action vocabulary as scene_grid (7.10), prefixed with title_ or subtitle_:
| Field | Action |
|---|---|
title_entity_id |
Activate scene/script |
title_page |
Open page |
title_activityId (+ title_hub) |
Start Harmony Activity |
title_harmonyDevice + title_harmonyCommand (+ title_hub) |
IR command via Harmony |
title_irDevice + title_irCommand
|
Local IR command |
title_activity |
Start composed Activity |
Example — a "See all →" subtitle that opens the Lights page:
{
"type": "title",
"options": {
"title": "Soggiorno",
"subtitle": "Vedi tutte le luci →",
"subtitle_page": "Luci",
"icon": "/sdcard/astrion/icons/sofa.png",
"divider": true,
"color": "#7FB3C4"
}
}These can also be written by hand. All options read by the code are listed.
| Option | Default | Description |
|---|---|---|
title / subtitle
|
— | Texts |
alignment |
"start" |
start, center, end, justify
|
icon |
— | PNG before the title (forces left alignment of the title row) |
divider |
false |
Line filling the row after the title |
color |
theme | Title and line colour |
| Option | Description |
|---|---|
entity_id |
Toggleable entity (switch.*, input_boolean.*, …) |
name |
Label |
icon |
heater/heat, fan, bulb/light; anything else → power |
on_color |
Colour when on. #AARRGGBB only (a #RRGGBB value ends up transparent) |
{ "type": "switch", "options": { "entity_id": "switch.stufa", "name": "Stufa", "icon": "heater", "on_color": "#B3902828" } }Tap = toggle; long press = detail popup.
| Option | Default | Description |
|---|---|---|
entity_id, name
|
— | |
layout |
default |
default, horizontal, vertical
|
use_light_color |
false |
Icon tinted with the light's RGB colour |
show_brightness |
true |
"N%" state instead of "On" |
show_brightness_control |
see note | Brightness slider |
show_color_temp_control |
false |
Colour temperature slider |
show_color_control |
false |
Colour swatches |
collapsible_controls |
false |
Hides controls while the light is off |
If none of the three
show_*_controlis present, only the brightness slider shows. As soon as you set one, only those set totrueshow. With several controls, an arrow button cycles through them.
{ "type": "light", "options": { "entity_id": "light.soggiorno", "name": "Soggiorno", "layout": "horizontal", "use_light_color": true, "show_brightness_control": true, "show_color_control": true } }Long press = popup with 25/50/75 % presets.
| Option | Default | Description |
|---|---|---|
entity_id, name, layout
|
as light
|
|
show_buttons_control |
see note | Open/stop/close |
show_position_control |
false |
Position slider |
show_tilt_position_control |
false |
Tilt slider |
Same logic as
light: with no flag the buttons show. Sliders only show if the entity exposes the matching attribute.
| Option | Default | Description |
|---|---|---|
entity_id, name
|
||
style |
auto |
auto, simple, step, full
|
preset_modes |
from entity | Array, order and filter of presets |
step |
entity percentage_step, then 20
|
% step |
show_captions |
true |
Captions above chip rows |
| Option | Default | Description |
|---|---|---|
entity_id, name
|
||
step |
1.0 |
Setpoint step. Ignored if the entity exposes target_temp_step
|
hvac_modes / fan_modes / swing_modes
|
from entity | Arrays: chip order and filter (off is always excluded) |
hvac_mode_style |
icons |
icons or label
|
fan_mode_style / swing_mode_style
|
label |
icons or label
|
show_captions |
true |
Captions |
{ "type": "climate", "options": { "entity_id": "climate.soggiorno", "name": "Clima", "step": 0.5, "hvac_modes": ["heat", "cool", "auto"], "fan_mode_style": "icons" } }-
select: forinput_select.*/select.*. Options:entity_id,name,icon_color,layout. -
source_select: source of amedia_player(source_list). Options:entity_id,name.
Compact: tap = play/pause; long press = details.
| Option | Default | Description |
|---|---|---|
entity_id, name
|
||
variant |
compact |
"full" for the large version |
use_media_info |
true |
Title/artist instead of name |
show_volume_level |
false |
Appends "⸱ N%" |
media_controls |
previous,play_pause,next |
Comma list: on_off, shuffle, previous, play_pause, next, repeat
|
volume_controls |
mute,buttons |
mute, buttons, set (slider) |
top_buttons |
— |
full only: { "name", "service", "entity_id", "data" }
|
Buttons not supported by the entity (supported_features) are hidden.
| Option | Default | Description |
|---|---|---|
columns |
3 |
Columns |
buttons |
— | { "name", "icon", "service", "entity_id", "data" } |
{
"type": "button_grid",
"options": {
"columns": 2,
"buttons": [
{ "name": "Netflix", "icon": "/sdcard/astrion/icons/netflix.png", "service": "media_player.play_media", "entity_id": "media_player.tv", "data": { "media_content_type": "app", "media_content_id": "com.netflix.ninja" } },
{ "name": "Buonanotte", "service": "script.buonanotte" }
]
}
}| Option | Default | Description |
|---|---|---|
columns |
2 |
Columns |
layout |
grid |
"row" = scrollable row |
show_labels |
true |
Name under the icon |
icon_fill |
false |
Icon fills the tile |
tile_height |
74 (120 with icon_fill) |
Tile height (dp) |
scenes |
— | Tiles (below) |
Fields of each tile: name, icon, color, and one action: entity_id (scene/script), page, activityId (+hub), harmonyDevice+harmonyCommand (+hub), irDevice+irCommand, activity. Optional track + room (+devices) to track it as an Activity. With activity, the page field is ignored (set the page on the Activity).
| Option | Default | Description |
|---|---|---|
entity_id |
— (req.) | camera.* |
name |
friendly name | |
mode |
stream |
stream (MJPEG) or snapshot
|
snapshot_interval |
2 |
Seconds between frames |
aspect |
1.777 |
W/H ratio |
fit |
cover |
cover or contain
|
Tap = full screen with zoom (pinch 1×–8×).
| Option | Description |
|---|---|
entity_id, name
|
|
map_image |
Map image.* entity |
map_rotation |
Map rotation (degrees) |
map_height |
Map height (dp) |
rooms |
{ "name", "id" } — map segment id |
room_clean_action |
Default Roborock/Xiaomi (vacuum.send_command app_segment_clean); otherwise { "domain", "service", "parameter" }
|
Options: entity_id (weather.*), time_format (12 default or 24), forecast_rows (default 4), calendar_entity.
| Option | Description |
|---|---|
name |
Default TV
|
remote_entity |
remote.* (req.) — sends remote.send_command
|
mute_entity |
Alternative entity for mute |
media_entity |
media_player used to launch apps |
commands |
Remaps commands: up, down, left, right, center, back, home, menu, power
|
apps |
{ "name", "app" } or { "name", "service", "entity_id", "data" }
|
Commands are sent directly to the Harmony hub. Options: deviceId (Harmony device id), hub (optional).
Options: host, token, media_entity, play_entity, play_content_type (video/url), source (default Plex), show_on_deck, show_recently_added_movies, show_recently_added_shows (default true), items_per_row (default 12).
⚠️ The Plex token is stored in clear text in the file: do not sharedashboard.json.
key names available on the HA100:
UP DOWN LEFT RIGHT CENTER, PAGE_UP PAGE_DOWN, VOLUME_UP VOLUME_DOWN MUTE, BACK HOME POWER VOICE, LIGHT CURTAIN SCENE AC, CUSTOM_1…CUSTOM_4.
Fields (note: here the entity is called entityId, camelCase):
| Field | Description |
|---|---|
key |
Key (req.) |
openOverlay |
settings or activities
|
openCurrentActivityRoom |
Room name: opens the page of the Activity active there |
page |
Opens the page |
harmonyDevice + harmonyCommand / harmonyActivity (+ hub) |
Harmony (harmonyActivity: "-1" = power off) |
irDevice + irCommand
|
Local IR |
service + entityId + data
|
HA service |
track + room (+ devices) |
Track as an Activity |
Use one action per key. Priority: openOverlay → openCurrentActivityRoom → page → … → service.
{ "key": "CUSTOM_1", "service": "media_player.play_media", "entityId": "media_player.tv", "data": { "media_content_type": "app", "media_content_id": "com.netflix.ninja" } }💡 Arrows and volume auto-repeat while held: do not give them
longHotkeys.
irDevices — IR devices driven by the remote's own blaster:
{
"startPage": 0,
"pages": [ { "name": "TV", "cards": [] } ],
"irDevices": [
{
"id": "tv_salotto",
"name": "TV Salotto",
"commands": {
"power": { "freq": 38000, "pattern": [9000, 4500, 560, 560] }
}
}
]
}Instead of commands: category + brand + model (database in /sdcard/astrion/ir-database/). target field: "local" (default) or { "extender": "<id>" }.
activities — multi-device scenarios:
| Field | Description |
|---|---|
id, name, room
|
id and room required
|
icon, page
|
Icon; page opened on start |
devices |
Required, non-empty: deviceId, source (ir/harmony/ha), hub, powerOnCommand, powerOffCommand, inputCommand, powerOnFirst (default true), powerOffOnExit (default true), delayAfterMs
|
volumeDeviceId, volumeUpCommand, volumeDownCommand, muteCommand
|
Volume control |
Harmony configuration (hub IP/ID) is done on the :8080 page, not in the JSON.
All optional (defaults in brackets): background (#0E2229), cardSurface (#1B343D), insetSurface (#152B33), controlBackground (#2C4C58), primaryText (#E6F0F1), mutedText (#93AFB6), iconTint (#CBDCE0), accent (#6EA8FE), accentSecondary (#4C6EF5), amber (#FFC24B), danger (#E06767), success (#4CAF50).
Five pages: Luci (lights), Casa (floor plan + sensors), Musica (speaker group), Clima (climate), Citofono (auto-open doorbell).
{
"startPage": 1,
"theme": { "background": "#0E2229", "accent": "#6EA8FE" },
"pages": [
{
"name": "Luci",
"cards": [
{ "type": "title", "options": { "title": "Luci", "subtitle": "Soggiorno e cucina", "divider": true, "subtitle_page": "Casa" } },
{ "type": "light", "options": { "entity_id": "light.soggiorno", "name": "Soggiorno", "show_brightness_control": true, "show_color_temp_control": true } },
{ "type": "row", "options": { "cards": [
{ "type": "switch", "options": { "entity_id": "switch.stufa", "name": "Stufa", "icon": "heater", "on_color": "#B3902828" } },
{ "type": "switch", "options": { "entity_id": "switch.ventilatore", "name": "Ventilatore", "icon": "fan" } }
] } },
{ "type": "scene_grid", "options": { "layout": "row", "pin": "bottom", "scenes": [
{ "entity_id": "scene.cena", "name": "Cena", "color": "#66FFB300" },
{ "entity_id": "script.tutto_spento", "name": "Spegni", "color": "#66000000" }
] } }
]
},
{
"name": "Casa",
"cards": [
{ "type": "clock_weather", "options": { "entity_id": "weather.casa", "time_format": 24, "forecast_rows": 3 } },
{ "type": "picture_elements", "options": {
"image": "/sdcard/astrion/planimetria.png",
"aspect": 1.3,
"elements": [
{ "entity_id": "light.soggiorno", "left": 30, "top": 40 },
{ "entity_id": "light.cucina", "left": 70, "top": 55 },
{ "service": "light.turn_off", "targets": ["light.soggiorno", "light.cucina"], "icon": "power", "left": 92, "top": 8 }
]
} },
{ "type": "monitor", "options": { "title": "Sensori", "entities": [
{ "entity_id": "sensor.temperatura_soggiorno", "name": "Soggiorno" },
{ "entity_id": "sensor.umidita_soggiorno", "name": "Umidità" },
{ "entity_id": "sensor.potenza_casa", "name": "Consumo" }
] } }
]
},
{
"name": "Musica",
"cards": [
{ "type": "media_player", "options": { "entity_id": "media_player.salotto", "variant": "full", "media_controls": "shuffle,previous,play_pause,next,repeat", "volume_controls": "mute,set" } },
{ "type": "speaker_group", "options": { "master": "media_player.salotto", "name": "Salotto", "speakers": [
{ "entity_id": "media_player.cucina", "name": "Cucina" },
{ "entity_id": "media_player.camera", "name": "Camera" }
] } },
{ "type": "source_select", "options": { "entity_id": "media_player.salotto", "name": "Sorgente" } }
]
},
{
"name": "Clima",
"cards": [
{ "type": "climate", "options": { "entity_id": "climate.soggiorno", "name": "Clima", "step": 0.5 } },
{ "type": "cover", "options": { "entity_id": "cover.tapparella_soggiorno", "name": "Tapparella", "show_buttons_control": true, "show_position_control": true } }
]
},
{
"name": "Citofono",
"parent": "Casa",
"openWhenEntity": "binary_sensor.campanello",
"cards": [
{ "type": "camera", "options": { "entity_id": "camera.ingresso", "mode": "stream", "fit": "cover" } }
]
}
],
"hotkeys": [
{ "key": "LIGHT", "page": "Luci" },
{ "key": "CURTAIN", "page": "Casa" },
{ "key": "SCENE", "page": "Musica" },
{ "key": "AC", "page": "Clima" },
{ "key": "VOLUME_UP", "service": "media_player.volume_up", "entityId": "media_player.salotto" },
{ "key": "VOLUME_DOWN", "service": "media_player.volume_down", "entityId": "media_player.salotto" },
{ "key": "VOICE", "openOverlay": "settings" }
],
"longHotkeys": [
{ "key": "LIGHT", "service": "script.turn_on", "entityId": "script.tutto_spento" }
]
}| Symptom | Likely cause | Fix |
|---|---|---|
| The sample dashboard appears | Invalid JSON or pages missing/empty |
Validate the JSON (e.g. python -m json.tool dashboard.json) |
| A card does not appear | Wrong type, or required option missing (master, remote_entity, entity_id) |
Check type name and options |
| Fields lost after editing in the editor | Saved through the simple form | Use "Options (raw JSON)" |
switch on but colour invisible |
6-digit on_color
|
Use #AARRGGBB
|
| PNG icons missing | Relative path or file not uploaded | Absolute path /sdcard/astrion/icons/…
|
| Service hotkey does nothing |
entity_id instead of entityId
|
Use entityId in keys |
| No radar dots |
<prefix>_<n>_x/_y entities missing or not numeric |
Check names in Developer tools → States |
| Auto-open page never opens | The entity never turns on
|
Use a binary entity or a template binary_sensor
|
| Speaker always "not grouped" | The integration doesn't expose group_members on the speaker |
Check the attribute in HA |