Skip to content

Commit

Permalink
Merge pull request #28 from davet2001/issue17-group-smallest-consumers
Browse files Browse the repository at this point in the history
Fix Issue17: GUI options to group smallest consumers
  • Loading branch information
davet2001 authored Jan 9, 2025
2 parents 7e9f84e + 3a6da06 commit cabdb6d
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 19 deletions.
9 changes: 9 additions & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@ export const POWER_CARD_NAME = `${PREFIX_NAME}-power-flow-card`;
export const POWER_CARD_EDITOR_NAME = `${POWER_CARD_NAME}-editor`;
export const ENERGY_CARD_NAME = `${PREFIX_NAME}-energy-elec-flow-card`;
export const ENERGY_CARD_EDITOR_NAME = `${ENERGY_CARD_NAME}-editor`;

export const HIDE_CONSUMERS_BELOW_THRESHOLD_W = 100;
export const HIDE_CONSUMERS_BELOW_THRESHOLD_WH = 100;

export const GENERIC_LABELS = [
"appearance",
"title",
"max_consumer_branches",
];
68 changes: 65 additions & 3 deletions src/elec-sankey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@ export class ElecSankey extends LitElement {
@property({ attribute: false })
public consumerRoutes: { [id: string]: ElecRoute } = {};

@property({ attribute: false })
public maxConsumerBranches: number = 0;

@property({ attribute: false })
public hideConsumersBelow: number = 100;

private _rateToWidthMultplier: number = 0.2;

private _phantomGridInRoute?: ElecRoute;
Expand Down Expand Up @@ -503,6 +509,12 @@ export class ElecSankey extends LitElement {
count++;
}
}
if (this.maxConsumerBranches !== 0) {
if (count > this.maxConsumerBranches - 2) {
count = this.maxConsumerBranches - 2;
}
}

const untracked = this._untrackedConsumerRoute.rate;
totalHeight += this._rateToWidth(untracked);
count++;
Expand Down Expand Up @@ -897,6 +909,53 @@ export class ElecSankey extends LitElement {
return [divRet, svgRet, bottomLeftY, bottomRightY];
}
private _getGroupedConsumerRoutes(): { [id: string]: ElecRoute } {
let consumerRoutes: { [id: string]: ElecRoute } = {};
consumerRoutes = structuredClone(this.consumerRoutes);
let groupedConsumer: ElecRoute = {
id: "other",
text: "Other",
rate: 0,
};
let groupedConsumerExists = false;
if (this.hideConsumersBelow > 0) {
for (const key in consumerRoutes) {
if (consumerRoutes[key].rate < this.hideConsumersBelow) {
groupedConsumer.rate += consumerRoutes[key].rate;
groupedConsumerExists = true;
delete consumerRoutes[key];
}
}
}
if (this.maxConsumerBranches !== 0) {
const numConsumerRoutes = Object.keys(consumerRoutes).length;
if (numConsumerRoutes > this.maxConsumerBranches - 1) {
let otherCount = numConsumerRoutes + 2 - this.maxConsumerBranches;
consumerRoutes = this.consumerRoutes;
const sortedConsumerRoutes: ElecRoute[]
= Object.values(this.consumerRoutes).sort((a, b) => a.rate - b.rate);
sortedConsumerRoutes.forEach((route) => {
if (otherCount > 0) {
groupedConsumer.rate += route.rate;
groupedConsumerExists = true;
if (route.id) {
delete consumerRoutes[route.id];
}
otherCount--;
}
});
}
}
if (groupedConsumerExists) {
consumerRoutes[groupedConsumer.id!] = groupedConsumer;
}
return consumerRoutes;
}
protected _renderConsumerFlows(
y6: number,
y7: number,
Expand All @@ -916,14 +975,17 @@ export class ElecSankey extends LitElement {
}
let svgRow: TemplateResult;
let divRow: TemplateResult;
for (const key in this.consumerRoutes) {
if (Object.prototype.hasOwnProperty.call(this.consumerRoutes, key)) {
const consumerRoutes = this._getGroupedConsumerRoutes();
for (const key in consumerRoutes) {
if (Object.prototype.hasOwnProperty.call(consumerRoutes, key)) {
[divRow, svgRow, yLeft, yRight] = this._renderConsumerFlow(
xLeft,
yLeft,
xRight,
yRight,
this.consumerRoutes[key],
consumerRoutes[key],
color,
svgScaleX,
i++
Expand Down
43 changes: 38 additions & 5 deletions src/energy-flow-card-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,38 @@ import { HaFormSchema } from "./utils/form/ha-form";
import "./ha/panels/lovelace/editor/hui-entities-card-row-editor";
import { fireEvent } from "./ha/common/dom/fire_event";

import { ENERGY_CARD_EDITOR_NAME } from "./const";
import { ENERGY_CARD_EDITOR_NAME, GENERIC_LABELS } from "./const";
import { mdiPalette } from "@mdi/js";
import setupCustomlocalize from "./localize";

const ENERGY_LABELS = [
"hide_small_consumers",
]
const schema = [
{ name: "title", selector: { text: {} } },

{
name: "appearance",
flatten: true,
type: "expandable",
iconPath: mdiPalette,
schema: [
{
name: "max_consumer_branches",
selector: {
number: {
min: 0,
max: 10,
mode: "slider",
}
}
},
{
name: "hide_small_consumers",
selector: { boolean: {} }
}
]
}
];

@customElement(ENERGY_CARD_EDITOR_NAME)
Expand All @@ -26,11 +54,16 @@ export class EnergyFlowCardEditor extends LitElement implements LovelaceCardEdit
}

private _computeLabel = (schema: HaFormSchema) => {
switch (schema.name) {
case "title": return "Title";
const customLocalize = setupCustomlocalize(this.hass!);

if (GENERIC_LABELS.includes(schema.name)) {
return customLocalize(`editor.card.generic.${schema.name}`);
}
console.error("Error name key missing for '" + schema.name + "'")
return ""
if (ENERGY_LABELS.includes(schema.name)) {
return customLocalize(`editor.card.energy_sankey.${schema.name}`);
} return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
};

protected render() {
Expand Down
8 changes: 7 additions & 1 deletion src/hui-energy-elec-flow-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import type { LovelaceCard, LovelaceCardEditor } from "./ha/panels/lovelace/type
import { EnergyElecFlowCardConfig } from "./types";

import { registerCustomCard } from "./utils/custom-cards";
import { ENERGY_CARD_EDITOR_NAME } from "./const";
import {
ENERGY_CARD_EDITOR_NAME,
HIDE_CONSUMERS_BELOW_THRESHOLD_WH,
} from "./const";

registerCustomCard({
type: "hui-energy-elec-flow-card",
Expand Down Expand Up @@ -85,6 +88,8 @@ export class HuiEnergyElecFlowCard
if (!this.hass || !this._config) {
return nothing;
}
const hideConsumersBelow = this._config.hide_consumers_below
? HIDE_CONSUMERS_BELOW_THRESHOLD_WH : 0;
return html`
<ha-card>
${this._config.title
Expand All @@ -101,6 +106,7 @@ export class HuiEnergyElecFlowCard
.gridOutRoute=${this._gridOutRoute || undefined}
.generationInRoutes=${this._generationInRoutes || {}}
.consumerRoutes=${this._consumerRoutes || {}}
.hideConsumersBelow=${hideConsumersBelow}
></ha-elec-sankey>
</div>
</ha-card>
Expand Down
13 changes: 12 additions & 1 deletion src/hui-power-flow-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ import { ExtEntityRegistryEntry, getExtendedEntityRegistryEntry } from "./ha/dat
//import "./power-flow-card-editor"


import { POWER_CARD_NAME, POWER_CARD_EDITOR_NAME } from "./const";
import {
POWER_CARD_NAME,
POWER_CARD_EDITOR_NAME,
HIDE_CONSUMERS_BELOW_THRESHOLD_W,
} from "./const";

registerCustomCard({
type: "hui-power-flow-card",
Expand Down Expand Up @@ -219,6 +223,11 @@ class HuiPowerFlowCard extends LitElement implements LovelaceCard {
delete (config.generation_entity)
}

const maxConsumerBranches = this._config.max_consumer_branches || 0;

const hideConsumersBelow = this._config.hide_small_consumers
? HIDE_CONSUMERS_BELOW_THRESHOLD_W : 0;

let gridInRoute: ElecRoute | null = null;
if (config.power_from_grid_entity) {
const stateObj = this.hass.states[config.power_from_grid_entity];
Expand Down Expand Up @@ -322,6 +331,8 @@ class HuiPowerFlowCard extends LitElement implements LovelaceCard {
.gridOutRoute=${gridOutRoute || undefined}
.generationInRoutes=${generationInRoutes}
.consumerRoutes=${consumerRoutes}
.maxConsumerBranches=${maxConsumerBranches}
.hideConsumersBelow=${hideConsumersBelow}
></ha-elec-sankey>
</div>
</ha-card>
Expand Down
91 changes: 91 additions & 0 deletions src/localize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { HomeAssistant } from "./ha/types";
// import * as ar from "./translations/ar.json";
// import * as bg from "./translations/bg.json";
// import * as ca from "./translations/ca.json";
// import * as cs from "./translations/cs.json";
// import * as da from "./translations/da.json";
// import * as de from "./translations/de.json";
// import * as el from "./translations/el.json";
import * as en from "./translations/en.json";
// import * as es from "./translations/es.json";
// import * as fi from "./translations/fi.json";
// import * as fr from "./translations/fr.json";
// import * as he from "./translations/he.json";
// import * as hu from "./translations/hu.json";
// import * as id from "./translations/id.json";
// import * as it from "./translations/it.json";
// import * as ko_KR from "./translations/ko-KR.json";
// import * as nb from "./translations/nb.json";
// import * as nl from "./translations/nl.json";
// import * as pl from "./translations/pl.json";
// import * as pt_BR from "./translations/pt-BR.json";
// import * as pt_PT from "./translations/pt-PT.json";
// import * as ro from "./translations/ro.json";
// import * as ru from "./translations/ru.json";
// import * as sl from "./translations/sl.json";
// import * as sk from "./translations/sk.json";
// import * as sv from "./translations/sv.json";
// import * as tr from "./translations/tr.json";
// import * as uk from "./translations/uk.json";
// import * as vi from "./translations/vi.json";
// import * as zh_Hans from "./translations/zh-Hans.json";
// import * as zh_Hant from "./translations/zh-Hant.json";

const languages: Record<string, unknown> = {
// ar,
// bg,
// ca,
// cs,
// da,
// de,
// el,
en,
// es,
// fi,
// fr,
// he,
// hu,
// id,
// it,
// "ko-KR": ko_KR,
// nb,
// nl,
// pl,
// "pt-BR": pt_BR,
// "pt-PT": pt_PT,
// ro,
// ru,
// sl,
// sk,
// sv,
// tr,
// uk,
// vi,
// "zh-Hans": zh_Hans,
// "zh-Hant": zh_Hant,
};

const DEFAULT_LANG = "en";

function getTranslatedString(key: string, lang: string): string | undefined {
try {
return key
.split(".")
.reduce(
(o, i) => (o as Record<string, unknown>)[i],
languages[lang]
) as string;
} catch (_) {
return undefined;
}
}

export default function setupCustomlocalize(hass?: HomeAssistant) {
return function (key: string) {
const lang = hass?.locale.language ?? DEFAULT_LANG;

let translated = getTranslatedString(key, lang);
if (!translated) translated = getTranslatedString(key, DEFAULT_LANG);
return translated ?? key;
};
}
Loading

0 comments on commit cabdb6d

Please sign in to comment.