ctucx.git: smartie-pwa

[js] smarthome web-gui

commit b858973727982e30aa5c7505ff1930a70bd1435c
parent 40386c4cb34c20d25b85675f7bb66705df26ca05
Author: Milan Pässler <me@pbb.lc>
Date: Thu, 20 Jun 2019 10:50:05 +0200

history loading spinner and more
9 files changed, 62 insertions(+), 49 deletions(-)
M
index.dev.html
|
2
+-
M
src/archive.js
|
25
+++++++++++++++----------
M
src/departures.js
|
2
+-
M
src/layout.js
|
2
+-
M
src/lights.js
|
2
+-
M
src/power-meter/history.js
|
61
++++++++++++++++++++++++++++++++-----------------------------
M
src/power-meter/live.js
|
13
+++++++++----
M
src/spinner.js
|
2
+-
M
src/state.js
|
2
+-
diff --git a/index.dev.html b/index.dev.html
@@ -15,6 +15,6 @@ body {
 	<body>
 		<smarthome-layout></smarthome-layout>
 		<noscript>JavaScript is required to use Smart Home</noscript>
-		<script type="module" src="main.min.js"></script>
+		<script type="module" src="main.js"></script>
 	</body>
 </html>
diff --git a/src/archive.js b/src/archive.js
@@ -2,11 +2,16 @@
 
 class Archive {
 	constructor() {
-		this.archive = {};
+		this.data = JSON.parse(localStorage.getItem("archive") || "{}");;
 		this._loading = {};
+		this._upToDate = {};
 		this._subscribers = [];
 	}
 
+	finishedLoading() {
+		return Object.keys(this._loading).length == 0;
+	}
+
 	subscribe(callback) {
 		this._subscribers.push(callback);
 		this._updateSubscribers();

@@ -19,19 +24,19 @@ class Archive {
 	}
 
 	load(file) {
-		if (!this.archive[file]) {
+		if (!this.data[file] || !this._upToDate[file]) {
 			this._loading[file] = true;
-			const p = fetch(`archive/${file}.json`)
+			fetch(`./archive/${file}.json`)
 				.then(resp => resp.json())
 				.then(data => {
-					this.archive[file] = data;
-					this._loading[file] = undefined;
+					this.data[file] = data;
+					this._upToDate[file] = true;
+					localStorage.setItem("archive", JSON.stringify(this.data));
+					delete this._loading[file];
 					this._updateSubscribers();
-					return data;
-				}).catch(e => console.warn(e));
-			return {};
-		} else {
-			return this.archive[file];
+				}).catch(e => {
+					console.warn(e);
+				});
 		}
 	}
 }
diff --git a/src/departures.js b/src/departures.js
@@ -64,7 +64,7 @@ class Departures extends LitElement {
 					</smarthome-card>
 				`)}
 			` : html`
-				<smarthome-spinner></smarthome-spinner>
+				<smarthome-spinner>Loading...</smarthome-spinner>
 			`}
 		`;
 	}
diff --git a/src/layout.js b/src/layout.js
@@ -22,7 +22,7 @@ const pages = {
 	"#/lights": { name: "Lights", content: html`<smarthome-lights></smarthome-lights>`, icon: "lightbulb" },
 	"#/powermeter": { name: "Power Meter", content: html`<smarthome-power-meter></smarthome-power-meter>`, icon: "power" },
 	"#/departures": { name: "Departures", content: html`<smarthome-departures></smarthome-departures>`, icon: "departure_board" },
-	"#/netdata": { name: "netdata", content: html`<smarthome-spinner></smarthome-spinner> ${netdataRedirect}`, icon: "show_chart" },
+	"#/netdata": { name: "netdata", content: html`<smarthome-spinner>Redirecting...</smarthome-spinner> ${netdataRedirect}`, icon: "show_chart" },
 	"#/settings": { name: "Settings", content: html`<smarthome-settings></smarthome-settings>`, icon: "settings" },
 };
 
diff --git a/src/lights.js b/src/lights.js
@@ -40,7 +40,7 @@ class Lights extends LitElement {
 					`)}
 				</smarthome-card>
 			` : html`
-				<smarthome-spinner></smarthome-spinner>
+				<smarthome-spinner>Trying to connect...</smarthome-spinner>
 			`}
 		`;
 	}
diff --git a/src/power-meter/history.js b/src/power-meter/history.js
@@ -37,41 +37,42 @@ class PowerMeterHistory extends LitElement {
 		const now = new Date();
 		this.year = now.getFullYear();
 		this.month = pad(now.getMonth() + 1, 2);
+		this.data = {};
 
-		state.subscribe(this.loadData.bind(this));
-		archive.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 archive.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();
+		state.subscribe(this.requestUpdate.bind(this));
+		archive.subscribe(this.requestUpdate.bind(this));
 	}
 
 	handleSelected(field) {
 		return (evt) => {
 			this[field] = evt.detail.item.value;
-			this.loadData();
+			this.requestUpdate();
 		};
 	}
 
 	render() {
 		const now = new Date();
+
+		this.data = {};
+		this.sel = this.year;
+		if (this.type === "day") this.sel += "_" + this.month;
+
+		archive.load("metadata");
+		const years = get(archive.data, [ "metadata", "availableData" ], {});
+		const months = get(archive.data, [ "metadata", "availableData", this.year ], []);
+		const counters = get(archive.data, [ "metadata", "meters" ], {});
+
+		for (let [id, p] of Object.entries(counters)) {
+			archive.load(`${this.type}/${id}_${this.sel}`);
+			const data = get(archive.data, [ `${this.type}/${id}_${this.sel}` ], {});
+			for (let day of Object.keys(data)) {
+				if (!this.data[day]) this.data[day] = {};
+				this.data[day][id] = data[day].imported;
+			}
+		}
+
 		return html`
-			<div id="selection" class="${state.data.powermeter && state.connected ? "" : "hidden"}">
+			<div id="selection" class="${Object.keys(years).length > 0 ? "" : "hidden"}">
 				<mwc-select label="Type">
 					<mwc-menu slot="menu" class="type-sel" @MDCMenu:selected=${this.handleSelected("type")}>
 						<mwc-list>

@@ -84,19 +85,19 @@ class PowerMeterHistory extends LitElement {
 				<mwc-select label="Year">
 					<mwc-menu slot="menu" class="year-sel" @MDCMenu:selected=${this.handleSelected("year")}>
 						<mwc-list>
-							${Object.keys(get(archive.load("metadata"), [ "availableData" ], {})).map(m => html`<mwc-list-item value="${m}">${m}</mwc-list-item>`)}
+							${Object.keys(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>
-							${get(archive.load("metadata"), [ "availableData", this.year ], []).map(m => html`<mwc-list-item value="${m}">${Number(m)}</mwc-list-item>`)}
+							${months.map(m => html`<mwc-list-item value="${m}">${Number(m)}</mwc-list-item>`)}
 						</mwc-list>
 					</mwc-menu>
 				</mwc-select>
 			</div>
-			${state.data.powermeter && state.connected ? html`
+			${archive.finishedLoading() ? html`
 				<smarthome-card>
 					<div class="table">
 						<div class="table-row table-head">

@@ -105,9 +106,9 @@ class PowerMeterHistory extends LitElement {
 								<div class="table-column">${formatName(this.type, day)}</table-column>
 							`)}
 						</div>
-						${Object.entries(state.data.powermeter).map(([id, d]) => html`
+						${Object.entries(counters).map(([id, name]) => html`
 							<div class="table-row">
-								<div class="table-column">${d.name}</div>
+								<div class="table-column">${name}</div>
 								${Object.entries(this.data).reverse().map(([day, d]) => html`
 									<div class="table-column">${d[id] ? round(d[id], 2) + " kWh" : "-"}</div>
 								`)}

@@ -115,7 +116,9 @@ class PowerMeterHistory extends LitElement {
 						`)}
 					</div>
 				</smarthome-card>
-			` : html``}
+			` : html`
+			  <smarthome-spinner>Loading...</smarthome-spinner>
+			`}
 		`;
 	}
 
diff --git a/src/power-meter/live.js b/src/power-meter/live.js
@@ -12,15 +12,20 @@ import "../spinner.js";
 class PowerMeterLive extends LitElement {
 	constructor() {
 		super(...arguments);
+		this._cachedDayArchive = {};
 		state.subscribe(this.requestUpdate.bind(this));
 		archive.subscribe(this.requestUpdate.bind(this));
 	}
 
 	getImport(id) {
+		const currentValue = state.data.powermeter[id].values.import;
 		const now = new Date();
-		const file = archive.load(`day/${id}_${now.getFullYear()}_${pad(now.getMonth() + 1, 2)}`);
-		const value = get(file, [ `${now.getFullYear()}${pad(now.getMonth() + 1, 2)}${pad(now.getDate(), 2)}`, "imported" ]);
-		return value ? round(value, 2) + " kWh" : "-";
+		const filename = `day/${id}_${now.getFullYear()}_${pad(now.getMonth() + 1, 2)}`;
+		if (this._cachedDayArchive[filename] !== now.getDate()) delete archive.data[filename];
+		archive.load(`day/${id}_${now.getFullYear()}_${pad(now.getMonth() + 1, 2)}`);
+		this._cachedDayArchive[filename] = now.getDate();
+		const lastValue = get(archive.data, [filename, `${now.getFullYear()}${pad(now.getMonth() + 1, 2)}${pad(now.getDate() - 1, 2)}`, "totalImported" ]);
+		return currentValue && lastValue ? round(currentValue - lastValue, 2) + " kWh" : "-";
 	}
 
 	render() {

@@ -59,7 +64,7 @@ class PowerMeterLive extends LitElement {
 					</div>
 				</smarthome-card>
 			` : html`
-				<smarthome-spinner></smarthome-spinner>
+				<smarthome-spinner>Trying to connect...</smarthome-spinner>
 			`}
 		`;
 	}
diff --git a/src/spinner.js b/src/spinner.js
@@ -7,7 +7,7 @@ import "@authentic/mwc-circular-progress";
 class Spinner extends LitElement {
 	render() {
 		return html`
-			<h3>Trying to connect...</h3>
+			<h3><slot></slot></h3>
 			<mwc-circular-progress></mwc-circular-progress>
 		`;
 	}
diff --git a/src/state.js b/src/state.js
@@ -28,7 +28,7 @@ class State {
 	}
 
 	_initWS() {
-		this.ws = new WebSocket(`${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.hostname}/${window.location.pathname}/ws`);
+		this.ws = new WebSocket(`${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.hostname}/${window.location.pathname.split("/").slice(0, -1).join("/")}/ws`);
 		this.ws.onclose = () => {
 			this.connected = false;
 			this._initWS();