- Veröffentlicht am
Energieübersicht in Home Assistant mit draw.io
- Autoren
- Name
- MajorTwip
- @MajorTwip
Übersicht
Home Assistant hat eine sehr nützliche Übersichtsseite für die Energieflüsse. Es gibt eine Leistungsübersicht als Community-Plugin als Lovelace-Card. Diese ist für viele Setups geeignet, aber meines ist ein wenig komplexer. Insbesondere meine Insel-Solaranlage konnte ich nicht zusätzlich zum Balkonkraftwerk darstellen. Somit habe ich kurzerhand ein Schema auf draw.io gezeichnet.
Dieses habe ich danach in Home Assistant importiert. Dazu nutze ich das Floorplan Plugin
Details
draw.io
ID im Element
Um die verschiedenen Textfelder über HA ansteuern zu können, benötigen diese ein id-Parameter im Element. Dies geht mit draw.io nicht direkt, da beim SVG-Export ein extrem verschachteltes SVG-Konstrukt bastelt. Mehr dazu später. Aber immerhin hat draw.io Plugins namens "plugins/props.js" und "plugins/svgdata.js" welches über Extras --> Plugins aktiviert werden können. Danach kann man über Rechtsklick auf ein Symbol --> Edit Data --> Doppelklick auf die ID, diese geändert werden. In HA werde wir diese dann über "cell-ID_aus_draw.io" ansprechen.
<div>
Textfelder werden ganzflächige Textfelder, genauer alle Rechteckigen Objekte aus der draw.io-Bibliothek beinhalten als SVG-Element zwei Unterelemente, wobei eines die gesammte Fläche bedeckt. Dies führt dazu, dass HA Floorplan die Klick-Flächen nicht sauber rendern kann. (auf der gesammten Map führt ein Click zum Anzeigen der History des zuletzt konfigurierten Objekts statt des effektiv angeklickten Elementes). Ein eigenes SVG mit einem <rect>
zu erstellen führt zum gleichen Problem. Somit habe ich ein SVG gebastelt, welches das Rechteck als <path>
zeichnet.
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 40" xml:space="preserve">
<style>
path.icon {
stroke-width: 1;
stroke: black;
@media (prefers-color-scheme: dark) {stroke: white; }
}
</style>
<path class="icon" d="M 0 0 h 100 v 40 h -100 Z" fill-opacity="0" />
</svg>
Darkmode
Die Icons habe ich bei SVG Repo gefunden. Im Darkmode bleiben diese aber schwarz. Somit habe ich denen einfach den Style-Tag (siehe oben) eingefügt und dem Path danach diese class zugewiesen.
Home Assistant Floorplan
Die nötigen Infos findet Ihr in der Doku von Floorplan. Einzig speziell ist die custom Function welche das unterste Element in dem extrem verschachtelten SVG-Export von draw.io sucht um dort den Text zu setzten. Durch die Rechtecke welche nun Paths sind wäre dies eigentlich nicht mehr nötig, da nun alle Elemente ein <text>
haben.
views:
- title: Floorplan
path: floorplan
theme: Google Dark Theme
badges: []
cards:
- type: custom:floorplan-card
full_height: true
config:
image: /local/floorplan/Power5.svg
cache: false
console_log_level: info
defaults:
hover_action: hover-info
tap_action: more-info
state_action:
action: call-service
service: floorplan.class_set
service_data: default-${entity.state}
rules:
- element: cell-val_grid_total
entity: sensor.power_mains
state_action:
- service: floorplan.execute
service_data: ${functions.setLowestElementsText(entity, element)};
- entity: sensor.ups_power
elements:
- cell-sw_island_bypass
- cell-sw_island_inv
state_action:
- service: floorplan.style_set
service_data:
element: cell-sw_island_bypass
style: '${(entity.state > 5) ? "opacity:1" : "opacity:0"}'
- service: floorplan.style_set
service_data:
element: cell-sw_island_inv
style: '${(entity.state > 5) ? "opacity:0" : "opacity:1"}'
- entity: binary_sensor.goe_223755_car_0
elements:
- cell-ico_car
- cell-val_car
- cell-line_car
state_action:
- service: floorplan.style_set
service_data:
style: >-
${(entity.state==="plugged in") ? "opacity:1" :
"opacity:0"}
functions: |
>
return {
setLowestElementsText: (entity, element, precision = 0) => {
const valNum = parseFloat(entity.state).toFixed(precision)
const value = valNum + entity.attributes.unit_of_measurement
const leaf = element.querySelector("text")
if(leaf){
leaf.textContent=value;
return;
}
let lowestNode = null;
let maxDepth = -1;
function traverse(node, depth) {
if (depth > maxDepth) {
maxDepth = depth;
lowestNode = node;
}
for (let child of node.children) {
traverse(child, depth + 1);
}
}
traverse(element, 0);
lowestNode.textContent=value;
return;
},
};
type: panel