Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor UI state handling, add navigation, fix systemd unit #30

Merged
merged 3 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"author": "Sam Stenvall <[email protected]>",
"license": "GPL-3.0-or-later",
"scripts": {
"postinstall": "cd webif/ && npm install",
"build": "rm -rf dist/* && npx tsc",
"build-all": "npm run build && cd webif/ && npm run build",
"prettier": "prettier --write src/ tests/",
"prettier-check": "prettier --check src/ tests/",
"lint": "eslint 'src/**/*.ts'",
Expand Down
3 changes: 2 additions & 1 deletion systemd/eachwatt.service
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ After=network.target

[Service]
WorkingDirectory=/opt/eachwatt
ExecStartPre=/usr/bin/npm run build
ExecStartPre=/usr/bin/npm install
ExecStartPre=/usr/bin/npm run build-all
ExecStart=/usr/bin/node /opt/eachwatt/dist/eachwatt.js -c /etc/eachwatt/config.yml
Restart=always
RestartSec=5
Expand Down
8 changes: 8 additions & 0 deletions webif/src/lib/stores.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { writable } from 'svelte/store'

export const configurationStore = writable({})
export const characteristicsStore = writable([])
export const mainSensorDataStore = writable([])
export const circuitSensorDataStore = writable([])
export const lastUpdateTimestampStore = writable(undefined)
export const webSocketUrlStore = writable(undefined)
90 changes: 87 additions & 3 deletions webif/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,36 +1,109 @@
<script>
import 'purecss/build/pure.css'

import {
configurationStore,
characteristicsStore,
mainSensorDataStore,
circuitSensorDataStore,
lastUpdateTimestampStore,
webSocketUrlStore
} from '../lib/stores'
import { onMount } from 'svelte'
import { determineWebSocketUrl } from '../lib/websocket'

const parseTimestamp = (sensorData) => {
return new Date(sensorData[0].timestamp)
}

onMount(() => {
// Determine WebSocket URL from URL parameters
$webSocketUrlStore = determineWebSocketUrl()
const ws = new WebSocket($webSocketUrlStore)

ws.addEventListener('open', () => {
console.log(`Connected to WebSocket at ${$webSocketUrlStore}`)
})

ws.addEventListener('message', (event) => {
const data = event.data
const message = JSON.parse(data)

// Parse last update timestamp from sensor data messages
switch (message.type) {
case 'characteristicsSensorData':
case 'powerSensorData':
$lastUpdateTimestampStore = parseTimestamp(message.data)
break
}

// Handle each message type
switch (message.type) {
case 'characteristicsSensorData':
$characteristicsStore = message.data
break
case 'powerSensorData':
$mainSensorDataStore = message.data.filter((d) => {
// Filter out unmetered
return d.circuit.type === 'main' && d.circuit.sensor.type !== 'unmetered'
})

$circuitSensorDataStore = message.data
break
case 'configuration':
$configurationStore = message.data
break
}
})
})
</script>
<svelte:head>
<title>EachWatt</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</svelte:head>
<div class="pure-g container">
<div class="pure-u-1-1 l-box">
<nav class="pure-menu pure-menu-horizontal">
<ul class="pure-menu-list">
<li class="pure-menu-item">
<a href="/" class="pure-menu-link">Dashboard</a>
</li>
<li class="pure-menu-item">
<a href="/configuration" class="pure-menu-link">Configuration</a>
</li>
</ul>
</nav>
</div>

<slot />
</div>

<style>
:root {
--background: #fff;
--color: #222;
--table-background: #e0e0e0;
--component-background: #e0e0e0;
--highlight-color: #ff3d00;
}

@media (prefers-color-scheme: dark){
:root {
--background: #272727;
--color: #aaa;
--table-background: #3b3b3b;
--component-background: #3b3b3b;
--highlight-color: #ff3d00;
}

nav {
background-color: var(--component-background);
}

/* pure-table overrides */
:global(.pure-table) {
border: 2px solid #424242;
}
:global(.pure-table thead) {
background-color: var(--table-background);
background-color: var(--component-background);
color: var(--color);
}

Expand Down Expand Up @@ -77,4 +150,15 @@
:global(.l-box) {
padding: 1em;
}

/* pure-menu tweaks */
:global(.pure-menu-link) {
border-bottom: 2px solid var(--component-background);
}

:global(.pure-menu-link:hover) {
border-bottom: 2px solid var(--highlight-color);
background: inherit !important;
}
/* end pure-menu tweaks */
</style>
69 changes: 10 additions & 59 deletions webif/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,63 +1,14 @@
<script>
import { onMount } from 'svelte'
import { determineWebSocketUrl } from '$lib/websocket'
import {
lastUpdateTimestampStore,
webSocketUrlStore
} from '../lib/stores'

import LastUpdate from './LastUpdate.svelte'
import Characteristics from './Characteristics.svelte'
import MainsPower from './MainsPower.svelte'
import Circuits from './Circuits.svelte'
import Loader from './Loader.svelte'

let webSocketUrl
let lastUpdateTimestamp
let characteristicsSensorData
let mainsSensorData
let circuitSensorData

const parseTimestamp = (sensorData) => {
return new Date(sensorData[0].timestamp)
}

onMount(() => {
// Determine WebSocket URL from URL parameters
webSocketUrl = determineWebSocketUrl()
const ws = new WebSocket(webSocketUrl)

ws.addEventListener('open', () => {
console.log(`Connected to WebSocket at ${webSocketUrl}`)
})

ws.addEventListener('message', (event) => {
const data = event.data
const message = JSON.parse(data)

// Parse last update timestamp from sensor data messages
switch (message.type) {
case 'characteristicsSensorData':
case 'powerSensorData':
lastUpdateTimestamp = parseTimestamp(message.data)
break
}

// Handle each message type
switch (message.type) {
case 'characteristicsSensorData':
characteristicsSensorData = message.data
break
case 'powerSensorData':
mainsSensorData = message.data.filter((d) => {
// Filter out unmetered
return d.circuit.type === 'main' && d.circuit.sensor.type !== 'unmetered'
})

circuitSensorData = message.data
break
case 'configuration':
// configuration = message.data
break
}
})
})
</script>

<style>
Expand All @@ -66,24 +17,24 @@
}
</style>

{#if lastUpdateTimestamp === undefined}
{#if $lastUpdateTimestampStore === undefined}
<div class="pure-u-1-8 l-box">
<Loader />
</div>
<div class="pure-u-7-8 l-box">
<p class="connecting">Connecting to {webSocketUrl}</p>
<p class="connecting">Connecting to {$webSocketUrlStore}</p>
</div>
{:else}
<div class="pure-u-1-1 l-box">
<LastUpdate {lastUpdateTimestamp} {webSocketUrl} />
<LastUpdate />
</div>
<div class="pure-u-1-1 l-box">
<Characteristics sensorData={characteristicsSensorData} />
<Characteristics />
</div>
<div class="pure-u-1-1 l-box">
<MainsPower sensorData={mainsSensorData} />
<MainsPower />
</div>
<div class="pure-u-1-1 l-box">
<Circuits sensorData={circuitSensorData} />
<Circuits />
</div>
{/if}
4 changes: 2 additions & 2 deletions webif/src/routes/Characteristics.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
export let sensorData = []
import { characteristicsStore } from '../lib/stores'
</script>

<h2>Characteristics</h2>
Expand All @@ -13,7 +13,7 @@
</tr>
</thead>
<tbody>
{#each sensorData as data}
{#each $characteristicsStore as data}
<tr>
<td>{data.characteristics.name}</td>
<td>{data.characteristics.phase}</td>
Expand Down
5 changes: 2 additions & 3 deletions webif/src/routes/Circuits.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script>
import { circuitSensorDataStore } from '../lib/stores'
import { formatPf } from '../lib/format'

export let sensorData = []
</script>

<h2>All circuits</h2>
Expand All @@ -19,7 +18,7 @@
</tr>
</thead>
<tbody>
{#each sensorData as data}
{#each $circuitSensorDataStore as data}
<tr>
<td>{data.circuit.name}</td>
<td>{data.circuit.group ?? ''}</td>
Expand Down
15 changes: 0 additions & 15 deletions webif/src/routes/Configuration.svelte

This file was deleted.

4 changes: 2 additions & 2 deletions webif/src/routes/LastUpdate.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
export let lastUpdateTimestamp, webSocketUrl
import { lastUpdateTimestampStore, webSocketUrlStore } from '../lib/stores'
</script>

<style>
Expand All @@ -9,5 +9,5 @@
</style>

<p>
Last update: {lastUpdateTimestamp?.toISOString()}, connected to {webSocketUrl}
Last update: {$lastUpdateTimestampStore?.toISOString()}, connected to {$webSocketUrlStore}
</p>
6 changes: 3 additions & 3 deletions webif/src/routes/MainsPower.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script>
export let sensorData = []
import { mainSensorDataStore} from '../lib/stores'
</script>

<div class="mains-power-cards">
{#each sensorData as data}
{#each $mainSensorDataStore as data}
<div class="mains-power-card">
<h4>{data.circuit.name}</h4>
<span>{data.power}W</span>
Expand All @@ -22,7 +22,7 @@
.mains-power-cards {
padding: 0.8em;
box-sizing: border-box;
background-color: var(--table-background);
background-color: var(--component-background);
margin-bottom: 1em;
}

Expand Down
18 changes: 18 additions & 0 deletions webif/src/routes/configuration/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script>
import { configurationStore } from '../../lib/stores'
</script>

<div class="pure-u-1-1 l-box">
<h2>Configuration</h2>
<pre id="configuration">{$configurationStore}</pre>
</div>

<style>
pre {
padding: 0.8em;
box-sizing: border-box;
background-color: var(--component-background);
overflow: hidden;
}
</style>

Loading