Skip to content

Commit 417e42e

Browse files
committed
Add Card visual editor
1 parent 208ece7 commit 417e42e

File tree

2 files changed

+262
-23
lines changed

2 files changed

+262
-23
lines changed
+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
const fireEvent = (node, type, detail, options) => {
2+
options = options || {};
3+
detail = detail === null || detail === undefined ? {} : detail;
4+
const event = new Event(type, {
5+
bubbles: options.bubbles === undefined ? true : options.bubbles,
6+
cancelable: Boolean(options.cancelable),
7+
composed: options.composed === undefined ? true : options.composed
8+
});
9+
event.detail = detail;
10+
node.dispatchEvent(event);
11+
return event;
12+
};
13+
14+
const locale = {
15+
da: {
16+
optionName: 'Navn (valgfrit)',
17+
optionEntity: 'Enhed (påkrævet)',
18+
optionShowCurrent: 'Vis nuværende status',
19+
optionShowDetails: 'Vis detaljer',
20+
optionShowGraph: 'Vis graf',
21+
optionShowInfo: 'Vis information'
22+
},
23+
de: {
24+
optionName: 'Name (optional)',
25+
optionEntity: 'Entity (Erforderlich)',
26+
optionShowCurrent: 'Aktuellen Status anzeigen',
27+
optionShowDetails: 'Details anzeigen',
28+
optionShowGraph: 'Grafik anzeigen',
29+
optionShowInfo: 'Informationen anzeigen'
30+
},
31+
en: {
32+
optionName: 'Name (Optional)',
33+
optionEntity: 'Entity (Required)',
34+
optionShowCurrent: 'Show Current State',
35+
optionShowDetails: 'Show Details',
36+
optionShowGraph: 'Show Graph',
37+
optionShowInfo: 'Show Info'
38+
},
39+
es: {
40+
optionName: 'Nombre (Opcional)',
41+
optionEntity: 'Entidad (Necesario)',
42+
optionShowCurrent: 'Mostrar Estado Actual',
43+
optionShowDetails: 'Mostrar Detalles',
44+
optionShowGraph: 'Mostrar Gráfico',
45+
optionShowInfo: 'Mostrar Información'
46+
},
47+
fr: {
48+
optionName: 'Nom (Facultatif)',
49+
optionEntity: 'Entity (Required)',
50+
optionShowCurrent: "Afficher l'état actuel",
51+
optionShowDetails: 'Afficher les détails',
52+
optionShowGraph: 'Afficher le graphique',
53+
optionShowInfo: 'Afficher les informations'
54+
},
55+
nl: {
56+
optionName: 'Naam (optioneel)',
57+
optionEntity: 'Entiteit (vereist)',
58+
optionShowCurrent: 'Toon huidige status',
59+
optionShowDetails: 'Details weergeven',
60+
optionShowGraph: 'Show Graph',
61+
optionShowInfo: 'Informatie weergeven'
62+
},
63+
ru: {
64+
optionName: 'Имя (необязательно)',
65+
optionEntity: 'Entity (обязательно)',
66+
optionShowCurrent: 'Показать текущий статус',
67+
optionShowDetails: 'Показать детали',
68+
optionShowGraph: 'Показать график',
69+
optionShowInfo: 'Показать информацию'
70+
},
71+
sv: {
72+
optionName: 'Namn (valfritt)',
73+
optionEntity: 'Enhet (obligatoriskt)',
74+
optionShowCurrent: 'Visa aktuell status',
75+
optionShowDetails: 'Visa detaljer',
76+
optionShowGraph: 'Visa graf',
77+
optionShowInfo: 'Visa information'
78+
}
79+
};
80+
81+
const LitElement = Object.getPrototypeOf(customElements.get('hui-view'));
82+
const html = LitElement.prototype.html;
83+
const css = LitElement.prototype.css;
84+
85+
export class PVPCHourlyPricingCardEditor extends LitElement {
86+
setConfig(config) {
87+
this._config = { ...config };
88+
}
89+
90+
static get properties() {
91+
return { hass: {}, _config: {} };
92+
}
93+
94+
get _entity() {
95+
return this._config.entity || '';
96+
}
97+
98+
get _name() {
99+
return this._config.name || '';
100+
}
101+
102+
get _current() {
103+
return this._config.current !== false;
104+
}
105+
106+
get _details() {
107+
return this._config.details !== false;
108+
}
109+
110+
get _graph() {
111+
return this._config.graph !== false;
112+
}
113+
114+
get _info() {
115+
return this._config.info !== false;
116+
}
117+
118+
render() {
119+
if (!this.hass) {
120+
return html``;
121+
}
122+
123+
this.lang = this.hass.selectedLanguage || this.hass.language;
124+
125+
const entities = Object.keys(this.hass.states).filter((eid) =>
126+
Object.keys(this.hass.states[eid].attributes).some((aid) => aid == 'min_price_at')
127+
);
128+
129+
return html`
130+
<div class="card-config">
131+
<div class="side-by-side">
132+
<paper-input
133+
label="${this.ll('optionName')}"
134+
.value="${this._name}"
135+
.configValue="${'name'}"
136+
@value-changed="${this._valueChanged}"
137+
>
138+
</paper-input>
139+
</div>
140+
<div class="side-by-side">
141+
<paper-dropdown-menu
142+
label="${this.ll('optionEntity')}"
143+
@value-changed="${this._valueChanged}"
144+
.configValue="${'entity'}"
145+
>
146+
<paper-listbox slot="dropdown-content" .selected="${entities.indexOf(this._entity)}">
147+
${entities.map((entity) => {
148+
return html` <paper-item>${entity}</paper-item> `;
149+
})}
150+
</paper-listbox>
151+
</paper-dropdown-menu>
152+
</div>
153+
<div class="side-by-side">
154+
<ha-switch .checked=${this._current} .configValue="${'current'}" @change="${this._valueChanged}"
155+
>${this.ll('optionShowCurrent')}</ha-switch
156+
>
157+
<ha-switch .checked=${this._details} .configValue="${'details'}" @change="${this._valueChanged}"
158+
>${this.ll('optionShowDetails')}</ha-switch
159+
>
160+
</div>
161+
<div class="side-by-side">
162+
<ha-switch .checked=${this._graph} .configValue="${'graph'}" @change="${this._valueChanged}"
163+
>${this.ll('optionShowGraph')}</ha-switch
164+
>
165+
<ha-switch .checked=${this._info} .configValue="${'info'}" @change="${this._valueChanged}"
166+
>${this.ll('optionShowInfo')}</ha-switch
167+
>
168+
</div>
169+
</div>
170+
`;
171+
}
172+
173+
_valueChanged(ev) {
174+
if (!this._config || !this.hass) {
175+
return;
176+
}
177+
178+
const target = ev.target;
179+
if (this[`_${target.configValue}`] === target.value) {
180+
return;
181+
}
182+
183+
if (target.configValue) {
184+
if (target.value === '') {
185+
delete this._config[target.configValue];
186+
} else {
187+
this._config = {
188+
...this._config,
189+
[target.configValue]: target.checked !== undefined ? target.checked : target.value
190+
};
191+
}
192+
}
193+
194+
fireEvent(this, 'config-changed', { config: this._config });
195+
}
196+
197+
ll(str) {
198+
if (locale[this.lang] === undefined) return locale.en[str];
199+
return locale[this.lang][str];
200+
}
201+
202+
static get styles() {
203+
return css`
204+
ha-switch {
205+
padding-top: 16px;
206+
}
207+
.side-by-side {
208+
display: flex;
209+
}
210+
.side-by-side > * {
211+
flex: 1;
212+
padding-right: 4px;
213+
}
214+
`;
215+
}
216+
}
217+
218+
customElements.define('pvpc-hourly-pricing-card-editor', PVPCHourlyPricingCardEditor);
219+
220+
window.customCards = window.customCards || [];
221+
window.customCards.push({
222+
type: 'pvpc-hourly-pricing-card',
223+
name: 'PVPC Hourly Pricing',
224+
preview: true,
225+
description: 'The PVPC Hourly Pricing card allows you to display propertly the PVPC Hourly Pricing entity.'
226+
});

dist/pvpc-hourly-pricing-card.js

+36-23
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const locale = {
7878
};
7979

8080
const tariffPeriodIcons = {
81+
error: 'mdi:alert-circle',
8182
peak: 'mdi:weather-sunny',
8283
valley: 'mdi:weather-night',
8384
'super-valley': 'mdi:car-electric'
@@ -114,8 +115,16 @@ class PVPCHourlyPricingCard extends LitElement {
114115
return { _config: {}, hass: {} };
115116
}
116117

117-
static getStubConfig() {
118-
return {};
118+
static async getConfigElement() {
119+
await import('./pvpc-hourly-pricing-card-editor.js');
120+
return document.createElement('pvpc-hourly-pricing-card-editor');
121+
}
122+
123+
static getStubConfig(hass, entities, entitiesFallback) {
124+
const entity = Object.keys(hass.states).find((eid) =>
125+
Object.keys(hass.states[eid].attributes).some((aid) => aid == 'min_price_at')
126+
);
127+
return { entity: entity };
119128
}
120129

121130
setConfig(config) {
@@ -154,10 +163,7 @@ class PVPCHourlyPricingCard extends LitElement {
154163

155164
this.setPVPCHourlyPricingObj();
156165
this.numberElements = 0;
157-
this.lang =
158-
this._config.language === undefined || this._config.language === 'hacs'
159-
? this.hass.selectedLanguage || this.hass.language
160-
: this._config.language;
166+
this.lang = this.hass.selectedLanguage || this.hass.language;
161167

162168
if (!this.pvpcHourlyPricingObj) {
163169
return html`
@@ -539,25 +545,32 @@ class PVPCHourlyPricingCard extends LitElement {
539545
getTariffPeriodIcon(tariff) {
540546
let icon;
541547

542-
if (tariff == 'discrimination') {
543-
const utcHours = new Date().getUTCHours();
544-
if (utcHours >= 21 || utcHours < 11) {
545-
icon = 'valley';
546-
} else {
547-
icon = 'peak';
548-
}
549-
} else if (tariff == 'electric_car') {
550-
const hours = new Date().getHours();
551-
if (hours >= 13 && hours < 23) {
552-
icon = 'peak';
553-
} else if (hours >= 1 && hours < 3) {
554-
icon = 'valley';
555-
} else {
556-
icon = 'super-valley';
557-
}
548+
switch (tariff) {
549+
case 'normal':
550+
break;
551+
case 'discrimination':
552+
const utcHours = new Date().getUTCHours();
553+
if (utcHours >= 21 || utcHours < 11) {
554+
icon = 'valley';
555+
} else {
556+
icon = 'peak';
557+
}
558+
break;
559+
case 'electric_car':
560+
const hours = new Date().getHours();
561+
if (hours >= 13 && hours < 23) {
562+
icon = 'peak';
563+
} else if (hours >= 1 && hours < 3) {
564+
icon = 'valley';
565+
} else {
566+
icon = 'super-valley';
567+
}
568+
break;
569+
default:
570+
icon = 'error';
558571
}
559572

560-
return tariffPeriodIcons[icon];
573+
return icon ? tariffPeriodIcons[icon] : '';
561574
}
562575

563576
getDateString(datetime) {

0 commit comments

Comments
 (0)