Various UI improvements

This commit is contained in:
Gunnar Skjold
2023-02-06 18:20:48 +01:00
parent dfef18fa09
commit bb2f74d1ca
18 changed files with 294 additions and 386 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -11,6 +11,10 @@
@apply bg-white m-2 p-2 rounded shadow-lg
}
.gwf {
@apply 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64
}
.in-pre {
@apply flex items-center bg-gray-100 rounded-l-md border border-r-0 border-gray-300 px-3 whitespace-nowrap text-sm
}
@@ -38,25 +42,28 @@
@apply text-right
}
.bd-grn {
.bd-green {
@apply my-auto bg-green-500 text-green-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
}
.bd-ylo {
.bd-yellow {
@apply my-auto bg-yellow-500 text-yellow-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
}
.bd-red {
@apply my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
}
.bd-blu {
.bd-blue {
@apply my-auto bg-blue-500 text-blue-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
}
.bd-gry {
.bd-gray {
@apply my-auto bg-gray-500 text-gray-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
}
.btn-pri {
@apply py-2 px-4 rounded bg-blue-500 text-white mr-3
}
.btn-pri-sm {
@apply text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3
}
.pl-root {
position: relative;

View File

@@ -1,7 +1,15 @@
<script>
import { fmtnum } from "./Helpers";
export let data;
export let currency;
export let hasExport;
let cols = 3
$: {
cols = currency ? 3 : 2;
}
</script>
<div class="mx-2 text-sm">
@@ -9,69 +17,50 @@
<br/><br/>
{#if data}
{#if hasExport && currency}
{#if hasExport}
<strong>Import</strong>
<div class="grid grid-cols-3 mb-3">
<div class="grid grid-cols-{cols} mb-3">
<div>Hour</div>
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</div>
<div class="text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.h.u,2)} kWh</div>
{#if currency}<div class="text-right">{fmtnum(data.h.c,2)} {currency}</div>{/if}
<div>Day</div>
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
<div class="text-right">{data.d.c ? data.d.c.toFixed(1) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.d.u,1)} kWh</div>
{#if currency}<div class="text-right">{fmtnum(data.d.c,1)} {currency}</div>{/if}
<div>Month</div>
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
<div class="text-right">{data.m.c ? data.m.c.toFixed(0) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.m.u)} kWh</div>
{#if currency}<div class="text-right">{fmtnum(data.m.c)} {currency}</div>{/if}
</div>
<strong>Export</strong>
<div class="grid grid-cols-3">
<div class="grid grid-cols-{cols}">
<div>Hour</div>
<div class="text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh</div>
<div class="text-right">{data.h.i ? data.h.i.toFixed(2) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.h.p,2)} kWh</div>
{#if currency}<div class="text-right">{fmtnum(data.h.i,2)} {currency}</div>{/if}
<div>Day</div>
<div class="text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh</div>
<div class="text-right">{data.d.i ? data.d.i.toFixed(1) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.d.p,1)} kWh</div>
{#if currency}<div class="text-right">{fmtnum(data.d.i,1)} {currency}</div>{/if}
<div>Month</div>
<div class="text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh</div>
<div class="text-right">{data.m.i ? data.m.i.toFixed(0) : '-'} {currency}</div>
</div>
{:else if hasExport}
<strong>Import</strong>
<div class="grid grid-cols-2 mb-3">
<div>Hour</div>
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</div>
<div>Day</div>
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
<div>Month</div>
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
</div>
<strong>Export</strong>
<div class="grid grid-cols-2">
<div>Hour</div>
<div class="text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh</div>
<div>Day</div>
<div class="text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh</div>
<div>Month</div>
<div class="text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh</div>
<div class="text-right">{fmtnum(data.m.p)} kWh</div>
{#if currency}<div class="text-right">{fmtnum(data.m.i)} {currency}</div>{/if}
</div>
{:else}
<strong>Consumption</strong>
<div class="grid grid-cols-2 mb-3">
<div>Hour</div>
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</div>
<div class="text-right">{fmtnum(data.h.u,2)} kWh</div>
<div>Day</div>
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
<div class="text-right">{fmtnum(data.d.u,1)} kWh</div>
<div>Month</div>
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
<div class="text-right">{fmtnum(data.m.u)} kWh</div>
</div>
{#if currency}
<strong>Cost</strong>
<div class="grid grid-cols-2">
<div>Hour</div>
<div class="text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.h.c,2)} {currency}</div>
<div>Day</div>
<div class="text-right">{data.d.c ? data.d.c.toFixed(1) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.d.c,1)} {currency}</div>
<div>Month</div>
<div class="text-right">{data.m.c ? data.m.c.toFixed(0) : '-'} {currency}</div>
<div class="text-right">{fmtnum(data.m.c)} {currency}</div>
</div>
{/if}
{/if}

View File

@@ -1,6 +1,6 @@
<script>
import BarChart from './BarChart.svelte';
import { ampcol } from './Helpers.js';
import { ampcol, fmtnum } from './Helpers.js';
export let u1;
export let u2;
@@ -12,32 +12,28 @@
let config = {};
function point(v) {
return {
label: fmtnum(v) +'A',
value: isNaN(v) ? 0 : v,
color: ampcol(v ? (v)/(max)*100 : 0)
};
};
$: {
let xTicks = [];
let points = [];
if(u1 > 0) {
xTicks.push({ label: 'L1' });
points.push({
label: i1 ? (i1 > 10 ? i1.toFixed(0) : i1.toFixed(1)) + 'A' : '-',
value: i1 ? i1 : 0,
color: ampcol(i1 ? (i1)/(max)*100 : 0)
});
points.push(point(i1));
}
if(u2 > 0) {
xTicks.push({ label: 'L2' });
points.push({
label: i2 ? (i2 > 10 ? i2.toFixed(0) : i2.toFixed(1)) + 'A' : '-',
value: i2 ? i2 : 0,
color: ampcol(i2 ? (i2)/(max)*100 : 0)
});
points.push(point(i2));
}
if(u3 > 0) {
xTicks.push({ label: 'L3' });
points.push({
label: i3 ? (i3 > 10 ? i3.toFixed(0) : i3.toFixed(1)) + 'A' : '-',
value: i3 ? i3 : 0,
color: ampcol(i3 ? (i3)/(max)*100 : 0)
});
points.push(point(i3));
}
config = {
padding: { top: 20, right: 15, bottom: 20, left: 35 },

View File

@@ -3,14 +3,4 @@
export let title;
export let text;
</script>
{#if color == 'green'}
<span title={title} class="bd-grn">{text}</span>
{:else if color === `yellow`}
<span title={title} class="bd-ylo">{text}</span>
{:else if color === `red`}
<span title={title} class="bd-red">{text}</span>
{:else if color === `blue`}
<span title={title} class="bd-blu">{text}</span>
{:else if color === `gray`}
<span title={title} class="bd-gry">{text}</span>
{/if}
<span title={title} class="bd-{color}">{text}</span>

View File

@@ -1,6 +1,7 @@
<script>
import { getConfiguration, configurationStore } from './ConfigurationStore'
import { sysinfoStore } from './DataStores.js';
import { wiki } from './Helpers.js';
import UartSelectOptions from './UartSelectOptions.svelte';
import Mask from './Mask.svelte'
import Badge from './Badge.svelte';
@@ -12,6 +13,41 @@
export let sysinfo = {}
let uiElements = [{
name: 'Import gauge',
key: 'i'
},{
name: 'Export gauge',
key: 'e'
},{
name: 'Voltage',
key: 'v'
},{
name: 'Amperage',
key: 'a'
},{
name: 'Reactive',
key: 'r'
},{
name: 'Realtime',
key: 'c'
},{
name: 'Peaks',
key: 't'
},{
name: 'Price',
key: 'p'
},{
name: 'Day plot',
key: 'd'
},{
name: 'Month plot',
key: 'm'
},{
name: 'Temperature plot',
key: 's'
}];
let loading = true;
let saving = false;
@@ -133,13 +169,18 @@
if(configuration.q.p == 8883) configuration.q.p = 1883;
}
}
let gpioMax = 44;
$: {
gpioMax = sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39;
}
</script>
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
<div class="cnt">
<strong class="text-sm">General</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/General-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('General-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="g" value="true"/>
<div class="my-1">
<div class="flex">
@@ -196,10 +237,9 @@
<div class="w-1/2">
Currency<br/>
<select name="pc" bind:value={configuration.p.c} class="in-f w-full">
<option value="NOK">NOK</option>
<option value="SEK">SEK</option>
<option value="DKK">DKK</option>
<option value="EUR">EUR</option>
{#each ["NOK","SEK","DKK","EUR"] as c}
<option value={c}>{c}</option>
{/each}
</select>
</div>
<div class="w-1/2">
@@ -235,20 +275,16 @@
</div>
<div class="cnt">
<strong class="text-sm">Meter</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Meter-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('Meter-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="m" value="true"/>
<div class="my-1">
<span>Serial configuration</span>
<div class="flex">
<select name="mb" bind:value={configuration.m.b} class="in-f">
<option value={0} disabled={configuration.m.b != 0}>Autodetect</option>
<option value={2400}>2400</option>
<option value={4800}>4800</option>
<option value={9600}>9600</option>
<option value={19200}>19200</option>
<option value={38400}>38400</option>
<option value={57600}>57600</option>
<option value={115200}>115200</option>
{#each [24,48,96,192,384,576,1152] as b}
<option value={b*100}>{b*100}</option>
{/each}
</select>
<select name="mp" bind:value={configuration.m.p} class="in-l" disabled={configuration.m.b == 0}>
<option value={0} disabled={configuration.m.b != 0}>-</option>
@@ -324,7 +360,7 @@
</div>
<div class="cnt">
<strong class="text-sm">WiFi</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/WiFi-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('WiFi-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="w" value="true"/>
<div class="my-1">
SSID<br/>
@@ -358,7 +394,7 @@
</div>
<div class="cnt">
<strong class="text-sm">Network</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Network-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('Network-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
<div class="my-1">
IP<br/>
<div class="flex">
@@ -398,7 +434,7 @@
</div>
<div class="cnt">
<strong class="text-sm">MQTT</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/MQTT-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('MQTT-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="q" value="true"/>
<div class="my-1">
Server
@@ -473,7 +509,7 @@
{#if configuration.q.m == 3}
<div class="cnt">
<strong class="text-sm">Domoticz</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/MQTT-configuration#domoticz" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('MQTT-configuration#domoticz')}" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="o" value="true"/>
<div class="my-1 flex">
<div class="w-1/2">
@@ -498,54 +534,16 @@
{#if configuration.p.r.startsWith("10YNO") || configuration.p.r == '10Y1001A1001A48H'}
<div class="cnt">
<strong class="text-sm">Tariff thresholds</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Threshold-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('Threshold-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="t" value="true"/>
<div class="flex flex-wrap my-1">
{#each {length: 9} as _, i}
<label class="flex w-40 m-1">
<span class="in-pre">1</span>
<input name="t0" bind:value={configuration.t.t[0]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">2</span>
<input name="t1" bind:value={configuration.t.t[1]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">3</span>
<input name="t2" bind:value={configuration.t.t[2]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">4</span>
<input name="t3" bind:value={configuration.t.t[3]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">5</span>
<input name="t4" bind:value={configuration.t.t[4]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">6</span>
<input name="t5" bind:value={configuration.t.t[5]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">7</span>
<input name="t6" bind:value={configuration.t.t[6]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">8</span>
<input name="t7" bind:value={configuration.t.t[7]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
<label class="flex w-40 m-1">
<span class="in-pre">9</span>
<input name="t8" bind:value={configuration.t.t[8]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-pre">{i+1}</span>
<input name="t{i}" bind:value={configuration.t.t[i]} type="number" min="0" max="255" class="in-txt w-full"/>
<span class="in-post">kWh</span>
</label>
{/each}
</div>
<label class="flex m-1">
<span class="in-pre">Average of</span>
@@ -556,103 +554,25 @@
{/if}
<div class="cnt">
<strong class="text-sm">User interface</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/User-interface" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('User-interface')}" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="u" value="true"/>
<div class="flex flex-wrap">
<div class="w-1/2">
Import gauge<br/>
<select name="ui" bind:value={configuration.u.i} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Export gauge<br/>
<select name="ue" bind:value={configuration.u.e} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Voltage<br/>
<select name="uv" bind:value={configuration.u.v} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Amperage<br/>
<select name="ua" bind:value={configuration.u.a} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Reactive<br/>
<select name="ur" bind:value={configuration.u.r} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Realtime<br/>
<select name="uc" bind:value={configuration.u.c} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Peaks<br/>
<select name="ut" bind:value={configuration.u.t} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Price<br/>
<select name="up" bind:value={configuration.u.p} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Day plot<br/>
<select name="ud" bind:value={configuration.u.d} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Month plot<br/>
<select name="um" bind:value={configuration.u.m} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
<div class="w-1/2">
Temperature plot<br/>
<select name="us" bind:value={configuration.u.s} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
{#each uiElements as el}
<div class="w-1/2">
{el.name}<br/>
<select name="u{el.key}" bind:value={configuration.u[el.key]} class="in-s">
<option value={0}>Hide</option>
<option value={1}>Show</option>
<option value={2}>Dynamic</option>
</select>
</div>
{/each}
</div>
</div>
{#if sysinfo.board > 20 || sysinfo.chip == 'esp8266'}
<div class="cnt">
<strong class="text-sm">Hardware</strong>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/GPIO-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<a href="{wiki('GPIO-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
{#if sysinfo.board > 20}
<input type="hidden" name="i" value="true"/>
<div class="flex flex-wrap">
@@ -664,34 +584,34 @@
</div>
<div class="w-1/3">
AP button<br/>
<input name="ia" bind:value={configuration.i.a} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-m tr w-full"/>
<input name="ia" bind:value={configuration.i.a} type="number" min="0" max={gpioMax} class="in-m tr w-full"/>
</div>
<div class="w-1/3">
LED<label class="ml-4"><input name="ili" value="true" bind:checked={configuration.i.l.i} type="checkbox" class="rounded mb-1"/> inv</label><br/>
<div class="flex">
<input name="ilp" bind:value={configuration.i.l.p} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-l tr w-full"/>
<input name="ilp" bind:value={configuration.i.l.p} type="number" min="0" max={gpioMax} class="in-l tr w-full"/>
</div>
</div>
<div class="w-full">
RGB<label class="ml-4"><input name="iri" value="true" bind:checked={configuration.i.r.i} type="checkbox" class="rounded mb-1"/> inverted</label><br/>
<div class="flex">
<input name="irr" bind:value={configuration.i.r.r} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-f tr w-1/3"/>
<input name="irg" bind:value={configuration.i.r.g} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-m tr w-1/3"/>
<input name="irb" bind:value={configuration.i.r.b} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-l tr w-1/3"/>
<input name="irr" bind:value={configuration.i.r.r} type="number" min="0" max={gpioMax} class="in-f tr w-1/3"/>
<input name="irg" bind:value={configuration.i.r.g} type="number" min="0" max={gpioMax} class="in-m tr w-1/3"/>
<input name="irb" bind:value={configuration.i.r.b} type="number" min="0" max={gpioMax} class="in-l tr w-1/3"/>
</div>
</div>
<div class="my-1 w-1/3">
Temperature<br/>
<input name="itd" bind:value={configuration.i.t.d} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-f tr w-full"/>
<input name="itd" bind:value={configuration.i.t.d} type="number" min="0" max={gpioMax} class="in-f tr w-full"/>
</div>
<div class="my-1 pr-1 w-1/3">
Analog temp<br/>
<input name="ita" bind:value={configuration.i.t.a} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-l tr w-full"/>
<input name="ita" bind:value={configuration.i.t.a} type="number" min="0" max={gpioMax} class="in-l tr w-full"/>
</div>
{#if sysinfo.chip != 'esp8266'}
<div class="my-1 pl-1 w-1/3">
Vcc<br/>
<input name="ivp" bind:value={configuration.i.v.p} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-s tr w-full"/>
<input name="ivp" bind:value={configuration.i.v.p} type="number" min="0" max={gpioMax} class="in-s tr w-full"/>
</div>
{/if}
{#if configuration.i.v.p > 0}

View File

@@ -2,6 +2,7 @@
import { sysinfoStore } from './DataStores.js';
import Mask from './Mask.svelte'
import { navigate } from 'svelte-navigator';
import { wiki } from './Helpers';
export let sysinfo = {}
@@ -41,7 +42,7 @@
<hr/>
<div class="my-3">
Enable one-click upgrade? (implies data collection)<br/>
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Data-collection-on-one-click-firmware-upgrade" target="_blank" class="text-blue-600 hover:text-blue-800">Read more</a><br/>
<a href="{wiki('Data-collection-on-one-click-firmware-upgrade')}" target="_blank" class="text-blue-600 hover:text-blue-800">Read more</a><br/>
<label><input type="radio" name="sf" value={1} checked={sysinfo.fwconsent === 1} class="rounded m-2" required/> Yes</label><label><input type="radio" name="sf" value={2} checked={sysinfo.fwconsent === 2} class="rounded m-2" required/> No</label><br/>
</div>
<div class="my-3">

View File

@@ -81,22 +81,22 @@
</div>
{/if}
{#if uiVisibility(sysinfo.ui.p, (typeof data.p == "number") && !Number.isNaN(data.p))}
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt gwf">
<PricePlot json={prices}/>
</div>
{/if}
{#if uiVisibility(sysinfo.ui.d, dayPlot)}
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt gwf">
<DayPlot json={dayPlot} />
</div>
{/if}
{#if uiVisibility(sysinfo.ui.m, monthPlot)}
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt gwf">
<MonthPlot json={monthPlot} />
</div>
{/if}
{#if uiVisibility(sysinfo.ui.s, data.t && data.t != -127 && temperatures.c > 1)}
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt gwf">
<TemperaturePlot json={temperatures} />
</div>
{/if}

View File

@@ -1,8 +1,8 @@
<script>
import { Link } from "svelte-navigator";
import { sysinfoStore, getGitHubReleases, gitHubReleaseStore } from './DataStores.js';
import { upgrade, getNextVersion } from './UpgradeHelper';
import { boardtype, hanError, mqttError, priceError, isBusPowered } from './Helpers.js';
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
import { boardtype, hanError, mqttError, priceError, isBusPowered, wiki, bcol } from './Helpers.js';
import AmsleserSvg from "./../assets/favicon.svg";
import GitHubLogo from './../assets/github.svg';
import Uptime from "./Uptime.svelte";
@@ -20,7 +20,7 @@
function askUpgrade() {
if(confirm('Do you want to upgrade this device to ' + nextVersion.tag_name + '?')) {
if(!isBusPowered(sysinfo.board) || confirm('WARNING: ' + boardtype(sysinfo.chip, sysinfo.board) + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.')) {
if(!isBusPowered(sysinfo.board) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
sysinfoStore.update(s => {
s.upgrading = true;
return s;
@@ -54,10 +54,10 @@
<div class="flex-none my-auto">Free mem: {data.m ? (data.m/1000).toFixed(1) : '-'}kb</div>
</div>
<div class="flex-auto flex-wrap my-auto justify-center p-2">
<Badge title="ESP" text={sysinfo.booting ? 'Booting' : data.v > 2.0 ? data.v.toFixed(2)+"V" : "ESP"} color={sysinfo.booting ? 'yellow' : data.em === 1 ? 'green' : data.em === 2 ? 'yellow' : data.em === 3 ? 'red' : 'gray'}/>
<Badge title="HAN" text="HAN" color={sysinfo.booting ? 'gray' : data.hm === 1 ? 'green' : data.hm === 2 ? 'yellow' : data.hm === 3 ? 'red' : 'gray'}/>
<Badge title="WiFi" text={data.r ? data.r.toFixed(0)+"dBm" : "WiFi"} color={sysinfo.booting ? 'gray' : data.wm === 1 ? 'green' : data.wm === 2 ? 'yellow' : data.wm === 3 ? 'red' : 'gray'}/>
<Badge title="MQTT" text="MQTT" color={sysinfo.booting ? 'gray' : data.mm === 1 ? 'green' : data.mm === 2 ? 'yellow' : data.mm === 3 ? 'red' : 'gray'}/>
<Badge title="ESP" text={sysinfo.booting ? 'Booting' : data.v > 2.0 ? data.v.toFixed(2)+"V" : "ESP"} color={bcol(sysinfo.booting ? 2 : data.em)}/>
<Badge title="HAN" text="HAN" color={bcol(sysinfo.booting ? 9 : data.hm)}/>
<Badge title="WiFi" text={data.r ? data.r.toFixed(0)+"dBm" : "WiFi"} color={bcol(sysinfo.booting ? 9 : data.wm)}/>
<Badge title="MQTT" text="MQTT" color={bcol(sysinfo.booting ? 9 : data.mm)}/>
</div>
{#if data.he < 0 || data.he > 0}
<div class="bd-red">{ 'HAN: ' + hanError(data.he) }</div>
@@ -84,7 +84,7 @@
</div>
{/if}
<div class="flex-none px-1 mt-1" title="Documentation">
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki" target='_blank' rel="noreferrer"><HelpIcon/></a>
<a href={wiki('')} target='_blank' rel="noreferrer"><HelpIcon/></a>
</div>
{#if sysinfo.fwconsent === 1 && nextVersion}
<div class="flex-none mr-3 text-yellow-500" title="New version: {nextVersion.tag_name}">

View File

@@ -1,5 +1,9 @@
export let monthnames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
export function bcol(num) {
return num === 1 ? 'green' : num === 2 ? 'yellow' : num === 3 ? 'red' : 'gray';
}
export function voltcol(volt) {
if(volt > 218 && volt < 242) return '#32d900';
if(volt > 212 && volt < 248) return '#b1d900';
@@ -160,3 +164,14 @@ export function isBusPowered(boardType) {
export function uiVisibility(choice, state) {
return choice == 1 || (choice == 2 && state);
}
export function wiki(page) {
return "https://github.com/UtilitechAS/amsreader-firmware/wiki/" + page;
}
export function fmtnum(v,d) {
if(isNaN(v)) return '-';
if(isNaN(d))
d = v < 10 ? 1 : 0;
return v.toFixed(d);
}

View File

@@ -5,8 +5,8 @@
export let exportTotal;
</script>
<div class="mx-2">
<strong class="text-sm">Reactive</strong>
<div class="mx-2 text-sm">
<strong>Reactive</strong>
<div class="grid grid-cols-2 mt-4">
<div>Instant in</div>

View File

@@ -1,13 +1,42 @@
<script>
import { metertype, boardtype, isBusPowered } from './Helpers.js';
import { getSysinfo, gitHubReleaseStore, sysinfoStore } from './DataStores.js';
import { upgrade, getNextVersion } from './UpgradeHelper';
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
import DownloadIcon from './DownloadIcon.svelte';
import { Link } from 'svelte-navigator';
import Mask from './Mask.svelte';
export let data;
export let sysinfo;
let cfgItems = [{
name: 'WiFi',
key: 'iw'
},{
name: 'MQTT',
key: 'im'
},{
name: 'Web',
key: 'ie'
},{
name: 'Meter',
key: 'it'
},{
name: 'Thresholds',
key: 'ih'
},{
name: 'GPIO',
key: 'ig'
},{
name: 'Domoticz',
key: 'id'
},{
name: 'NTP',
key: 'in'
},{
name: 'Price API',
key: 'is'
}];
let nextVersion = {};
gitHubReleaseStore.subscribe(releases => {
@@ -19,7 +48,7 @@
function askUpgrade() {
if(confirm('Do you want to upgrade this device to ' + nextVersion.tag_name + '?')) {
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm('WARNING: ' + boardtype(sysinfo.chip, sysinfo.board) + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.')) {
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
sysinfoStore.update(s => {
s.upgrading = true;
return s;
@@ -64,7 +93,7 @@
Chip: {sysinfo.chip}
</div>
<div class="my-2">
Device: {boardtype(sysinfo.chip, sysinfo.board)}
Device: <Link to="/vendor">{boardtype(sysinfo.chip, sysinfo.board)}</Link>
</div>
<div class="my-2">
MAC: {sysinfo.mac}
@@ -76,7 +105,7 @@
{/if}
<div class="my-2">
<Link to="/consent">
<span class="text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3 ">Update consents</span>
<span class="btn-pri-sm">Update consents</span>
</Link>
<button on:click={askReboot} class="text-xs py-1 px-2 rounded bg-yellow-500 text-white mr-3 float-right">Reboot</button>
</div>
@@ -129,13 +158,13 @@
</div>
{#if sysinfo.fwconsent === 2}
<div class="my-2">
<div class="bd-ylo">You have disabled one-click firmware upgrade, link to self-upgrade is disabled</div>
<div class="bd-yellow">You have disabled one-click firmware upgrade, link to self-upgrade is disabled</div>
</div>
{/if}
{/if}
{#if (sysinfo.security == 0 || data.a) && isBusPowered(sysinfo.board) }
<div class="bd-red">
{boardtype(sysinfo.chip, sysinfo.board)} must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.
{upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board))}
</div>
{/if}
{#if sysinfo.security == 0 || data.a}
@@ -143,10 +172,10 @@
<form action="/firmware" enctype="multipart/form-data" method="post" on:submit={() => firmwareUploading=true} autocomplete="off">
<input style="display:none" name="file" type="file" accept=".bin" bind:this={firmwareFileInput} bind:files={firmwareFiles}>
{#if firmwareFiles.length == 0}
<button type="button" on:click={()=>{firmwareFileInput.click();}} class="text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Select firmware file for upgrade</button>
<button type="button" on:click={()=>{firmwareFileInput.click();}} class="btn-pri-sm float-right">Select firmware file for upgrade</button>
{:else}
{firmwareFiles[0].name}
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Upload</button>
<button type="submit" class="btn-pri-sm float-right">Upload</button>
{/if}
</form>
</div>
@@ -157,28 +186,22 @@
<strong class="text-sm">Configuration</strong>
<form method="get" action="/configfile.cfg" autocomplete="off">
<div class="grid grid-cols-2">
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="iw" value="true" checked/> WiFi</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="im" value="true" checked/> MQTT</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="ie" value="true" checked/> Web</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="it" value="true" checked/> Meter</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="ih" value="true" checked/> Thresholds</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="ig" value="true" checked/> GPIO</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="id" value="true" checked/> Domoticz</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="in" value="true" checked/> NTP</label>
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="is" value="true" checked/> Price API</label>
{#each cfgItems as el}
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="{el.key}" value="true" checked/> {el.name}</label>
{/each}
<label class="my-1 mx-3 col-span-2"><input type="checkbox" class="rounded" name="ic" value="true"/> Include Secrets<br/><small>(SSID, PSK, passwords and tokens)</small></label>
</div>
{#if configFiles.length == 0}
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Download</button>
<button type="submit" class="btn-pri-sm float-right">Download</button>
{/if}
</form>
<form action="/configfile" enctype="multipart/form-data" method="post" on:submit={() => configUploading=true} autocomplete="off">
<input style="display:none" name="file" type="file" accept=".cfg" bind:this={configFileInput} bind:files={configFiles}>
{#if configFiles.length == 0}
<button type="button" on:click={()=>{configFileInput.click();}} class="text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3">Select file...</button>
<button type="button" on:click={()=>{configFileInput.click();}} class="btn-pri-sm">Select file...</button>
{:else}
{configFiles[0].name}
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3">Upload</button>
<button type="submit" class="btn-pri-sm">Upload</button>
{/if}
</form>
</div>

View File

@@ -1,5 +1,10 @@
<script>
export let chip;
let gpioMax = 44;
$: {
gpioMax = chip == 'esp8266' ? 16 : chip == 'esp32s2' ? 44 : 39;
}
</script>
<option value={3}>UART0</option>
@@ -13,48 +18,15 @@
{#if chip == 'esp32s2'}
<option value={18}>UART1</option>
{/if}
<option value={4}>GPIO4</option>
<option value={5}>GPIO5</option>
{#if chip.startsWith('esp32')}
<option value={6}>GPIO6</option>
<option value={7}>GPIO7</option>
<option value={8}>GPIO8</option>
{/if}
{#if chip == 'esp8266'}
<option value={9}>GPIO9</option>
{/if}
<option value={10}>GPIO10</option>
{#if chip.startsWith('esp32')}
<option value={11}>GPIO11</option>
{/if}
<option value={12}>GPIO12</option>
<option value={13}>GPIO13</option>
<option value={14}>GPIO14</option>
<option value={15}>GPIO15</option>
{#if chip == 'esp32s2'}
<option value={16}>GPIO16</option>
{/if}
{#if chip.startsWith('esp32')}
<option value={17}>GPIO17</option>
{#if chip != 'esp32s2'}
<option value={18}>GPIO18</option>
{/if}
<option value={19}>GPIO19</option>
<option value={21}>GPIO21</option>
<option value={22}>GPIO22</option>
<option value={23}>GPIO23</option>
<option value={25}>GPIO25</option>
<option value={32}>GPIO32</option>
<option value={33}>GPIO33</option>
<option value={34}>GPIO34</option>
<option value={35}>GPIO35</option>
<option value={36}>GPIO36</option>
<option value={39}>GPIO39</option>
{/if}
{#if chip == 'esp32s2'}
<option value={40}>GPIO40</option>
<option value={41}>GPIO41</option>
<option value={42}>GPIO42</option>
<option value={43}>GPIO43</option>
<option value={44}>GPIO44</option>
{#each {length: gpioMax+1} as _, i}
{#if i > 3
&& !(chip == 'esp32' && (i == 9 || i == 16))
&& !(chip == 'esp32s2' && i == 18)
&& !(chip == 'esp8266' && (i == 3 || i == 113))
}
<option value={i}>GPIO{i}</option>
{/if}
{/each}
{/if}

View File

@@ -1,31 +1,33 @@
export async function upgrade(version) {
const data = new URLSearchParams()
data.append('version', version.tag_name);
export function upgradeWarningText(board) {
return 'WARNING: ' + board + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.'
}
export async function upgrade() {
const response = await fetch('/upgrade', {
method: 'POST',
body: data
method: 'POST'
});
let res = (await response.json())
await response.json();
}
export function getNextVersion(currentVersion, releases) {
export function getNextVersion(currentVersion, releases_orig) {
if(/^v\d{1,2}\.\d{1,2}\.\d{1,2}$/.test(currentVersion)) {
var v = currentVersion.substring(1).split('.');
var v_major = parseInt(v[0]);
var v_minor = parseInt(v[1]);
var v_patch = parseInt(v[2]);
let v = currentVersion.substring(1).split('.');
let v_major = parseInt(v[0]);
let v_minor = parseInt(v[1]);
let v_patch = parseInt(v[2]);
let releases = [...releases_orig];
releases.reverse();
var next_patch;
var next_minor;
var next_major;
for(var i = 0; i < releases.length; i++) {
var release = releases[i];
var ver2 = release.tag_name;
var v2 = ver2.substring(1).split('.');
var v2_major = parseInt(v2[0]);
var v2_minor = parseInt(v2[1]);
var v2_patch = parseInt(v2[2]);
let next_patch;
let next_minor;
let next_major;
for(let i = 0; i < releases.length; i++) {
let release = releases[i];
let ver2 = release.tag_name;
let v2 = ver2.substring(1).split('.');
let v2_major = parseInt(v2[0]);
let v2_minor = parseInt(v2[1]);
let v2_patch = parseInt(v2[2]);
if(v2_major == v_major) {
if(v2_minor == v_minor) {
@@ -37,10 +39,10 @@ export function getNextVersion(currentVersion, releases) {
}
} else if(v2_major == v_major+1) {
if(next_major) {
var mv = next_major.tag_name.substring(1).split('.');
var mv_major = parseInt(mv[0]);
var mv_minor = parseInt(mv[1]);
var mv_patch = parseInt(mv[2]);
let mv = next_major.tag_name.substring(1).split('.');
let mv_major = parseInt(mv[0]);
let mv_minor = parseInt(mv[1]);
let mv_patch = parseInt(mv[2]);
if(v2_minor == mv_minor) {
next_major = release;
}
@@ -58,6 +60,6 @@ export function getNextVersion(currentVersion, releases) {
}
return false;
} else {
return releases[0];
return releases_orig[0];
}
}

View File

@@ -31,6 +31,11 @@
});
navigate(sysinfo.usrcfg ? "/" : "/setup");
}
let cc = false;
$: {
cc = !sysinfo.usrcfg;
}
</script>
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
@@ -38,6 +43,9 @@
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
<input type="hidden" name="v" value="true"/>
<strong class="text-sm">Initial configuration</strong>
{#if sysinfo.usrcfg}
<div class="bd-red">WARNING: Changing this configuration will affect basic configuration of your device. Only make changes here if instructed by vendor</div>
{/if}
<div class="my-3">
Board type<br/>
<select name="vb" bind:value={sysinfo.board} class="in-s">
@@ -53,7 +61,7 @@
</div>
{/if}
<div class="my-3">
<label><input type="checkbox" name="vr" value="true" class="rounded mb-1" checked /> Clear all other configuration</label>
<label><input type="checkbox" name="vr" value="true" class="rounded mb-1" bind:checked={cc} /> Clear all other configuration</label>
</div>
<div class="my-3">
<button type="submit" class="btn-pri">Save</button>

View File

@@ -1,48 +1,42 @@
<script>
import BarChart from './BarChart.svelte';
import { voltcol } from './Helpers.js';
import { fmtnum, voltcol } from './Helpers.js';
export let u1;
export let u2;
export let u3;
export let ds;
let min = 200;
let max = 260;
let config = {};
function point(v) {
return {
label: fmtnum(v) + 'V',
value: isNaN(v) ? 0 : v,
color: voltcol(v ? v : 0)
};
};
$: {
let xTicks = [];
let points = [];
if(u1 > 0) {
xTicks.push({ label: ds === 1 ? 'L1-L2' : 'L1' });
points.push({
label: u1 ? u1.toFixed(0) + 'V' : '-',
value: u1 ? u1 : 0,
color: voltcol(u1 ? u1 : 0)
});
points.push(point(u1));
}
if(u2 > 0) {
xTicks.push({ label: ds === 1 ? 'L1-L3' : 'L2' });
points.push({
label: u2 ? u2.toFixed(0) + 'V' : '-',
value: u2 ? u2 : 0,
color: voltcol(u2 ? u2 : 0)
});
points.push(point(u2));
}
if(u3 > 0) {
xTicks.push({ label: ds === 1 ? 'L2-L3' : 'L3' });
points.push({
label: u3 ? u3.toFixed(0) + 'V' : '-',
value: u3 ? u3 : 0,
color: voltcol(u3 ? u3 : 0)
});
points.push(point(u3));
}
config = {
padding: { top: 20, right: 15, bottom: 20, left: 35 },
y: {
min: min,
max: max,
min: 200,
max: 260,
ticks: [
{ value: 207, label: '-10%' },
{ value: 230, label: '230v' },

View File

@@ -17,18 +17,18 @@ export default defineConfig({
plugins: [svelte()],
server: {
proxy: {
"/data.json": "http://192.168.233.244",
"/energyprice.json": "http://192.168.233.244",
"/dayplot.json": "http://192.168.233.244",
"/monthplot.json": "http://192.168.233.244",
"/temperature.json": "http://192.168.233.244",
"/sysinfo.json": "http://192.168.233.244",
"/configuration.json": "http://192.168.233.244",
"/tariff.json": "http://192.168.233.244",
"/save": "http://192.168.233.244",
"/reboot": "http://192.168.233.244",
"/configfile": "http://192.168.233.244",
"/upgrade": "http://192.168.233.244"
"/data.json": "http://192.168.233.235",
"/energyprice.json": "http://192.168.233.235",
"/dayplot.json": "http://192.168.233.235",
"/monthplot.json": "http://192.168.233.235",
"/temperature.json": "http://192.168.233.235",
"/sysinfo.json": "http://192.168.233.235",
"/configuration.json": "http://192.168.233.235",
"/tariff.json": "http://192.168.233.235",
"/save": "http://192.168.233.235",
"/reboot": "http://192.168.233.235",
"/configfile": "http://192.168.233.235",
"/upgrade": "http://192.168.233.235"
}
}
})