More v2.2

This commit is contained in:
Gunnar Skjold
2022-11-25 19:18:32 +01:00
parent d4d9d2224f
commit 2dcc874592
47 changed files with 2205 additions and 746 deletions

View File

@@ -3,7 +3,6 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<title>AMS reader</title>
</head>
<body class="bg-gray-100">

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"@tailwindcss/forms": "^0.5.2",
"autoprefixer": "^10.4.7",
"http-proxy-middleware": "^2.0.1",
"postcss": "^8.4.14",
@@ -18,7 +19,9 @@
"svelte-navigator": "^3.2.2",
"svelte-preprocess": "^4.10.7",
"tailwindcss": "^3.1.5",
"@tailwindcss/forms": "^0.5.2",
"vite": "^3.0.7"
},
"dependencies": {
"cssnano": "^5.1.14"
}
}

View File

@@ -1,5 +1,6 @@
const tailwindcss = require("tailwindcss");
const autoprefixer = require("autoprefixer");
const cssnano = require("cssnano");
const config = {
plugins: [
@@ -7,6 +8,7 @@ const config = {
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer,
cssnano()
],
};

View File

@@ -9,6 +9,7 @@
import VendorPanel from './lib/VendorPanel.svelte';
import SetupPanel from './lib/SetupPanel.svelte';
import Mask from './lib/Mask.svelte';
import FileUploadComponent from "./lib/FileUploadComponent.svelte";
let sysinfo = {};
sysinfoStore.subscribe(update => {
@@ -37,7 +38,16 @@
<ConfigurationPanel sysinfo={sysinfo}/>
</Route>
<Route path="/status">
<StatusPage sysinfo={sysinfo} data={data}/>
<StatusPage sysinfo={sysinfo}/>
</Route>
<Route path="/mqtt-ca">
<FileUploadComponent title="CA" action="/mqtt-ca"/>
</Route>
<Route path="/mqtt-cert">
<FileUploadComponent title="certificate" action="/mqtt-cert"/>
</Route>
<Route path="/mqtt-key">
<FileUploadComponent title="private key" action="/mqtt-key"/>
</Route>
</Router>

View File

@@ -5,4 +5,140 @@
.gh-logo {
width: 2rem;
height: 2rem;
}
}
.cnt {
@apply bg-white m-2 p-2 rounded shadow-lg
}
.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
}
.in-post {
@apply flex items-center bg-gray-100 rounded-r-md border border-l-0 border-gray-300 px-3 whitespace-nowrap text-sm
}
.in-txt {
@apply h-10 shadow-sm border-gray-300 disabled:bg-gray-200
}
.in-f {
@apply in-txt rounded-l-md
}
.in-m {
@apply in-txt border-l-0 w-full
}
.in-l {
@apply in-txt border-l-0 rounded-r-md
}
.in-s {
@apply in-txt rounded-md w-full
}
.tr {
@apply text-right
}
.bd-grn {
@apply my-auto bg-green-500 text-green-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
}
.bd-ylo {
@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 {
@apply my-auto bg-blue-500 text-blue-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
}
.bd-gry {
@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
}
.pl-root {
position: relative;
}
.pl-ov {
position: absolute;
top: 35%;
left: 25%;
width: 50%;
text-align: center;
}
.pl-val {
font-size: 1.7rem;
}
.pl-unt {
font-size: 1.0rem;
color: grey;
}
.pl-lab {
font-size: 1.0rem;
}
.chart {
width: 100%;
height: 100%;
margin: 0 auto;
}
svg {
position: relative;
width: 100%;
}
.tick {
font-family: Helvetica, Arial;
font-size: 0.85em;
font-weight: 200;
}
.tick line {
stroke: #e2e2e2;
stroke-dasharray: 2;
}
.tick text {
fill: #999;
text-anchor: start;
}
.tick.tick-0 line {
stroke-dasharray: 0;
}
.tick.tick-green line {
stroke: #32d900 !important;
}
.tick.tick-green text {
fill: #32d900 !important;
}
.tick.tick-orange line {
stroke: #d95600 !important;
}
.tick.tick-orange text {
fill: #d95600 !important;
}
.x-axis .tick text {
text-anchor: middle;
}
.bars rect {
stroke: rgb(0,0,0);
stroke-opacity: 0.25;
opacity: 0.9;
}
.bars text {
font-family: Helvetica, Arial;
font-size: 0.85em;
display: block;
text-align: center;
}

View File

@@ -8,45 +8,47 @@
<div class="mx-2 text-sm">
<strong>Real time calculation</strong>
{#if data && data.h !== undefined}
<div class="flex">
<div>Hour</div>
<div class="flex-auto text-right">{data && data.h && data.h.u ? data.h.u.toFixed(2) : '-'} kWh {#if currency && (hasExport)}/ {data && data.h && data.h.c ? data.h.c.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh {#if currency && (hasExport)}/ {data.h.c ? data.h.c.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Day</div>
<div class="flex-auto text-right">{data && data.d && data.d.u ? data.d.u.toFixed(1) : '-'} kWh {#if currency && (hasExport)}/ {data && data.d && data.d.c ? data.d.c.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh {#if currency && (hasExport)}/ {data.d.c ? data.d.c.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Month</div>
<div class="flex-auto text-right">{data && data.m && data.m.u ? data.m.u.toFixed(0) : '-'} kWh {#if currency && (hasExport)}/ {data && data.m && data.m.c ? data.m.c.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh {#if currency && (hasExport)}/ {data.m.c ? data.m.c.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="mt-4">
{#if hasExport}
<div class="flex">
<div>Hour</div>
<div class="flex-auto text-right">{data && data.h && data.h.p ? data.h.p.toFixed(2) : '-'} kWh {#if currency}/ {data && data.h && data.h.pc ? data.h.pc.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh {#if currency}/ {data.h.pc ? data.h.pc.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Day</div>
<div class="flex-auto text-right">{data && data.d && data.d.p ? data.d.p.toFixed(1) : '-'} kWh {#if currency}/ {data && data.d && data.d.pc ? data.d.pc.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh {#if currency}/ {data.d.pc ? data.d.pc.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Month</div>
<div class="flex-auto text-right">{data && data.m && data.m.p ? data.m.p.toFixed(0) : '-'} kWh {#if currency}/ {data && data.m && data.m.pc ? data.m.pc.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh {#if currency}/ {data.m.pc ? data.m.pc.toFixed(2) : '-'} {currency}{/if}</div>
</div>
{:else}
<div class="flex">
<div>Hour</div>
<div class="flex-auto text-right">{data && data.h && data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
<div class="flex-auto text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
</div>
<div class="flex">
<div>Day</div>
<div class="flex-auto text-right">{data && data.d && data.d.c ? data.d.c.toFixed(2) : '-'} {currency}</div>
<div class="flex-auto text-right">{data.d.c ? data.d.c.toFixed(2) : '-'} {currency}</div>
</div>
<div class="flex">
<div>Month</div>
<div class="flex-auto text-right">{data && data.m && data.m.c ? data.m.c.toFixed(2) : '-'} {currency}</div>
<div class="flex-auto text-right">{data.m.c ? data.m.c.toFixed(2) : '-'} {currency}</div>
</div>
{/if}
</div>
{/if}
</div>

View File

@@ -4,13 +4,13 @@
export let text;
</script>
{#if color == 'green'}
<span title={title} class="my-auto bg-green-500 text-green-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">{text}</span>
<span title={title} class="bd-grn">{text}</span>
{:else if color === `yellow`}
<span title={title} class="my-auto bg-yellow-500 text-yellow-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">{text}</span>
<span title={title} class="bd-ylo">{text}</span>
{:else if color === `red`}
<span title={title} class="my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">{text}</span>
<span title={title} class="bd-red">{text}</span>
{:else if color === `blue`}
<span title={title} class="my-auto bg-blue-500 text-blue-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">{text}</span>
<span title={title} class="bd-blu">{text}</span>
{:else if color === `gray`}
<span title={title} class="my-auto bg-gray-500 text-gray-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">{text}</span>
<span title={title} class="bd-gry">{text}</span>
{/if}

View File

@@ -94,69 +94,3 @@
</g>
</svg>
</div>
<style>
.chart {
width: 100%;
height: 100%;
margin: 0 auto;
}
svg {
position: relative;
width: 100%;
}
.tick {
font-family: Helvetica, Arial;
font-size: .725em;
font-weight: 200;
}
.tick line {
stroke: #e2e2e2;
stroke-dasharray: 2;
}
.tick text {
fill: #999;
text-anchor: start;
}
.tick.tick-0 line {
stroke-dasharray: 0;
}
.tick.tick-green line {
stroke: #32d900 !important;
}
.tick.tick-green text {
fill: #32d900 !important;
}
.tick.tick-orange line {
stroke: #d95600 !important;
}
.tick.tick-orange text {
fill: #d95600 !important;
}
.x-axis .tick text {
text-anchor: middle;
}
.bars rect {
stroke: rgb(0,0,0);
stroke-opacity: 0.25;
opacity: 0.9;
}
.bars text {
font-family: Helvetica, Arial;
font-size: .725em;
display: block;
text-align: center;
}
</style>

View File

@@ -1,8 +1,7 @@
<script>
import { zeropad } from './Helpers.js';
import { zeropad, monthnames } from './Helpers.js';
export let timestamp;
let monthnames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
</script>
{#if Math.abs(new Date().getTime()-timestamp.getTime()) < 300000 }

View File

@@ -5,6 +5,9 @@
import Mask from './Mask.svelte'
import Badge from './Badge.svelte';
import HelpIcon from './HelpIcon.svelte';
import CountrySelectOptions from './CountrySelectOptions.svelte';
import { Link, navigate } from 'svelte-navigator';
export let sysinfo = {}
@@ -87,71 +90,95 @@
});
loadingOrSaving = false;
getConfiguration();
navigate("/");
}
async function reboot() {
const response = await fetch('/reboot', {
method: 'POST'
});
let res = (await response.json())
}
const askReboot = function() {
if(confirm('Are you sure you want to reboot the device?')) {
sysinfoStore.update(s => {
s.booting = true;
return s;
});
reboot();
}
}
const updateMqttPort = function() {
if(configuration.q.s.e) {
if(configuration.q.p == 1883) configuration.q.p = 8883;
} else {
if(configuration.q.p == 8883) configuration.q.p = 1883;
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">General</strong>
<input type="hidden" name="g" value="true"/>
<div class="my-1">
<div class="flex">
<div>
Hostname<br/>
<input name="gh" bind:value={configuration.g.h} type="text" class="h-10 rounded-l-md shadow-sm border-gray-300 w-full"/>
<input name="gh" bind:value={configuration.g.h} type="text" class="in-f w-full"/>
</div>
<div>
Timezone<br/>
<select name="gt" bind:value={configuration.g.t} class="h-10 rounded-r-md border-l-0 shadow-sm border-gray-300">
<option value="UTC">UTC</option>
<option value="CET/CEST">CET/CEST</option>
Time zone<br/>
<select name="gt" bind:value={configuration.g.t} class="in-l">
<CountrySelectOptions/>
</select>
</div>
</div>
</div>
<input type="hidden" name="p" value="true"/>
<div class="my-1">
Price region<br/>
<select name="pr" bind:value={configuration.p.r} class="in-s">
<optgroup label="Norway">
<option value="10YNO-1--------2">NO1</option>
<option value="10YNO-2--------T">NO2</option>
<option value="10YNO-3--------J">NO3</option>
<option value="10YNO-4--------9">NO4</option>
<option value="10Y1001A1001A48H">NO5</option>
</optgroup>
<optgroup label="Sweden">
<option value="10Y1001A1001A44P">SE1</option>
<option value="10Y1001A1001A45N">SE2</option>
<option value="10Y1001A1001A46L">SE3</option>
<option value="10Y1001A1001A47J">SE4</option>
</optgroup>
<optgroup label="Denmark">
<option value="10YDK-1--------W">DK1</option>
<option value="10YDK-2--------M">DK2</option>
</optgroup>
<option value="10YAT-APG------L">Austria</option>
<option value="10YBE----------2">Belgium</option>
<option value="10YCZ-CEPS-----N">Czech Republic</option>
<option value="10Y1001A1001A39I">Estonia</option>
<option value="10YFI-1--------U">Finland</option>
<option value="10YFR-RTE------C">France</option>
<option value="10Y1001A1001A83F">Germany</option>
<option value="10YGB----------A">Great Britain</option>
<option value="10YLV-1001A00074">Latvia</option>
<option value="10YLT-1001A0008Q">Lithuania</option>
<option value="10YNL----------L">Netherland</option>
<option value="10YPL-AREA-----S">Poland</option>
<option value="10YCH-SWISSGRIDZ">Switzerland</option>
</select>
</div>
<div class="my-1">
<div class="flex">
<div>
Price region<br/>
<select name="pr" bind:value={configuration.p.r} class="h-10 rounded-l-md shadow-sm border-gray-300">
<optgroup label="Norway">
<option value="10YNO-1--------2">NO1</option>
<option value="10YNO-2--------T">NO2</option>
<option value="10YNO-3--------J">NO3</option>
<option value="10YNO-4--------9">NO4</option>
<option value="10Y1001A1001A48H">NO5</option>
</optgroup>
<optgroup label="Sweden">
<option value="10Y1001A1001A44P">SE1</option>
<option value="10Y1001A1001A45N">SE2</option>
<option value="10Y1001A1001A46L">SE3</option>
<option value="10Y1001A1001A47J">SE4</option>
</optgroup>
<optgroup label="Denmark">
<option value="10YDK-1--------W">DK1</option>
<option value="10YDK-2--------M">DK2</option>
</optgroup>
<option value="10YAT-APG------L">Austria</option>
<option value="10YBE----------2">Belgium</option>
<option value="10YCZ-CEPS-----N">Czech Republic</option>
<option value="10Y1001A1001A39I">Estonia</option>
<option value="10YFI-1--------U">Finland</option>
<option value="10YFR-RTE------C">France</option>
<option value="10Y1001A1001A83F">Germany</option>
<option value="10YGB----------A">Great Britain</option>
<option value="10YLV-1001A00074">Latvia</option>
<option value="10YLT-1001A0008Q">Lithuania</option>
<option value="10YNL----------L">Netherland</option>
<option value="10YPL-AREA-----S">Poland</option>
<option value="10YCH-SWISSGRIDZ">Switzerland</option>
</select>
</div>
<div>
Currency<br/>
<select name="pc" bind:value={configuration.p.c} class="h-10 border-l-0 shadow-sm border-gray-300">
<select name="pc" bind:value={configuration.p.c} class="in-f">
<option value="NOK">NOK</option>
<option value="SEK">SEK</option>
<option value="DKK">DKK</option>
@@ -160,7 +187,7 @@
</div>
<div>
Multiplier<br/>
<input name="pm" bind:value={configuration.p.m} type="number" min="0.001" max="1000" step="0.001" class="h-10 rounded-r-md border-l-0 shadow-sm border-gray-300 w-24 text-right"/>
<input name="pm" bind:value={configuration.p.m} type="number" min="0.001" max="1000" step="0.001" class="in-l tr"/>
</div>
</div>
</div>
@@ -168,13 +195,13 @@
<div class="my-1">
<label><input type="checkbox" name="pe" bind:checked={configuration.p.e} class="rounded mb-1"/> ENTSO-E token</label>
{#if configuration.p.e}
<br/><input name="pt" bind:value={configuration.p.t} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<br/><input name="pt" bind:value={configuration.p.t} type="text" class="in-s"/>
{/if}
</div>
{/if}
<div class="my-1">
Security<br/>
<select name="gs" bind:value={configuration.g.s} class="h-10 rounded-md shadow-sm border-gray-300">
<select name="gs" bind:value={configuration.g.s} class="in-s">
<option value={0}>None</option>
<option value={1}>Only configuration</option>
<option value={2}>Everything</option>
@@ -183,22 +210,22 @@
{#if configuration.g.s > 0}
<div class="my-1">
Username<br/>
<input name="gu" bind:value={configuration.g.u} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="gu" bind:value={configuration.g.u} type="text" class="in-s"/>
</div>
<div class="my-1">
Password<br/>
<input name="gp" bind:value={configuration.g.p} type="password" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="gp" bind:value={configuration.g.p} type="password" class="in-s"/>
</div>
{/if}
</div>
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Meter</strong>
<a href="https://github.com/gskjold/AmsToMqttBridge/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="h-10 rounded-l-md shadow-sm border-gray-300">
<select name="mb" bind:value={configuration.m.b} class="in-f">
<option value={2400}>2400</option>
<option value={4800}>4800</option>
<option value={9600}>9600</option>
@@ -207,37 +234,37 @@
<option value={57600}>57600</option>
<option value={115200}>115200</option>
</select>
<select name="mp" bind:value={configuration.m.p} class="h-10 rounded-r-md border-l-0 shadow-sm border-gray-300">
<select name="mp" bind:value={configuration.m.p} class="in-l">
<option value={2}>7N1</option>
<option value={3}>8N1</option>
<option value={10}>7E1</option>
<option value={11}>8E1</option>
</select>
<label class="mt-2 ml-3"><input name="mi" value="true" bind:checked={configuration.m.i} type="checkbox" class="rounded mb-1"/> inverted</label>
<label class="mt-2 ml-3 whitespace-nowrap"><input name="mi" value="true" bind:checked={configuration.m.i} type="checkbox" class="rounded mb-1"/> inverted</label>
</div>
</div>
<div class="my-1">
Voltage<br/>
<select name="md" bind:value={configuration.m.d} class="in-s">
<option value={0}></option>
<option value={1}>230V (IT/TT)</option>
<option value={2}>400V (TN)</option>
</select>
</div>
<div class="my-1 flex">
<div class="w-32">
Voltage<br/>
<select name="md" bind:value={configuration.m.d} class="h-10 rounded-l-md shadow-sm border-gray-300 w-full">
<option value={0}></option>
<option value={1}>230V</option>
<option value={2}>400V</option>
</select>
</div>
<div>
<div class="mx-1">
Main fuse<br/>
<label class="flex">
<input name="mf" bind:value={configuration.m.f} type="number" min="5" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">A</span>
<input name="mf" bind:value={configuration.m.f} type="number" min="5" max="255" class="in-f tr"/>
<span class="in-post">A</span>
</label>
</div>
<div>
<div class="mx-1">
Production<br/>
<label class="flex">
<input name="mr" bind:value={configuration.m.r} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWp</span>
<input name="mr" bind:value={configuration.m.r} type="number" min="0" max="255" class="in-f tr"/>
<span class="in-post">kWp</span>
</label>
</div>
</div>
@@ -247,13 +274,13 @@
<div class="my-1">
<label><input type="checkbox" name="me" value="true" bind:checked={configuration.m.e.e} class="rounded mb-1"/> Meter is encrypted</label>
{#if configuration.m.e.e}
<br/><input name="mek" bind:value={configuration.m.e.k} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<br/><input name="mek" bind:value={configuration.m.e.k} type="text" class="in-s"/>
{/if}
</div>
{#if configuration.m.e.e}
<div class="my-1">
Authentication key<br/>
<input name="mea" bind:value={configuration.m.e.a} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="mea" bind:value={configuration.m.e.a} type="text" class="in-s"/>
</div>
{/if}
@@ -262,39 +289,39 @@
<div class="flex my-1">
<div class="w-1/4">
Instant<br/>
<input name="mmw" bind:value={configuration.m.m.w} type="number" min="0.00" max="655.35" step="0.01" class="h-10 rounded-l-md shadow-sm border-gray-300 w-full text-right"/>
<input name="mmw" bind:value={configuration.m.m.w} type="number" min="0.00" max="655.35" step="0.01" class="in-f tr w-full"/>
</div>
<div class="w-1/4">
Volt<br/>
<input name="mmv" bind:value={configuration.m.m.v} type="number" min="0.00" max="655.35" step="0.01" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<input name="mmv" bind:value={configuration.m.m.v} type="number" min="0.00" max="655.35" step="0.01" class="in-m tr"/>
</div>
<div class="w-1/4">
Amp<br/>
<input name="mma" bind:value={configuration.m.m.a} type="number" min="0.00" max="655.35" step="0.01" class="h-10 border-r-0 shadow-sm border-gray-300 w-full text-right"/>
<input name="mma" bind:value={configuration.m.m.a} type="number" min="0.00" max="655.35" step="0.01" class="in-m tr"/>
</div>
<div class="w-1/4">
Acc.<br/>
<input name="mmc" bind:value={configuration.m.m.c} type="number" min="0.00" max="655.35" step="0.01" class="h-10 rounded-r-md shadow-sm border-gray-300 w-full text-right"/>
<input name="mmc" bind:value={configuration.m.m.c} type="number" min="0.00" max="655.35" step="0.01" class="in-l tr w-full"/>
</div>
</div>
{/if}
</div>
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">WiFi</strong>
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki/WiFi-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="w" value="true"/>
<div class="my-1">
SSID<br/>
<input name="ws" bind:value={configuration.w.s} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="ws" bind:value={configuration.w.s} type="text" class="in-s"/>
</div>
<div class="my-1">
Password<br/>
<input name="wp" bind:value={configuration.w.p} type="password" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="wp" bind:value={configuration.w.p} type="password" class="in-s"/>
</div>
<div class="my-1 flex">
<div>
Power saving<br/>
<select name="wz" bind:value={configuration.w.z} class="h-10 rounded-md shadow-sm border-gray-300">
<select name="wz" bind:value={configuration.w.z} class="in-s">
<option value={255}>Default</option>
<option value={0}>Off</option>
<option value={1}>Minimum</option>
@@ -304,24 +331,24 @@
<div class="ml-2">
Power<br/>
<label class="flex">
<input name="ww" bind:value={configuration.w.w} type="number" min="0" max="20.5" step="0.5" class="h-10 rounded-l-md shadow-sm border-gray-300 text-right w-full"/>
<span class="flex items-center bg-gray-100 rounded-r-md border-l-0 border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">dBm</span>
<input name="ww" bind:value={configuration.w.w} type="number" min="0" max="20.5" step="0.5" class="in-f tr"/>
<span class="in-post">dBm</span>
</label>
</div>
</div>
</div>
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Network</strong>
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki/Network-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<div class="my-1">
IP<br/>
<div class="flex">
<select name="nm" bind:value={configuration.n.m} class="h-10 rounded-l-md shadow-sm border border-gray-300">
<select name="nm" bind:value={configuration.n.m} class="in-f">
<option value="dhcp">DHCP</option>
<option value="static">Static</option>
</select>
<input name="ni" bind:value={configuration.n.i} type="text" class="h-10 border-x-0 shadow-sm border-gray-300 w-full disabled:bg-gray-200" disabled={configuration.n.m == 'dhcp'}/>
<select name="ns" bind:value={configuration.n.s} class="h-10 rounded-r-md shadow-sm border-gray-300 disabled:bg-gray-200" disabled={configuration.n.m == 'dhcp'}>
<input name="ni" bind:value={configuration.n.i} type="text" class="in-m" disabled={configuration.n.m == 'dhcp'}/>
<select name="ns" bind:value={configuration.n.s} class="in-l" disabled={configuration.n.m == 'dhcp'}>
<option value="255.255.255.0">/24</option>
<option value="255.255.0.0">/16</option>
<option value="255.0.0.0">/8</option>
@@ -331,13 +358,13 @@
{#if configuration.n.m == 'static'}
<div class="my-1">
Gateway<br/>
<input name="ng" bind:value={configuration.n.g} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full disabled:bg-gray-200"/>
<input name="ng" bind:value={configuration.n.g} type="text" class="in-s"/>
</div>
<div class="my-1">
DNS<br/>
<div class="flex">
<input name="nd1" bind:value={configuration.n.d1} type="text" class="h-10 rounded-l-md shadow-sm border-gray-300 w-full disabled:bg-gray-200"/>
<input name="nd2" bind:value={configuration.n.d2} type="text" class="h-10 border-l-0 rounded-r-md shadow-sm border-gray-300 w-full disabled:bg-gray-200"/>
<input name="nd1" bind:value={configuration.n.d1} type="text" class="in-f w-full"/>
<input name="nd2" bind:value={configuration.n.d2} type="text" class="in-l w-full"/>
</div>
</div>
{/if}
@@ -348,62 +375,70 @@
<div class="my-1">
NTP <label class="ml-4"><input name="ntpd" value="true" bind:checked={configuration.n.h} type="checkbox" class="rounded mb-1"/> obtain from DHCP</label><br/>
<div class="flex">
<input name="ntph" bind:value={configuration.n.n1} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="ntph" bind:value={configuration.n.n1} type="text" class="in-s"/>
</div>
</div>
</div>
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">MQTT</strong>
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki/MQTT-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="q" value="true"/>
<div class="my-1">
Server
<label class="float-right mr-3"><input type="checkbox" name="qs" bind:checked={configuration.q.s.e} class="rounded mb-1"/> SSL</label>
{#if sysinfo.chip != 'esp8266'}
<label class="float-right mr-3"><input type="checkbox" name="qs" value="true" bind:checked={configuration.q.s.e} class="rounded mb-1" on:change={updateMqttPort}/> SSL</label>
{/if}
<br/>
<div class="flex">
<input name="qh" bind:value={configuration.q.h} type="text" class="h-10 rounded-l-md shadow-sm border-gray-300 w-full"/>
<input name="qp" bind:value={configuration.q.p} type="number" min="1024" max="65535" class="h-10 border-l-0 rounded-r-md shadow-sm border-gray-300 w-20 text-right"/>
<input name="qh" bind:value={configuration.q.h} type="text" class="in-f w-full"/>
<input name="qp" bind:value={configuration.q.p} type="number" min="1024" max="65535" class="in-l tr"/>
</div>
</div>
{#if configuration.q.s.e}
<div class="my-1">
<div>
{#if configuration.q.s.c}
<Badge color="green" text="CA OK" title="Click here to replace CA"/>
{:else}
<Badge color="blue" text="Upload CA" title="Click here to upload CA"/>
{/if}
<Link to="/mqtt-ca">
{#if configuration.q.s.c}
<Badge color="green" text="CA OK" title="Click here to replace CA"/>
{:else}
<Badge color="blue" text="Upload CA" title="Click here to upload CA"/>
{/if}
</Link>
{#if configuration.q.s.r}
<Badge color="green" text="Cert OK" title="Click here to replace certificate"/>
{:else}
<Badge color="blue" text="Upload cert" title="Click here to upload certificate"/>
{/if}
<Link to="/mqtt-cert">
{#if configuration.q.s.r}
<Badge color="green" text="Cert OK" title="Click here to replace certificate"/>
{:else}
<Badge color="blue" text="Upload cert" title="Click here to upload certificate"/>
{/if}
</Link>
{#if configuration.q.s.k}
<Badge color="green" text="Key OK" title="Click here to replace key"/>
{:else}
<Badge color="blue" text="Upload key" title="Click here to upload key"/>
{/if}
<Link to="/mqtt-key">
{#if configuration.q.s.k}
<Badge color="green" text="Key OK" title="Click here to replace key"/>
{:else}
<Badge color="blue" text="Upload key" title="Click here to upload key"/>
{/if}
</Link>
</div>
</div>
{/if}
<div class="my-1">
Username<br/>
<input name="qu" bind:value={configuration.q.u} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="qu" bind:value={configuration.q.u} type="text" class="in-s"/>
</div>
<div class="my-1">
Password<br/>
<input name="qa" bind:value={configuration.q.a} type="password" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="qa" bind:value={configuration.q.a} type="password" class="in-s"/>
</div>
<div class="my-1 flex">
<div>
Client ID<br/>
<input name="qc" bind:value={configuration.q.c} type="text" class="h-10 rounded-l-md shadow-sm border-gray-300 w-full"/>
<input name="qc" bind:value={configuration.q.c} type="text" class="in-f w-full"/>
</div>
<div>
Payload<br/>
<select name="qm" bind:value={configuration.q.m} class="h-10 border-l-0 rounded-r-md shadow-sm border-gray-300 w-36">
<select name="qm" bind:value={configuration.q.m} class="in-l">
<option value={0}>JSON</option>
<option value={1}>Raw (minimal)</option>
<option value={2}>Raw (full)</option>
@@ -415,70 +450,70 @@
</div>
<div class="my-1">
Publish topic<br/>
<input name="qb" bind:value={configuration.q.b} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="qb" bind:value={configuration.q.b} type="text" class="in-s"/>
</div>
</div>
{#if configuration.p.r.startsWith("10YNO") || configuration.p.r == '10Y1001A1001A48H'}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Tariff thresholds</strong>
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki/Threshold-configuration" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="t" value="true"/>
<div class="flex flex-wrap my-1">
<label class="flex w-40 m-1">
<span class="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">1</span>
<input name="t0" bind:value={configuration.t.t[0]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">2</span>
<input name="t1" bind:value={configuration.t.t[1]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">3</span>
<input name="t2" bind:value={configuration.t.t[2]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">4</span>
<input name="t3" bind:value={configuration.t.t[3]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">5</span>
<input name="t4" bind:value={configuration.t.t[4]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">6</span>
<input name="t5" bind:value={configuration.t.t[5]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">7</span>
<input name="t6" bind:value={configuration.t.t[6]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">8</span>
<input name="t7" bind:value={configuration.t.t[7]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">9</span>
<input name="t8" bind:value={configuration.t.t[8]} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-full text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">kWh</span>
<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-post">kWh</span>
</label>
</div>
<label class="flex m-1">
<span class="flex items-center bg-gray-100 rounded-l-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">Average of</span>
<input name="th" bind:value={configuration.t.h} type="number" min="0" max="255" class="h-10 border-x-0 shadow-sm border-gray-300 w-24 text-right"/>
<span class="flex items-center bg-gray-100 rounded-r-md border border-gray-300 px-3 whitespace-no-wrap text-grey-dark text-sm">hours</span>
<span class="in-pre">Average of</span>
<input name="th" bind:value={configuration.t.h} type="number" min="0" max="255" class="in-txt tr w-full"/>
<span class="in-post">hours</span>
</label>
</div>
{/if}
{#if sysinfo.board > 20 || sysinfo.chip == 'esp8266'}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Hardware</strong>
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki/GPIO-configuration" target="_blank" class="float-right"><HelpIcon/></a>
{#if sysinfo.board > 20}
@@ -486,48 +521,48 @@
<div class="flex flex-wrap">
<div>
HAN<br/>
<select name="ih" bind:value={configuration.i.h} class="h-10 rounded-l-md shadow-sm border-gray-300">
<select name="ih" bind:value={configuration.i.h} class="in-f">
<UartSelectOptions chip={sysinfo.chip}/>
</select>
</div>
<div>
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="h-10 border-x-0 shadow-sm border-gray-300 text-right"/>
<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"/>
</div>
<div>
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="h-10 rounded-r-md shadow-sm border-gray-300 text-right"/>
<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"/>
</div>
</div>
<div>
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="h-10 rounded-l-md shadow-sm border-gray-300 text-right"/>
<input name="irg" bind:value={configuration.i.r.g} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="h-10 border-x-0 shadow-sm border-gray-300 text-right"/>
<input name="irb" bind:value={configuration.i.r.b} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="h-10 rounded-r-md shadow-sm border-gray-300 text-right"/>
<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"/>
<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"/>
<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"/>
</div>
</div>
<div class="my-1">
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="h-10 rounded-l-md shadow-sm border-gray-300 text-right"/>
<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"/>
</div>
<div class="my-1">
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="h-10 border-x-0 shadow-sm border-gray-300 text-right"/>
<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"/>
</div>
{#if sysinfo.chip != 'esp8266'}
<div class="my-1">
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="h-10 rounded-r-md shadow-sm border-gray-300 text-right"/>
<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"/>
</div>
{/if}
{#if configuration.i.v.p > 0}
<div class="my-1">
Voltage divider<br/>
<div class="flex">
<input name="ivdv" bind:value={configuration.i.v.d.v} type="number" min="0" max="65535" class="h-10 rounded-l-md shadow-sm border-gray-300 text-right" placeholder="VCC"/>
<input name="ivdg" bind:value={configuration.i.v.d.g} type="number" min="0" max="65535" class="h-10 border-l-0 rounded-r-md shadow-sm border-gray-300 text-right" placeholder="GND"/>
<input name="ivdv" bind:value={configuration.i.v.d.v} type="number" min="0" max="65535" class="in-f tr w-full" placeholder="VCC"/>
<input name="ivdg" bind:value={configuration.i.v.d.g} type="number" min="0" max="65535" class="in-l tr w-full" placeholder="GND"/>
</div>
</div>
{/if}
@@ -538,38 +573,39 @@
<div class="my-1 flex flex-wrap">
<div>
Vcc offset<br/>
<input name="ivo" bind:value={configuration.i.v.o} type="number" min="0.0" max="3.5" step="0.01" class="h-10 rounded-l-md shadow-sm border-gray-300 w-24 text-right"/>
<input name="ivo" bind:value={configuration.i.v.o} type="number" min="0.0" max="3.5" step="0.01" class="in-f tr"/>
</div>
<div>
multiplier<br/>
<input name="ivm" bind:value={configuration.i.v.m} type="number" min="0.1" max="10" step="0.01" class="h-10 border-l-0 rounded-r-md shadow-sm border-gray-300 w-24 text-right"/>
Multiplier<br/>
<input name="ivm" bind:value={configuration.i.v.m} type="number" min="0.1" max="10" step="0.01" class="in-l tr"/>
</div>
{#if sysinfo.board == 2 || sysinfo.board == 100}
<div class="ml-2">
boot limit<br/>
<input name="ivb" bind:value={configuration.i.v.b} type="number" min="2.5" max="3.5" step="0.1" class="h-10 rounded-md shadow-sm border-gray-300 w-24 text-right"/>
<div>
Boot limit<br/>
<input name="ivb" bind:value={configuration.i.v.b} type="number" min="2.5" max="3.5" step="0.1" class="in-s tr"/>
</div>
{/if}
</div>
{/if}
</div>
{/if}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Debugging</strong>
<a href="https://amsleser.no/blog/post/24-telnet-debug" target="_blank" class="float-right"><HelpIcon/></a>
<input type="hidden" name="d" value="true"/>
<div class="mt-3">
<label><input type="checkbox" name="ds" value="true" bind:checked={configuration.d.s} class="rounded mb-1"/> Enable debugging</label>
</div>
{#if configuration.d.s}
<div class="my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">Debug can cause sudden reboots. Do not leave on!</div>
<div class="bd-red">Debug can cause sudden reboots. Do not leave on!</div>
<div class="my-1">
<label><input type="checkbox" name="dt" value="true" bind:checked={configuration.d.t} class="rounded mb-1"/> Enable telnet</label>
</div>
{#if configuration.d.t}
<div class="my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">Telnet is unsafe and should be off when not in use</div>
<div class="bd-red">Telnet is unsafe and should be off when not in use</div>
{/if}
<div class="my-1">
<select name="dl" bind:value={configuration.d.l} class="form-control form-control-sh-10 rounded-md shadow-sm border-gray-300m">
<select name="dl" bind:value={configuration.d.l} class="in-s">
<option value={1}>Verbose</option>
<option value={2}>Debug</option>
<option value={3}>Info</option>
@@ -579,8 +615,17 @@
{/if}
</div>
</div>
<button type="button" on:click={factoryReset} class="font-bold py-2 px-4 rounded bg-red-500 text-white ml-2">Factory reset</button>
<button type="submit" class="font-bold py-2 px-4 rounded bg-blue-500 text-white float-right mr-2">Save</button>
<div class="grid grid-cols-3">
<div>
<button type="button" on:click={factoryReset} class="py-2 px-4 rounded bg-red-500 text-white ml-2">Factory reset</button>
</div>
<div class="text-center">
<button type="button" on:click={askReboot} class="py-2 px-4 rounded bg-yellow-500 text-white">Reboot</button>
</div>
<div class="text-right">
<button type="submit" class="btn-pri">Save</button>
</div>
</div>
</form>
<Mask active={loadingOrSaving} message="Loading configuration"/>
<Mask active={isFactoryReset} message="Device have been factory reset and switched to AP mode"/>

View File

@@ -1,10 +0,0 @@
<script>
let count = 0
const increment = () => {
count += 1
}
</script>
<button on:click={increment}>
count is {count}
</button>

View File

@@ -0,0 +1,10 @@
<script>
let europe = ["Amsterdam","Athens","Belfast","Berlin","Bratislava","Brussels","Bucharest","Budapest","Copenhagen","Dublin",
"Helsinki","Lisbon","Ljubljana","London","Luxembourg","Madrid","Malta","Nicosia","Oslo","Paris","Prague","Riga","Rome",
"Sofia","Stockholm","Tallinn","Vienna","Vilnius","Warsaw","Zagreb","Zurich"];
</script>
<option>GMT</option>
{#each europe as c}
<option>Europe/{c}</option>
{/each}

View File

@@ -32,7 +32,7 @@
</script>
<div class="grid xl:grid-cols-6 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2">
<div class="bg-white m-2 p-2 rounded shadow-lg">
<div class="cnt">
<div class="grid grid-cols-2">
<div class="col-span-2">
<PowerGauge val={data.i ? data.i : 0} max={data.im} unit="W" label="Import"/>
@@ -42,7 +42,7 @@
</div>
</div>
{#if data.om || data.e > 0}
<div class="bg-white m-2 p-2 rounded shadow-lg">
<div class="cnt">
<div class="grid grid-cols-2">
<div class="col-span-2">
<PowerGauge val={data.e ? data.e : 0} max={data.om ? data.om : 10000} unit="W" label="Export"/>
@@ -52,36 +52,40 @@
</div>
</div>
{/if}
<div class="bg-white m-2 p-2 rounded shadow-lg">
{#if data.u1 > 100 || data.u2 > 100 || data.u3 > 100}
<div class="cnt">
<VoltPlot u1={data.u1} u2={data.u2} u3={data.u3} ds={data.ds}/>
</div>
<div class="bg-white m-2 p-2 rounded shadow-lg">
{/if}
{#if data.i1 > 0.01 || data.i2 > 0.01 || data.i3 > 0.01}
<div class="cnt">
<AmpPlot u1={data.u1} u2={data.u2} u3={data.u3} i1={data.i1} i2={data.i2} i3={data.i3} max={data.mf ? data.mf : 32}/>
</div>
<div class="bg-white m-2 p-2 rounded shadow-lg">
{/if}
<div class="cnt">
<ReactiveData importInstant={data.ri} exportInstant={data.re} importTotal={data.ric} exportTotal={data.rec}/>
</div>
<div class="bg-white m-2 p-2 rounded shadow-lg">
<div class="cnt">
<AccountingData data={data.ea} currency={prices.currency}/>
</div>
{#if prices.currency == 'NOK'}
<div class="bg-white m-2 p-2 rounded shadow-lg h-64">
{#if data && data.ea}
<div class="cnt h-64">
<TariffPeakChart data={data.ea} />
</div>
{/if}
{#if prices.currency}
<div class="bg-white m-2 p-2 rounded shadow-lg xl:col-span-6 lg:col-span-3 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt xl:col-span-6 lg:col-span-3 md:col-span-3 sm:col-span-2 h-64">
<PricePlot json={prices}/>
</div>
{/if}
<div class="bg-white m-2 p-2 rounded shadow-lg xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<DayPlot json={dayPlot} />
</div>
<div class="bg-white m-2 p-2 rounded shadow-lg xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<MonthPlot json={monthPlot} />
</div>
{#if data.t && data.t != -127 && temperatures.c > 1}
<div class="bg-white m-2 p-2 rounded shadow-lg xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<TemperaturePlot json={temperatures} />
</div>
{/if}

View File

@@ -127,6 +127,19 @@ export const temperaturesStore = writable(temperatures, (set) => {
return function stop() {}
});
let tariff = {};
export async function getTariff() {
const response = await fetchWithTimeout("/tariff.json");
tariff = (await response.json())
tariffStore.set(tariff);
let date = new Date();
setTimeout(getTariff, (61-date.getMinutes())*60000)
}
export const tariffStore = writable(tariff, (set) => {
return function stop() {}
});
let releases = [];
export const gitHubReleaseStore = writable(releases);

View File

@@ -14,6 +14,7 @@
let xTicks = [];
let points = [];
let cur = new Date();
let offset = -cur.getTimezoneOffset()/60;
for(i = cur.getUTCHours(); i<24; i++) {
let imp = json["i"+zeropad(i)];
let exp = json["e"+zeropad(i)];
@@ -21,7 +22,7 @@
if(exp === undefined) exp = 0;
xTicks.push({
label: zeropad(i)
label: zeropad((i+offset)%24)
});
points.push({
label: imp.toFixed(1),
@@ -40,7 +41,7 @@
if(exp === undefined) exp = 0;
xTicks.push({
label: zeropad(i)
label: zeropad((i+offset)%24)
});
points.push({
label: imp.toFixed(1),
@@ -58,13 +59,15 @@
max = boundary;
min = min == 0 ? 0 : boundary*-1;
let yTickDistDown = min/4;
for(i = 0; i < 5; i++) {
let val = (yTickDistDown*i);
yTicks.push({
value: val,
label: (val/10).toFixed(1)
});
if(min < 0) {
let yTickDistDown = min/4;
for(i = 1; i < 5; i++) {
let val = (yTickDistDown*i);
yTicks.push({
value: val,
label: (val/10).toFixed(1)
});
}
}
let yTickDistUp = max/4;

View File

@@ -0,0 +1,18 @@
<script>
export let action;
export let title;
</script>
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
<div class="cnt">
<strong>Upload {title}</strong>
<p class="mb-4">Select a suitable file and click upload</p>
<form action="{action}" enctype="multipart/form-data" method="post">
<input name="file" type="file">
<div class="w-full text-right mt-4">
<button type="submit" class="btn-pri">Upload</button>
</div>
</form>
</div>
</div>

View File

@@ -10,7 +10,6 @@
import GearIcon from './GearIcon.svelte';
import InfoIcon from "./InfoIcon.svelte";
import HelpIcon from "./HelpIcon.svelte";
import ReloadIcon from "./ReloadIcon.svelte";
import DownloadIcon from "./DownloadIcon.svelte";
export let data = {}

View File

@@ -1,3 +1,5 @@
export let monthnames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
export function voltcol(pct) {
if(pct > 85) return '#d90000';
else if(pct > 75) return'#e32100';

View File

@@ -61,13 +61,15 @@
max = boundary;
min = min == 0 ? 0 : boundary*-1;
let yTickDistDown = min/4;
for(i = 0; i < 5; i++) {
let val = (yTickDistDown*i);
yTicks.push({
value: val,
label: val.toFixed(0)
});
if(min < 0) {
let yTickDistDown = min/4;
for(i = 0; i < 5; i++) {
let val = (yTickDistDown*i);
yTicks.push({
value: val,
label: val.toFixed(0)
});
}
}
let yTickDistUp = max/4;

View File

@@ -8,35 +8,12 @@
export let label;
</script>
<div class="overlay-plot">
<div class="pl-root">
<PowerGaugeSvg pct={val/max * 100} color={ampcol(val/max * 100)}/>
<span class="plot-overlay">
<span class="plot-value">{val}</span>
<span class="plot-unit">{unit}</span>
<span class="pl-ov">
<span class="pl-val">{val}</span>
<span class="pl-unt">{unit}</span>
<br/>
<span class="plot-label">{label}</span>
<span class="pl-lab">{label}</span>
</span>
</div>
<style>
.overlay-plot {
position: relative;
}
.plot-overlay {
position: absolute;
top: 35%;
left: 25%;
width: 50%;
text-align: center;
}
.plot-value {
font-size: 1.7rem;
}
.plot-unit {
font-size: 1.0rem;
color: grey;
}
.plot-label {
font-size: 1.0rem;
}
</style>

View File

@@ -56,13 +56,15 @@
max = Math.ceil(max);
min = Math.floor(min);
let yTickDistDown = min/4;
for(i = 0; i < 5; i++) {
let val = (yTickDistDown*i);
yTicks.push({
value: val,
label: (val/100).toFixed(2)
});
if(min < 0) {
let yTickDistDown = min/4;
for(i = 1; i < 5; i++) {
let val = (yTickDistDown*i);
yTicks.push({
value: val,
label: (val/100).toFixed(2)
});
}
}
let yTickDistUp = max/4;
@@ -74,6 +76,8 @@
});
}
console.log(yTicks);
config = {
title: "Future energy price (" + json.currency + ")",
padding: { top: 20, right: 15, bottom: 20, left: 35 },

View File

@@ -1,6 +0,0 @@
<script></script>
<!-- Heroicons -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
</svg>

View File

@@ -65,30 +65,30 @@
</script>
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
<div class="bg-white m-2 p-3 rounded-md shadow-lg pb-4 text-gray-700">
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
<div class="cnt">
<form on:submit|preventDefault={handleSubmit}>
<input type="hidden" name="s" value="true"/>
<strong class="text-sm">Setup</strong>
<div class="my-3">
SSID<br/>
<input name="ss" type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="ss" type="text" class="in-s"/>
</div>
<div class="my-3">
PSK<br/>
<input name="sp" type="password" class="h-10 rounded-md shadow-sm border-gray-300 w-full"/>
<input name="sp" type="password" class="in-s"/>
</div>
<div>
Hostname:
<input name="sh" bind:value={sysinfo.hostname} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader"/>
<input name="sh" bind:value={sysinfo.hostname} type="text" class="in-s" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader"/>
</div>
<div class="my-3">
<label><input type="checkbox" name="sm" value="static" class="rounded mb-1" bind:checked={staticIp} /> Static IP</label>
{#if staticIp}
<br/>
<div class="flex">
<input name="si" type="text" class="h-10 rounded-l-md shadow-sm border-gray-300 w-full" required={staticIp}/>
<select name="su" class="h-10 rounded-r-md shadow-sm border-l-0 border-gray-300" required={staticIp}>
<input name="si" type="text" class="in-f w-full" required={staticIp}/>
<select name="su" class="in-l" required={staticIp}>
<option value="255.255.255.0">/24</option>
<option value="255.255.0.0">/16</option>
<option value="255.0.0.0">/8</option>
@@ -100,11 +100,11 @@
<div class="my-3 flex">
<div>
Gateway<br/>
<input name="sg" type="text" class="h-10 rounded-l-md shadow-sm border-gray-300 w-full"/>
<input name="sg" type="text" class="in-f w-full"/>
</div>
<div>
DNS<br/>
<input name="sd" type="text" class="h-10 rounded-r-md border-l-0 shadow-sm border-gray-300 w-full"/>
<input name="sd" type="text" class="in-l w-full"/>
</div>
</div>
{/if}
@@ -113,7 +113,7 @@
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki/Data-collection-on-one-click-firmware-upgrade" target="_blank" class="text-blue-600 hover:text-blue-800">Read more</a>
</div>
<div class="my-3">
<button type="submit" class="font-bold py-1 px-4 rounded bg-blue-500 text-white float-right">Save</button>
<button type="submit" class="btn-pri">Save</button>
</div>
</form>
</div>

View File

@@ -3,11 +3,9 @@
import { getSysinfo, gitHubReleaseStore, sysinfoStore } from './DataStores.js';
import { upgrade, getNextVersion } from './UpgradeHelper';
import DownloadIcon from './DownloadIcon.svelte';
import UploadIcon from './UploadIcon.svelte';
export let sysinfo = {}
export let data = {}
export let sysinfo;
let nextVersion = {};
gitHubReleaseStore.subscribe(releases => {
nextVersion = getNextVersion(sysinfo.version, releases);
@@ -45,14 +43,12 @@
}
}
let fileinput;
getSysinfo();
</script>
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Device information</strong>
<div class="my-2">
Chip: {sysinfo.chip}
@@ -68,7 +64,7 @@
</div>
</div>
{#if sysinfo.meter}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Meter</strong>
<div class="my-2">
Manufacturer: {metertype(sysinfo.meter.mfg)}
@@ -82,7 +78,7 @@
</div>
{/if}
{#if sysinfo.net}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Network</strong>
<div class="my-2">
IP: {sysinfo.net.ip}
@@ -98,7 +94,7 @@
</div>
</div>
{/if}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<div class="cnt">
<strong class="text-sm">Firmware</strong>
<div class="my-2">
Installed version: {sysinfo.version}
@@ -115,12 +111,12 @@
</div>
{#if sysinfo.fwconsent === 2}
<div class="my-2">
<div class="my-auto bg-yellow-500 text-yellow-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">You have disabled one-click firmware upgrade, link to self-upgrade is disabled</div>
<div class="bd-ylo">You have disabled one-click firmware upgrade, link to self-upgrade is disabled</div>
</div>
{/if}
{/if}
{#if sysinfo.board == 2 || sysinfo.board == 4 || sysinfo.board == 7 }
<div class="my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
<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.
</div>
{/if}

View File

@@ -1,16 +1,17 @@
<script>
import { zeropad } from './Helpers.js';
import { monthnames, zeropad } from './Helpers.js';
import BarChart from './BarChart.svelte';
import { tariffStore, getTariff } from './DataStores';
let monthnames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
export let data;
let config = {};
let max = 0;
let min = 0;
let thresholds = [5,10,15,20,25,50,75,100,150];
let tariffData;
tariffStore.subscribe(update => {
tariffData = update;
});
getTariff();
$: {
let i = 0;
@@ -23,10 +24,25 @@
label: 0
});
if(data && data.x) {
for(i = 0; i < thresholds.length; i++) {
let val = thresholds[i];
if(val >= data.x) break;
if(tariffData && tariffData.p) {
for(i = 0; i < tariffData.p.length; i++) {
let peak = tariffData.p[i];
points.push({
label: peak.v.toFixed(2),
value: peak.v,
color: '#7c3aed'
});
xTicks.push({
label: peak.d > 0 ? zeropad(peak.d) + "." + monthnames[new Date().getMonth()] : "-"
})
max = Math.max(max, peak.v);
}
}
if(tariffData && tariffData.t) {
for(i = 0; i < tariffData.t.length; i++) {
let val = tariffData.t[i];
if(val >= max) break;
yTicks.push({
value: val,
label: val
@@ -34,34 +50,20 @@
}
yTicks.push({
label: data.x.toFixed(1),
label: tariffData.m.toFixed(1),
align: 'right',
color: 'green',
value: data.x,
value: tariffData.m,
});
}
if(data && data.t) {
if(tariffData && tariffData.c) {
yTicks.push({
label: data.t.toFixed(1),
label: tariffData.c.toFixed(1),
color: 'orange',
value: data.t,
value: tariffData.c,
});
}
if(data && data.p) {
for(i = 0; i < data.p.length; i++) {
let val = data.p[i];
points.push({
label: val.toFixed(2),
value: val,
color: '#7c3aed'
});
xTicks.push({
label: monthnames[new Date().getMonth()]
})
max = Math.max(max, val);
}
max = Math.max(max, tariffData.c);
}
max = Math.ceil(max);

View File

@@ -1,5 +1,4 @@
<script>
import { zeropad } from './Helpers.js';
import BarChart from './BarChart.svelte';
export let json;
@@ -9,12 +8,8 @@
let min = 0;
$: {
let hour = new Date().getUTCHours();
let i = 0;
let val = 0;
let imp = 0;
let exp = 0;
let h = 0;
let yTicks = [];
let xTicks = [];
let points = [];

View File

@@ -2,36 +2,41 @@
export let chip;
</script>
{#if chip == 'esp8266'}
<option value={3}>UART0</option>
{#if chip == 'esp8266'}
<option value={113}>UART2</option>
<option value={4}>GPIO4</option>
<option value={5}>GPIO5</option>
<option value={9}>GPIO9</option>
<option value={10}>GPIO10</option>
<option value={12}>GPIO12</option>
<option value={13}>GPIO13</option>
<option value={14}>GPIO14</option>
<option value={15}>GPIO15</option>
<option value={16}>GPIO16</option>
{/if}
{#if chip == 'esp32' || chip == 'esp32solo'}
<option value={3}>UART0</option>
<option value={9}>UART1</option>
<option value={16}>UART2</option>
{/if}
{#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.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>
@@ -45,33 +50,6 @@
<option value={39}>GPIO39</option>
{/if}
{#if chip == 'esp32s2'}
<option value={3}>UART0</option>
<option value={18}>UART1</option>
<option value={4}>GPIO4</option>
<option value={5}>GPIO5</option>
<option value={6}>GPIO6</option>
<option value={7}>GPIO7</option>
<option value={8}>GPIO8</option>
<option value={9}>GPIO9</option>
<option value={10}>GPIO10</option>
<option value={11}>GPIO11</option>
<option value={12}>GPIO12</option>
<option value={13}>GPIO13</option>
<option value={14}>GPIO14</option>
<option value={15}>GPIO15</option>
<option value={16}>GPIO16</option>
<option value={17}>GPIO17</option>
<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>
<option value={40}>GPIO40</option>
<option value={41}>GPIO41</option>
<option value={42}>GPIO42</option>

View File

@@ -1,5 +1,3 @@
export async function upgrade(version) {
const data = new URLSearchParams()
data.append('version', version.tag_name);

View File

@@ -1,6 +0,0 @@
<script></script>
<!-- Heroicons -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 8.25H7.5a2.25 2.25 0 00-2.25 2.25v9a2.25 2.25 0 002.25 2.25h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25H15m0-3l-3-3m0 0l-3 3m3-3V15" />
</svg>

View File

@@ -31,27 +31,27 @@
}
</script>
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
<div class="bg-white m-2 p-3 rounded-md shadow-lg pb-4 text-gray-700">
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
<div class="cnt">
<form on:submit|preventDefault={handleSubmit}>
<input type="hidden" name="v" value="true"/>
<strong class="text-sm">Initial configuration</strong>
<div class="my-3">
Board type<br/>
<select name="vb" bind:value={sysinfo.board} class="h-10 rounded-md shadow-sm border-gray-300 p-0 w-full">
<select name="vb" bind:value={sysinfo.board} class="in-s">
<BoardTypeSelectOptions chip={sysinfo.chip}/>
</select>
</div>
{#if sysinfo.board && sysinfo.board > 20}
<div class="my-3">
HAN GPIO<br/>
<select name="vh" class="h-10 rounded-md shadow-sm border-gray-300">
<select name="vh" class="in-s">
<UartSelectOptions chip={sysinfo.chip}/>
</select>
</div>
{/if}
<div class="my-3">
<button type="submit" class="font-bold py-1 px-4 rounded bg-blue-500 text-white float-right">Save</button>
<button type="submit" class="btn-pri">Save</button>
</div>
<span class="clear-both">&nbsp;</span>
</form>

View File

@@ -17,17 +17,18 @@ export default defineConfig({
plugins: [svelte()],
server: {
proxy: {
"/data.json": "http://192.168.233.229",
"/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.229",
"/configuration.json": "http://192.168.233.229",
"/save": "http://192.168.233.229",
"/reboot": "http://192.168.233.229",
"/firmware": "http://192.168.233.229",
"/upgrade": "http://192.168.233.229"
"/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",
"/firmware": "http://192.168.233.244",
"/upgrade": "http://192.168.233.244"
}
}
})

View File

@@ -84,6 +84,7 @@ private:
void energyPriceJson();
void temperatureJson();
void wifiScanJson();
void tariffJson();
void configurationJson();
void handleSave();
@@ -93,6 +94,9 @@ private:
void firmwareUpload();
void isAliveCheck();
void mqttCaUpload();
void mqttCertUpload();
void mqttKeyUpload();
HTTPUpload& uploadFile(const char* path);
void factoryResetPost();

View File

@@ -53,5 +53,6 @@
"p" : %.2f
}
},
"pr" : "%s",
"c" : %lu
}

View File

@@ -0,0 +1,5 @@
{
"success": %s,
"message": "%s",
"reboot": %s
}

View File

@@ -14,6 +14,7 @@
#include "html/monthplot_json.h"
#include "html/energyprice_json.h"
#include "html/tempsensor_json.h"
#include "html/response_json.h"
#include "version.h"
@@ -40,6 +41,10 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
server.on(F("/"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on(F("/configuration"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on(F("/status"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on(F("/mqtt-ca"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on(F("/mqtt-cert"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on(F("/mqtt-key"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on(F("/index.css"), HTTP_GET, std::bind(&AmsWebServer::indexCss, this));
server.on(F("/index.js"), HTTP_GET, std::bind(&AmsWebServer::indexJs, this));
server.on(F("/github.svg"), HTTP_GET, std::bind(&AmsWebServer::githubSvg, this));
@@ -50,6 +55,7 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
server.on(F("/monthplot.json"), HTTP_GET, std::bind(&AmsWebServer::monthplotJson, this));
server.on(F("/energyprice.json"), HTTP_GET, std::bind(&AmsWebServer::energyPriceJson, this));
server.on(F("/temperature.json"), HTTP_GET, std::bind(&AmsWebServer::temperatureJson, this));
server.on(F("/tariff.json"), HTTP_GET, std::bind(&AmsWebServer::tariffJson, this));
server.on(F("/wifiscan.json"), HTTP_GET, std::bind(&AmsWebServer::wifiScanJson, this));
@@ -64,6 +70,10 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
server.on(F("/robots.txt"), HTTP_GET, std::bind(&AmsWebServer::robotstxt, this));
server.on(F("/mqtt-ca"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::mqttCaUpload, this));
server.on(F("/mqtt-cert"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::mqttCertUpload, this));
server.on(F("/mqtt-key"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::mqttKeyUpload, this));
server.onNotFound(std::bind(&AmsWebServer::notFound, this));
server.begin(); // Web server start
@@ -157,55 +167,55 @@ void AmsWebServer::sysinfoJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /sysinfo.json over http...\n");
DynamicJsonDocument doc(512);
doc["version"] = VERSION;
doc[PSTR("version")] = VERSION;
#if defined(CONFIG_IDF_TARGET_ESP32S2)
doc["chip"] = "esp32s2";
doc[PSTR("chip")] = "esp32s2";
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
doc["chip"] = "esp32c3";
doc[PSTR("chip")] = "esp32c3";
#elif defined(ESP32)
doc["chip"] = "esp32";
doc[PSTR("chip")] = "esp32";
#elif defined(ESP8266)
doc["chip"] = "esp8266";
doc[PSTR("chip")] = "esp8266";
#endif
uint16_t chipId;
uint32_t chipId;
#if defined(ESP32)
chipId = ESP.getEfuseMac();
chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF;
#else
chipId = ESP.getChipId();
#endif
String chipIdStr = String(chipId, HEX);;
doc["chipId"] = chipIdStr;
doc["mac"] = WiFi.macAddress();
doc[PSTR("chipId")] = chipIdStr;
doc[PSTR("mac")] = WiFi.macAddress();
SystemConfig sys;
config->getSystemConfig(sys);
doc["board"] = sys.boardType;
doc["vndcfg"] = sys.vendorConfigured;
doc["usrcfg"] = sys.userConfigured;
doc["fwconsent"] = sys.dataCollectionConsent;
doc["country"] = sys.country;
doc[PSTR("board")] = sys.boardType;
doc[PSTR("vndcfg")] = sys.vendorConfigured;
doc[PSTR("usrcfg")] = sys.userConfigured;
doc[PSTR("fwconsent")] = sys.dataCollectionConsent;
doc[PSTR("country")] = sys.country;
if(sys.userConfigured) {
WiFiConfig wifiConfig;
config->getWiFiConfig(wifiConfig);
doc["hostname"] = wifiConfig.hostname;
doc[PSTR("hostname")] = wifiConfig.hostname;
} else {
doc["hostname"] = "ams-"+chipIdStr;
doc[PSTR("hostname")] = "ams-"+chipIdStr;
}
doc["booting"] = performRestart;
doc["upgrading"] = rebootForUpgrade;
doc[PSTR("booting")] = performRestart;
doc[PSTR("upgrading")] = rebootForUpgrade;
doc["net"]["ip"] = WiFi.localIP().toString();
doc["net"]["mask"] = WiFi.subnetMask().toString();
doc["net"]["gw"] = WiFi.gatewayIP().toString();
doc["net"]["dns1"] = WiFi.dnsIP(0).toString();
doc["net"]["dns2"] = WiFi.dnsIP(1).toString();
doc[PSTR("net")][PSTR("ip")] = WiFi.localIP().toString();
doc[PSTR("net")][PSTR("mask")] = WiFi.subnetMask().toString();
doc[PSTR("net")][PSTR("gw")] = WiFi.gatewayIP().toString();
doc[PSTR("net")][PSTR("dns1")] = WiFi.dnsIP(0).toString();
doc[PSTR("net")][PSTR("dns2")] = WiFi.dnsIP(1).toString();
doc["meter"]["mfg"] = meterState->getMeterType();
doc["meter"]["model"] = meterState->getMeterModel();
doc["meter"]["id"] = meterState->getMeterId();
doc[PSTR("meter")][PSTR("mfg")] = meterState->getMeterType();
doc[PSTR("meter")][PSTR("model")] = meterState->getMeterModel();
doc[PSTR("meter")][PSTR("id")] = meterState->getMeterId();
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
@@ -300,7 +310,7 @@ void AmsWebServer::dataJson() {
String peaks = "";
for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) {
if(!peaks.isEmpty()) peaks += ",";
peaks += String(ea->getPeak(i));
peaks += String(ea->getPeak(i).value);
}
snprintf_P(buf, BufferSize, DATA_JSON,
@@ -350,6 +360,7 @@ void AmsWebServer::dataJson() {
ea->getUseThisMonth(),
ea->getCostThisMonth(),
ea->getProducedThisMonth(),
eapi == NULL ? "" : eapi->getArea(),
(uint32_t) time(nullptr)
);
@@ -659,7 +670,7 @@ void AmsWebServer::configurationJson() {
return;
DynamicJsonDocument doc(2048);
doc["version"] = VERSION;
doc[PSTR("version")] = VERSION;
NtpConfig ntpConfig;
config->getNtpConfig(ntpConfig);
@@ -668,15 +679,11 @@ void AmsWebServer::configurationJson() {
WebConfig webConfig;
config->getWebConfig(webConfig);
if(ntpConfig.offset == 0 && ntpConfig.summerOffset == 0)
doc["g"]["t"] = "UTC";
else if(ntpConfig.offset == 360 && ntpConfig.summerOffset == 360)
doc["g"]["t"] = "CET/CEST";
doc["g"]["h"] = wifiConfig.hostname;
doc["g"]["s"] = webConfig.security;
doc["g"]["u"] = webConfig.username;
doc["g"]["p"] = strlen(webConfig.password) > 0 ? "***" : "";
doc[PSTR("g")][PSTR("t")] = ntpConfig.timezone;
doc[PSTR("g")][PSTR("h")] = wifiConfig.hostname;
doc[PSTR("g")][PSTR("s")] = webConfig.security;
doc[PSTR("g")][PSTR("u")] = webConfig.username;
doc[PSTR("g")][PSTR("p")] = strlen(webConfig.password) > 0 ? "***" : "";
bool encen = false;
for(uint8_t i = 0; i < 16; i++) {
@@ -686,161 +693,161 @@ void AmsWebServer::configurationJson() {
}
config->getMeterConfig(*meterConfig);
doc["m"]["b"] = meterConfig->baud;
doc["m"]["p"] = meterConfig->parity;
doc["m"]["i"] = meterConfig->invert;
doc["m"]["d"] = meterConfig->distributionSystem;
doc["m"]["f"] = meterConfig->mainFuse;
doc["m"]["r"] = meterConfig->productionCapacity;
doc["m"]["e"]["e"] = encen;
doc["m"]["e"]["k"] = toHex(meterConfig->encryptionKey, 16);
doc["m"]["e"]["a"] = toHex(meterConfig->authenticationKey, 16);
doc["m"]["m"]["e"] = meterConfig->wattageMultiplier > 1 || meterConfig->voltageMultiplier > 1 || meterConfig->amperageMultiplier > 1 || meterConfig->accumulatedMultiplier > 1;
doc["m"]["m"]["w"] = meterConfig->wattageMultiplier / 1000.0;
doc["m"]["m"]["v"] = meterConfig->voltageMultiplier / 1000.0;
doc["m"]["m"]["a"] = meterConfig->amperageMultiplier / 1000.0;
doc["m"]["m"]["c"] = meterConfig->accumulatedMultiplier / 1000.0;
doc[PSTR("m")][PSTR("b")] = meterConfig->baud;
doc[PSTR("m")][PSTR("p")] = meterConfig->parity;
doc[PSTR("m")][PSTR("i")] = meterConfig->invert;
doc[PSTR("m")][PSTR("d")] = meterConfig->distributionSystem;
doc[PSTR("m")][PSTR("f")] = meterConfig->mainFuse;
doc[PSTR("m")][PSTR("r")] = meterConfig->productionCapacity;
doc[PSTR("m")][PSTR("e")][PSTR("e")] = encen;
doc[PSTR("m")][PSTR("e")][PSTR("k")] = toHex(meterConfig->encryptionKey, 16);
doc[PSTR("m")][PSTR("e")][PSTR("a")] = toHex(meterConfig->authenticationKey, 16);
doc[PSTR("m")][PSTR("m")][PSTR("e")] = meterConfig->wattageMultiplier > 1 || meterConfig->voltageMultiplier > 1 || meterConfig->amperageMultiplier > 1 || meterConfig->accumulatedMultiplier > 1;
doc[PSTR("m")][PSTR("m")][PSTR("w")] = meterConfig->wattageMultiplier / 1000.0;
doc[PSTR("m")][PSTR("m")][PSTR("v")] = meterConfig->voltageMultiplier / 1000.0;
doc[PSTR("m")][PSTR("m")][PSTR("a")] = meterConfig->amperageMultiplier / 1000.0;
doc[PSTR("m")][PSTR("m")][PSTR("c")] = meterConfig->accumulatedMultiplier / 1000.0;
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
doc["t"]["t"][0] = eac.thresholds[0];
doc["t"]["t"][1] = eac.thresholds[1];
doc["t"]["t"][2] = eac.thresholds[2];
doc["t"]["t"][3] = eac.thresholds[3];
doc["t"]["t"][4] = eac.thresholds[4];
doc["t"]["t"][5] = eac.thresholds[5];
doc["t"]["t"][6] = eac.thresholds[6];
doc["t"]["t"][7] = eac.thresholds[7];
doc["t"]["t"][8] = eac.thresholds[8];
doc["t"]["t"][9] = eac.thresholds[9];
doc["t"]["h"] = eac.hours;
doc[PSTR("t")][PSTR("t")][0] = eac.thresholds[0];
doc[PSTR("t")][PSTR("t")][1] = eac.thresholds[1];
doc[PSTR("t")][PSTR("t")][2] = eac.thresholds[2];
doc[PSTR("t")][PSTR("t")][3] = eac.thresholds[3];
doc[PSTR("t")][PSTR("t")][4] = eac.thresholds[4];
doc[PSTR("t")][PSTR("t")][5] = eac.thresholds[5];
doc[PSTR("t")][PSTR("t")][6] = eac.thresholds[6];
doc[PSTR("t")][PSTR("t")][7] = eac.thresholds[7];
doc[PSTR("t")][PSTR("t")][8] = eac.thresholds[8];
doc[PSTR("t")][PSTR("t")][9] = eac.thresholds[9];
doc[PSTR("t")][PSTR("h")] = eac.hours;
doc["w"]["s"] = wifiConfig.ssid;
doc["w"]["p"] = strlen(wifiConfig.psk) > 0 ? "***" : "";
doc["w"]["w"] = wifiConfig.power / 10.0;
doc["w"]["z"] = wifiConfig.sleep;
doc[PSTR("w")][PSTR("s")] = wifiConfig.ssid;
doc[PSTR("w")][PSTR("p")] = strlen(wifiConfig.psk) > 0 ? "***" : "";
doc[PSTR("w")][PSTR("w")] = wifiConfig.power / 10.0;
doc[PSTR("w")][PSTR("z")] = wifiConfig.sleep;
doc["n"]["m"] = strlen(wifiConfig.ip) > 0 ? "static" : "dhcp";
doc["n"]["i"] = wifiConfig.ip;
doc["n"]["s"] = wifiConfig.subnet;
doc["n"]["g"] = wifiConfig.gateway;
doc["n"]["d1"] = wifiConfig.dns1;
doc["n"]["d2"] = wifiConfig.dns2;
doc["n"]["d"] = wifiConfig.mdns;
doc["n"]["n1"] = ntpConfig.server;
doc["n"]["h"] = ntpConfig.dhcp;
doc[PSTR("n")][PSTR("m")] = strlen(wifiConfig.ip) > 0 ? "static" : "dhcp";
doc[PSTR("n")][PSTR("i")] = wifiConfig.ip;
doc[PSTR("n")][PSTR("s")] = wifiConfig.subnet;
doc[PSTR("n")][PSTR("g")] = wifiConfig.gateway;
doc[PSTR("n")][PSTR("d1")] = wifiConfig.dns1;
doc[PSTR("n")][PSTR("d2")] = wifiConfig.dns2;
doc[PSTR("n")][PSTR("d")] = wifiConfig.mdns;
doc[PSTR("n")][PSTR("n1")] = ntpConfig.server;
doc[PSTR("n")][PSTR("h")] = ntpConfig.dhcp;
MqttConfig mqttConfig;
config->getMqttConfig(mqttConfig);
doc["q"]["h"] = mqttConfig.host;
doc["q"]["p"] = mqttConfig.port;
doc["q"]["u"] = mqttConfig.username;
doc["q"]["a"] = strlen(mqttConfig.password) > 0 ? "***" : "";
doc["q"]["c"] = mqttConfig.clientId;
doc["q"]["b"] = mqttConfig.publishTopic;
doc["q"]["m"] = mqttConfig.payloadFormat;
doc["q"]["s"]["e"] = mqttConfig.ssl;
doc[PSTR("q")][PSTR("h")] = mqttConfig.host;
doc[PSTR("q")][PSTR("p")] = mqttConfig.port;
doc[PSTR("q")][PSTR("u")] = mqttConfig.username;
doc[PSTR("q")][PSTR("a")] = strlen(mqttConfig.password) > 0 ? "***" : "";
doc[PSTR("q")][PSTR("c")] = mqttConfig.clientId;
doc[PSTR("q")][PSTR("b")] = mqttConfig.publishTopic;
doc[PSTR("q")][PSTR("m")] = mqttConfig.payloadFormat;
doc[PSTR("q")][PSTR("s")][PSTR("e")] = mqttConfig.ssl;
if(LittleFS.begin()) {
doc["q"]["s"]["c"] = LittleFS.exists(FILE_MQTT_CA);
doc["q"]["s"]["r"] = LittleFS.exists(FILE_MQTT_CERT);
doc["q"]["s"]["k"] = LittleFS.exists(FILE_MQTT_KEY);
doc[PSTR("q")][PSTR("s")][PSTR("c")] = LittleFS.exists(FILE_MQTT_CA);
doc[PSTR("q")][PSTR("s")][PSTR("r")] = LittleFS.exists(FILE_MQTT_CERT);
doc[PSTR("q")][PSTR("s")][PSTR("k")] = LittleFS.exists(FILE_MQTT_KEY);
LittleFS.end();
} else {
doc["q"]["s"]["c"] = false;
doc["q"]["s"]["r"] = false;
doc["q"]["s"]["k"] = false;
doc[PSTR("q")][PSTR("s")][PSTR("c")] = false;
doc[PSTR("q")][PSTR("s")][PSTR("r")] = false;
doc[PSTR("q")][PSTR("s")][PSTR("k")] = false;
}
EntsoeConfig entsoe;
config->getEntsoeConfig(entsoe);
doc["p"]["e"] = strlen(entsoe.token) > 0;
doc["p"]["t"] = entsoe.token;
doc["p"]["r"] = entsoe.area;
doc["p"]["c"] = entsoe.currency;
doc["p"]["m"] = entsoe.multiplier / 1000.0;
doc[PSTR("p")][PSTR("e")] = strlen(entsoe.token) > 0;
doc[PSTR("p")][PSTR("t")] = entsoe.token;
doc[PSTR("p")][PSTR("r")] = entsoe.area;
doc[PSTR("p")][PSTR("c")] = entsoe.currency;
doc[PSTR("p")][PSTR("m")] = entsoe.multiplier / 1000.0;
DebugConfig debugConfig;
config->getDebugConfig(debugConfig);
doc["d"]["s"] = debugConfig.serial;
doc["d"]["t"] = debugConfig.telnet;
doc["d"]["l"] = debugConfig.level;
doc[PSTR("d")][PSTR("s")] = debugConfig.serial;
doc[PSTR("d")][PSTR("t")] = debugConfig.telnet;
doc[PSTR("d")][PSTR("l")] = debugConfig.level;
GpioConfig gpioConfig;
config->getGpioConfig(gpioConfig);
if(gpioConfig.hanPin == 0xff)
doc["i"]["h"] = nullptr;
doc[PSTR("i")][PSTR("h")] = nullptr;
else
doc["i"]["h"] = gpioConfig.hanPin;
doc[PSTR("i")][PSTR("h")] = gpioConfig.hanPin;
if(gpioConfig.apPin == 0xff)
doc["i"]["a"] = nullptr;
doc[PSTR("i")][PSTR("a")] = nullptr;
else
doc["i"]["a"] = gpioConfig.apPin;
doc[PSTR("i")][PSTR("a")] = gpioConfig.apPin;
if(gpioConfig.ledPin == 0xff)
doc["i"]["l"]["p"] = nullptr;
doc[PSTR("i")][PSTR("l")][PSTR("p")] = nullptr;
else
doc["i"]["l"]["p"] = gpioConfig.ledPin;
doc[PSTR("i")][PSTR("l")][PSTR("p")] = gpioConfig.ledPin;
doc["i"]["l"]["i"] = gpioConfig.ledInverted;
doc[PSTR("i")][PSTR("l")][PSTR("i")] = gpioConfig.ledInverted;
if(gpioConfig.ledPinRed == 0xff)
doc["i"]["r"]["r"] = nullptr;
doc[PSTR("i")][PSTR("r")][PSTR("r")] = nullptr;
else
doc["i"]["r"]["r"] = gpioConfig.ledPinRed;
doc[PSTR("i")][PSTR("r")][PSTR("r")] = gpioConfig.ledPinRed;
if(gpioConfig.ledPinGreen == 0xff)
doc["i"]["r"]["g"] = nullptr;
doc[PSTR("i")][PSTR("r")][PSTR("g")] = nullptr;
else
doc["i"]["r"]["g"] = gpioConfig.ledPinGreen;
doc[PSTR("i")][PSTR("r")][PSTR("g")] = gpioConfig.ledPinGreen;
if(gpioConfig.ledPinBlue == 0xff)
doc["i"]["r"]["b"] = nullptr;
doc[PSTR("i")][PSTR("r")][PSTR("b")] = nullptr;
else
doc["i"]["r"]["b"] = gpioConfig.ledPinBlue;
doc[PSTR("i")][PSTR("r")][PSTR("b")] = gpioConfig.ledPinBlue;
doc["i"]["r"]["i"] = gpioConfig.ledRgbInverted;
doc[PSTR("i")][PSTR("r")][PSTR("i")] = gpioConfig.ledRgbInverted;
if(gpioConfig.tempSensorPin == 0xff)
doc["i"]["t"]["d"] = nullptr;
doc[PSTR("i")][PSTR("t")][PSTR("d")] = nullptr;
else
doc["i"]["t"]["d"] = gpioConfig.tempSensorPin;
doc[PSTR("i")][PSTR("t")][PSTR("d")] = gpioConfig.tempSensorPin;
if(gpioConfig.tempAnalogSensorPin == 0xff)
doc["i"]["t"]["a"] = nullptr;
doc[PSTR("i")][PSTR("t")][PSTR("a")] = nullptr;
else
doc["i"]["t"]["a"] = gpioConfig.tempAnalogSensorPin;
doc[PSTR("i")][PSTR("t")][PSTR("a")] = gpioConfig.tempAnalogSensorPin;
if(gpioConfig.vccPin == 0xff)
doc["i"]["v"]["p"] = nullptr;
doc[PSTR("i")][PSTR("v")][PSTR("p")] = nullptr;
else
doc["i"]["v"]["p"] = gpioConfig.vccPin;
doc[PSTR("i")][PSTR("v")][PSTR("p")] = gpioConfig.vccPin;
if(gpioConfig.vccOffset == 0)
doc["i"]["v"]["o"] = nullptr;
doc[PSTR("i")][PSTR("v")][PSTR("o")] = nullptr;
else
doc["i"]["v"]["o"] = gpioConfig.vccOffset / 100.0;
doc[PSTR("i")][PSTR("v")][PSTR("o")] = gpioConfig.vccOffset / 100.0;
if(gpioConfig.vccMultiplier == 0)
doc["i"]["v"]["m"] = nullptr;
doc[PSTR("i")][PSTR("v")][PSTR("m")] = nullptr;
else
doc["i"]["v"]["m"] = gpioConfig.vccMultiplier / 1000.0;
doc[PSTR("i")][PSTR("v")][PSTR("m")] = gpioConfig.vccMultiplier / 1000.0;
if(gpioConfig.vccResistorVcc == 0)
doc["i"]["v"]["d"]["v"] = nullptr;
doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("v")] = nullptr;
else
doc["i"]["v"]["d"]["v"] = gpioConfig.vccResistorVcc;
doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("v")] = gpioConfig.vccResistorVcc;
if(gpioConfig.vccResistorGnd == 0)
doc["i"]["v"]["d"]["g"] = nullptr;
doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("g")] = nullptr;
else
doc["i"]["v"]["d"]["g"] = gpioConfig.vccResistorGnd;
doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("g")] = gpioConfig.vccResistorGnd;
if(gpioConfig.vccBootLimit == 0)
doc["i"]["v"]["b"] = nullptr;
doc[PSTR("i")][PSTR("v")][PSTR("b")] = nullptr;
else
doc["i"]["v"]["b"] = gpioConfig.vccBootLimit / 10.0;
doc[PSTR("i")][PSTR("v")][PSTR("b")] = gpioConfig.vccBootLimit / 10.0;
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
@@ -1094,18 +1101,23 @@ void AmsWebServer::handleSave() {
if(server.hasArg(F("q")) && server.arg(F("q")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received MQTT config"));
MqttConfig mqtt;
config->getMqttConfig(mqtt);
if(server.hasArg(F("qh")) && !server.arg(F("qh")).isEmpty()) {
strcpy(mqtt.host, server.arg(F("qh")).c_str());
strcpy(mqtt.clientId, server.arg(F("qc")).c_str());
strcpy(mqtt.publishTopic, server.arg(F("qb")).c_str());
strcpy(mqtt.subscribeTopic, server.arg(F("qr")).c_str());
strcpy(mqtt.username, server.arg(F("qu")).c_str());
String pass = server.arg(F("qp"));
String pass = server.arg(F("qa"));
if(!pass.equals("***")) {
strcpy(mqtt.password, pass.c_str());
}
mqtt.payloadFormat = server.arg(F("qm")).toInt();
#if defined(ESP8266)
mqtt.ssl = false;
#else
mqtt.ssl = server.arg(F("qs")) == F("true");
#endif
mqtt.port = server.arg(F("qp")).toInt();
if(mqtt.port == 0) {
@@ -1156,14 +1168,7 @@ void AmsWebServer::handleSave() {
NtpConfig ntp;
config->getNtpConfig(ntp);
String tz = server.arg(F("gt"));
if(tz.equals("UTC")) {
ntp.offset = 0;
ntp.summerOffset = 0;
} else if(tz.equals("CET/CEST")) {
ntp.offset = 360;
ntp.summerOffset = 360;
}
strcpy(ntp.timezone, server.arg(F("gt")).c_str());
config->setNtpConfig(ntp);
}
@@ -1251,22 +1256,23 @@ void AmsWebServer::handleSave() {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Saving configuration now..."));
DynamicJsonDocument doc(128);
if (config->save()) {
doc["success"] = success;
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Successfully saved."));
if(config->isWifiChanged() || performRestart) {
performRestart = true;
doc["reboot"] = true;
} else {
doc["reboot"] = false;
hw->setup(gpioConfig, config);
}
} else {
doc["success"] = false;
doc["reboot"] = false;
success = false;
}
serializeJson(doc, buf, BufferSize);
snprintf_P(buf, BufferSize, RESPONSE_JSON,
success ? "true" : "false",
"",
performRestart ? "true" : "false"
);
server.setContentLength(strlen(buf));
server.send(200, MIME_JSON, buf);
server.handleClient();
@@ -1300,7 +1306,7 @@ void AmsWebServer::reboot() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /reboot over http...\n");
DynamicJsonDocument doc(128);
doc["reboot"] = true;
doc[PSTR("reboot")] = true;
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
@@ -1321,14 +1327,18 @@ void AmsWebServer::reboot() {
void AmsWebServer::upgrade() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /upgrade over http...\n");
if(!checkSecurity(1))
return;
SystemConfig sys;
config->getSystemConfig(sys);
DynamicJsonDocument doc(128);
doc["success"] = sys.dataCollectionConsent == 1;
doc["reboot"] = sys.dataCollectionConsent == 1;
serializeJson(doc, buf, BufferSize);
snprintf_P(buf, BufferSize, RESPONSE_JSON,
sys.dataCollectionConsent == 1 ? "true" : "false",
"",
sys.dataCollectionConsent == 1 ? "true" : "false"
);
server.setContentLength(strlen(buf));
server.send(200, MIME_JSON, buf);
if(sys.dataCollectionConsent == 1) {
@@ -1460,7 +1470,13 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) {
LittleFS.end();
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("An Error has occurred while writing file"));
server.send_P(500, MIME_JSON, PSTR("{ \"success\": false, \"message\": \"Unable to upload\" }"));
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"Unable to upload",
"false"
);
server.setContentLength(strlen(buf));
server.send(500, MIME_JSON, buf);
}
}
} else if(upload.status == UPLOAD_FILE_END) {
@@ -1469,7 +1485,13 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) {
file.close();
// LittleFS.end();
} else {
server.send_P(500, MIME_JSON, PSTR("{ \"success\": false, \"message\": \"Unable to upload\" }"));
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"Unable to upload",
"false"
);
server.setContentLength(strlen(buf));
server.send(500, MIME_JSON, buf);
}
}
return upload;
@@ -1496,11 +1518,12 @@ void AmsWebServer::factoryResetPost() {
success = true;
}
DynamicJsonDocument doc(128);
doc["success"] = success;
doc["reboot"] = success;
serializeJson(doc, buf, BufferSize);
snprintf_P(buf, BufferSize, RESPONSE_JSON,
success ? "true" : "false",
"",
"true"
);
server.setContentLength(strlen(buf));
server.send(200, MIME_JSON, buf);
server.handleClient();
@@ -1518,3 +1541,85 @@ void AmsWebServer::factoryResetPost() {
void AmsWebServer::robotstxt() {
server.send_P(200, MIME_HTML, PSTR("User-agent: *\nDisallow: /\n"));
}
void AmsWebServer::mqttCaUpload() {
if(!checkSecurity(1))
return;
uploadFile(FILE_MQTT_CA);
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_END) {
server.sendHeader(HEADER_LOCATION,F("/configuration"));
server.send(303);
MqttConfig mqttConfig;
if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) {
config->setMqttChanged();
}
}
}
void AmsWebServer::mqttCertUpload() {
if(!checkSecurity(1))
return;
uploadFile(FILE_MQTT_CERT);
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_END) {
server.sendHeader(HEADER_LOCATION,F("/configuration"));
server.send(303);
MqttConfig mqttConfig;
if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) {
config->setMqttChanged();
}
}
}
void AmsWebServer::mqttKeyUpload() {
if(!checkSecurity(1))
return;
uploadFile(FILE_MQTT_KEY);
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_END) {
server.sendHeader(HEADER_LOCATION,F("/configuration"));
server.send(303);
MqttConfig mqttConfig;
if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) {
config->setMqttChanged();
}
}
}
void AmsWebServer::tariffJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /tariff.json over http...\n");
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
if(!checkSecurity(1))
return;
EnergyAccountingConfig* eac = ea->getConfig();
EnergyAccountingData data = ea->getData();
DynamicJsonDocument doc(512);
JsonArray thresholds = doc.createNestedArray(PSTR("t"));
for(uint8_t x = 0;x < 10; x++) {
thresholds.add(eac->thresholds[x]);
}
JsonArray peaks = doc.createNestedArray(PSTR("p"));
for(uint8_t x = 0;x < min((uint8_t) 5, eac->hours); x++) {
JsonObject p = peaks.createNestedObject();
EnergyAccountingPeak peak = ea->getPeak(x);
p["d"] = peak.day;
p["v"] = peak.value / 100.0;
}
doc["c"] = ea->getCurrentThreshold();
doc["m"] = ea->getMonthMax();
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
}