ctucx.git: smartie-pwa

[js] smarthome web-gui

commit 76de8ad99924bbbe7735f47c76f2e17283b89640
parent 097408bccd45cd920dc8c3dd65eb67796e6a5ff4
Author: Milan Pässler <me@pbb.lc>
Date: Sun, 9 Jun 2019 19:00:50 +0200

power meter history
2 files changed, 278 insertions(+), 0 deletions(-)
A
src/connection-status.js
|
68
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
src/power-meter-history.js
|
210
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/src/connection-status.js b/src/connection-status.js
@@ -0,0 +1,68 @@
+"use strict";
+
+import { LitElement, html, css } from "lit-element";
+import { state } from "./state.js";
+
+import "@authentic/mwc-icon";
+
+const pad = (string, amount) => Array(amount).join('0').substr(0, amount - String(string).length) + string;
+
+const formatDate = (date) => (
+	'' +
+	date.getFullYear() +
+	'/' +
+	pad(date.getMonth() + 1, 2) +
+	'/' +
+	pad(date.getDate(), 2) +
+	' ' +
+	date.getHours() +
+	':' +
+	pad(date.getMinutes(), 2)
+);
+
+class ConnectionStatus extends LitElement {
+	constructor() {
+		super(...arguments);
+		state.subscribe(this.requestUpdate.bind(this));
+	}
+
+	render() {
+		return html`
+			${state.connected ? html`
+				<p><mwc-icon>cloud_queue</mwc-icon> Connected</p>
+			` : html`
+				<p><mwc-icon>cloud_off</mwc-icon> Disconnected</p>
+				<p class="lastupdate">last updated ${formatDate(state.data.lastUpdated)}</p>
+			`}
+		`;
+	}
+
+	static get styles() {
+		return css`
+			:host {
+				display: flex;
+				flex-direction: column;
+				--mdc-theme-on-primary: white;
+				--mdc-theme-primary: #43a047;
+				--mdc-theme-on-secondary: white;
+				--mdc-theme-secondary: #616161;
+				justify-content: center;
+				align-items: center;
+			}
+			mwc-icon {
+				padding-right: .5em;
+			}
+			p {
+				display: flex;
+				flex-direction: row;
+				justify-content: center;
+				align-items: center;
+			}
+			p.lastupdate {
+				font-size: 1em;
+			}
+		`;
+	}
+}
+
+customElements.define("smarthome-connection-status", ConnectionStatus);
diff --git a/src/power-meter-history.js b/src/power-meter-history.js
@@ -0,0 +1,210 @@
+"use strict";
+
+import { LitElement, html, css } from "lit-element";
+import { state } from "./state.js";
+
+import "@authentic/mwc-circular-progress";
+import "@authentic/mwc-icon";
+import "@authentic/mwc-select";
+import "@authentic/mwc-menu";
+import "@authentic/mwc-list";
+import "./card.js";
+import "./spinner.js";
+
+const round = (num, places) => {
+	const factor = Math.pow(10, places);
+	return Math.round(num * factor) / factor;
+};
+
+const pad = (string, amount) => Array(amount).join('0').substr(0, amount - String(string).length) + string;
+
+const archive = {};
+const loading = {};
+
+const years = [ 2019 ];
+const months = [...Array(12).keys()].map(n => n+1);
+
+class PowerMeterHistory extends LitElement {
+	constructor() {
+		super(...arguments);
+		this.type = "day";
+		const now = new Date();
+		this.year = now.getFullYear();
+		this.month = pad(now.getMonth() + 1, 2);
+		state.subscribe(this.loadData.bind(this));
+	}
+
+	async loadData(e) {
+		const d = {};
+
+		this.sel = this.year;
+		if (this.type === "day") this.sel += "_" + this.month;
+
+		await Promise.all(Object.entries(state.data.powermeter).map(async ([id, p]) => {
+			const data = await this.load(`${this.type}/${id}_${this.sel}`);
+			if(!data) return;
+			for (let day of Object.keys(data)) {
+				if (!d[day]) d[day] = {};
+				d[day][id] = data[day].imported;
+			}
+		}));
+		this.data = d;
+
+		this.requestUpdate();
+	}
+
+	handleSelected(field) {
+		return (evt) => {
+			this[field] = evt.detail.item.value;
+			this.loadData();
+		};
+	}
+
+	async load(file) {
+		console.log("loading " + file);
+		if (!archive[file] && !loading[file]) {
+			loading[file] = true;
+			return fetch(`archive/${file}.json`)
+				.then(resp => resp.json())
+				.then(data => {
+					archive[file] = data;
+					loading[file] = undefined;
+					return data;
+				}).catch(e => console.warn(e));
+		} else {
+			return archive[file];
+		}
+	}
+
+	render() {
+		const now = new Date();
+		return html`
+			<div id="selection" class="${state.data.powermeter && state.connected ? "" : "hidden"}">
+				<mwc-select label="Type">
+					<mwc-menu slot="menu" class="type-sel" @MDCMenu:selected=${this.handleSelected("type")}>
+						<mwc-list>
+							<mwc-list-item value="day">Days</mwc-list-item>
+							<mwc-list-item value="week">Weeks</mwc-list-item>
+							<mwc-list-item value="month">Months</mwc-list-item>
+						</mwc-list>
+					</mwc-menu>
+				</mwc-select>
+				<mwc-select label="Year">
+					<mwc-menu slot="menu" class="year-sel" @MDCMenu:selected=${this.handleSelected("year")}>
+						<mwc-list>
+							${years.map(m => html`<mwc-list-item value="${m}">${m}</mwc-list-item>`)}
+						</mwc-list>
+					</mwc-menu>
+				</mwc-select>
+				<mwc-select label="Month" class="${this.type == "day" ? "" : "hidden"}">
+					<mwc-menu slot="menu" class="month-sel" @MDCMenu:selected=${this.handleSelected("month")}>
+						<mwc-list>
+							${months.map(m => html`<mwc-list-item value="${pad(m, 2)}">${m}</mwc-list-item>`)}
+						</mwc-list>
+					</mwc-menu>
+				</mwc-select>
+			</div>
+			${state.data.powermeter && state.connected ? html`
+				<smarthome-card>
+					<div class="table">
+						<div class="table-row table-head">
+							<div class="table-column">Name</div>
+							${Object.keys(this.data).map((day) => html`
+								<div class="table-column">${day}</table-column>
+							`)}
+						</div>
+						${Object.entries(state.data.powermeter).map(([id, d]) => html`
+							<div class="table-row">
+								<div class="table-column">${d.name}</div>
+								${Object.entries(this.data).map(([day, d]) => html`
+									<div class="table-column">${d[id] ? round(d[id], 2) + " kWh" : "-"}</div>
+								`)}
+							</div>
+						`)}
+					</div>
+			</smarthome-card>
+			` : html``}
+		`;
+	}
+
+	static get styles() {
+		return css`
+			:host {
+				display: flex;
+				flex-direction: column;
+				--mdc-theme-on-primary: white;
+				--mdc-theme-primary: #43a047;
+				--mdc-theme-on-secondary: white;
+				--mdc-theme-secondary: #616161;
+			}
+			#connection-status mwc-icon {
+				padding-right: .5em;
+			}
+			#connection-status {
+				display: flex;
+				flex-direction: column;
+				justify-content: center;
+				align-items: center;
+			}
+			#connection-status>p {
+				display: flex;
+				flex-direction: row;
+				justify-content: center;
+				align-items: center;
+			}
+			#connection-status>p.lastupdate {
+				font-size: 1em;
+			}
+			.table {
+				display: flex;
+				flex-direction: row;
+			}
+			.table-row {
+				display: flex;
+				flex-direction: column;
+				width: 100%;
+			}
+			.table-column {
+				padding: 1em;
+				height: 1em;
+				display: flex;
+				flex-direction: column;
+				justify-content: center;
+			}
+
+			.table-column {
+				background-color: #ddd;
+			}
+			.table-column:nth-child(2n) {
+				background-color: #fff;
+			}
+			@media (min-width: 600px) {
+				#connection-status {
+					margin-top: 20px;
+				}
+			}
+
+			.hidden {
+				display: none;
+			}
+			#selection {
+				display: flex;
+				flex-wrap: wrap;
+				flex-direction: row;
+				width: 450px;
+				max-width: 100%;
+				margin-left: auto;
+				margin-right: auto;
+				margin-top: 40px;
+				margin-bottom: -20px;
+				/*justify-content: space-between;*/
+			}
+			mwc-select {
+				width: 100px;
+				margin-right: 20px;
+			}
+		`;
+	}
+}
+
+customElements.define("smarthome-power-meter-history", PowerMeterHistory);