Skip to content

Commit

Permalink
Merge pull request #648 from maxwroc/FixMinMaxGroup
Browse files Browse the repository at this point in the history
Fixed min/max setting on groups
  • Loading branch information
maxwroc authored Jan 12, 2024
2 parents 9475255 + b31029e commit 24290c4
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 14 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "battery-state-card",
"version": "3.1.3",
"version": "3.1.4",
"description": "Battery State card for Home Assistant",
"main": "dist/battery-state-card.js",
"author": "Max Chodorowski",
Expand Down
2 changes: 1 addition & 1 deletion src/custom-elements/battery-state-card.views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const collapsableWrapper = (model: IBatteryGroup, batteries: IBatteryColl
</div>
<div class="chevron">&lsaquo;</div>
</div>
<div style="max-height: ${Object.keys(batteries).length * 50}px">
<div style="max-height: ${Object.keys(batteries).length * 50}px" class="groupItems">
${model.batteryIds.map(id => batteryWrapper(batteries[id]))}
</div>
</div>
Expand Down
13 changes: 6 additions & 7 deletions src/grouping.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { log } from "./utils";
import { log, toNumber } from "./utils";
import { IBatteryCollection, IBatteryCollectionItem } from "./battery-provider";
import { BatteryStateEntity } from "./custom-elements/battery-state-entity";

export interface IBatteryGroup {
title?: string;
Expand Down Expand Up @@ -87,7 +86,7 @@ const getGroupIndex = (config: IGroupConfig[], battery: IBatteryCollectionItem,
return false
}

const level = isNaN(Number(battery.state)) ? 0 : Number(battery.state);
const level = isNaN(toNumber(battery.state)) ? 0 : toNumber(battery.state);

return level >= group.min! && level <= group.max!;
});
Expand Down Expand Up @@ -152,14 +151,14 @@ const getEnrichedText = (text: string, group: IBatteryGroup, batteries: IBattery
text = text.replace(/\{[a-z]+\}/g, keyword => {
switch (keyword) {
case "{min}":
return group.batteryIds.reduce((agg, id) => agg > Number(batteries[id].state) ? Number(batteries[id].state) : agg, 100).toString();
return group.batteryIds.reduce((agg, id) => agg > toNumber(batteries[id].state) ? toNumber(batteries[id].state) : agg, 100).toString();
case "{max}":
return group.batteryIds.reduce((agg, id) => agg < Number(batteries[id].state) ? Number(batteries[id].state) : agg, 0).toString();
return group.batteryIds.reduce((agg, id) => agg < toNumber(batteries[id].state) ? toNumber(batteries[id].state) : agg, 0).toString();
case "{count}":
return group.batteryIds.length.toString();
case "{range}":
const min = group.batteryIds.reduce((agg, id) => agg > Number(batteries[id].state) ? Number(batteries[id].state) : agg, 100).toString();
const max = group.batteryIds.reduce((agg, id) => agg < Number(batteries[id].state) ? Number(batteries[id].state) : agg, 0).toString();
const min = group.batteryIds.reduce((agg, id) => agg > toNumber(batteries[id].state) ? toNumber(batteries[id].state) : agg, 100).toString();
const max = group.batteryIds.reduce((agg, id) => agg < toNumber(batteries[id].state) ? toNumber(batteries[id].state) : agg, 0).toString();
return min == max ? min : min + "-" + max;
default:
return keyword;
Expand Down
78 changes: 78 additions & 0 deletions test/card/grouping.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { BatteryStateCard } from "../../src/custom-elements/battery-state-card";
import { CardElements, HomeAssistantMock } from "../helpers";

describe("Grouping", () => {
test.each([
[["10", "24", "25", "26", "50"], 25, "10 %|24 %", "25 %|26 %|50 %"],
[["10.1", "24.2", "25.3", "26.4", "50.5"], 25, "10,1 %|24,2 %", "25,3 %|26,4 %|50,5 %", ","],
[["10.1", "24.2", "25.3", "26.4", "50.5"], 25, "10.1 %|24.2 %", "25.3 %|26.4 %|50.5 %", "."],
])("works with 'min' setting", async (entityStates: string[], min: number, ungrouped: string, inGroup: string, decimalPoint = ".") => {

const hass = new HomeAssistantMock<BatteryStateCard>();
const entities = entityStates.map((state, i) => {
const batt = hass.addEntity(`Batt ${i + 1}`, state);
return batt.entity_id;
});
const groupEntity = hass.addEntity("My group", "30", { entity_id: entities }, "group");

hass.mockFunc("formatEntityState", (entityData: any) => `${entityData.state.replace(".", decimalPoint)} %`);

const cardElem = hass.addCard("battery-state-card", {
title: "Header",
entities: [],
//sort: "state",
collapse: [
{
group_id: groupEntity.entity_id,
min
}
]
});

// waiting for card to be updated/rendered
await cardElem.cardUpdated;

const card = new CardElements(cardElem);

const ungroupedStates = card.items.map(e => e.stateText).join("|");
expect(ungroupedStates).toBe(ungrouped);

expect(card.groupsCount).toBe(1);

const groupStates = card.group(0).items.map(e => e.stateText).join("|");
expect(groupStates).toBe(inGroup);
});

test.each([
[["10", "24", "25", "26", "50"], "Min {min}, Max {max}, Range {range}, Count {count}", "Min 25, Max 50, Range 25-50, Count 3"],
])("secondary info keywords", async (entityStates: string[], secondaryInfo: string, expectedSecondaryInfo: string) => {

const hass = new HomeAssistantMock<BatteryStateCard>();
const entities = entityStates.map((state, i) => {
const batt = hass.addEntity(`Batt ${i + 1}`, state);
return batt.entity_id;
});
const groupEntity = hass.addEntity("My group", "30", { entity_id: entities }, "group");

const cardElem = hass.addCard("battery-state-card", {
title: "Header",
entities: [],
//sort: "state",
collapse: [
{
group_id: groupEntity.entity_id,
min: 25,
secondary_info: secondaryInfo
}
]
});

// waiting for card to be updated/rendered
await cardElem.cardUpdated;

const card = new CardElements(cardElem);

expect(card.groupsCount).toBe(1);
expect(card.group(0).secondaryInfoText).toBe(expectedSecondaryInfo);
});
});
67 changes: 62 additions & 5 deletions test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export class CardElements {
return this.card.shadowRoot!.querySelectorAll(".card-content > * > battery-state-entity").length;
}

get groupsCount() {
return this.card.shadowRoot!.querySelectorAll(".card-content > .expandWrapper").length;
}

get items(): EntityElements[] {
const result: EntityElements[] = [];
for (let index = 0; index < this.itemsCount; index++) {
Expand All @@ -35,6 +39,15 @@ export class CardElements {
return result;
}

get groups(): GroupElement[] {
const result: GroupElement[] = [];
for (let index = 0; index < this.groupsCount; index++) {
result.push(this.group(index));
}

return result;
}

item(index: number) {
const entity = this.card.shadowRoot!.querySelectorAll<BatteryStateEntity>(".card-content > * > battery-state-entity")[index];
if (!entity) {
Expand All @@ -43,37 +56,81 @@ export class CardElements {

return new EntityElements(entity);
}

group(index: number) {
const group = this.card.shadowRoot!.querySelectorAll<BatteryStateEntity>(".card-content > .expandWrapper")[index];
if (!group) {
throw new Error("Group element not found: " + index);
}

return new GroupElement(group);
}
}

export class EntityElements {
constructor(private card: BatteryStateEntity) {

private root: HTMLElement;

constructor(private card: BatteryStateEntity, isShadowRoot: boolean = true) {
this.root = isShadowRoot ? <any>card.shadowRoot! : card;
}

get iconName() {
return this.card.shadowRoot?.querySelector("ha-icon")?.getAttribute("icon")
return this.root.querySelector("ha-icon")?.getAttribute("icon");
}

get nameText() {
return this.card.shadowRoot?.querySelector(".name")?.textContent?.trim();
return this.root.querySelector(".name")?.textContent?.trim();
}

get secondaryInfo() {
return this.card.shadowRoot?.querySelector(".secondary");
return this.root.querySelector(".secondary");
}

get secondaryInfoText() {
return this.secondaryInfo?.textContent?.trim();
}

get stateText() {
return this.card.shadowRoot?.querySelector(".state")
return this.root.querySelector(".state")
?.textContent
?.trim()
.replace(String.fromCharCode(160), " "); // replace non breakable space
}
}

export class GroupElement extends EntityElements {
constructor(private elem: HTMLElement) {
super(<BatteryStateEntity>elem.querySelector(".toggler"), false);
}

private get batteryNodes(): NodeListOf<BatteryStateEntity> {
return this.elem.querySelectorAll<BatteryStateEntity>(".groupItems > * > battery-state-entity");
}

get itemsCount() {
return this.batteryNodes.length;
}

get items(): EntityElements[] {
const result: EntityElements[] = [];
for (let index = 0; index < this.itemsCount; index++) {
result.push(this.item(index));
}

return result;
}

item(index: number): EntityElements {
const entity = this.batteryNodes[index];
if (!entity) {
throw new Error("Card element not found: " + index);
}

return new EntityElements(entity);
}
}


export class HomeAssistantMock<T extends LovelaceCard<any>> {

Expand Down

0 comments on commit 24290c4

Please sign in to comment.