Skip to content

Commit 331ed6a

Browse files
committed
chore: remove dependency with custom-card-helpers
1 parent 9ea1b51 commit 331ed6a

13 files changed

+170
-129
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@
6868
"dependencies": {
6969
"@ctrl/tinycolor": "^3.1.6",
7070
"@material/mwc-ripple": "^0.19.1",
71-
"custom-card-helpers": "^1.9.0",
7271
"fast-copy": "^2.1.0",
7372
"home-assistant-js-websocket": "^8.2.0",
7473
"lit": "^2.7.6",

src/action-handler.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { noChange } from 'lit-html';
22
// import '@material/mwc-ripple';
33
// tslint:disable-next-line
44
import { Ripple } from '@material/mwc-ripple';
5-
import { myFireEvent } from './my-fire-event';
5+
import { fireEvent } from './common/fire-event';
66
import { deepEqual } from './deep-equal';
77
import { AttributePart, Directive, DirectiveParameters, directive } from 'lit-html/directive';
88

@@ -154,7 +154,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
154154
if (options.repeat && !this.isRepeating) {
155155
this.isRepeating = true;
156156
this.repeatTimeout = setInterval(() => {
157-
myFireEvent(element, 'action', { action: 'hold' });
157+
fireEvent(element, 'action', { action: 'hold' });
158158
}, options.repeat);
159159
}
160160
}, this.holdTime);
@@ -186,21 +186,21 @@ class ActionHandler extends HTMLElement implements ActionHandler {
186186
}
187187
if (options.hasHold && this.held) {
188188
if (!options.repeat) {
189-
myFireEvent(target, 'action', { action: 'hold' });
189+
fireEvent(target, 'action', { action: 'hold' });
190190
}
191191
} else if (options.hasDoubleClick) {
192192
if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) {
193193
this.dblClickTimeout = window.setTimeout(() => {
194194
this.dblClickTimeout = undefined;
195-
myFireEvent(target, 'action', { action: 'tap' });
195+
fireEvent(target, 'action', { action: 'tap' });
196196
}, 250);
197197
} else {
198198
clearTimeout(this.dblClickTimeout);
199199
this.dblClickTimeout = undefined;
200-
myFireEvent(target, 'action', { action: 'double_tap' });
200+
fireEvent(target, 'action', { action: 'double_tap' });
201201
}
202202
} else {
203-
myFireEvent(target, 'action', { action: 'tap' });
203+
fireEvent(target, 'action', { action: 'tap' });
204204
}
205205
};
206206

src/at_least_version.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/button-card.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { styleMap, StyleInfo } from 'lit-html/directives/style-map';
99
import { unsafeHTML } from 'lit-html/directives/unsafe-html';
1010
import { classMap, ClassInfo } from 'lit-html/directives/class-map';
1111
import { HassEntity } from 'home-assistant-js-websocket';
12-
import { timerTimeRemaining, createThing, DOMAINS_TOGGLE, computeStateDomain } from 'custom-card-helpers';
1312
import { LovelaceCard } from './types/lovelace';
1413
import {
1514
ButtonCardConfig,
@@ -36,17 +35,20 @@ import {
3635
getLovelaceCast,
3736
secondsToDuration,
3837
durationToSeconds,
38+
computeStateDomain,
3939
} from './helpers';
40+
import { createThing } from './common/create-thing';
4041
import { styles } from './styles';
41-
import { computeStateDisplay } from './compute_state_display';
42+
import { computeStateDisplay } from './common/compute_state_display';
4243
import copy from 'fast-copy';
4344
import * as pjson from '../package.json';
4445
import { deepEqual } from './deep-equal';
45-
import { stateColorCss } from './state_color';
46-
import { ON } from './common/const';
46+
import { stateColorCss } from './common/state_color';
47+
import { ON, DOMAINS_TOGGLE } from './common/const';
4748
import { handleAction } from './handle-action';
48-
import { myFireEvent } from './my-fire-event';
49+
import { fireEvent } from './common/fire-event';
4950
import { HomeAssistant } from './types/homeassistant';
51+
import { timerTimeRemaining } from './common/timer';
5052

5153
let helpers = (window as any).cardHelpers;
5254
const helperPromise = new Promise<void>(async (resolve) => {
@@ -145,7 +147,7 @@ class ButtonCard extends LitElement {
145147
else {
146148
const element = createThing(config);
147149
helperPromise.then(() => {
148-
myFireEvent(element, 'll-rebuild', {});
150+
fireEvent(element, 'll-rebuild', {});
149151
});
150152
return element;
151153
}

src/compute_state_display.ts renamed to src/common/compute_state_display.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import { HassConfig, HassEntity } from 'home-assistant-js-websocket';
2-
import { LocalizeFunc } from 'custom-card-helpers';
3-
import { computeDomain, isNumericFromAttributes } from './helpers';
4-
import { formatNumber, getNumberFormatOptions, blankBeforePercent } from './common/format_number';
5-
import { EntityRegistryDisplayEntry, FrontendLocaleData, HomeAssistant, TimeZone } from './types/homeassistant';
6-
import { UNIT_TO_MILLISECOND_CONVERT, formatDuration } from './common/duration';
7-
import { formatDateTime } from './common/format_date_time';
8-
import { formatDate } from './common/format_date';
9-
import { formatTime } from './common/format_time';
10-
import { UPDATE_SUPPORT_PROGRESS, updateIsInstallingFromAttributes } from './common/update';
11-
import { supportsFeatureFromAttributes } from './common/supports-features';
2+
import { computeDomain, isNumericFromAttributes } from '../helpers';
3+
import { formatNumber, getNumberFormatOptions, blankBeforePercent } from './format_number';
4+
import {
5+
LocalizeFunc,
6+
EntityRegistryDisplayEntry,
7+
FrontendLocaleData,
8+
HomeAssistant,
9+
TimeZone,
10+
} from '../types/homeassistant';
11+
import { UNIT_TO_MILLISECOND_CONVERT, formatDuration } from './duration';
12+
import { formatDateTime } from './format_date_time';
13+
import { formatDate } from './format_date';
14+
import { formatTime } from './format_time';
15+
import { UPDATE_SUPPORT_PROGRESS, updateIsInstallingFromAttributes } from './update';
16+
import { supportsFeatureFromAttributes } from './supports-features';
1217

1318
const UNAVAILABLE = 'unavailable';
1419
const UNKNOWN = 'unknown';

src/common/const.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
export const UNAVAILABLE = 'unavailable';
22
export const BINARY_STATE_ON = 'on';
33
export const BINARY_STATE_OFF = 'off';
4-
const arrayLiteralIncludes = <T extends readonly unknown[]>(array: T) => (
5-
searchElement: unknown,
6-
fromIndex?: number,
7-
): searchElement is T[number] => array.includes(searchElement as T[number], fromIndex);
4+
const arrayLiteralIncludes =
5+
<T extends readonly unknown[]>(array: T) =>
6+
(searchElement: unknown, fromIndex?: number): searchElement is T[number] =>
7+
array.includes(searchElement as T[number], fromIndex);
88

99
export const UNKNOWN = 'unknown';
1010
export const ON = 'on';
@@ -15,3 +15,5 @@ export const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF] as const;
1515

1616
export const isUnavailableState = arrayLiteralIncludes(UNAVAILABLE_STATES);
1717
export const isOffState = arrayLiteralIncludes(OFF_STATES);
18+
19+
export const DOMAINS_TOGGLE = new Set(['fan', 'input_boolean', 'light', 'switch', 'group', 'automation', 'humidifier']);

src/common/create-thing.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { fireEvent } from './fire-event';
2+
3+
const SPECIAL_TYPES = new Set(['call-service', 'divider', 'section', 'weblink', 'cast', 'select']);
4+
const DOMAIN_TO_ELEMENT_TYPE = {
5+
alert: 'toggle',
6+
automation: 'toggle',
7+
climate: 'climate',
8+
cover: 'cover',
9+
fan: 'toggle',
10+
group: 'group',
11+
input_boolean: 'toggle',
12+
input_number: 'input-number',
13+
input_select: 'input-select',
14+
input_text: 'input-text',
15+
light: 'toggle',
16+
lock: 'lock',
17+
media_player: 'media-player',
18+
remote: 'toggle',
19+
scene: 'scene',
20+
script: 'script',
21+
sensor: 'sensor',
22+
timer: 'timer',
23+
switch: 'toggle',
24+
vacuum: 'toggle',
25+
// Temporary. Once climate is rewritten,
26+
// water heater should get it's own row.
27+
water_heater: 'climate',
28+
input_datetime: 'input-datetime',
29+
};
30+
31+
declare global {
32+
// eslint-disable-next-line
33+
interface HASSDomEvents {
34+
'll-rebuild': Record<string, unknown>;
35+
'll-badge-rebuild': Record<string, unknown>;
36+
}
37+
}
38+
39+
export const createThing = (cardConfig, isRow = false) => {
40+
const _createError = (error, config) => {
41+
return _createThing('hui-error-card', {
42+
type: 'error',
43+
error,
44+
config,
45+
});
46+
};
47+
48+
const _createThing = (tag, config) => {
49+
const element = window.document.createElement(tag);
50+
try {
51+
// Preventing an error-card infinity loop: https://github.com/custom-cards/custom-card-helpers/issues/54
52+
if (!element.setConfig) return;
53+
element.setConfig(config);
54+
} catch (err) {
55+
console.error(tag, err);
56+
return _createError((err as Error).message, config);
57+
}
58+
return element;
59+
};
60+
61+
if (!cardConfig || typeof cardConfig !== 'object' || (!isRow && !cardConfig.type))
62+
return _createError('No type defined', cardConfig);
63+
let tag = cardConfig.type;
64+
if (tag && tag.startsWith('custom:')) {
65+
tag = tag.substr('custom:'.length);
66+
} else if (isRow) {
67+
if (SPECIAL_TYPES.has(tag)) {
68+
tag = `hui-${tag}-row`;
69+
} else {
70+
if (!cardConfig.entity) {
71+
return _createError('Invalid config given.', cardConfig);
72+
}
73+
74+
const domain = cardConfig.entity.split('.', 1)[0];
75+
tag = `hui-${DOMAIN_TO_ELEMENT_TYPE[domain] || 'text'}-entity-row`;
76+
}
77+
} else {
78+
tag = `hui-${tag}-card`;
79+
}
80+
81+
if (customElements.get(tag)) return _createThing(tag, cardConfig);
82+
83+
// If element doesn't exist (yet) create an error
84+
const element = _createError(`Custom element doesn't exist: ${cardConfig.type}.`, cardConfig);
85+
element.style.display = 'None';
86+
const timer = setTimeout(() => {
87+
element.style.display = '';
88+
}, 2000);
89+
// Remove error if element is defined later
90+
customElements.whenDefined(cardConfig.type).then(() => {
91+
clearTimeout(timer);
92+
fireEvent(element, 'll-rebuild', {}, element);
93+
});
94+
95+
return element;
96+
};

src/my-fire-event.ts renamed to src/common/fire-event.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export interface HASSDomEvent<T> extends Event {
5555
* @return {Event} The new event that was fired.
5656
*/
5757
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
58-
export const myFireEvent = <HassEvent extends ValidHassDomEvent>(
58+
export const fireEvent = <HassEvent extends ValidHassDomEvent>(
5959
node: HTMLElement | Window,
6060
type: HassEvent,
6161
detail?: HASSDomEvents[HassEvent],

src/state_color.ts renamed to src/common/state_color.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/** Return an color representing a state. */
22
import { HassEntity } from 'home-assistant-js-websocket';
3-
import { UNAVAILABLE } from './common/const';
4-
import { computeGroupDomain, GroupEntity } from './helpers';
5-
import { computeCssVariable } from './helpers';
6-
import { computeDomain, slugify } from './helpers';
7-
import { batteryStateColorProperty } from './helpers';
8-
import { stateActive } from './helpers';
3+
import { UNAVAILABLE } from './const';
4+
import { computeGroupDomain, GroupEntity } from '../helpers';
5+
import { computeCssVariable } from '../helpers';
6+
import { computeDomain, slugify } from '../helpers';
7+
import { batteryStateColorProperty } from '../helpers';
8+
import { stateActive } from '../helpers';
99

1010
const STATE_COLORED_DOMAIN = new Set([
1111
'alarm_control_panel',

src/common/timer.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { HassEntity } from 'home-assistant-js-websocket';
2+
import { durationToSeconds } from '../helpers';
3+
4+
export const timerTimeRemaining = (stateObj: HassEntity): undefined | number => {
5+
if (!stateObj.attributes.remaining) {
6+
return undefined;
7+
}
8+
let timeRemaining = durationToSeconds(stateObj.attributes.remaining);
9+
10+
if (stateObj.state === 'active') {
11+
const now = new Date().getTime();
12+
const madeActive = new Date(stateObj.last_changed).getTime();
13+
timeRemaining = Math.max(timeRemaining - (now - madeActive) / 1000, 0);
14+
}
15+
16+
return timeRemaining;
17+
};

0 commit comments

Comments
 (0)