mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-01-13 23:45:25 +00:00
Possibility to specify fixed energy price
This commit is contained in:
parent
ea43b2b632
commit
700f023292
@ -180,7 +180,8 @@ struct EntsoeConfig {
|
||||
char currency[4];
|
||||
uint32_t multiplier;
|
||||
bool enabled;
|
||||
}; // 62
|
||||
uint16_t fixedPrice;
|
||||
}; // 64
|
||||
|
||||
struct EnergyAccountingConfig {
|
||||
uint16_t thresholds[10];
|
||||
|
||||
@ -551,6 +551,7 @@ bool AmsConfiguration::setEntsoeConfig(EntsoeConfig& config) {
|
||||
entsoeChanged |= strcmp(config.currency, existing.currency) != 0;
|
||||
entsoeChanged |= config.multiplier != existing.multiplier;
|
||||
entsoeChanged |= config.enabled != existing.enabled;
|
||||
entsoeChanged |= config.fixedPrice != existing.fixedPrice;
|
||||
} else {
|
||||
entsoeChanged = true;
|
||||
}
|
||||
@ -1037,6 +1038,11 @@ bool AmsConfiguration::relocateConfig102() {
|
||||
clearHomeAssistantConfig(haconf);
|
||||
EEPROM.put(CONFIG_HA_START, haconf);
|
||||
|
||||
EntsoeConfig entsoe;
|
||||
EEPROM.get(CONFIG_ENTSOE_START, entsoe);
|
||||
entsoe.fixedPrice = 0;
|
||||
EEPROM.put(CONFIG_ENTSOE_START, entsoe);
|
||||
|
||||
EEPROM.put(EEPROM_CONFIG_ADDRESS, 103);
|
||||
bool ret = EEPROM.commit();
|
||||
EEPROM.end();
|
||||
|
||||
@ -81,6 +81,9 @@ public:
|
||||
EnergyAccountingData getData();
|
||||
void setData(EnergyAccountingData&);
|
||||
|
||||
void setFixedPrice(double price);
|
||||
double getPriceForHour(uint8_t h);
|
||||
|
||||
private:
|
||||
RemoteDebug* debugger = NULL;
|
||||
unsigned long lastUpdateMillis = 0;
|
||||
@ -93,6 +96,7 @@ private:
|
||||
double use = 0, costHour = 0, costDay = 0;
|
||||
double produce = 0, incomeHour = 0, incomeDay = 0;
|
||||
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
|
||||
double fixedPrice = 0;
|
||||
|
||||
void calcDayCost();
|
||||
bool updateMax(uint16_t val, uint8_t day);
|
||||
|
||||
@ -68,7 +68,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
init = true;
|
||||
}
|
||||
|
||||
if(!initPrice && eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
|
||||
if(!initPrice && eapi != NULL && getPriceForHour(0) != ENTSOE_NO_VALUE) {
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing prices at %lu\n", (int32_t) now);
|
||||
calcDayCost();
|
||||
}
|
||||
@ -129,8 +129,8 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
if(kwhi > 0) {
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh import\n", kwhi);
|
||||
use += kwhi;
|
||||
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
|
||||
float price = eapi->getValueForHour(0);
|
||||
if(getPriceForHour(0) != ENTSOE_NO_VALUE) {
|
||||
float price = getPriceForHour(0);
|
||||
float cost = price * kwhi;
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) and %.4f %s\n", cost / 100.0, eapi->getCurrency());
|
||||
costHour += cost;
|
||||
@ -140,8 +140,8 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
if(kwhe > 0) {
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh export\n", kwhe);
|
||||
produce += kwhe;
|
||||
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
|
||||
float price = eapi->getValueForHour(0);
|
||||
if(getPriceForHour(0) != ENTSOE_NO_VALUE) {
|
||||
float price = getPriceForHour(0);
|
||||
float income = price * kwhe;
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) and %.4f %s\n", income / 100.0, eapi->getCurrency());
|
||||
incomeHour += income;
|
||||
@ -163,13 +163,13 @@ void EnergyAccounting::calcDayCost() {
|
||||
tmElements_t local, utc;
|
||||
breakTime(tz->toLocal(now), local);
|
||||
|
||||
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
|
||||
if(eapi != NULL && getPriceForHour(0) != ENTSOE_NO_VALUE) {
|
||||
if(initPrice) {
|
||||
costDay = 0;
|
||||
incomeDay = 0;
|
||||
}
|
||||
for(int i = 0; i < currentHour; i++) {
|
||||
float price = eapi->getValueForHour(i - local.Hour);
|
||||
float price = getPriceForHour(i - local.Hour);
|
||||
if(price == ENTSOE_NO_VALUE) break;
|
||||
breakTime(now - ((local.Hour - i) * 3600), utc);
|
||||
int16_t wh = ds->getHourImport(utc.Hour);
|
||||
@ -502,4 +502,14 @@ bool EnergyAccounting::updateMax(uint16_t val, uint8_t day) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnergyAccounting::setFixedPrice(double price) {
|
||||
this->fixedPrice = price;
|
||||
}
|
||||
|
||||
double EnergyAccounting::getPriceForHour(uint8_t h) {
|
||||
if(fixedPrice > 0.0) return fixedPrice;
|
||||
if(eapi == NULL) return ENTSOE_NO_VALUE;
|
||||
return eapi->getValueForHour(h);
|
||||
}
|
||||
18
lib/SvelteUi/app/dist/index.js
vendored
18
lib/SvelteUi/app/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -6,9 +6,11 @@
|
||||
export let currency;
|
||||
export let hasExport;
|
||||
|
||||
let hasCost = false;
|
||||
let cols = 3
|
||||
$: {
|
||||
cols = currency ? 3 : 2;
|
||||
hasCost = data && data.h && (data.h.c || data.d.c || data.m.c || data.h.i || data.d.i || data.m.i);
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -22,25 +24,25 @@
|
||||
<div class="grid grid-cols-{cols} mb-3">
|
||||
<div>Hour</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}
|
||||
{#if hasCost}<div class="text-right">{fmtnum(data.h.c,2)} {currency}</div>{/if}
|
||||
<div>Day</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}
|
||||
{#if hasCost}<div class="text-right">{fmtnum(data.d.c,1)} {currency}</div>{/if}
|
||||
<div>Month</div>
|
||||
<div class="text-right">{fmtnum(data.m.u)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.m.c)} {currency}</div>{/if}
|
||||
{#if hasCost}<div class="text-right">{fmtnum(data.m.c)} {currency}</div>{/if}
|
||||
</div>
|
||||
<strong>Export</strong>
|
||||
<div class="grid grid-cols-{cols}">
|
||||
<div>Hour</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}
|
||||
{#if hasCost}<div class="text-right">{fmtnum(data.h.i,2)} {currency}</div>{/if}
|
||||
<div>Day</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}
|
||||
{#if hasCost}<div class="text-right">{fmtnum(data.d.i,1)} {currency}</div>{/if}
|
||||
<div>Month</div>
|
||||
<div class="text-right">{fmtnum(data.m.p)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.m.i)} {currency}</div>{/if}
|
||||
{#if hasCost}<div class="text-right">{fmtnum(data.m.i)} {currency}</div>{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<strong>Consumption</strong>
|
||||
@ -52,7 +54,7 @@
|
||||
<div>Month</div>
|
||||
<div class="text-right">{fmtnum(data.m.u)} kWh</div>
|
||||
</div>
|
||||
{#if currency}
|
||||
{#if hasCost}
|
||||
<strong>Cost</strong>
|
||||
<div class="grid grid-cols-2">
|
||||
<div>Hour</div>
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
t: [0,0,0,0,0,0,0,0,0,0], h: 1
|
||||
},
|
||||
p: {
|
||||
e: false, t: '', r: '', c: '', m: 1.0
|
||||
e: false, t: '', r: '', c: '', m: 1.0, f: null
|
||||
},
|
||||
d: {
|
||||
s: false, t: false, l: 5
|
||||
@ -202,49 +202,57 @@
|
||||
</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>
|
||||
<div class="flex">
|
||||
<div class="w-full">
|
||||
Price region<br/>
|
||||
<select name="pr" bind:value={configuration.p.r} class="in-f w-full">
|
||||
<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="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>
|
||||
<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="in-l">
|
||||
{#each ["NOK","SEK","DKK","EUR"] as c}
|
||||
<option value={c}>{c}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-1">
|
||||
<div class="flex">
|
||||
<div class="w-1/2">
|
||||
Currency<br/>
|
||||
<select name="pc" bind:value={configuration.p.c} class="in-f w-full">
|
||||
{#each ["NOK","SEK","DKK","EUR"] as c}
|
||||
<option value={c}>{c}</option>
|
||||
{/each}
|
||||
</select>
|
||||
Fixed price<br/>
|
||||
<input name="pf" bind:value={configuration.p.f} type="number" min="0.001" max="65" step="0.001" class="in-f tr w-full"/>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Multiplier<br/>
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
<div class="cnt">
|
||||
<div class="grid grid-cols-2">
|
||||
<div class="col-span-2">
|
||||
<PowerGauge val={data.i ? data.i : 0} max={data.im ? data.im : 15000} unit="W" label="Import" sub={data.p} subunit={prices.currency} colorFn={ampcol}/>
|
||||
<PowerGauge val={data.i ? data.i : 0} max={data.im ? data.im : 15000} unit="W" label="Import" sub={data.p} subunit={data.pc} colorFn={ampcol}/>
|
||||
</div>
|
||||
<div>{data.mt ? metertype(data.mt) : '-'}</div>
|
||||
<div class="text-right">{data.ic ? data.ic.toFixed(1) : '-'} kWh</div>
|
||||
@ -72,7 +72,7 @@
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.c, data.ea)}
|
||||
<div class="cnt">
|
||||
<AccountingData data={data.ea} currency={prices.currency} hasExport={data.om > 0 || data.e > 0}/>
|
||||
<AccountingData data={data.ea} currency={data.pc} hasExport={data.om > 0 || data.e > 0}/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.t, data.pr && (data.pr.startsWith("10YNO") || data.pr == '10Y1001A1001A48H'))}
|
||||
@ -80,7 +80,7 @@
|
||||
<TariffPeakChart />
|
||||
</div>
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.p, (typeof data.p == "number") && !Number.isNaN(data.p))}
|
||||
{#if uiVisibility(sysinfo.ui.p, data.pe && !Number.isNaN(data.p))}
|
||||
<div class="cnt gwf">
|
||||
<PricePlot json={prices}/>
|
||||
</div>
|
||||
|
||||
@ -50,7 +50,7 @@ export const dataStore = readable(data, (set) => {
|
||||
lastTemp = data.t;
|
||||
setTimeout(getTemperatures, 2000);
|
||||
}
|
||||
if(lastPrice != data.p) {
|
||||
if(lastPrice != data.p && data.pe) {
|
||||
lastPrice = data.p;
|
||||
setTimeout(getPrices, 4000);
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public:
|
||||
void setTimezone(Timezone* tz);
|
||||
void setMqttEnabled(bool);
|
||||
void setEntsoeApi(EntsoeApi* eapi);
|
||||
void setPriceRegion(String);
|
||||
void setPriceSettings(String region, String currency);
|
||||
|
||||
private:
|
||||
RemoteDebug* debugger;
|
||||
@ -61,6 +61,7 @@ private:
|
||||
bool performUpgrade = false;
|
||||
bool rebootForUpgrade = false;
|
||||
String priceRegion = "";
|
||||
String priceCurrency = "";
|
||||
#if defined(AMS2MQTT_FIRMWARE_URL)
|
||||
String customFirmwareUrl = AMS2MQTT_FIRMWARE_URL;
|
||||
#else
|
||||
|
||||
@ -3,5 +3,6 @@
|
||||
"t": "%s",
|
||||
"r": "%s",
|
||||
"c": "%s",
|
||||
"m": %.3f
|
||||
"m": %.3f,
|
||||
"f": %s
|
||||
},
|
||||
|
||||
@ -56,7 +56,9 @@
|
||||
"i" : %.2f
|
||||
}
|
||||
},
|
||||
"pe" : %s,
|
||||
"pr" : "%s",
|
||||
"pc" : "%s",
|
||||
"he" : %d,
|
||||
"ee" : %d,
|
||||
"c" : %lu,
|
||||
|
||||
@ -413,9 +413,7 @@ void AmsWebServer::dataJson() {
|
||||
mqttStatus = 3;
|
||||
}
|
||||
|
||||
float price = ENTSOE_NO_VALUE;
|
||||
if(eapi != NULL)
|
||||
price = eapi->getValueForHour(0);
|
||||
float price = ea->getPriceForHour(0);
|
||||
|
||||
String peaks = "";
|
||||
for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) {
|
||||
@ -475,7 +473,9 @@ void AmsWebServer::dataJson() {
|
||||
ea->getCostThisMonth(),
|
||||
ea->getProducedThisMonth(),
|
||||
ea->getIncomeThisMonth(),
|
||||
eapi == NULL ? "" : priceRegion.c_str(),
|
||||
eapi == NULL ? "false" : "true",
|
||||
priceRegion.c_str(),
|
||||
priceCurrency.c_str(),
|
||||
meterState->getLastError(),
|
||||
eapi == NULL ? 0 : eapi->getLastError(),
|
||||
(uint32_t) now,
|
||||
@ -902,7 +902,8 @@ void AmsWebServer::configurationJson() {
|
||||
entsoe.token,
|
||||
entsoe.area,
|
||||
entsoe.currency,
|
||||
entsoe.multiplier / 1000.0
|
||||
entsoe.multiplier / 1000.0,
|
||||
entsoe.fixedPrice == 0 ? "null" : String(entsoe.fixedPrice / 1000.0, 10).c_str()
|
||||
);
|
||||
server.sendContent(buf);
|
||||
snprintf_P(buf, BufferSize, CONF_DEBUG_JSON,
|
||||
@ -1423,6 +1424,7 @@ void AmsWebServer::handleSave() {
|
||||
strcpy(entsoe.area, priceRegion.c_str());
|
||||
strcpy(entsoe.currency, server.arg(F("pc")).c_str());
|
||||
entsoe.multiplier = server.arg(F("pm")).toFloat() * 1000;
|
||||
entsoe.fixedPrice = server.arg(F("pf")).toFloat() * 1000;
|
||||
config->setEntsoeConfig(entsoe);
|
||||
}
|
||||
|
||||
@ -1884,8 +1886,9 @@ void AmsWebServer::tariffJson() {
|
||||
server.send(200, MIME_JSON, buf);
|
||||
}
|
||||
|
||||
void AmsWebServer::setPriceRegion(String priceRegion) {
|
||||
this->priceRegion = priceRegion;
|
||||
void AmsWebServer::setPriceSettings(String region, String currency) {
|
||||
this->priceRegion = region;
|
||||
this->priceCurrency = currency;
|
||||
}
|
||||
|
||||
void AmsWebServer::configFileDownload() {
|
||||
|
||||
@ -172,7 +172,8 @@ void setup() {
|
||||
eapi->setup(entsoe);
|
||||
ws.setEntsoeApi(eapi);
|
||||
}
|
||||
ws.setPriceRegion(entsoe.area);
|
||||
ws.setPriceSettings(entsoe.area, entsoe.currency);
|
||||
ea.setFixedPrice(entsoe.fixedPrice / 1000.0);
|
||||
bool shared = false;
|
||||
config.getMeterConfig(meterConfig);
|
||||
Serial.flush();
|
||||
@ -537,8 +538,9 @@ void loop() {
|
||||
eapi = NULL;
|
||||
ws.setEntsoeApi(NULL);
|
||||
}
|
||||
ws.setPriceRegion(entsoe.area);
|
||||
ws.setPriceSettings(entsoe.area, entsoe.currency);
|
||||
config.ackEntsoeChange();
|
||||
ea.setFixedPrice(entsoe.fixedPrice / 1000.0);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
debugE("Exception in ENTSO-E loop (%s)", e.what());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user