From 32a90a3e575e9e06e1eefca26f182c0db506c703 Mon Sep 17 00:00:00 2001 From: Max Chodorowski Date: Tue, 24 Oct 2023 12:05:02 +0100 Subject: [PATCH] Dynamic custom icons #586 --- README.md | 5 ++++- src/entity-fields/get-icon.ts | 5 ++++- test/other/entity-fields/get-icon.test.ts | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5061a865..32775b94 100644 --- a/README.md +++ b/README.md @@ -606,12 +606,15 @@ entities: ### Other use cases -![image](https://github.com/maxwroc/battery-state-card/assets/8268674/d66bcd53-e37a-4518-a087-bd7e708b3425) +![image](https://github.com/maxwroc/battery-state-card/assets/8268674/40957377-d523-45d2-99ae-39325b5ddacc) +![image](https://github.com/maxwroc/battery-state-card/assets/8268674/477149f8-9d88-4858-b1f4-f7c615186845) ```yaml type: custom:battery-state-card secondary_info: '{last_changed}' icon: mdi:signal +# below an example with dynamic icon +# icon: "mdi:signal-cellular-{state|abs()|greaterthan(69,outline)|greaterthan(59,1)|greaterthan(49,2)|greaterthan(2,3)}" filter: include: - name: attributes.device_class diff --git a/src/entity-fields/get-icon.ts b/src/entity-fields/get-icon.ts index 8ae9f5a3..8b87f19a 100644 --- a/src/entity-fields/get-icon.ts +++ b/src/entity-fields/get-icon.ts @@ -1,5 +1,6 @@ import { HomeAssistant } from "custom-card-helpers"; import { log } from "../utils"; +import { RichStringProcessor } from "../rich-string-processor"; /** * Gets MDI icon class @@ -16,6 +17,7 @@ export const getIcon = (config: IBatteryEntityConfig, level: number, isCharging: if (config.icon) { const attribPrefix = "attribute."; + // check if we should return the icon/string from the attribute value if (hass && config.icon.startsWith(attribPrefix)) { const attribName = config.icon.substr(attribPrefix.length); const val = hass.states[config.entity].attributes[attribName] as string | undefined; @@ -27,7 +29,8 @@ export const getIcon = (config: IBatteryEntityConfig, level: number, isCharging: return val; } - return config.icon; + const processor = new RichStringProcessor(hass, config.entity); + return processor.process(config.icon); } if (isNaN(level) || level > 100 || level < 0) { diff --git a/test/other/entity-fields/get-icon.test.ts b/test/other/entity-fields/get-icon.test.ts index 83229590..3d2b9073 100644 --- a/test/other/entity-fields/get-icon.test.ts +++ b/test/other/entity-fields/get-icon.test.ts @@ -1,4 +1,5 @@ import { getIcon } from "../../../src/entity-fields/get-icon"; +import { HomeAssistantMock } from "../../helpers"; describe("Get icon", () => { test("charging and charging icon set in config", () => { @@ -45,4 +46,19 @@ describe("Get icon", () => { let icon = getIcon({ entity: "", icon: "mdi:custom" }, 20, false, undefined); expect(icon).toBe("mdi:custom"); }); + + test.each([ + ["signal-cellular-{state}", "20", "signal-cellular-20"], + ["signal-cellular-{state|abs()|greaterthan(69,outline)|greaterthan(59,1)|greaterthan(49,2)|greaterthan(2,3)}", "40", "signal-cellular-3"], + ["signal-cellular-{state|abs()|greaterthan(69,outline)|greaterthan(59,1)|greaterthan(49,2)|greaterthan(2,3)}", "55", "signal-cellular-2"], + ["signal-cellular-{state|abs()|greaterthan(69,outline)|greaterthan(59,1)|greaterthan(49,2)|greaterthan(2,3)}", "65", "signal-cellular-1"], + ["signal-cellular-{state|abs()|greaterthan(69,outline)|greaterthan(59,1)|greaterthan(49,2)|greaterthan(2,3)}", "75", "signal-cellular-outline"], + ])("returns dynamic icon", (configuredIcon: string, state: string, expectedResult: string) => { + + const hassMock = new HomeAssistantMock(); + hassMock.addEntity("Battery state", state); + + let icon = getIcon({ entity: "battery_state", icon: configuredIcon }, Number(state), false, hassMock.hass); + expect(icon).toBe(expectedResult); + }) }); \ No newline at end of file