For the bar-char look for bar-card on HACS
type: custom:bar-card
name: HDD
positions:
icon: outside
name: inside
color: '#17A589'
entities:
- entity: sensor.disk_use_percent_home
And this is the code for the router cards.
type: vertical-stack
cards:
- type: horizontal-stack
cards:
- show_name: true
show_icon: false
type: custom:button-card
tap_action:
action: none
name: ANTARES
color_type: card
color: rgb(50,78,78)
hold_action:
action: none
styles:
card:
- height: 10px
- type: entities
entities:
- type: custom:button-card
entity: binary_sensor.velop_mesh_wan_status
show_name: false
icon: hass:web
size: 75px
tap_action:
action: none
custom_fields:
attr_dns_servers: '[[[ return entity.attributes.dns ]]]'
attr_public_ip: '[[[ return entity.attributes.ip ]]]'
attr_speedtest_latest: |
[[[
var entity_speedtest = states['sensor.velop_mesh_speedtest_latest']
var d = new Date(entity_speedtest.state)
return d.toLocaleString()
]]]
attr_speedtest_details: |
[[[
var round2 = (num) => Math.round(num * 100) / 100
var spacing_internal = 5
var spacing_external = 30
var icon_size = 22
var entity_speedtest = states['sensor.velop_mesh_speedtest_latest']
var latency = entity_speedtest.attributes.latency
var download_bandwidth = round2(entity_speedtest.attributes.download_bandwidth / 1000)
var upload_bandwidth = round2(entity_speedtest.attributes.upload_bandwidth / 1000)
return `<span style="margin-right: ${spacing_external}px;">
<ha-icon icon="hass:swap-horizontal" style="width: ${icon_size}px;"></ha-icon>
<span>${latency}ms</span>
</span>
<span style="margin-right: ${spacing_external}px;">
<ha-icon icon="hass:cloud-download-outline" style="width: ${icon_size}px;"></ha-icon>
<span>${download_bandwidth} Mbps</span>
</span>
<span>
<ha-icon icon="hass:cloud-upload-outline" style="width: ${icon_size}px;"></ha-icon>
<span>${upload_bandwidth} Mbps</span>
</span>
`
]]]
state:
- value: 'on'
color: darkcyan
- value: 'off'
color: darkred
styles:
card:
- box-shadow: none
- padding: 16px 8px
grid:
- grid-template-areas: >-
"attr_dns_servers . attr_public_ip" "i i i"
"attr_speedtest_details attr_speedtest_details
attr_speedtest_details" "attr_speedtest_latest
attr_speedtest_latest attr_speedtest_latest"
- grid-template-rows: 5% 1fr 15% 5%
- grid-template-columns: 1fr min-content 1fr
custom_fields:
attr_dns_servers:
- justify-self: self-start
attr_public_ip:
- justify-self: self-end
extra_styles: >
div[id^="attr_"] { font-size: smaller; color:
var(--disabled-text-color);
}
div[id^="attr_speedtest_"] { margin-top: 10px; }
#attr_speedtest_latest::before { content: 'As at:' }
#attr_public_ip::before { content: 'Public IP: ' }
#attr_dns_servers::before { content: 'DNS: ' }
- type: conditional
conditions:
- entity: binary_sensor.velop_mesh_speedtest_status
state: 'on'
row:
type: section
- type: custom:template-entity-row
condition: '{{ is_state(''binary_sensor.velop_mesh_speedtest_status'', ''on'') }}'
name: >-
{{ state_attr('binary_sensor.velop_mesh_speedtest_status', 'status')
}}
tap_action:
action: none
card_mod:
style: >
state-badge { display: none; } state-badge + div.info { margin-left:
8px !important; margin-right: 8px; text-align: center; }
- type: section
- type: custom:auto-entities
card:
type: horizontal-stack
card_param: cards
sort:
method: friendly_name
filter:
include:
- entity_id: /^(button|switch)\.velop_mesh_/
options:
type: custom:button-card
hold_action:
action: more-info
tap_action:
action: >-
[[[ return (entity.entity_id.startsWith("button")) ?
"call-service" : "toggle" ]]]
service: >-
[[[ return (entity.entity_id.startsWith("button")) ?
"button.press" : undefined ]]]
service_data:
entity_id: entity
name: |-
[[[
var friendly_name = entity.attributes.friendly_name.replace("Velop Mesh:", "").trim()
var idx = friendly_name.lastIndexOf(" ");
var ret = friendly_name.substring(0, idx) + "<br />" + friendly_name.substring(idx + 1)
return ret
]]]
styles:
card:
- box-shadow: none
- margin-bottom: 3px
icon:
- animation: |-
[[[
var ret
if (entity.entity_id == "button.velop_mesh_start_speedtest" && states["binary_sensor.velop_mesh_speedtest_status"].state == "on") {
ret = "rotating 2s linear infinite"
}
return ret
]]]
- color: |-
[[[
var ret
var col_on = "darkcyan"
var col_off = "var(--primary-text-color)"
ret = (entity.state == "on") ? col_on : col_off
if (entity.entity_id == "button.velop_mesh_start_speedtest") {
ret = (states["binary_sensor.velop_mesh_speedtest_status"].state == "on") ? col_on : col_off
}
return ret
]]]
name:
- font-size: smaller
- color: |-
[[[
var ret
var col_on = "darkcyan"
var col_off = "var(--primary-text-color)"
ret = (entity.state == "on") ? col_on : col_off
if (entity.entity_id == "button.velop_mesh_start_speedtest") {
ret = (states["binary_sensor.velop_mesh_speedtest_status"].state == "on") ? col_on : col_off
}
return ret
]]]
- type: section
- type: custom:fold-entity-row
padding: 0
head:
type: custom:template-entity-row
entity: sensor.velop_mesh_online_devices
tap_action:
action: fire-dom-event
fold_row: true
name: >-
{% set friendly_name = state_attr(config.entity, 'friendly_name') %}
{% if friendly_name %}
{{ friendly_name.split(':')[1].strip() }}
{% endif %}
card_mod:
style: |
.info.pointer { font-weight: 500; }
.state { margin-right: 10px; }
entities:
- type: custom:hui-element
card_type: markdown
card_mod:
style:
.: |
ha-card { border-radius: 0px; box-shadow: none; }
ha-markdown { padding: 16px 0px 0px !important; }
ha-markdown$: >
table { width: 100%; border-collapse: separate;
border-spacing: 0px; }
tbody tr:nth-child(2n+1) { background-color:
var(--table-row-background-color); }
thead tr th, tbody tr td { padding: 4px 10px; }
content: >
{% set devices = state_attr('sensor.velop_mesh_online_devices',
'devices') %} | # | Name | IP | Type |
|:---:|:---|---:|:---:| {%- for device in devices -%}
{% set idx = loop.index %}
{%- for device_name, device_details in device.items() -%}
{%- set device_ip = device_details.ip -%}
{%- set connection_type = device_details.connection | lower -%}
{%- set guest_network = device_details.guest_network -%}
{%- if connection_type == "wired" -%}
{%- set connection_icon = "ethernet" -%}
{% elif connection_type == "wireless" -%}
{%- set connection_icon = "wifi" -%}
{% elif connection_type == "unknown" -%}
{%- set connection_icon = "help" -%}
{% else -%}
{%- set connection_icon = "" -%}
{%- endif %}
{{ "| {} | {}{} | {} | {} |".format(idx, device_name,
' <ha-icon icon="hass:account-multiple"></ha-icon>' if
guest_network else '', device_ip, '<ha-icon icon="hass:' ~
connection_icon ~ '"></ha-icon>') }}
{%- endfor %}
{%- endfor %}
- type: custom:fold-entity-row
padding: 0
head:
type: custom:template-entity-row
entity: sensor.velop_mesh_offline_devices
tap_action:
action: fire-dom-event
fold_row: true
name: >-
{% set friendly_name = state_attr(config.entity, 'friendly_name') %}
{% if friendly_name %}
{{ friendly_name.split(':')[1].strip() }}
{% endif %}
card_mod:
style: |
.info.pointer { font-weight: 500; }
.state { margin-right: 10px; }
entities:
- type: custom:hui-element
card_type: markdown
card_mod:
style:
.: |
ha-card { border-radius: 0px; box-shadow: none; }
ha-markdown { padding: 16px 0px 0px !important; }
ha-markdown$: >
table { width: 100%; border-collapse: separate;
border-spacing: 0px; }
tbody tr:nth-child(2n+1) { background-color:
var(--table-row-background-color); }
thead tr th, tbody tr td { padding: 4px 10px; }
content: >
{% set devices = state_attr('sensor.velop_mesh_offline_devices',
'devices') %}
| # | Name |
|:---:|:---|
{% for device in devices %} {{ "| {} | {} |".format(loop.index,
device) }}
{% endfor %}
- type: conditional
conditions:
- entity: sensor.velop_mesh_available_storage
state_not: '0'
- entity: sensor.velop_mesh_available_storage
state_not: unavailable
row:
type: custom:fold-entity-row
padding: 0
head:
type: section
label: Storage
entities:
- type: custom:hui-element
card_type: markdown
card_mod:
style:
.: |
ha-card { border-radius: 0px; box-shadow: none; }
ha-markdown { padding: 16px 0px 0px !important; }
ha-markdown$: >
table { width: 100%; border-collapse: separate;
border-spacing: 0px; }
tbody tr:nth-child(2n+1) { background-color:
var(--table-row-background-color); }
thead tr th, tbody tr td { padding: 4px 10px; }
content: >
{% set partitions =
state_attr('sensor.velop_mesh_available_storage', 'partitions')
%}
| Host | Label | %age used
|:---:|:---:|:---:|
{% for partition in partitions %} {{ "| {} | {} | {}
|".format(partition.ip, partition.label, partition.used_percent)
}}
{% endfor %}
state_color: false
card_mod:
style:
.: |
#states { padding-top: 0px; }
fold-entity-row:
$:
template-entity-row:
$: |
state-badge { display: none; }
state-badge + div { margin-left: 8px !important; }
.info.pointer { font-weight: 500; }
.state { margin-right: 10px; }
- type: custom:auto-entities
card:
type: vertical-stack
card_param: cards
sort:
method: friendly_name
reverse: false
filter:
include:
- entity_id: /^binary_sensor\.velop_(?!(mesh)).*_status/
options:
type: custom:config-template-card
variables:
BUTTONS: |
() => {
var ret = []
var entity_prefix = "button." + "this.entity_id".split(".")[1].split("_").slice(0, -1).join("_")
for (var entity_id in states) {
if (entity_id.startsWith(entity_prefix)) {
var entity_action = states[entity_id].attributes.friendly_name.split(':')[1].trim()
var entity_name = states[entity_id].attributes.friendly_name.split(':')[0].replace('Velop', '').trim()
ret.push({
'entity': entity_id,
'name': entity_action,
'tap_action': {
'action': 'call-service',
'service': 'linksys_velop.' + entity_action.toLowerCase() + '_node',
'service_data': {
'node_name': entity_name,
},
'confirmation': {
'text': 'Are you sure you want to reboot the ' + entity_name + ' node?'
}
}
})
}
}
return ret
}
ID_CONNECTED_DEVICES: >
"sensor." + "this.entity_id".split(".")[1].split("_").slice(0,
-1).join("_") + "_connected_devices"
ID_ENTITY_PICTURE: >
"sensor." + "this.entity_id".split(".")[1].split("_").slice(0,
-1).join("_") + "_image"
ID_LAST_UPDATE_CHECK: >
"sensor." + "this.entity_id".split(".")[1].split("_").slice(0,
-1).join("_") + "_last_update_check"
ID_MODEL: >
"sensor." +
"this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
+ "_model"
ID_PARENT: >
"sensor." +
"this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
+ "_parent"
ID_SERIAL: >
"sensor." +
"this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
+ "_serial"
ID_UPDATE_AVAILABLE: >
"update." +
"this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
+ "_update"
CONNECTED_DEVICES_TEXT: |
(entity_id) => {
var ret = `
| # | Name | IP | Type |
|:---:|:---|---:|:---:|
`
if (states[entity_id].attributes.devices) {
states[entity_id].attributes.devices.forEach((device, idx) => {
var connection_icon
switch (device.type.toLowerCase()) {
case "wireless":
connection_icon = "wifi"
break
case "wired":
connection_icon = "ethernet"
break
case "unknown":
connection_icon = "help"
break
}
ret += "| " + (idx + 1) + " | " + device.name + ((device.guest_network) ? " <ha-icon icon='hass:account-multiple'></ha-icon>" : "") + " | " + device.ip + " | <ha-icon icon='hass:" + connection_icon + "'></ha-icon> |\n"
})
}
return ret
}
getEntityPicture: |
() => {
if (states[vars['ID_ENTITY_PICTURE']]) {
return states[vars['ID_ENTITY_PICTURE']].state
} else {
return '/local/velop_nodes/' + states[vars['ID_MODEL']].state + '.png'
}
}
entities:
- this.entity_id
- ${ID_CONNECTED_DEVICES}
- ${ID_LAST_UPDATE_CHECK}
- ${ID_MODEL}
- ${ID_PARENT}
- ${ID_SERIAL}
- ${ID_UPDATE_AVAILABLE}
card:
type: entities
card_mod:
style:
.: |
#states { padding-top: 0px; }
fold-entity-row:
$:
template-entity-row:
$: |
state-badge { display: none; }
state-badge + div { margin-left: 8px !important; }
.info.pointer { font-weight: 500; }
.state { margin-right: 10px; }
entities:
- type: custom:button-card
entity: this.entity_id
size: 100%
show_entity_picture: true
show_last_changed: true
show_state: true
tap_action:
action: none
entity_picture: ${ getEntityPicture() }
name: |
[[[
var ret = entity.attributes.friendly_name
if (ret) {
ret = ret.replace("Velop", "").split(":")[0].trim()
}
return ret || "N/A"
]]]
state_display: |
[[[
return `<ha-icon
icon="hass:checkbox-blank-circle"
style="width: 24px; height: 24px;">
</ha-icon>`
]]]
custom_fields:
attr_label_model: Model
attr_model: ${states[ID_MODEL].state}
attr_label_serial: Serial
attr_serial: ${states[ID_SERIAL].state}
attr_parent: >-
${(states[ID_PARENT].state && states[ID_PARENT].state !=
'unknown') ? 'Connected to ' + states[ID_PARENT].state :
'N/A'}
attr_label_ip: IP Address
attr_ip: '[[[ return entity.attributes.ip || ''N/A'' ]]]'
attr_update: |
[[[
var ret
var entity_update = 'update.' + entity.entity_id.split('.')[1].split('_').slice(0, -1).join('_') + '_update'
var update_available = states[entity_update].state
if (update_available == 'on') {
ret = `<ha-icon
icon="hass:package-up"
style="width: 24px; height: 20px;"
>
</ha-icon>`
}
return ret
]]]
extra_styles: >
div[id^="attr_"] { justify-self: start; }
div[id^="attr_label_"] { justify-self: start; margin-left:
20px; } #label, #attr_parent { padding-top: 25px; font-size:
smaller; }
styles:
card:
- box-shadow: none
- padding: 10px 8px
grid:
- grid-template-areas: >-
"n n attr_update s" "i attr_label_model
attr_label_model attr_model" "i attr_label_serial
attr_label_serial attr_serial" "i attr_label_ip
attr_label_ip attr_ip" "l l l attr_parent "
- grid-template-columns: 30% 1fr 30px 150px
name:
- font-size: large
- justify-self: start
- padding-bottom: 20px
label:
- justify-self: start
custom_fields:
attr_label_last_update_check:
- margin-left: 0px
attr_last_update_check:
- justify-self: end
attr_parent:
- justify-self: end
attr_update:
- color: darkred
- justify-self: end
- padding-bottom: 25px
attr_label_model:
- color: white
- font-size: 16px
- justify-self: start
attr_label_serial:
- color: white
- font-size: 16px
- justify-self: start
attr_label_ip:
- color: white
- font-size: 16px
- justify-self: start
attr_model:
- color: darkwhite
- font-size: 12px
- justify-self: end
attr_serial:
- color: darkwhite
- font-size: 12px
- justify-self: end
attr_ip:
- color: darkwhite
- font-size: 12px
- justify-self: end
state:
- justify-self: end
- padding-bottom: 20px
- color: |-
[[[
return (entity.state == 'on' ? 'darkcyan' : 'darkred')
]]]
- type: custom:auto-entities
show_empty: true
card:
type: custom:fold-entity-row
head:
type: section
label: Additional Information
padding: 0
filter:
include:
- entity_id: ${ID_LAST_UPDATE_CHECK}
options:
name: Last update check
- entity_id: ${ID_PARENT}
not:
state: unknown
options:
type: custom:template-entity-row
name: Backhaul
state: >-
{% set backhaul_info = state_attr(config.entity,
'backhaul') %} {% set backhaul_speed =
backhaul_info.speed_mbps | round(2) %} {% if
(backhaul_speed | string).split('.')[1] == '0' %}
{% set backhaul_speed = backhaul_speed | int %}
{% endif %} {{ backhaul_info.connection }} ({{
backhaul_speed }} Mbps)
- type: custom:auto-entities
show_empty: false
filter:
include:
- domain: button
entity_id: >-
${"/" +
"this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
+ "/"}
card:
type: custom:fold-entity-row
padding: 0
head:
type: section
label: Actions
entities:
- type: buttons
entities: ${BUTTONS()}
- type: section
- type: custom:fold-entity-row
padding: 0
head:
type: custom:template-entity-row
tap_action:
action: fire-dom-event
fold_row: true
entity: ${ID_CONNECTED_DEVICES}
name: >-
{% set name = state_attr(config.entity, 'friendly_name')
%} {% if name %}
{{ name.split(':')[1].strip() }}
{% endif %}
entities:
- type: custom:hui-element
card_type: markdown
content: ${CONNECTED_DEVICES_TEXT(ID_CONNECTED_DEVICES)}
card_mod:
style:
.: |
ha-card { border-radius: 0px; box-shadow: none; }
ha-markdown { padding: 16px 0px 0px !important; }
ha-markdown$: >
table { width: 100%; border-collapse: collapse; }
tbody tr:nth-child(2n+1) { background-color:
var(--table-row-background-color); }
thead tr th, tbody tr td { padding: 4px 10px; }