mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 03:36:44 +00:00
Use the new included_in_stat
hierarchy in the Energy Sankey card (#25306)
This commit is contained in:
parent
4d2d94c54f
commit
6692d9c6aa
@ -105,10 +105,41 @@ export class HaSankeyChart extends LitElement {
|
|||||||
|
|
||||||
private _createData = memoizeOne((data: SankeyChartData, width = 0) => {
|
private _createData = memoizeOne((data: SankeyChartData, width = 0) => {
|
||||||
const filteredNodes = data.nodes.filter((n) => n.value > 0);
|
const filteredNodes = data.nodes.filter((n) => n.value > 0);
|
||||||
const indexes = [...new Set(filteredNodes.map((n) => n.index))];
|
const indexes = [...new Set(filteredNodes.map((n) => n.index))].sort();
|
||||||
const depthMap = new Map<number, number>();
|
const depthMap = new Map<number, number>();
|
||||||
indexes.sort().forEach((index, i) => {
|
const sections: Node[][] = [];
|
||||||
|
indexes.forEach((index, i) => {
|
||||||
depthMap.set(index, i);
|
depthMap.set(index, i);
|
||||||
|
const nodesWithIndex = filteredNodes.filter((n) => n.index === index);
|
||||||
|
if (nodesWithIndex.length > 0) {
|
||||||
|
sections.push(
|
||||||
|
sections.length > 0
|
||||||
|
? nodesWithIndex.sort((a, b) => {
|
||||||
|
// sort by the order of their parents in the previous section with orphans at the end
|
||||||
|
const aParentIndex = this._findParentIndex(
|
||||||
|
a.id,
|
||||||
|
data.links,
|
||||||
|
sections
|
||||||
|
);
|
||||||
|
const bParentIndex = this._findParentIndex(
|
||||||
|
b.id,
|
||||||
|
data.links,
|
||||||
|
sections
|
||||||
|
);
|
||||||
|
if (aParentIndex === bParentIndex) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (aParentIndex === -1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (bParentIndex === -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return aParentIndex - bParentIndex;
|
||||||
|
})
|
||||||
|
: nodesWithIndex
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const links = this._processLinks(filteredNodes, data.links);
|
const links = this._processLinks(filteredNodes, data.links);
|
||||||
const sectionWidth = width / indexes.length;
|
const sectionWidth = width / indexes.length;
|
||||||
@ -117,7 +148,7 @@ export class HaSankeyChart extends LitElement {
|
|||||||
return {
|
return {
|
||||||
id: "sankey",
|
id: "sankey",
|
||||||
type: "sankey",
|
type: "sankey",
|
||||||
nodes: filteredNodes.map((node) => ({
|
nodes: sections.flat().map((node) => ({
|
||||||
id: node.id,
|
id: node.id,
|
||||||
value: node.value,
|
value: node.value,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
@ -227,6 +258,23 @@ export class HaSankeyChart extends LitElement {
|
|||||||
return links;
|
return links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _findParentIndex(id: string, links: Link[], sections: Node[][]) {
|
||||||
|
const parent = links.find((l) => l.target === id)?.source;
|
||||||
|
if (!parent) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
let offset = 0;
|
||||||
|
for (let i = sections.length - 1; i >= 0; i--) {
|
||||||
|
const section = sections[i];
|
||||||
|
const index = section.findIndex((n) => n.id === parent);
|
||||||
|
if (index !== -1) {
|
||||||
|
return offset + index;
|
||||||
|
}
|
||||||
|
offset += section.length;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -227,6 +227,7 @@ class HuiEnergySankeyCard
|
|||||||
|
|
||||||
let untrackedConsumption = homeNode.value;
|
let untrackedConsumption = homeNode.value;
|
||||||
const deviceNodes: Node[] = [];
|
const deviceNodes: Node[] = [];
|
||||||
|
const parentLinks: Record<string, string> = {};
|
||||||
prefs.device_consumption.forEach((device, idx) => {
|
prefs.device_consumption.forEach((device, idx) => {
|
||||||
const value =
|
const value =
|
||||||
device.stat_consumption in this._data!.stats
|
device.stat_consumption in this._data!.stats
|
||||||
@ -238,7 +239,7 @@ class HuiEnergySankeyCard
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
untrackedConsumption -= value;
|
untrackedConsumption -= value;
|
||||||
deviceNodes.push({
|
const node = {
|
||||||
id: device.stat_consumption,
|
id: device.stat_consumption,
|
||||||
label:
|
label:
|
||||||
device.name ||
|
device.name ||
|
||||||
@ -251,12 +252,24 @@ class HuiEnergySankeyCard
|
|||||||
tooltip: `${formatNumber(value, this.hass.locale)} kWh`,
|
tooltip: `${formatNumber(value, this.hass.locale)} kWh`,
|
||||||
color: getGraphColorByIndex(idx, computedStyle),
|
color: getGraphColorByIndex(idx, computedStyle),
|
||||||
index: 4,
|
index: 4,
|
||||||
|
parent: device.included_in_stat,
|
||||||
|
};
|
||||||
|
if (node.parent) {
|
||||||
|
parentLinks[node.id] = node.parent;
|
||||||
|
links.push({
|
||||||
|
source: node.parent,
|
||||||
|
target: node.id,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
deviceNodes.push(node);
|
||||||
});
|
});
|
||||||
|
const devicesWithoutParent = deviceNodes.filter(
|
||||||
|
(node) => !parentLinks[node.id]
|
||||||
|
);
|
||||||
|
|
||||||
const { group_by_area, group_by_floor } = this._config;
|
const { group_by_area, group_by_floor } = this._config;
|
||||||
if (group_by_area || group_by_floor) {
|
if (group_by_area || group_by_floor) {
|
||||||
const { areas, floors } = this._groupByFloorAndArea(deviceNodes);
|
const { areas, floors } = this._groupByFloorAndArea(devicesWithoutParent);
|
||||||
|
|
||||||
Object.keys(floors)
|
Object.keys(floors)
|
||||||
.sort(
|
.sort(
|
||||||
@ -310,7 +323,6 @@ class HuiEnergySankeyCard
|
|||||||
|
|
||||||
// Link devices to the appropriate target (area, floor, or home)
|
// Link devices to the appropriate target (area, floor, or home)
|
||||||
areas[areaId].devices.forEach((device) => {
|
areas[areaId].devices.forEach((device) => {
|
||||||
nodes.push(device);
|
|
||||||
links.push({
|
links.push({
|
||||||
source: targetNodeId,
|
source: targetNodeId,
|
||||||
target: device.id,
|
target: device.id,
|
||||||
@ -320,8 +332,7 @@ class HuiEnergySankeyCard
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deviceNodes.forEach((deviceNode) => {
|
devicesWithoutParent.forEach((deviceNode) => {
|
||||||
nodes.push(deviceNode);
|
|
||||||
links.push({
|
links.push({
|
||||||
source: "home",
|
source: "home",
|
||||||
target: deviceNode.id,
|
target: deviceNode.id,
|
||||||
@ -329,6 +340,12 @@ class HuiEnergySankeyCard
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const deviceSections = this._getDeviceSections(parentLinks, deviceNodes);
|
||||||
|
deviceSections.forEach((section, index) => {
|
||||||
|
section.forEach((node: Node) => {
|
||||||
|
nodes.push({ ...node, index: 4 + index });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// untracked consumption
|
// untracked consumption
|
||||||
if (untrackedConsumption > 0) {
|
if (untrackedConsumption > 0) {
|
||||||
@ -340,7 +357,7 @@ class HuiEnergySankeyCard
|
|||||||
value: untrackedConsumption,
|
value: untrackedConsumption,
|
||||||
tooltip: `${formatNumber(untrackedConsumption, this.hass.locale)} kWh`,
|
tooltip: `${formatNumber(untrackedConsumption, this.hass.locale)} kWh`,
|
||||||
color: computedStyle.getPropertyValue("--state-unavailable-color"),
|
color: computedStyle.getPropertyValue("--state-unavailable-color"),
|
||||||
index: 4,
|
index: 3 + deviceSections.length,
|
||||||
});
|
});
|
||||||
links.push({
|
links.push({
|
||||||
source: "home",
|
source: "home",
|
||||||
@ -428,6 +445,31 @@ class HuiEnergySankeyCard
|
|||||||
return { areas, floors };
|
return { areas, floors };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _getDeviceSections(
|
||||||
|
parentLinks: Record<string, string>,
|
||||||
|
deviceNodes: Node[]
|
||||||
|
): Node[][] {
|
||||||
|
const parentSection: Node[] = [];
|
||||||
|
const childSection: Node[] = [];
|
||||||
|
const parentIds = Object.values(parentLinks);
|
||||||
|
const remainingLinks: typeof parentLinks = {};
|
||||||
|
deviceNodes.forEach((deviceNode) => {
|
||||||
|
if (parentIds.includes(deviceNode.id)) {
|
||||||
|
parentSection.push(deviceNode);
|
||||||
|
remainingLinks[deviceNode.id] = parentLinks[deviceNode.id];
|
||||||
|
} else {
|
||||||
|
childSection.push(deviceNode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (parentSection.length > 0) {
|
||||||
|
return [
|
||||||
|
...this._getDeviceSections(remainingLinks, parentSection),
|
||||||
|
childSection,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [deviceNodes];
|
||||||
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user