Skip to content

Commit a5cd0aa

Browse files
authored
Merge pull request #139 from esphome/dev
2 parents 174120a + f90efd5 commit a5cd0aa

File tree

6 files changed

+149
-50
lines changed

6 files changed

+149
-50
lines changed

packages/v2/src/esp-entity-table.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ export class EntityTable extends LitElement implements RestAction {
103103
restAction(entity: entityConfig, action: string) {
104104
fetch(`${basePath}/${entity.domain}/${entity.id}/${action}`, {
105105
method: "POST",
106-
body: "true",
106+
headers:{
107+
'Content-Type': 'application/x-www-form-urlencoded'
108+
},
107109
}).then((r) => {
108110
console.log(r);
109111
});
@@ -549,4 +551,10 @@ class ActionRenderer {
549551
${target_temp_slider} ${modes}
550552
`;
551553
}
554+
render_valve() {
555+
if (!this.entity) return;
556+
return html`${this._actionButton(this.entity, "| |", "open")}
557+
${this._actionButton(this.entity, "☐", "stop")}
558+
${this._actionButton(this.entity, "|-|", "close")}`;
559+
}
552560
}

packages/v2/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export default defineConfig({
8989
"/text": proxy_target,
9090
"/date": proxy_target,
9191
"/time": proxy_target,
92+
"/valve": proxy_target,
9293
},
9394
},
9495
});

packages/v3/src/css/button.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,14 @@ export default css`
2727
background-color: rgba(127, 127, 127, 0.2);
2828
transition-duration: 1s;
2929
}
30+
31+
.abuttonIsState {
32+
background-color: #28a745;
33+
color: white;
34+
border: none;
35+
padding: 10px 20px;
36+
font-size: 16px;
37+
border-radius: 4px;
38+
transition: background-color 0.3s ease;
39+
}
3040
`;

packages/v3/src/esp-entity-chart.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,27 +52,50 @@ export class ChartElement extends LitElement {
5252
],
5353
},
5454
options: {
55+
animation: { duration: 0 },
5556
plugins: { legend: { display: false } },
5657
scales: { x: { display: false }, y: { position: "right" } },
5758
responsive: true,
5859
maintainAspectRatio: false,
5960
},
6061
});
62+
this.updateStylesIfExpanded();
63+
}
64+
// since the :host-context(.expanded) selector is not supported in Safari and Firefox we need to use JS to apply styles
65+
// whether the parent element is expanded or not
66+
updateStylesIfExpanded() {
67+
const parentElement = this.parentElement;
68+
const expandedClass = "expanded";
69+
70+
const applyStyles = () => {
71+
if (parentElement && parentElement.classList.contains(expandedClass)) {
72+
this.style.height = "240px";
73+
this.style.opacity = "0.5";
74+
} else {
75+
this.style.height = "42px";
76+
this.style.opacity = "0.1";
77+
}
78+
};
79+
80+
applyStyles();
81+
82+
// Observe class changes
83+
const observer = new MutationObserver(applyStyles);
84+
if (parentElement)
85+
observer.observe(parentElement, {
86+
attributes: true,
87+
attributeFilter: ["class"],
88+
});
6189
}
6290

6391
static get styles() {
6492
return css`
6593
:host {
6694
position: absolute;
6795
left: 24px;
68-
height: 42px !important;
96+
height: 42px;
6997
width: calc(100% - 42px);
7098
z-index: -100;
71-
opacity: 0.1;
72-
}
73-
:host-context(.expanded) {
74-
height: 240px !important;
75-
opacity: 0.5;
7699
}
77100
`;
78101
}

packages/v3/src/esp-entity-table.ts

Lines changed: 99 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -84,47 +84,17 @@ export class EntityTable extends LitElement implements RestAction {
8484
"Diagnostic",
8585
];
8686

87+
private _unknown_state_events: {[key: string]: number} = {};
88+
8789
connectedCallback() {
8890
super.connectedCallback();
89-
window.source?.addEventListener("state", (e: Event) => {
91+
92+
window.source?.addEventListener('state', (e: Event) => {
9093
const messageEvent = e as MessageEvent;
9194
const data = JSON.parse(messageEvent.data);
9295
let idx = this.entities.findIndex((x) => x.unique_id === data.id);
93-
if (idx === -1 && data.id) {
94-
// Dynamically add discovered..
95-
let parts = data.id.split("-");
96-
let entity = {
97-
...data,
98-
domain: parts[0],
99-
unique_id: data.id,
100-
id: parts.slice(1).join("-"),
101-
entity_category: data.entity_category,
102-
sorting_group: data.sorting_group ?? (EntityTable.ENTITY_CATEGORIES[parseInt(data.entity_category)] || EntityTable.ENTITY_UNDEFINED),
103-
value_numeric_history: [data.value],
104-
} as entityConfig;
105-
entity.has_action = this.hasAction(entity);
106-
if (entity.has_action) {
107-
this.has_controls = true;
108-
}
109-
this.entities.push(entity);
110-
this.entities.sort((a, b) => {
111-
const sortA = a.sorting_weight ?? a.name;
112-
const sortB = b.sorting_weight ?? b.name;
113-
return a.sorting_group < b.sorting_group
114-
? -1
115-
: a.sorting_group === b.sorting_group
116-
? sortA === sortB
117-
? a.name.toLowerCase() < b.name.toLowerCase()
118-
? -1
119-
: 1
120-
: sortA < sortB
121-
? -1
122-
: 1
123-
: 1
124-
});
125-
this.requestUpdate();
126-
} else {
127-
if (typeof data.value === "number") {
96+
if (idx != -1 && data.id) {
97+
if (typeof data.value === 'number') {
12898
let history = [...this.entities[idx].value_numeric_history];
12999
history.push(data.value);
130100
this.entities[idx].value_numeric_history = history.splice(-50);
@@ -135,6 +105,44 @@ export class EntityTable extends LitElement implements RestAction {
135105
delete data.unique_id;
136106
Object.assign(this.entities[idx], data);
137107
this.requestUpdate();
108+
} else {
109+
// is it a `detail_all` event already?
110+
if (data?.name) {
111+
this.addEntity(data);
112+
} else {
113+
if (this._unknown_state_events[data.id]) {
114+
this._unknown_state_events[data.id]++;
115+
} else {
116+
this._unknown_state_events[data.id] = 1;
117+
}
118+
// ignore the first few events, maybe the esp will send a detail_all
119+
// event soon
120+
if (this._unknown_state_events[data.id] < 1) {
121+
return;
122+
}
123+
124+
let parts = data.id.split('-');
125+
let domain = parts[0];
126+
let id = parts.slice(1).join('-');
127+
128+
fetch(`${this._basePath}/${domain}/${id}?detail=all`, {
129+
method: 'GET',
130+
})
131+
.then((r) => {
132+
console.log(r);
133+
if (!r.ok) {
134+
throw new Error(`HTTP error! Status: ${r.status}`);
135+
}
136+
return r.json();
137+
})
138+
.then((data) => {
139+
console.log(data);
140+
this.addEntity(data);
141+
})
142+
.catch((error) => {
143+
console.error('Fetch error:', error);
144+
});
145+
}
138146
}
139147
});
140148

@@ -166,6 +174,45 @@ export class EntityTable extends LitElement implements RestAction {
166174
});
167175
}
168176

177+
addEntity(data: any) {
178+
let idx = this.entities.findIndex((x) => x.unique_id === data.id);
179+
if (idx === -1 && data.id) {
180+
// Dynamically add discovered..
181+
let parts = data.id.split("-");
182+
let entity = {
183+
...data,
184+
domain: parts[0],
185+
unique_id: data.id,
186+
id: parts.slice(1).join("-"),
187+
entity_category: data.entity_category,
188+
sorting_group: data.sorting_group ?? (EntityTable.ENTITY_CATEGORIES[parseInt(data.entity_category)] || EntityTable.ENTITY_UNDEFINED),
189+
value_numeric_history: [data.value],
190+
} as entityConfig;
191+
entity.has_action = this.hasAction(entity);
192+
if (entity.has_action) {
193+
this.has_controls = true;
194+
}
195+
this.entities.push(entity);
196+
this.entities.sort((a, b) => {
197+
const sortA = a.sorting_weight ?? a.name;
198+
const sortB = b.sorting_weight ?? b.name;
199+
return a.sorting_group < b.sorting_group
200+
? -1
201+
: a.sorting_group === b.sorting_group
202+
? sortA === sortB
203+
? a.name.toLowerCase() < b.name.toLowerCase()
204+
? -1
205+
: 1
206+
: sortA < sortB
207+
? -1
208+
: 1
209+
: 1
210+
});
211+
this.requestUpdate();
212+
}
213+
214+
}
215+
169216
hasAction(entity: entityConfig): boolean {
170217
return `render_${entity.domain}` in this._actionRenderer;
171218
}
@@ -181,7 +228,9 @@ export class EntityTable extends LitElement implements RestAction {
181228
restAction(entity: entityConfig, action: string) {
182229
fetch(`${this._basePath}/${entity.domain}/${entity.id}/${action}`, {
183230
method: "POST",
184-
body: "true",
231+
headers:{
232+
'Content-Type': 'application/x-www-form-urlencoded'
233+
},
185234
}).then((r) => {
186235
console.log(r);
187236
});
@@ -323,11 +372,12 @@ class ActionRenderer {
323372
return this[method]();
324373
}
325374

326-
private _actionButton(entity: entityConfig, label: string, action: string) {
375+
private _actionButton(entity: entityConfig, label: string, action: string, isCurrentState: boolean = false) {
327376
if (!entity) return;
328377
let a = action || label.toLowerCase();
329378
return html`<button
330-
class="abutton"
379+
class="${isCurrentState ? 'abuttonIsState' : 'abutton'}"
380+
?disabled=${isCurrentState}
331381
@click=${() => this.actioner?.restAction(entity, a)}
332382
>
333383
${label}
@@ -607,16 +657,16 @@ class ActionRenderer {
607657

608658
render_lock() {
609659
if (!this.entity) return;
610-
return html`${this._actionButton(this.entity, "🔐", "lock")}
611-
${this._actionButton(this.entity, "🔓", "unlock")}
660+
return html`${this._actionButton(this.entity, "🔐", "lock", this.entity.state === "LOCKED")}
661+
${this._actionButton(this.entity, "🔓", "unlock", this.entity.state === "UNLOCKED")}
612662
${this._actionButton(this.entity, "↑", "open")} `;
613663
}
614664

615665
render_cover() {
616666
if (!this.entity) return;
617-
return html`${this._actionButton(this.entity, "↑", "open")}
667+
return html`${this._actionButton(this.entity, "↑", "open", this.entity.state === "OPEN")}
618668
${this._actionButton(this.entity, "☐", "stop")}
619-
${this._actionButton(this.entity, "↓", "close")}`;
669+
${this._actionButton(this.entity, "↓", "close", this.entity.state === "CLOSED")}`;
620670
}
621671

622672
render_button() {
@@ -735,4 +785,10 @@ class ActionRenderer {
735785
</div>
736786
`;
737787
}
788+
render_valve() {
789+
if (!this.entity) return;
790+
return html`${this._actionButton(this.entity, "OPEN", "open", this.entity.state === "OPEN")}
791+
${this._actionButton(this.entity, "☐", "stop")}
792+
${this._actionButton(this.entity, "CLOSE", "close", this.entity.state === "CLOSED")}`;
793+
}
738794
}

packages/v3/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export default defineConfig({
9090
"/text": proxy_target,
9191
"/date": proxy_target,
9292
"/time": proxy_target,
93+
"/valve": proxy_target,
9394
},
9495
},
9596
});

0 commit comments

Comments
 (0)