custom-card-helpers 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.github/dependabot.yml +13 -0
  2. package/.github/workflows/claude-code-review.yml +44 -0
  3. package/.github/workflows/claude.yml +50 -0
  4. package/.github/workflows/release.yml +4 -0
  5. package/.github/workflows/test.yml +1 -0
  6. package/dist/compute-name.d.ts +7 -0
  7. package/dist/compute-state-display.d.ts +1 -2
  8. package/dist/datetime/duration_to_seconds.d.ts +1 -1
  9. package/dist/datetime/seconds_to_duration.d.ts +1 -1
  10. package/dist/datetime/select_unit.d.ts +8 -0
  11. package/dist/get-lovelace.d.ts +5 -1
  12. package/dist/index.js +259 -23
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.m.js +254 -23
  15. package/dist/index.m.js.map +1 -1
  16. package/dist/index.umd.js +263 -25
  17. package/dist/index.umd.js.map +1 -1
  18. package/dist/scroll-to-target.d.ts +1 -1
  19. package/dist/translations/localize.d.ts +1 -2
  20. package/dist/turn-on-off-entity.d.ts +2 -0
  21. package/dist/types.d.ts +156 -4
  22. package/package.json +7 -8
  23. package/src/compute-name.ts +7 -0
  24. package/src/compute-state-display.ts +2 -3
  25. package/src/cover_icon.ts +74 -6
  26. package/src/datetime/duration_to_seconds.ts +1 -1
  27. package/src/datetime/relative_time.ts +1 -2
  28. package/src/datetime/seconds_to_duration.ts +1 -1
  29. package/src/datetime/select_unit.ts +94 -0
  30. package/src/datetime/timer_time_remaining.ts +1 -1
  31. package/src/get-lovelace.ts +22 -16
  32. package/src/handle-action.ts +19 -2
  33. package/src/handle-click.ts +1 -1
  34. package/src/scroll-to-target.ts +1 -1
  35. package/src/state_icon.ts +1 -1
  36. package/src/translations/localize.ts +1 -3
  37. package/src/turn-on-off-entity.ts +10 -0
  38. package/src/types.ts +203 -3
  39. package/test/datetime.test.ts +2 -2
  40. package/test/icons.test.ts +51 -0
  41. package/test/service-actions.test.ts +19 -1
@@ -0,0 +1,13 @@
1
+ # Source - https://stackoverflow.com/a/66819358
2
+ # Posted by Johnny, modified by community. See post 'Timeline' for change history
3
+ # Retrieved 2026-02-27, License - CC BY-SA 4.0
4
+
5
+ version: 2
6
+ updates:
7
+ - package-ecosystem: "npm"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "daily"
11
+ # Always increase the version requirement
12
+ # to match the new version.
13
+ versioning-strategy: increase
@@ -0,0 +1,44 @@
1
+ name: Claude Code Review
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, ready_for_review, reopened]
6
+ # Optional: Only run on specific file changes
7
+ # paths:
8
+ # - "src/**/*.ts"
9
+ # - "src/**/*.tsx"
10
+ # - "src/**/*.js"
11
+ # - "src/**/*.jsx"
12
+
13
+ jobs:
14
+ claude-review:
15
+ # Optional: Filter by PR author
16
+ # if: |
17
+ # github.event.pull_request.user.login == 'external-contributor' ||
18
+ # github.event.pull_request.user.login == 'new-developer' ||
19
+ # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20
+
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: read
24
+ pull-requests: read
25
+ issues: read
26
+ id-token: write
27
+
28
+ steps:
29
+ - name: Checkout repository
30
+ uses: actions/checkout@v4
31
+ with:
32
+ fetch-depth: 1
33
+
34
+ - name: Run Claude Code Review
35
+ id: claude-review
36
+ uses: anthropics/claude-code-action@v1
37
+ with:
38
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
+ plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
40
+ plugins: 'code-review@claude-code-plugins'
41
+ prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
42
+ # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
43
+ # or https://code.claude.com/docs/en/cli-reference for available options
44
+
@@ -0,0 +1,50 @@
1
+ name: Claude Code
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_review_comment:
7
+ types: [created]
8
+ issues:
9
+ types: [opened, assigned]
10
+ pull_request_review:
11
+ types: [submitted]
12
+
13
+ jobs:
14
+ claude:
15
+ if: |
16
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19
+ (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: read
23
+ pull-requests: read
24
+ issues: read
25
+ id-token: write
26
+ actions: read # Required for Claude to read CI results on PRs
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 1
32
+
33
+ - name: Run Claude Code
34
+ id: claude
35
+ uses: anthropics/claude-code-action@v1
36
+ with:
37
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
+
39
+ # This is an optional setting that allows Claude to read CI results on PRs
40
+ additional_permissions: |
41
+ actions: read
42
+
43
+ # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
44
+ # prompt: 'Update the pull request description to include a summary of changes.'
45
+
46
+ # Optional: Add claude_args to customize behavior and configuration
47
+ # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
48
+ # or https://code.claude.com/docs/en/cli-reference for available options
49
+ # claude_args: '--allowed-tools Bash(gh pr *)'
50
+
@@ -27,6 +27,10 @@ jobs:
27
27
  - name: Install dependencies
28
28
  run: npm ci
29
29
 
30
+ - name: Bump version to match release tag
31
+ if: github.event_name == 'release'
32
+ run: npm version ${GITHUB_REF_NAME#v} --no-git-tag-version
33
+
30
34
  - name: Build
31
35
  run: npm run build
32
36
 
@@ -7,6 +7,7 @@ on:
7
7
  pull_request:
8
8
  branches:
9
9
  - master
10
+ workflow_dispatch :
10
11
 
11
12
  jobs:
12
13
  build:
@@ -1,2 +1,9 @@
1
1
  import { HassEntity } from "home-assistant-js-websocket";
2
+ /**
3
+ * @deprecated Reads `friendly_name`, which Home Assistant is moving away from -
4
+ * it now always includes the device name, so this duplicates context the
5
+ * surrounding UI already shows. Prefer `hass.formatEntityName(stateObj, name)`
6
+ * on HA 2026.4 and later, which resolves the name from the entity's registry
7
+ * context and lets users choose which parts to include.
8
+ */
2
9
  export declare const computeName: (stateObj: HassEntity) => string;
@@ -1,4 +1,3 @@
1
1
  import { HassEntity } from "home-assistant-js-websocket";
2
- import { LocalizeFunc } from "./translations/localize";
3
- import { FrontendLocaleData } from "./types";
2
+ import { FrontendLocaleData, LocalizeFunc } from "./types";
4
3
  export declare const computeStateDisplay: (localize: LocalizeFunc, stateObj: HassEntity, locale: FrontendLocaleData, state?: string) => string;
@@ -3,4 +3,4 @@
3
3
  * @param duration hh:mm:ss formated duration
4
4
  * @returns duration in seconds
5
5
  */
6
- export default function durationToSeconds(duration: string): number;
6
+ export declare function durationToSeconds(duration: string): number;
@@ -1 +1 @@
1
- export default function secondsToDuration(d: number): string | null;
1
+ export declare function secondsToDuration(d: number): string | null;
@@ -0,0 +1,8 @@
1
+ export type Unit = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
2
+ export declare function selectUnit(from: Date | number, to?: Date | number, thresholds?: Partial<Thresholds>): {
3
+ value: number;
4
+ unit: Unit;
5
+ };
6
+ type Thresholds = Record<'second' | 'minute' | 'hour' | 'day', number>;
7
+ export declare const DEFAULT_THRESHOLDS: Thresholds;
8
+ export {};
@@ -1 +1,5 @@
1
- export declare const getLovelace: () => any;
1
+ export declare const getLovelace: () => {
2
+ [key: string]: any;
3
+ config: import("./types").LovelaceConfig;
4
+ current_view: number;
5
+ } | null;
package/dist/index.js CHANGED
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var intlUtils = require('@formatjs/intl-utils');
4
-
5
3
  /**
6
4
  * Apply a theme to an element by setting the CSS variables on it.
7
5
  *
@@ -87,6 +85,13 @@ const computeIcon = (stateObj, icon) => {
87
85
  return DOMAIN_ICONS[domain] || "mdi:bookmark";
88
86
  };
89
87
 
88
+ /**
89
+ * @deprecated Reads `friendly_name`, which Home Assistant is moving away from -
90
+ * it now always includes the device name, so this duplicates context the
91
+ * surrounding UI already shows. Prefer `hass.formatEntityName(stateObj, name)`
92
+ * on HA 2026.4 and later, which resolves the name from the entity's registry
93
+ * context and lets users choose which parts to include.
94
+ */
90
95
  const computeName = (stateObj) => stateObj.attributes.friendly_name || stateObj.entity_id;
91
96
 
92
97
  function computeRTL(hass) {
@@ -137,6 +142,30 @@ exports.TimeFormat = void 0;
137
142
  TimeFormat["am_pm"] = "12";
138
143
  TimeFormat["twenty_four"] = "24";
139
144
  })(exports.TimeFormat || (exports.TimeFormat = {}));
145
+ exports.TimeZone = void 0;
146
+ (function (TimeZone) {
147
+ TimeZone["local"] = "local";
148
+ TimeZone["server"] = "server";
149
+ })(exports.TimeZone || (exports.TimeZone = {}));
150
+ exports.DateFormat = void 0;
151
+ (function (DateFormat) {
152
+ DateFormat["language"] = "language";
153
+ DateFormat["system"] = "system";
154
+ DateFormat["DMY"] = "DMY";
155
+ DateFormat["MDY"] = "MDY";
156
+ DateFormat["YMD"] = "YMD";
157
+ })(exports.DateFormat || (exports.DateFormat = {}));
158
+ exports.FirstWeekday = void 0;
159
+ (function (FirstWeekday) {
160
+ FirstWeekday["language"] = "language";
161
+ FirstWeekday["monday"] = "monday";
162
+ FirstWeekday["tuesday"] = "tuesday";
163
+ FirstWeekday["wednesday"] = "wednesday";
164
+ FirstWeekday["thursday"] = "thursday";
165
+ FirstWeekday["friday"] = "friday";
166
+ FirstWeekday["saturday"] = "saturday";
167
+ FirstWeekday["sunday"] = "sunday";
168
+ })(exports.FirstWeekday || (exports.FirstWeekday = {}));
140
169
 
141
170
  // REF: https://github.com/home-assistant/frontend/blob/dev/src/common/datetime/use_am_pm.ts
142
171
  /**
@@ -735,6 +764,74 @@ function durationToSeconds(duration) {
735
764
  return parts[0] * 3600 + parts[1] * 60 + parts[2];
736
765
  }
737
766
 
767
+ /*
768
+ * selectUnit() taken from @formatjs/intl-units 3.8.4.
769
+ *
770
+ */
771
+ const MS_PER_SECOND = 1e3;
772
+ const SECS_PER_MIN = 60;
773
+ const SECS_PER_HOUR = SECS_PER_MIN * 60;
774
+ const SECS_PER_DAY = SECS_PER_HOUR * 24;
775
+ const SECS_PER_WEEK = SECS_PER_DAY * 7;
776
+ function selectUnit(from, to = Date.now(), thresholds = {}) {
777
+ const resolvedThresholds = Object.assign(Object.assign({}, DEFAULT_THRESHOLDS), (thresholds || {}));
778
+ const secs = (+from - +to) / MS_PER_SECOND;
779
+ if (Math.abs(secs) < resolvedThresholds.second) {
780
+ return {
781
+ value: Math.round(secs),
782
+ unit: 'second',
783
+ };
784
+ }
785
+ const mins = secs / SECS_PER_MIN;
786
+ if (Math.abs(mins) < resolvedThresholds.minute) {
787
+ return {
788
+ value: Math.round(mins),
789
+ unit: 'minute',
790
+ };
791
+ }
792
+ const hours = secs / SECS_PER_HOUR;
793
+ if (Math.abs(hours) < resolvedThresholds.hour) {
794
+ return {
795
+ value: Math.round(hours),
796
+ unit: 'hour',
797
+ };
798
+ }
799
+ const days = secs / SECS_PER_DAY;
800
+ if (Math.abs(days) < resolvedThresholds.day) {
801
+ return {
802
+ value: Math.round(days),
803
+ unit: 'day',
804
+ };
805
+ }
806
+ const fromDate = new Date(from);
807
+ const toDate = new Date(to);
808
+ const years = fromDate.getFullYear() - toDate.getFullYear();
809
+ if (Math.round(Math.abs(years)) > 0) {
810
+ return {
811
+ value: Math.round(years),
812
+ unit: 'year',
813
+ };
814
+ }
815
+ const months = years * 12 + fromDate.getMonth() - toDate.getMonth();
816
+ if (Math.round(Math.abs(months)) > 0) {
817
+ return {
818
+ value: Math.round(months),
819
+ unit: 'month',
820
+ };
821
+ }
822
+ const weeks = secs / SECS_PER_WEEK;
823
+ return {
824
+ value: Math.round(weeks),
825
+ unit: 'week',
826
+ };
827
+ }
828
+ const DEFAULT_THRESHOLDS = {
829
+ second: 45, // seconds to minute
830
+ minute: 45, // minutes to hour
831
+ hour: 22, // hour to day
832
+ day: 5, // day to week
833
+ };
834
+
738
835
  //REF: https://github.com/home-assistant/frontend/blob/dev/src/common/datetime/relative_time.ts
739
836
  const formatRelTimeMem = (locale) => new Intl.RelativeTimeFormat(locale.language, { numeric: "auto" });
740
837
  /**
@@ -743,7 +840,7 @@ const formatRelTimeMem = (locale) => new Intl.RelativeTimeFormat(locale.language
743
840
  * Example output: 5 minutes ago, in 3 days.
744
841
  */
745
842
  const relativeTime = (from, locale, to, includeTense = true) => {
746
- const diff = intlUtils.selectUnit(from, to);
843
+ const diff = selectUnit(from, to);
747
844
  if (includeTense) {
748
845
  return formatRelTimeMem(locale).format(diff.value, diff.unit);
749
846
  }
@@ -754,6 +851,24 @@ const relativeTime = (from, locale, to, includeTense = true) => {
754
851
  }).format(Math.abs(diff.value));
755
852
  };
756
853
 
854
+ //REF: https://github.com/home-assistant/frontend/blob/dev/src/common/datetime/seconds_to_duration.ts
855
+ const leftPad = (num) => (num < 10 ? `0${num}` : num);
856
+ function secondsToDuration(d) {
857
+ const h = Math.floor(d / 3600);
858
+ const m = Math.floor((d % 3600) / 60);
859
+ const s = Math.floor((d % 3600) % 60);
860
+ if (h > 0) {
861
+ return `${h}:${leftPad(m)}:${leftPad(s)}`;
862
+ }
863
+ if (m > 0) {
864
+ return `${m}:${leftPad(s)}`;
865
+ }
866
+ if (s > 0) {
867
+ return "" + s;
868
+ }
869
+ return null;
870
+ }
871
+
757
872
  function timerTimeRemaining(stateObj) {
758
873
  let timeRemaining = durationToSeconds(stateObj.attributes.remaining);
759
874
  if (stateObj.state === "active") {
@@ -1041,18 +1156,15 @@ const formatTimestamp = (timestamp) => {
1041
1156
  };
1042
1157
 
1043
1158
  const getLovelace = () => {
1044
- let root = document.querySelector('home-assistant');
1045
- root = root && root.shadowRoot;
1046
- root = root && root.querySelector('home-assistant-main');
1047
- root = root && root.shadowRoot;
1048
- root = root && root.querySelector('app-drawer-layout partial-panel-resolver');
1049
- root = root && root.shadowRoot || root;
1050
- root = root && root.querySelector('ha-panel-lovelace');
1051
- root = root && root.shadowRoot;
1052
- root = root && root.querySelector('hui-root');
1053
- if (root) {
1054
- const ll = root.lovelace;
1055
- ll.current_view = root.___curView;
1159
+ var _a, _b, _c, _d, _e, _f;
1160
+ const root = (_c = (_b = (_a = document
1161
+ .querySelector("home-assistant")) === null || _a === void 0 ? void 0 : _a.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector("home-assistant-main")) === null || _c === void 0 ? void 0 : _c.shadowRoot;
1162
+ const resolver = (root === null || root === void 0 ? void 0 : root.querySelector("ha-drawer partial-panel-resolver")) ||
1163
+ (root === null || root === void 0 ? void 0 : root.querySelector("app-drawer-layout partial-panel-resolver"));
1164
+ const huiRoot = (_f = (_e = (_d = ((resolver === null || resolver === void 0 ? void 0 : resolver.shadowRoot) || resolver)) === null || _d === void 0 ? void 0 : _d.querySelector("ha-panel-lovelace")) === null || _e === void 0 ? void 0 : _e.shadowRoot) === null || _f === void 0 ? void 0 : _f.querySelector("hui-root");
1165
+ if (huiRoot) {
1166
+ const ll = huiRoot.lovelace;
1167
+ ll.current_view = huiRoot.___curView;
1056
1168
  return ll;
1057
1169
  }
1058
1170
  return null;
@@ -1090,6 +1202,8 @@ const turnOnOffEntity = (hass, entityId, turnOn = true) => {
1090
1202
  }
1091
1203
  return hass.callService(serviceDomain, service, { entity_id: entityId });
1092
1204
  };
1205
+ const turnOnEntity = (hass, entityId) => turnOnOffEntity(hass, entityId, true);
1206
+ const turnOffEntity = (hass, entityId) => turnOnOffEntity(hass, entityId, false);
1093
1207
 
1094
1208
  const toggleEntity = (hass, entityId) => {
1095
1209
  const turnOn = STATES_OFF.includes(hass.states[entityId].state);
@@ -1135,13 +1249,21 @@ const handleActionConfig = (node, hass, config, actionConfig) => {
1135
1249
  forwardHaptic("success");
1136
1250
  }
1137
1251
  break;
1252
+ case "turn_on":
1253
+ turnOnOffEntity(hass, actionConfig.entity || config.entity, true);
1254
+ forwardHaptic("success");
1255
+ break;
1256
+ case "turn_off":
1257
+ turnOnOffEntity(hass, actionConfig.entity || config.entity, false);
1258
+ forwardHaptic("success");
1259
+ break;
1138
1260
  case "call-service": {
1139
1261
  if (!actionConfig.service) {
1140
1262
  forwardHaptic("failure");
1141
1263
  return;
1142
1264
  }
1143
1265
  const [domain, service] = actionConfig.service.split(".", 2);
1144
- hass.callService(domain, service, actionConfig.service_data, actionConfig.target);
1266
+ hass.callService(domain, service, actionConfig.data, actionConfig.target);
1145
1267
  forwardHaptic("success");
1146
1268
  break;
1147
1269
  }
@@ -1226,7 +1348,7 @@ const handleClick = (node, hass, config, hold, dblClick) => {
1226
1348
  return;
1227
1349
  }
1228
1350
  const [domain, service] = actionConfig.service.split(".", 2);
1229
- const serviceData = Object.assign({}, actionConfig.service_data);
1351
+ const serviceData = Object.assign({}, actionConfig.data);
1230
1352
  if (serviceData.entity_id === "entity") {
1231
1353
  serviceData.entity_id = config.entity;
1232
1354
  }
@@ -1271,6 +1393,46 @@ function hasDoubleClick(config) {
1271
1393
  return config !== undefined && config.action !== "none";
1272
1394
  }
1273
1395
 
1396
+ /**
1397
+ * Scroll to a specific y coordinate.
1398
+ *
1399
+ * Copied from paper-scroll-header-panel.
1400
+ *
1401
+ * @method scroll
1402
+ * @param {number} top The coordinate to scroll to, along the y-axis.
1403
+ * @param {boolean} smooth true if the scroll position should be smoothly adjusted.
1404
+ */
1405
+ function scrollToTarget(element, target) {
1406
+ // the scroll event will trigger _updateScrollState directly,
1407
+ // However, _updateScrollState relies on the previous `scrollTop` to update the states.
1408
+ // Calling _updateScrollState will ensure that the states are synced correctly.
1409
+ const top = 0;
1410
+ const scroller = target;
1411
+ const easingFn = function easeOutQuad(t, b, c, d) {
1412
+ /* eslint-disable no-param-reassign, space-infix-ops, no-mixed-operators */
1413
+ t /= d;
1414
+ return -c * t * (t - 2) + b;
1415
+ /* eslint-enable no-param-reassign, space-infix-ops, no-mixed-operators */
1416
+ };
1417
+ const animationId = Math.random();
1418
+ const duration = 200;
1419
+ const startTime = Date.now();
1420
+ const currentScrollTop = scroller.scrollTop;
1421
+ const deltaScrollTop = top - currentScrollTop;
1422
+ element._currentAnimationId = animationId;
1423
+ (function updateFrame() {
1424
+ const now = Date.now();
1425
+ const elapsedTime = now - startTime;
1426
+ if (elapsedTime > duration) {
1427
+ scroller.scrollTop = top;
1428
+ }
1429
+ else if (element._currentAnimationId === animationId) {
1430
+ scroller.scrollTop = easingFn(elapsedTime, currentScrollTop, deltaScrollTop, duration);
1431
+ requestAnimationFrame(updateFrame.bind(element));
1432
+ }
1433
+ }.call(element));
1434
+ }
1435
+
1274
1436
  /** Return an icon representing a binary sensor state. */
1275
1437
  const binarySensorIcon = (state, stateObj) => {
1276
1438
  const is_off = state === "off";
@@ -1333,17 +1495,86 @@ const coverIcon = (state) => {
1333
1495
  const open = state.state !== "closed";
1334
1496
  switch (state.attributes.device_class) {
1335
1497
  case "garage":
1336
- return open ? "mdi:garage-open" : "mdi:garage";
1498
+ switch (state.state) {
1499
+ case "opening":
1500
+ return "mdi:arrow-up-box";
1501
+ case "closing":
1502
+ return "mdi:arrow-down-box";
1503
+ case "closed":
1504
+ return "mdi:garage";
1505
+ default:
1506
+ return "mdi:garage-open";
1507
+ }
1508
+ case "gate":
1509
+ switch (state.state) {
1510
+ case "opening":
1511
+ case "closing":
1512
+ return "mdi:gate-arrow-right";
1513
+ case "closed":
1514
+ return "mdi:gate";
1515
+ default:
1516
+ return "mdi:gate-open";
1517
+ }
1337
1518
  case "door":
1338
1519
  return open ? "mdi:door-open" : "mdi:door-closed";
1520
+ case "damper":
1521
+ return open ? "mdi:circle" : "mdi:circle-slice-8";
1339
1522
  case "shutter":
1340
- return open ? "mdi:window-shutter-open" : "mdi:window-shutter";
1523
+ switch (state.state) {
1524
+ case "opening":
1525
+ return "mdi:arrow-up-box";
1526
+ case "closing":
1527
+ return "mdi:arrow-down-box";
1528
+ case "closed":
1529
+ return "mdi:window-shutter";
1530
+ default:
1531
+ return "mdi:window-shutter-open";
1532
+ }
1533
+ case "curtain":
1534
+ switch (state.state) {
1535
+ case "opening":
1536
+ return "mdi:arrow-split-vertical";
1537
+ case "closing":
1538
+ return "mdi:arrow-collapse-horizontal";
1539
+ case "closed":
1540
+ return "mdi:curtains-closed";
1541
+ default:
1542
+ return "mdi:curtains";
1543
+ }
1341
1544
  case "blind":
1342
- return open ? "mdi:blinds-open" : "mdi:blinds";
1545
+ case "shade":
1546
+ switch (state.state) {
1547
+ case "opening":
1548
+ return "mdi:arrow-up-box";
1549
+ case "closing":
1550
+ return "mdi:arrow-down-box";
1551
+ case "closed":
1552
+ return "mdi:blinds";
1553
+ default:
1554
+ return "mdi:blinds-open";
1555
+ }
1343
1556
  case "window":
1344
- return open ? "mdi:window-open" : "mdi:window-closed";
1557
+ switch (state.state) {
1558
+ case "opening":
1559
+ return "mdi:arrow-up-box";
1560
+ case "closing":
1561
+ return "mdi:arrow-down-box";
1562
+ case "closed":
1563
+ return "mdi:window-closed";
1564
+ default:
1565
+ return "mdi:window-open";
1566
+ }
1345
1567
  default:
1346
- return domainIcon("cover", state.state);
1568
+ switch (state.state) {
1569
+ case "opening":
1570
+ return "mdi:arrow-up-box";
1571
+ case "closing":
1572
+ return "mdi:arrow-down-box";
1573
+ case "closed":
1574
+ return "mdi:window-closed";
1575
+ default:
1576
+ return "mdi:window-open";
1577
+ }
1347
1578
  }
1348
1579
  };
1349
1580
 
@@ -1404,7 +1635,7 @@ const inputDateTimeIcon = (state) => {
1404
1635
  };
1405
1636
 
1406
1637
  const domainIcons = {
1407
- binary_sensor: binarySensorIcon,
1638
+ binary_sensor: (stateObj) => binarySensorIcon(stateObj.state, stateObj),
1408
1639
  cover: coverIcon,
1409
1640
  sensor: sensorIcon,
1410
1641
  input_datetime: inputDateTimeIcon,
@@ -1480,6 +1711,7 @@ exports.createThing = createThing;
1480
1711
  exports.debounce = debounce;
1481
1712
  exports.deepEqual = deepEqual;
1482
1713
  exports.domainIcon = domainIcon;
1714
+ exports.durationToSeconds = durationToSeconds;
1483
1715
  exports.evaluateFilter = evaluateFilter;
1484
1716
  exports.fireEvent = fireEvent;
1485
1717
  exports.fixedIcons = fixedIcons;
@@ -1511,9 +1743,13 @@ exports.navigate = navigate;
1511
1743
  exports.numberFormatToLocale = numberFormatToLocale;
1512
1744
  exports.relativeTime = relativeTime;
1513
1745
  exports.round = round;
1746
+ exports.scrollToTarget = scrollToTarget;
1747
+ exports.secondsToDuration = secondsToDuration;
1514
1748
  exports.stateIcon = stateIcon;
1515
1749
  exports.timerTimeRemaining = timerTimeRemaining;
1516
1750
  exports.toggleEntity = toggleEntity;
1751
+ exports.turnOffEntity = turnOffEntity;
1752
+ exports.turnOnEntity = turnOnEntity;
1517
1753
  exports.turnOnOffEntities = turnOnOffEntities;
1518
1754
  exports.turnOnOffEntity = turnOnOffEntity;
1519
1755
  //# sourceMappingURL=index.js.map