mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-05-23 13:46:28 +00:00
Compare commits
29 Commits
v2.5.5
...
upgrade/sv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a8e26dcc4 | ||
|
|
4673feaaf3 | ||
|
|
2c96b4d94f | ||
|
|
6011d3169e | ||
|
|
f0c3873635 | ||
|
|
fb4eea8208 | ||
|
|
e628056e56 | ||
|
|
df5611844f | ||
|
|
3cb6e09341 | ||
|
|
f7ccd2a96b | ||
|
|
13aff62aff | ||
|
|
64a0667947 | ||
|
|
4d128700c1 | ||
|
|
6743750d8f | ||
|
|
640e957065 | ||
|
|
d4f11c0412 | ||
|
|
01acc6d6e8 | ||
|
|
e89bb53941 | ||
|
|
009c4686ee | ||
|
|
33dc5fc177 | ||
|
|
faf047e25f | ||
|
|
b4322c5f9c | ||
|
|
0b4884652f | ||
|
|
82aeae8699 | ||
|
|
a7333653b0 | ||
|
|
24e63d5e32 | ||
|
|
eb7c266378 | ||
|
|
cf8c48ab99 | ||
|
|
78a1cd78ea |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '19.x'
|
||||
node-version: '22.x'
|
||||
- name: Build with node
|
||||
run: |
|
||||
cd lib/SvelteUi/app
|
||||
|
||||
82
.github/workflows/pr-build-env.yml
vendored
Normal file
82
.github/workflows/pr-build-env.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
name: PR build with env
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
env:
|
||||
description: 'The environment to build for'
|
||||
required: true
|
||||
type: string
|
||||
is_esp32:
|
||||
description: 'Whether the build is for ESP32 based firmware'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code from repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache Python dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
- name: Cache PlatformIO dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.pio/libdeps
|
||||
key: ${{ runner.os }}-pio-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Inject secrets into ini file
|
||||
run: |
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_KEY/AMS2MQTT_PRICE_KEY="${{secrets.AMS2MQTT_PRICE_KEY}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_AUTHENTICATION/AMS2MQTT_PRICE_AUTHENTICATION="${{secrets.AMS2MQTT_PRICE_AUTHENTICATION}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_SC_KEY/AMS2MQTT_SC_KEY=\\"${{secrets.AMS2MQTT_SC_KEY}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_USER/ENERGY_SPEEDOMETER_USER=\\"${{secrets.ENERGY_SPEEDOMETER_USER}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_PASS/ENERGY_SPEEDOMETER_PASS=\\"${{secrets.ENERGY_SPEEDOMETER_PASS}}\\"/g' platformio.ini
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio css_html_js_minify
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '19.x'
|
||||
|
||||
- name: Build with node
|
||||
run: |
|
||||
cd lib/SvelteUi/app
|
||||
npm ci
|
||||
npm run build
|
||||
cd -
|
||||
env:
|
||||
CI: false
|
||||
|
||||
- name: PlatformIO lib install
|
||||
run: pio lib install
|
||||
|
||||
- name: Build firmware
|
||||
run: pio run -e ${{ inputs.env }}
|
||||
|
||||
- name: Create zip file
|
||||
run: /bin/sh scripts/${{ inputs.env }}/mkzip.sh
|
||||
|
||||
- name: Upload zip as artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ inputs.env }}.zip
|
||||
path: ${{ inputs.env }}.zip
|
||||
archive: false
|
||||
retention-days: 7
|
||||
110
.github/workflows/pull-request.yml
vendored
Normal file
110
.github/workflows/pull-request.yml
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
name: Pull Request build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
build-esp32s2:
|
||||
uses: ./.github/workflows/pr-build-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32s2
|
||||
is_esp32: true
|
||||
|
||||
build-esp32s3:
|
||||
uses: ./.github/workflows/pr-build-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32s3
|
||||
is_esp32: true
|
||||
|
||||
build-esp32c3:
|
||||
uses: ./.github/workflows/pr-build-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32c3
|
||||
is_esp32: true
|
||||
|
||||
build-esp32:
|
||||
uses: ./.github/workflows/pr-build-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32
|
||||
is_esp32: true
|
||||
|
||||
build-esp32solo:
|
||||
uses: ./.github/workflows/pr-build-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32solo
|
||||
is_esp32: true
|
||||
|
||||
build-esp8266:
|
||||
uses: ./.github/workflows/pr-build-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp8266
|
||||
is_esp32: false
|
||||
|
||||
comment:
|
||||
needs:
|
||||
- build-esp32s2
|
||||
- build-esp32s3
|
||||
- build-esp32c3
|
||||
- build-esp32
|
||||
- build-esp32solo
|
||||
- build-esp8266
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Post PR comment with download links
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const runId = context.runId;
|
||||
const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`;
|
||||
|
||||
// Get the commit SHA (short version)
|
||||
const sha = context.payload.pull_request.head.sha;
|
||||
const shortSha = sha.substring(0, 7);
|
||||
|
||||
// Fetch the list of artifacts for this run via the API
|
||||
const artifactsResp = await github.rest.actions.listWorkflowRunArtifacts({ owner, repo, run_id: runId });
|
||||
const artifacts = artifactsResp.data.artifacts;
|
||||
|
||||
const envs = ['esp32s2', 'esp32s3', 'esp32c3', 'esp32', 'esp32solo', 'esp8266'];
|
||||
const lines = envs.map(env => {
|
||||
const artifact = artifacts.find(a => a.name === `${env}.zip`);
|
||||
if (artifact) {
|
||||
// The artifact download page URL - directly navigable in the browser
|
||||
const artifactUrl = `${runUrl}#artifacts-${env}`;
|
||||
return `- **${env}**: [Download ${env}.zip](https://github.com/${owner}/${repo}/actions/runs/${runId}/artifacts/${artifact.id})`;
|
||||
}
|
||||
return `- **${env}**: ⚠️ artifact not found`;
|
||||
});
|
||||
|
||||
const body = [
|
||||
'## 🔧 PR Build Artifacts',
|
||||
'',
|
||||
`**Version**: \`${shortSha}\``,
|
||||
'',
|
||||
'All environments built successfully. Download the zip files:',
|
||||
'',
|
||||
...lines,
|
||||
'',
|
||||
`> Artifacts expire after 7 days. [View workflow run](${runUrl})`,
|
||||
].join('\n');
|
||||
|
||||
// Find and delete any previous bot comment to keep the PR clean
|
||||
const comments = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber });
|
||||
for (const comment of comments.data) {
|
||||
if (comment.user.type === 'Bot' && comment.body.includes('PR Build Artifacts')) {
|
||||
await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id });
|
||||
}
|
||||
}
|
||||
|
||||
await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body });
|
||||
2
.github/workflows/release-deploy-env.yml
vendored
2
.github/workflows/release-deploy-env.yml
vendored
@@ -73,7 +73,7 @@ jobs:
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '19.x'
|
||||
node-version: '22.x'
|
||||
- name: Build with node
|
||||
run: |
|
||||
cd lib/SvelteUi/app
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -381,6 +381,8 @@ public:
|
||||
bool isZmartChargeConfigChanged();
|
||||
void ackZmartChargeConfig();
|
||||
|
||||
uint32_t getChipId();
|
||||
void getUniqueName(char* buffer, size_t length);
|
||||
|
||||
void clear();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -124,16 +124,12 @@ void AmsConfiguration::clearNetworkConfig(NetworkConfig& config) {
|
||||
memset(config.ssid, 0, 32);
|
||||
memset(config.psk, 0, 64);
|
||||
clearNetworkConfigIp(config);
|
||||
|
||||
uint16_t chipId;
|
||||
getUniqueName(config.hostname, 32);
|
||||
#if defined(ESP32)
|
||||
chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF;
|
||||
config.power = 195;
|
||||
#else
|
||||
chipId = ESP.getChipId();
|
||||
config.power = 205;
|
||||
#endif
|
||||
strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str());
|
||||
config.mdns = true;
|
||||
config.sleep = 0xFF;
|
||||
config.use11b = 1;
|
||||
@@ -591,7 +587,6 @@ void AmsConfiguration::clearGpio(GpioConfig& config, bool all) {
|
||||
config.tempAnalogSensorPin = 0xFF;
|
||||
config.vccPin = 0xFF;
|
||||
config.ledDisablePin = 0xFF;
|
||||
config.powersaving = 0;
|
||||
|
||||
if(all) {
|
||||
config.vccOffset = 0;
|
||||
@@ -600,6 +595,7 @@ void AmsConfiguration::clearGpio(GpioConfig& config, bool all) {
|
||||
config.vccResistorGnd = 0;
|
||||
config.vccResistorVcc = 0;
|
||||
config.ledBehaviour = LED_BEHAVIOUR_DEFAULT;
|
||||
config.powersaving = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,6 +979,23 @@ void AmsConfiguration::setUiLanguageChanged() {
|
||||
uiLanguageChanged = true;
|
||||
}
|
||||
|
||||
uint32_t AmsConfiguration::getChipId() {
|
||||
uint32_t chipId;
|
||||
#if defined(ESP32)
|
||||
for(int i=0; i<17; i=i+8) {
|
||||
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
|
||||
}
|
||||
#else
|
||||
chipId = ESP.getChipId();
|
||||
#endif
|
||||
return chipId;
|
||||
}
|
||||
|
||||
void AmsConfiguration::getUniqueName(char* buffer, size_t length) {
|
||||
uint32_t chipId = getChipId();
|
||||
snprintf(buffer, length, "ams-%06x", chipId);
|
||||
}
|
||||
|
||||
void AmsConfiguration::clear() {
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -7,8 +7,7 @@
|
||||
#ifndef _AMSDATA_H
|
||||
#define _AMSDATA_H
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <Timezone.h>
|
||||
#include <WString.h>
|
||||
#include "OBIScodes.h"
|
||||
|
||||
enum AmsType {
|
||||
@@ -28,7 +27,7 @@ public:
|
||||
AmsData();
|
||||
|
||||
void apply(AmsData& other);
|
||||
void apply(const OBIS_code_t obis, double value);
|
||||
void apply(const OBIS_code_t obis, double value, uint64_t millis64);
|
||||
|
||||
uint64_t getLastUpdateMillis();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AmsData.h"
|
||||
#include <algorithm>
|
||||
|
||||
AmsData::AmsData() {}
|
||||
|
||||
@@ -17,7 +18,6 @@ void AmsData::apply(AmsData& other) {
|
||||
uint32_t power = (activeImportPower + other.getActiveImportPower()) / 2;
|
||||
float add = power * (((float) ms) / 3600000.0);
|
||||
activeImportCounter += add / 1000.0;
|
||||
//Serial.printf("%dW, %dms, %.6fkWh added\n", other.getActiveImportPower(), ms, add);
|
||||
}
|
||||
|
||||
if(other.getListType() > 1) {
|
||||
@@ -112,7 +112,7 @@ void AmsData::apply(AmsData& other) {
|
||||
this->activeExportPower = other.getActiveExportPower();
|
||||
}
|
||||
|
||||
void AmsData::apply(OBIS_code_t obis, double value) {
|
||||
void AmsData::apply(OBIS_code_t obis, double value, uint64_t millis64) {
|
||||
if(obis.sensor == 0 && obis.gr == 0 && obis.tariff == 0) {
|
||||
meterType = value;
|
||||
}
|
||||
@@ -127,138 +127,137 @@ void AmsData::apply(OBIS_code_t obis, double value) {
|
||||
}
|
||||
}
|
||||
if(obis.tariff != 0) {
|
||||
Serial.println("Tariff not implemented");
|
||||
return;
|
||||
}
|
||||
if(obis.gr == 7) { // Instant values
|
||||
switch(obis.sensor) {
|
||||
case 1:
|
||||
activeImportPower = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 2:
|
||||
activeExportPower = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 3:
|
||||
reactiveImportPower = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 4:
|
||||
reactiveExportPower = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 13:
|
||||
powerFactor = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 21:
|
||||
l1activeImportPower = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 22:
|
||||
l1activeExportPower = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 31:
|
||||
l1current = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 32:
|
||||
l1voltage = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 33:
|
||||
l1PowerFactor = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 41:
|
||||
l2activeImportPower = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 42:
|
||||
l2activeExportPower = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 51:
|
||||
l2current = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 52:
|
||||
l2voltage = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 53:
|
||||
l2PowerFactor = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 61:
|
||||
l3activeImportPower = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 62:
|
||||
l3activeExportPower = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 71:
|
||||
l3current = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 72:
|
||||
l3voltage = value;
|
||||
listType = max(listType, (uint8_t) 2);
|
||||
listType = std::max(listType, (uint8_t) 2);
|
||||
break;
|
||||
case 73:
|
||||
l3PowerFactor = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
}
|
||||
} else if(obis.gr == 8) { // Accumulated values
|
||||
switch(obis.sensor) {
|
||||
case 1:
|
||||
activeImportCounter = value;
|
||||
listType = max(listType, (uint8_t) 3);
|
||||
listType = std::max(listType, (uint8_t) 3);
|
||||
break;
|
||||
case 2:
|
||||
activeExportCounter = value;
|
||||
listType = max(listType, (uint8_t) 3);
|
||||
listType = std::max(listType, (uint8_t) 3);
|
||||
break;
|
||||
case 3:
|
||||
reactiveImportCounter = value;
|
||||
listType = max(listType, (uint8_t) 3);
|
||||
listType = std::max(listType, (uint8_t) 3);
|
||||
break;
|
||||
case 4:
|
||||
reactiveExportCounter = value;
|
||||
listType = max(listType, (uint8_t) 3);
|
||||
listType = std::max(listType, (uint8_t) 3);
|
||||
break;
|
||||
case 21:
|
||||
l1activeImportCounter = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 22:
|
||||
l1activeExportCounter = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 41:
|
||||
l2activeImportCounter = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 42:
|
||||
l2activeExportCounter = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 61:
|
||||
l3activeImportCounter = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
case 62:
|
||||
l3activeExportCounter = value;
|
||||
listType = max(listType, (uint8_t) 4);
|
||||
listType = std::max(listType, (uint8_t) 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(listType > 0)
|
||||
lastUpdateMillis = millis();
|
||||
lastUpdateMillis = millis64;
|
||||
|
||||
threePhase = l1voltage > 0 && l2voltage > 0 && l3voltage > 0;
|
||||
if(!threePhase)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -639,25 +639,22 @@ bool AmsDataStorage::isDayHappy(time_t now) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(now < FirmwareVersion::BuildEpoch) return false;
|
||||
|
||||
if(now < day.lastMeterReadTime) {
|
||||
// If the timestamp is before the firware was built, there is something seriously wrong..
|
||||
if(now < FirmwareVersion::BuildEpoch) {
|
||||
return false;
|
||||
}
|
||||
// There are cases where the meter reports before the hour. The update method will then receive the meter timestamp as reference, thus there will not be 3600s between.
|
||||
// Leaving a 100s buffer for these cases
|
||||
if(now-day.lastMeterReadTime > 3500) {
|
||||
|
||||
// If the timestamp is before the last time we updated, there is also something wrong..
|
||||
if(now < day.lastMeterReadTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tmElements_t tm, last;
|
||||
breakTime(tz->toLocal(now), tm);
|
||||
breakTime(tz->toLocal(day.lastMeterReadTime), last);
|
||||
if(tm.Hour != last.Hour) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// If the timestamp is at the same day and hour as last update, we are happy
|
||||
return tm.Day == last.Day && tm.Hour == last.Hour;
|
||||
}
|
||||
|
||||
bool AmsDataStorage::isMonthHappy(time_t now) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx, bool hastag) {
|
||||
footersize += authkeylen;
|
||||
memcpy(additional_authenticated_data + 1, authentication_key, 16);
|
||||
memcpy(authentication_tag, ptr + len - footersize - 5, authkeylen);
|
||||
for(uint8_t i; i < 16; i++) authenticate |= authentication_key[i] > 0;
|
||||
for(uint8_t i = 0; i < 16; i++) authenticate |= authentication_key[i] > 0;
|
||||
}
|
||||
|
||||
#if defined(ESP8266)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <Print.h>
|
||||
@@ -39,6 +44,8 @@
|
||||
#define AMS_UPDATE_ERR_SUCCESS_CONFIRMED 123
|
||||
|
||||
#define UPDATE_BUF_SIZE 4096
|
||||
#define UPDATE_MAX_BLOCK_RETRY 25
|
||||
#define UPDATE_MAX_REBOOT_RETRY 12
|
||||
|
||||
class AmsFirmwareUpdater {
|
||||
public:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
#include "AmsFirmwareUpdater.h"
|
||||
#include "AmsStorage.h"
|
||||
#include "FirmwareVersion.h"
|
||||
@@ -74,7 +79,7 @@ void AmsFirmwareUpdater::setUpgradeInformation(UpgradeInformation& upinfo) {
|
||||
#endif
|
||||
debugger->printf_P(PSTR("Resuming uprade to %s\n"), updateStatus.toVersion);
|
||||
|
||||
if(updateStatus.reboot_count++ < 8) {
|
||||
if(updateStatus.reboot_count++ < UPDATE_MAX_REBOOT_RETRY) {
|
||||
updateStatus.errorCode = AMS_UPDATE_ERR_OK;
|
||||
} else {
|
||||
updateStatus.errorCode = AMS_UPDATE_ERR_REBOOT;
|
||||
@@ -129,7 +134,7 @@ void AmsFirmwareUpdater::loop() {
|
||||
HTTPClient http;
|
||||
start = millis();
|
||||
if(!fetchFirmwareChunk(http)) {
|
||||
if(updateStatus.retry_count++ == 3) {
|
||||
if(updateStatus.retry_count++ > UPDATE_MAX_BLOCK_RETRY) {
|
||||
updateStatus.errorCode = AMS_UPDATE_ERR_FETCH;
|
||||
updateStatusChanged = true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "AmsDataStorage.h"
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
#include "AmsJsonGenerator.h"
|
||||
|
||||
void AmsJsonGenerator::generateDayPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -7,8 +7,6 @@
|
||||
#ifndef _ENERGYACCOUNTING_H
|
||||
#define _ENERGYACCOUNTING_H
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "AmsData.h"
|
||||
#include "AmsDataStorage.h"
|
||||
#include "PriceService.h"
|
||||
|
||||
@@ -83,7 +81,7 @@ public:
|
||||
void setPriceService(PriceService *ps);
|
||||
void setTimezone(Timezone*);
|
||||
EnergyAccountingConfig* getConfig();
|
||||
bool update(AmsData* amsData);
|
||||
bool update(time_t now, uint64_t lastUpdatedMillis, uint8_t listType, uint32_t activeImportPower, uint32_t activeExportPower);
|
||||
bool load();
|
||||
bool save();
|
||||
bool isInitialized();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -54,9 +54,8 @@ bool EnergyAccounting::isInitialized() {
|
||||
return this->init;
|
||||
}
|
||||
|
||||
bool EnergyAccounting::update(AmsData* amsData) {
|
||||
bool EnergyAccounting::update(time_t now, uint64_t lastUpdatedMillis, uint8_t listType, uint32_t activeImportPower, uint32_t activeExportPower) {
|
||||
if(config == NULL) return false;
|
||||
time_t now = time(nullptr);
|
||||
if(now < FirmwareVersion::BuildEpoch) return false;
|
||||
if(tz == NULL) {
|
||||
return false;
|
||||
@@ -90,7 +89,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
calcDayCost();
|
||||
}
|
||||
|
||||
if(local.Hour != realtimeData->currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) {
|
||||
if(local.Hour != realtimeData->currentHour && (listType >= 3 || local.Minute == 1)) {
|
||||
tmElements_t oneHrAgo, oneHrAgoLocal;
|
||||
breakTime(now-3600, oneHrAgo);
|
||||
uint16_t val = round(ds->getHourImport(oneHrAgo.Hour) / 10.0);
|
||||
@@ -156,9 +155,9 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
}
|
||||
}
|
||||
|
||||
if(realtimeData->lastImportUpdateMillis < amsData->getLastUpdateMillis()) {
|
||||
unsigned long ms = amsData->getLastUpdateMillis() - realtimeData->lastImportUpdateMillis;
|
||||
float kwhi = (amsData->getActiveImportPower() * (((float) ms) / 3600000.0)) / 1000.0;
|
||||
if(realtimeData->lastImportUpdateMillis < lastUpdatedMillis) {
|
||||
unsigned long ms = lastUpdatedMillis - realtimeData->lastImportUpdateMillis;
|
||||
float kwhi = (activeImportPower * (((float) ms) / 3600000.0)) / 1000.0;
|
||||
if(kwhi > 0) {
|
||||
realtimeData->use += kwhi;
|
||||
float importPrice = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_IMPORT);
|
||||
@@ -168,12 +167,12 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
realtimeData->costDay += cost;
|
||||
}
|
||||
}
|
||||
realtimeData->lastImportUpdateMillis = amsData->getLastUpdateMillis();
|
||||
realtimeData->lastImportUpdateMillis = lastUpdatedMillis;
|
||||
}
|
||||
|
||||
if(amsData->getListType() > 1 && realtimeData->lastExportUpdateMillis < amsData->getLastUpdateMillis()) {
|
||||
unsigned long ms = amsData->getLastUpdateMillis() - realtimeData->lastExportUpdateMillis;
|
||||
float kwhe = (amsData->getActiveExportPower() * (((float) ms) / 3600000.0)) / 1000.0;
|
||||
if(listType > 1 && realtimeData->lastExportUpdateMillis < lastUpdatedMillis) {
|
||||
unsigned long ms = lastUpdatedMillis - realtimeData->lastExportUpdateMillis;
|
||||
float kwhe = (activeExportPower * (((float) ms) / 3600000.0)) / 1000.0;
|
||||
if(kwhe > 0) {
|
||||
realtimeData->produce += kwhe;
|
||||
float exportPrice = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_EXPORT);
|
||||
@@ -183,7 +182,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
realtimeData->incomeDay += income;
|
||||
}
|
||||
}
|
||||
realtimeData->lastExportUpdateMillis = amsData->getLastUpdateMillis();
|
||||
realtimeData->lastExportUpdateMillis = lastUpdatedMillis;
|
||||
}
|
||||
|
||||
if(config != NULL) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name" : "%s%s",
|
||||
"stat_t" : "%s%s",
|
||||
"uniq_id" : "%s_%s",
|
||||
"obj_id" : "%s_%s",
|
||||
"default_entity_id" : "sensor.%s_%s",
|
||||
"val_tpl" : "{{ value_json.%s | is_defined }}",
|
||||
"expire_after" : %d,
|
||||
"dev" : {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
bool applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterConfig& meterConfig, uint8_t hanPin);
|
||||
void setup(SystemConfig* sys, GpioConfig* gpio);
|
||||
float getVcc();
|
||||
void setMaxVcc(float maxVcc);
|
||||
uint8_t getTempSensorCount();
|
||||
TempSensorData* getTempSensorData(uint8_t);
|
||||
bool updateTemperatures();
|
||||
@@ -68,7 +69,7 @@ private:
|
||||
uint8_t vccPin, vccGnd_r, vccVcc_r;
|
||||
float vccOffset, vccMultiplier;
|
||||
float vcc = 3.3; // Last known Vcc
|
||||
float maxVcc = 3.25; // Best to have this close to max as a start, in case Pow-U reboots and starts off with a low voltage, we dont want that to be perceived as max
|
||||
float maxVcc = 3.28; // Best to have this close to max as a start, in case Pow-U reboots and starts off with a low voltage, we dont want that to be perceived as max
|
||||
unsigned long lastVccRead = 0;
|
||||
|
||||
uint16_t analogRange = 1024;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -664,7 +664,8 @@ bool HwTools::isVoltageOptimal(float range) {
|
||||
lastVccRead = now;
|
||||
}
|
||||
if(vcc > 3.4 || vcc < 2.8) {
|
||||
maxVcc = 0; // Voltage is outside the operating range, we have to assume voltage is OK
|
||||
maxVcc = 0;
|
||||
return true; // Voltage is outside the operating range, we have to assume voltage is OK
|
||||
} else if(vcc > maxVcc) {
|
||||
maxVcc = vcc;
|
||||
} else {
|
||||
@@ -677,4 +678,8 @@ bool HwTools::isVoltageOptimal(float range) {
|
||||
|
||||
uint8_t HwTools::getBoardType() {
|
||||
return boardType;
|
||||
}
|
||||
|
||||
void HwTools::setMaxVcc(float vcc) {
|
||||
this->maxVcc = min(3.3f, vcc);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "AmsConfiguration.h"
|
||||
#include "DataParser.h"
|
||||
#include "Cosem.h"
|
||||
#include "Timezone.h"
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
#include "RemoteDebug.h"
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2024
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: All rights reserved
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2024
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -24,8 +24,6 @@ void KmpCommunicator::configure(MeterConfig& meterConfig) {
|
||||
}
|
||||
|
||||
bool KmpCommunicator::loop() {
|
||||
uint64_t now = millis64();
|
||||
|
||||
bool ret = talker->loop();
|
||||
int lastError = getLastError();
|
||||
if(ret) {
|
||||
@@ -58,35 +56,36 @@ AmsData* KmpCommunicator::getData(AmsData& meterState) {
|
||||
if(talker == NULL) return NULL;
|
||||
KmpDataHolder kmpData;
|
||||
talker->getData(kmpData);
|
||||
uint64_t now = millis64();
|
||||
AmsData* data = new AmsData();
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT, kmpData.activeImportCounter);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_COUNT, kmpData.activeExportCounter);
|
||||
data->apply(OBIS_REACTIVE_IMPORT_COUNT, kmpData.reactiveImportCounter);
|
||||
data->apply(OBIS_REACTIVE_EXPORT_COUNT, kmpData.reactiveExportCounter);
|
||||
data->apply(OBIS_ACTIVE_IMPORT, kmpData.activeImportPower);
|
||||
data->apply(OBIS_ACTIVE_EXPORT, kmpData.activeExportPower);
|
||||
data->apply(OBIS_REACTIVE_IMPORT, kmpData.reactiveImportPower);
|
||||
data->apply(OBIS_REACTIVE_EXPORT, kmpData.reactiveExportPower);
|
||||
data->apply(OBIS_VOLTAGE_L1, kmpData.l1voltage);
|
||||
data->apply(OBIS_VOLTAGE_L2, kmpData.l2voltage);
|
||||
data->apply(OBIS_VOLTAGE_L3, kmpData.l3voltage);
|
||||
data->apply(OBIS_CURRENT_L1, kmpData.l1current);
|
||||
data->apply(OBIS_CURRENT_L2, kmpData.l2current);
|
||||
data->apply(OBIS_CURRENT_L3, kmpData.l3current);
|
||||
data->apply(OBIS_POWER_FACTOR_L1, kmpData.l1PowerFactor);
|
||||
data->apply(OBIS_POWER_FACTOR_L2, kmpData.l2PowerFactor);
|
||||
data->apply(OBIS_POWER_FACTOR_L3, kmpData.l3PowerFactor);
|
||||
data->apply(OBIS_POWER_FACTOR, kmpData.powerFactor);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_L1, kmpData.l1activeImportPower);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_L2, kmpData.l2activeImportPower);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_L3, kmpData.l3activeImportPower);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_L1, kmpData.l1activeExportPower);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_L2, kmpData.l2activeExportPower);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_L3, kmpData.l3activeExportPower);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L1, kmpData.l1activeImportCounter);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L2, kmpData.l2activeImportCounter);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L3, kmpData.l3activeImportCounter);
|
||||
data->apply(OBIS_METER_ID, kmpData.meterId);
|
||||
data->apply(OBIS_NULL, AmsTypeKamstrup);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT, kmpData.activeImportCounter, now);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_COUNT, kmpData.activeExportCounter, now);
|
||||
data->apply(OBIS_REACTIVE_IMPORT_COUNT, kmpData.reactiveImportCounter, now);
|
||||
data->apply(OBIS_REACTIVE_EXPORT_COUNT, kmpData.reactiveExportCounter, now);
|
||||
data->apply(OBIS_ACTIVE_IMPORT, kmpData.activeImportPower, now);
|
||||
data->apply(OBIS_ACTIVE_EXPORT, kmpData.activeExportPower, now);
|
||||
data->apply(OBIS_REACTIVE_IMPORT, kmpData.reactiveImportPower, now);
|
||||
data->apply(OBIS_REACTIVE_EXPORT, kmpData.reactiveExportPower, now);
|
||||
data->apply(OBIS_VOLTAGE_L1, kmpData.l1voltage, now);
|
||||
data->apply(OBIS_VOLTAGE_L2, kmpData.l2voltage, now);
|
||||
data->apply(OBIS_VOLTAGE_L3, kmpData.l3voltage, now);
|
||||
data->apply(OBIS_CURRENT_L1, kmpData.l1current, now);
|
||||
data->apply(OBIS_CURRENT_L2, kmpData.l2current, now);
|
||||
data->apply(OBIS_CURRENT_L3, kmpData.l3current, now);
|
||||
data->apply(OBIS_POWER_FACTOR_L1, kmpData.l1PowerFactor, now);
|
||||
data->apply(OBIS_POWER_FACTOR_L2, kmpData.l2PowerFactor, now);
|
||||
data->apply(OBIS_POWER_FACTOR_L3, kmpData.l3PowerFactor, now);
|
||||
data->apply(OBIS_POWER_FACTOR, kmpData.powerFactor, now);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_L1, kmpData.l1activeImportPower, now);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_L2, kmpData.l2activeImportPower, now);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_L3, kmpData.l3activeImportPower, now);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_L1, kmpData.l1activeExportPower, now);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_L2, kmpData.l2activeExportPower, now);
|
||||
data->apply(OBIS_ACTIVE_EXPORT_L3, kmpData.l3activeExportPower, now);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L1, kmpData.l1activeImportCounter, now);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L2, kmpData.l2activeImportCounter, now);
|
||||
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L3, kmpData.l3activeImportCounter, now);
|
||||
data->apply(OBIS_METER_ID, kmpData.meterId, now);
|
||||
data->apply(OBIS_NULL, AmsTypeKamstrup, now);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -102,6 +102,8 @@ char* PriceService::getSource() {
|
||||
return this->today->getSource();
|
||||
} else if(tomorrow != NULL) {
|
||||
return this->tomorrow->getSource();
|
||||
} else if(!this->config->enabled && this->priceConfig.capacity() != 0) {
|
||||
return "FIX"; // Fixed price
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
#include "PricesContainer.h"
|
||||
#include <cstring>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
@@ -7,7 +7,6 @@
|
||||
#ifndef _REALTIMEPLOT_H
|
||||
#define _REALTIMEPLOT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "AmsData.h"
|
||||
|
||||
#define REALTIME_SAMPLE 10000
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2023
|
||||
* @copyright Utilitech AS 2023-2026
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "RealtimePlot.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
59
lib/SvelteUi/app/README.md
Normal file
59
lib/SvelteUi/app/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# SvelteUi App
|
||||
|
||||
Web interface for AMS Reader firmware built with Svelte 5 and Vite 6.
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
- Node.js 20.x or 22.x LTS (required for Vite 6)
|
||||
- npm
|
||||
|
||||
### Local Development Configuration
|
||||
|
||||
To develop against your AMS reader device, you need to configure the proxy target:
|
||||
|
||||
1. Copy the example config file:
|
||||
```bash
|
||||
cp vite.config.local.example.js vite.config.local.js
|
||||
```
|
||||
|
||||
2. Edit `vite.config.local.js` and update the IP address to match your device:
|
||||
```javascript
|
||||
export default {
|
||||
proxyTarget: "http://192.168.1.100" // Your device's IP
|
||||
}
|
||||
```
|
||||
|
||||
3. The `vite.config.local.js` file is gitignored, so your personal settings won't be committed.
|
||||
|
||||
### Running Development Server
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The dev server will proxy API requests to your configured device IP.
|
||||
|
||||
### Building for Production
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The build output will be in the `dist/` directory.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `src/` - Application source code
|
||||
- `routes/` - Page components using svelte-spa-router
|
||||
- `lib/` - Shared components and utilities
|
||||
- `public/` - Static assets (favicon, etc.)
|
||||
- `dist/` - Build output (not committed to git)
|
||||
|
||||
## Key Technologies
|
||||
|
||||
- **Svelte 5.17.0** - UI framework
|
||||
- **Vite 6.0.7** - Build tool
|
||||
- **svelte-spa-router 4.0.1** - Hash-based routing
|
||||
- **Tailwind CSS** - Styling
|
||||
2
lib/SvelteUi/app/dist/index.css
vendored
2
lib/SvelteUi/app/dist/index.css
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user