commit 61932b21de4e9e55e8f4f177285f03d50a6ac6b4
parent a5749f61beb532f02141fc3cdeff8395ca816476
Author: Milan Pässler <me@pbb.lc>
Date: Tue, 18 Jun 2019 02:25:44 +0200
parent a5749f61beb532f02141fc3cdeff8395ca816476
Author: Milan Pässler <me@pbb.lc>
Date: Tue, 18 Jun 2019 02:25:44 +0200
power meter tabs
7 files changed, 383 insertions(+), 362 deletions(-)
D
|
208
-------------------------------------------------------------------------------
D
|
128
-------------------------------------------------------------------------------
A
|
186
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
|
127
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/src/departures.js b/src/departures.js @@ -76,24 +76,6 @@ class Departures extends LitElement { --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; @@ -114,11 +96,6 @@ class Departures extends LitElement { .table-column { background-color: #ddd; } - @media (min-width: 600px) { - #connection-status { - margin-top: 20px; - } - } .line-column { width: 20%; }
diff --git a/src/layout.js b/src/layout.js @@ -9,8 +9,7 @@ import "@authentic/mwc-icon"; import "@authentic/mwc-drawer"; import "./lights.js"; -import "./power-meter.js"; -import "./power-meter-history.js"; +import "./power-meter/index.js"; import "./departures.js"; import "./settings.js"; import "./spinner.js"; @@ -21,7 +20,7 @@ netdataRedirect.innerHTML = "window.location = `http://${window.location.host}/n 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><smarthome-power-meter-history></smarthome-power-meter-history>`, icon: "power" }, + "#/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" }, "#/settings": { name: "Settings", content: html`<smarthome-settings></smarthome-settings>`, icon: "settings" },
diff --git a/src/power-meter-history.js b/src/power-meter-history.js @@ -1,208 +0,0 @@ -"use strict"; - -import { LitElement, html, css } from "lit-element"; -import { state } from "./state.js"; -import { pad, round, weekdays, months, get } from "./util.js"; -import { archive } from "./archive.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 formatName = (type, name) => { - if (type == "day") { - const date = new Date(`${name.substr(0, 4)}-${name.substr(4, 2)}-${name.substr(6, 2)}`); - return `${weekdays[date.getDay()]} ${date.getDate()}th`; - } else if (type == "week") { - let res = ""; - const date = new Date(name.substr(0, 4), 0, 1 + 7 * (Number(name.substr(4, 2)) - 1)); - date.setDate(date.getDate() + 1 - date.getDay()); - res += `${date.getDate()}.${date.getMonth()+1}.`; - date.setDate(date.getDate() + 7 - date.getDay()); - res += ` - ${date.getDate()}.${date.getMonth()+1}.`; - return res; - } else if (type == "month") { - const date = new Date(`${name.substr(0, 4)}-${name.substr(4, 2)}-01`); - return months[date.getMonth()]; - } -}; - -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)); - 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(); - } - - handleSelected(field) { - return (evt) => { - this[field] = evt.detail.item.value; - this.loadData(); - }; - } - - 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> - ${Object.keys(get(archive.load("metadata"), [ "availableData" ], {})).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>`)} - </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).reverse().map((day) => html` - <div class="table-column">${formatName(this.type, 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).reverse().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; - } - - .hidden { - display: none; - } - #selection { - justify-content: center; - display: flex; - flex-wrap: wrap; - flex-direction: row; - width: 450px; - max-width: 100%; - margin-left: auto; - margin-right: auto; - margin-top: 40px; - margin-bottom: 0px; - /*justify-content: space-between;*/ - } - mwc-select { - width: 100px; - margin-right: 10px; - margin-left: 10px; - } - @media (min-width: 600px) { - #connection-status { - margin-top: 20px; - } - #selection { - margin-bottom: -20px; - } - } - `; - } -} - -customElements.define("smarthome-power-meter-history", PowerMeterHistory);
diff --git a/src/power-meter.js b/src/power-meter.js @@ -1,128 +0,0 @@ -"use strict"; - -import { LitElement, html, css } from "lit-element"; -import { state } from "./state.js"; -import { pad, formatDate, round, get } from "./util.js"; -import { archive } from "./archive.js"; - -import "@authentic/mwc-circular-progress"; -import "@authentic/mwc-icon"; -import "./card.js"; -import "./spinner.js"; - -class PowerMeter extends LitElement { - constructor() { - super(...arguments); - state.subscribe(this.requestUpdate.bind(this)); - archive.subscribe(this.requestUpdate.bind(this)); - } - - getImport(id) { - 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" : "-"; - } - - render() { - return html` - ${state.data.powermeter ? html` - <div id="connection-status"> - ${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> - `} - </div> - <smarthome-card> - <div class="table"> - <div class="table-row table-head"> - <div class="table-column">Name</div> - <div class="table-column">Voltage</div> - <div class="table-column">Power</div> - <div class="table-column">Power Factor</div> - <div class="table-column">Frequency</div> - <div class="table-column">Total Import</div> - <div class="table-column">Import</div> - </div> - ${Object.entries(state.data.powermeter).map(([id, d]) => html` - <div class="table-row"> - <div class="table-column">${d.name}</div> - <div class="table-column">${d.values.voltage} V</div> - <div class="table-column">${d.values.power} W</div> - <div class="table-column">${d.values.cosphi}</div> - <div class="table-column">${d.values.frequency}</div> - <div class="table-column">${d.values.import} kWh</div> - <div class="table-column">${this.getImport(id)}</div> - </div> - `)} - </div> - </smarthome-card> - ` : html` - <smarthome-spinner></smarthome-spinner> - `} - `; - } - - 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; - } - } - `; - } -} - -customElements.define("smarthome-power-meter", PowerMeter);
diff --git a/src/power-meter/history.js b/src/power-meter/history.js @@ -0,0 +1,186 @@ +"use strict"; + +import { LitElement, html, css } from "lit-element"; +import { state } from "../state.js"; +import { pad, round, weekdays, months, get } from "../util.js"; +import { archive } from "../archive.js"; + +import "@authentic/mwc-icon"; +import "@authentic/mwc-select"; +import "@authentic/mwc-menu"; +import "@authentic/mwc-list"; +import "../card.js"; +import "../spinner.js"; + +const formatName = (type, name) => { + if (type == "day") { + const date = new Date(`${name.substr(0, 4)}-${name.substr(4, 2)}-${name.substr(6, 2)}`); + return `${weekdays[date.getDay()]} ${date.getDate()}th`; + } else if (type == "week") { + let res = ""; + const date = new Date(name.substr(0, 4), 0, 1 + 7 * (Number(name.substr(4, 2)) - 1)); + date.setDate(date.getDate() + 1 - date.getDay()); + res += `${date.getDate()}.${date.getMonth()+1}.`; + date.setDate(date.getDate() + 7 - date.getDay()); + res += ` - ${date.getDate()}.${date.getMonth()+1}.`; + return res; + } else if (type == "month") { + const date = new Date(`${name.substr(0, 4)}-${name.substr(4, 2)}-01`); + return months[date.getMonth()]; + } +}; + +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)); + 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(); + } + + handleSelected(field) { + return (evt) => { + this[field] = evt.detail.item.value; + this.loadData(); + }; + } + + 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> + ${Object.keys(get(archive.load("metadata"), [ "availableData" ], {})).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>`)} + </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).reverse().map((day) => html` + <div class="table-column">${formatName(this.type, 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).reverse().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; + } + .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; + } + + .hidden { + display: none; + } + #selection { + justify-content: center; + display: flex; + flex-wrap: wrap; + flex-direction: row; + width: 450px; + max-width: 100%; + margin-left: auto; + margin-right: auto; + margin-top: 40px; + margin-bottom: 0px; + /*justify-content: space-between;*/ + } + mwc-select { + width: 100px; + margin-right: 10px; + margin-left: 10px; + } + @media (min-width: 600px) { + #selection { + margin-bottom: -20px; + } + } + `; + } +} + +customElements.define("smarthome-power-meter-history", PowerMeterHistory);
diff --git a/src/power-meter/index.js b/src/power-meter/index.js @@ -0,0 +1,68 @@ +"use strict"; + +import { LitElement, html, css } from "lit-element"; + +import "./history.js"; +import "./live.js"; + +const pages = { + "live": { name: "Live", content: html`<smarthome-power-meter-live></smarthome-power-meter-live>` }, + "history": { name: "History", content: html`<smarthome-power-meter-history></smarthome-power-meter-history>` }, +}; + +class PowerMeter extends LitElement { + constructor() { + super(...arguments); + this.activePage = pages["live"]; + } + + _setPage(path) { + return () => { + this.activePage = pages[path]; + this.requestUpdate(); + }; + } + + render() { + return html` + <div id="pages"> + ${Object.entries(pages).map(([path, page]) => html` + <a href="#/powermeter" class="page-link ${this.activePage == page ? "active" : "inactive"}" @click=${this._setPage(path)}> + ${page.name} + </a> + `)} + </div> + ${this.activePage.content} + `; + } + + 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; + } + + #pages { + display: flex; + flex-direction: row; + justify-content: center; + } + .page-link { + padding: .5em; + margin: .5em; + color: black; + text-decoration: none; + } + .page-link.active { + border-bottom: 4px solid #43a047; + } + `; + } +} + +customElements.define("smarthome-power-meter", PowerMeter);
diff --git a/src/power-meter/live.js b/src/power-meter/live.js @@ -0,0 +1,127 @@ +"use strict"; + +import { LitElement, html, css } from "lit-element"; +import { state } from "../state.js"; +import { pad, formatDate, round, get } from "../util.js"; +import { archive } from "../archive.js"; + +import "@authentic/mwc-icon"; +import "../card.js"; +import "../spinner.js"; + +class PowerMeterLive extends LitElement { + constructor() { + super(...arguments); + state.subscribe(this.requestUpdate.bind(this)); + archive.subscribe(this.requestUpdate.bind(this)); + } + + getImport(id) { + 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" : "-"; + } + + render() { + return html` + ${state.data.powermeter ? html` + <div id="connection-status"> + ${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> + `} + </div> + <smarthome-card> + <div class="table"> + <div class="table-row table-head"> + <div class="table-column">Name</div> + <div class="table-column">Voltage</div> + <div class="table-column">Power</div> + <div class="table-column">Power Factor</div> + <div class="table-column">Frequency</div> + <div class="table-column">Total Import</div> + <div class="table-column">Import</div> + </div> + ${Object.entries(state.data.powermeter).map(([id, d]) => html` + <div class="table-row"> + <div class="table-column">${d.name}</div> + <div class="table-column">${d.values.voltage} V</div> + <div class="table-column">${d.values.power} W</div> + <div class="table-column">${d.values.cosphi}</div> + <div class="table-column">${d.values.frequency}</div> + <div class="table-column">${d.values.import} kWh</div> + <div class="table-column">${this.getImport(id)}</div> + </div> + `)} + </div> + </smarthome-card> + ` : html` + <smarthome-spinner></smarthome-spinner> + `} + `; + } + + 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; + } + } + `; + } +} + +customElements.define("smarthome-power-meter-live", PowerMeterLive);