Merge branch 'development'
This commit is contained in:
commit
9da5be7fd8
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -52,6 +52,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Get tags
|
||||||
|
run: git fetch --force --tags origin
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
This is a fork from the Hoymiles project OpenDTU.
|
This is a fork from the Hoymiles project OpenDTU.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Extensions to the original OpenDTU
|
## Extensions to the original OpenDTU
|
||||||
|
|
||||||
This project is still under development and adds following features:
|
This project is still under development and adds following features:
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "SDM.h"
|
#include "SDM.h"
|
||||||
|
#include "sml.h"
|
||||||
|
|
||||||
#ifndef SDM_RX_PIN
|
#ifndef SDM_RX_PIN
|
||||||
#define SDM_RX_PIN 13
|
#define SDM_RX_PIN 13
|
||||||
@ -16,6 +17,16 @@
|
|||||||
#define SDM_TX_PIN 32
|
#define SDM_TX_PIN 32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef SML_RX_PIN
|
||||||
|
#define SML_RX_PIN 35
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const unsigned char OBIS[6];
|
||||||
|
void (*Fn)(double&);
|
||||||
|
float* Arg;
|
||||||
|
} OBISHandler;
|
||||||
|
|
||||||
class PowerMeterClass {
|
class PowerMeterClass {
|
||||||
public:
|
public:
|
||||||
enum SOURCE {
|
enum SOURCE {
|
||||||
@ -23,6 +34,7 @@ public:
|
|||||||
SOURCE_SDM1PH = 1,
|
SOURCE_SDM1PH = 1,
|
||||||
SOURCE_SDM3PH = 2,
|
SOURCE_SDM3PH = 2,
|
||||||
SOURCE_HTTP = 3,
|
SOURCE_HTTP = 3,
|
||||||
|
SOURCE_SML = 4
|
||||||
};
|
};
|
||||||
void init();
|
void init();
|
||||||
void mqtt();
|
void mqtt();
|
||||||
@ -40,14 +52,20 @@ private:
|
|||||||
float _powerMeter1Power = 0.0;
|
float _powerMeter1Power = 0.0;
|
||||||
float _powerMeter2Power = 0.0;
|
float _powerMeter2Power = 0.0;
|
||||||
float _powerMeter3Power = 0.0;
|
float _powerMeter3Power = 0.0;
|
||||||
float _powerMeterTotalPower = 0.0;
|
|
||||||
float _powerMeter1Voltage = 0.0;
|
float _powerMeter1Voltage = 0.0;
|
||||||
float _powerMeter2Voltage = 0.0;
|
float _powerMeter2Voltage = 0.0;
|
||||||
float _powerMeter3Voltage = 0.0;
|
float _powerMeter3Voltage = 0.0;
|
||||||
float _PowerMeterImport = 0.0;
|
float _powerMeterImport = 0.0;
|
||||||
float _PowerMeterExport = 0.0;
|
float _powerMeterExport = 0.0;
|
||||||
|
|
||||||
bool mqttInitDone = false;
|
bool mqttInitDone = false;
|
||||||
|
|
||||||
|
bool smlReadLoop();
|
||||||
|
const std::list<OBISHandler> smlHandlerList{
|
||||||
|
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter1Power},
|
||||||
|
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport},
|
||||||
|
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PowerMeterClass PowerMeter;
|
extern PowerMeterClass PowerMeter;
|
||||||
|
|||||||
405
lib/SMLParser/sml.cpp
Normal file
405
lib/SMLParser/sml.cpp
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "sml.h"
|
||||||
|
#include "smlCrcTable.h"
|
||||||
|
|
||||||
|
#ifdef SML_DEBUG
|
||||||
|
char logBuff[200];
|
||||||
|
|
||||||
|
#ifdef SML_NATIVE
|
||||||
|
#define SML_LOG(...) \
|
||||||
|
do { \
|
||||||
|
printf(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#define SML_TREELOG(level, ...) \
|
||||||
|
do { \
|
||||||
|
printf("%.*s", level, " "); \
|
||||||
|
printf(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#elif ARDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
#define SML_LOG(...) \
|
||||||
|
do { \
|
||||||
|
sprintf(logBuff, __VA_ARGS__); \
|
||||||
|
Serial.print(logBuff); \
|
||||||
|
} while (0)
|
||||||
|
#define SML_TREELOG(level, ...) \
|
||||||
|
do { \
|
||||||
|
sprintf(logBuff, __VA_ARGS__); \
|
||||||
|
Serial.print(logBuff); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define SML_LOG(...) \
|
||||||
|
do { \
|
||||||
|
} while (0)
|
||||||
|
#define SML_TREELOG(level, ...) \
|
||||||
|
do { \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_LIST_SIZE 80
|
||||||
|
#define MAX_TREE_SIZE 10
|
||||||
|
|
||||||
|
static sml_states_t currentState = SML_START;
|
||||||
|
static char nodes[MAX_TREE_SIZE];
|
||||||
|
static unsigned char currentLevel = 0;
|
||||||
|
static unsigned short crc = 0xFFFF;
|
||||||
|
static signed char sc;
|
||||||
|
static unsigned short crcMine = 0xFFFF;
|
||||||
|
static unsigned short crcReceived = 0x0000;
|
||||||
|
static unsigned char len = 4;
|
||||||
|
static unsigned char listBuffer[MAX_LIST_SIZE]; /* keeps a list
|
||||||
|
as length + state + data */
|
||||||
|
static unsigned char listPos = 0;
|
||||||
|
|
||||||
|
void crc16(unsigned char &byte)
|
||||||
|
{
|
||||||
|
#ifdef ARDUINO
|
||||||
|
crc =
|
||||||
|
pgm_read_word_near(&smlCrcTable[(byte ^ crc) & 0xff]) ^ (crc >> 8 & 0xff);
|
||||||
|
#else
|
||||||
|
crc = smlCrcTable[(byte ^ crc) & 0xff] ^ (crc >> 8 & 0xff);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void setState(sml_states_t state, int byteLen)
|
||||||
|
{
|
||||||
|
currentState = state;
|
||||||
|
len = byteLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushListBuffer(unsigned char byte)
|
||||||
|
{
|
||||||
|
if (listPos < MAX_LIST_SIZE) {
|
||||||
|
listBuffer[listPos++] = byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reduceList()
|
||||||
|
{
|
||||||
|
if (currentLevel <= MAX_TREE_SIZE && nodes[currentLevel] > 0)
|
||||||
|
nodes[currentLevel]--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlNewList(unsigned char size)
|
||||||
|
{
|
||||||
|
reduceList();
|
||||||
|
if (currentLevel < MAX_TREE_SIZE)
|
||||||
|
currentLevel++;
|
||||||
|
nodes[currentLevel] = size;
|
||||||
|
SML_TREELOG(currentLevel, "LISTSTART on level %i with %i nodes\n",
|
||||||
|
currentLevel, size);
|
||||||
|
setState(SML_LISTSTART, size);
|
||||||
|
// @todo workaround for lists inside obis lists
|
||||||
|
if (size > 5) {
|
||||||
|
listPos = 0;
|
||||||
|
memset(listBuffer, '\0', MAX_LIST_SIZE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pushListBuffer(size);
|
||||||
|
pushListBuffer(currentState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkMagicByte(unsigned char &byte)
|
||||||
|
{
|
||||||
|
unsigned int size = 0;
|
||||||
|
while (currentLevel > 0 && nodes[currentLevel] == 0) {
|
||||||
|
/* go back in tree if no nodes remaining */
|
||||||
|
SML_TREELOG(currentLevel, "back to previous list\n");
|
||||||
|
currentLevel--;
|
||||||
|
}
|
||||||
|
if (byte > 0x70 && byte <= 0x7F) {
|
||||||
|
/* new list */
|
||||||
|
size = byte & 0x0F;
|
||||||
|
smlNewList(size);
|
||||||
|
}
|
||||||
|
else if (byte >= 0x01 && byte <= 0x6F && nodes[currentLevel] > 0) {
|
||||||
|
if (byte == 0x01) {
|
||||||
|
/* no data, get next */
|
||||||
|
SML_TREELOG(currentLevel, " Data %i (empty)\n", nodes[currentLevel]);
|
||||||
|
pushListBuffer(0);
|
||||||
|
pushListBuffer(currentState);
|
||||||
|
if (nodes[currentLevel] == 1) {
|
||||||
|
setState(SML_LISTEND, 1);
|
||||||
|
SML_TREELOG(currentLevel, "LISTEND\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(SML_NEXT, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size = (byte & 0x0F) - 1;
|
||||||
|
setState(SML_DATA, size);
|
||||||
|
if ((byte & 0xF0) == 0x50) {
|
||||||
|
setState(SML_DATA_SIGNED_INT, size);
|
||||||
|
}
|
||||||
|
else if ((byte & 0xF0) == 0x60) {
|
||||||
|
setState(SML_DATA_UNSIGNED_INT, size);
|
||||||
|
}
|
||||||
|
else if ((byte & 0xF0) == 0x00) {
|
||||||
|
setState(SML_DATA_OCTET_STRING, size);
|
||||||
|
}
|
||||||
|
SML_TREELOG(currentLevel,
|
||||||
|
" Data %i (length = %i%s): ", nodes[currentLevel], size,
|
||||||
|
(currentState == SML_DATA_SIGNED_INT) ? ", signed int"
|
||||||
|
: (currentState == SML_DATA_UNSIGNED_INT) ? ", unsigned int"
|
||||||
|
: (currentState == SML_DATA_OCTET_STRING) ? ", octet string"
|
||||||
|
: "");
|
||||||
|
pushListBuffer(size);
|
||||||
|
pushListBuffer(currentState);
|
||||||
|
}
|
||||||
|
reduceList();
|
||||||
|
}
|
||||||
|
else if (byte == 0x00) {
|
||||||
|
/* end of block */
|
||||||
|
reduceList();
|
||||||
|
SML_TREELOG(currentLevel, "End of block at level %i\n", currentLevel);
|
||||||
|
if (currentLevel == 0) {
|
||||||
|
setState(SML_NEXT, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(SML_BLOCKEND, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (byte & 0x80) {
|
||||||
|
// MSB bit is set, another TL byte will follow
|
||||||
|
if (byte >= 0x80 && byte <= 0x8F) {
|
||||||
|
// Datatype Octet String
|
||||||
|
setState(SML_HDATA, (byte & 0x0F) << 4);
|
||||||
|
}
|
||||||
|
else if (byte >= 0xF0 /*&& byte <= 0xFF*/) {
|
||||||
|
/* Datatype List of ...*/
|
||||||
|
setState(SML_LISTEXTENDED, (byte & 0x0F) << 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (byte == 0x1B && currentLevel == 0) {
|
||||||
|
/* end sequence */
|
||||||
|
setState(SML_END, 3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Unexpected Byte */
|
||||||
|
SML_TREELOG(currentLevel,
|
||||||
|
"UNEXPECTED magicbyte >%02X< at currentLevel %i\n", byte,
|
||||||
|
currentLevel);
|
||||||
|
setState(SML_UNEXPECTED, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sml_states_t smlState(unsigned char ¤tByte)
|
||||||
|
{
|
||||||
|
unsigned char size;
|
||||||
|
if (len > 0)
|
||||||
|
len--;
|
||||||
|
crc16(currentByte);
|
||||||
|
switch (currentState) {
|
||||||
|
case SML_UNEXPECTED:
|
||||||
|
case SML_CHECKSUM_ERROR:
|
||||||
|
case SML_FINAL:
|
||||||
|
case SML_START:
|
||||||
|
currentState = SML_START;
|
||||||
|
currentLevel = 0; // Reset current level at the begin of a new transmission
|
||||||
|
// to prevent problems
|
||||||
|
if (currentByte != 0x1b)
|
||||||
|
setState(SML_UNEXPECTED, 4);
|
||||||
|
if (len == 0) {
|
||||||
|
SML_TREELOG(0, "START\n");
|
||||||
|
/* completely clean any garbage from crc checksum */
|
||||||
|
crc = 0xFFFF;
|
||||||
|
currentByte = 0x1b;
|
||||||
|
crc16(currentByte);
|
||||||
|
crc16(currentByte);
|
||||||
|
crc16(currentByte);
|
||||||
|
crc16(currentByte);
|
||||||
|
setState(SML_VERSION, 4);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SML_VERSION:
|
||||||
|
if (currentByte != 0x01)
|
||||||
|
setState(SML_UNEXPECTED, 4);
|
||||||
|
if (len == 0) {
|
||||||
|
setState(SML_BLOCKSTART, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SML_END:
|
||||||
|
if (currentByte != 0x1b) {
|
||||||
|
SML_LOG("UNEXPECTED char >%02X< at SML_END\n", currentByte);
|
||||||
|
setState(SML_UNEXPECTED, 4);
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
setState(SML_CHECKSUM, 4);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SML_CHECKSUM:
|
||||||
|
// SML_LOG("CHECK: %02X\n", currentByte);
|
||||||
|
if (len == 2) {
|
||||||
|
crcMine = crc ^ 0xFFFF;
|
||||||
|
}
|
||||||
|
if (len == 1) {
|
||||||
|
crcReceived += currentByte;
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
crcReceived = crcReceived | (currentByte << 8);
|
||||||
|
SML_LOG("Received checksum: %02X\n", crcReceived);
|
||||||
|
SML_LOG("Calculated checksum: %02X\n", crcMine);
|
||||||
|
if (crcMine == crcReceived) {
|
||||||
|
setState(SML_FINAL, 4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(SML_CHECKSUM_ERROR, 4);
|
||||||
|
}
|
||||||
|
crc = 0xFFFF;
|
||||||
|
crcReceived = 0x000; /* reset CRC */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SML_HDATA:
|
||||||
|
size = len + currentByte - 1;
|
||||||
|
setState(SML_DATA, size);
|
||||||
|
pushListBuffer(size);
|
||||||
|
pushListBuffer(currentState);
|
||||||
|
SML_TREELOG(currentLevel, " Data (length = %i): ", size);
|
||||||
|
break;
|
||||||
|
case SML_LISTEXTENDED:
|
||||||
|
size = len + (currentByte & 0x0F);
|
||||||
|
SML_TREELOG(currentLevel, "Extended List with Size=%i\n", size);
|
||||||
|
smlNewList(size);
|
||||||
|
break;
|
||||||
|
case SML_DATA:
|
||||||
|
case SML_DATA_SIGNED_INT:
|
||||||
|
case SML_DATA_UNSIGNED_INT:
|
||||||
|
case SML_DATA_OCTET_STRING:
|
||||||
|
SML_LOG("%02X ", currentByte);
|
||||||
|
pushListBuffer(currentByte);
|
||||||
|
if (nodes[currentLevel] == 0 && len == 0) {
|
||||||
|
SML_LOG("\n");
|
||||||
|
SML_TREELOG(currentLevel, "LISTEND on level %i\n", currentLevel);
|
||||||
|
currentState = SML_LISTEND;
|
||||||
|
}
|
||||||
|
else if (len == 0) {
|
||||||
|
currentState = SML_DATAEND;
|
||||||
|
SML_LOG("\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SML_DATAEND:
|
||||||
|
case SML_NEXT:
|
||||||
|
case SML_LISTSTART:
|
||||||
|
case SML_LISTEND:
|
||||||
|
case SML_BLOCKSTART:
|
||||||
|
case SML_BLOCKEND:
|
||||||
|
checkMagicByte(currentByte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool smlOBISCheck(const unsigned char *obis)
|
||||||
|
{
|
||||||
|
return (memcmp(obis, &listBuffer[2], 6) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlOBISManufacturer(unsigned char *str, int maxSize)
|
||||||
|
{
|
||||||
|
int i = 0, pos = 0, size = 0;
|
||||||
|
while (i < listPos) {
|
||||||
|
size = (int)listBuffer[i];
|
||||||
|
i++;
|
||||||
|
pos++;
|
||||||
|
if (pos == 6) {
|
||||||
|
/* get manufacturer at position 6 in list */
|
||||||
|
size = (size > maxSize - 1) ? maxSize : size;
|
||||||
|
memcpy(str, &listBuffer[i + 1], size);
|
||||||
|
str[size + 1] = 0;
|
||||||
|
}
|
||||||
|
i += size + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlPow(double &val, signed char &scaler)
|
||||||
|
{
|
||||||
|
if (scaler < 0) {
|
||||||
|
while (scaler++) {
|
||||||
|
val /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while (scaler--) {
|
||||||
|
val *= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlOBISByUnit(long long int &val, signed char &scaler, sml_units_t unit)
|
||||||
|
{
|
||||||
|
unsigned char i = 0, pos = 0, size = 0, y = 0, skip = 0;
|
||||||
|
sml_states_t type;
|
||||||
|
val = -1; /* unknown or error */
|
||||||
|
while (i < listPos) {
|
||||||
|
pos++;
|
||||||
|
size = (int)listBuffer[i++];
|
||||||
|
type = (sml_states_t)listBuffer[i++];
|
||||||
|
if (type == SML_LISTSTART && size > 0) {
|
||||||
|
// skip a list inside an obis list
|
||||||
|
skip = size;
|
||||||
|
while (skip > 0) {
|
||||||
|
size = (int)listBuffer[i++];
|
||||||
|
type = (sml_states_t)listBuffer[i++];
|
||||||
|
i += size;
|
||||||
|
skip--;
|
||||||
|
}
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
if (pos == 4 && listBuffer[i] != unit) {
|
||||||
|
/* return unknown (-1) if unit does not match */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pos == 5) {
|
||||||
|
scaler = listBuffer[i];
|
||||||
|
}
|
||||||
|
if (pos == 6) {
|
||||||
|
y = size;
|
||||||
|
// initialize 64bit signed integer based on MSB from received value
|
||||||
|
val =
|
||||||
|
(type == SML_DATA_SIGNED_INT && (listBuffer[i] & (1 << 7))) ? ~0 : 0;
|
||||||
|
for (y = 0; y < size; y++) {
|
||||||
|
// left shift received bytes to 64 bit signed integer
|
||||||
|
val = (val << 8) | listBuffer[i + y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlOBISWh(double &wh)
|
||||||
|
{
|
||||||
|
long long int val;
|
||||||
|
smlOBISByUnit(val, sc, SML_WATT_HOUR);
|
||||||
|
wh = val;
|
||||||
|
smlPow(wh, sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlOBISW(double &w)
|
||||||
|
{
|
||||||
|
long long int val;
|
||||||
|
smlOBISByUnit(val, sc, SML_WATT);
|
||||||
|
w = val;
|
||||||
|
smlPow(w, sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlOBISVolt(double &v)
|
||||||
|
{
|
||||||
|
long long int val;
|
||||||
|
smlOBISByUnit(val, sc, SML_VOLT);
|
||||||
|
v = val;
|
||||||
|
smlPow(v, sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlOBISAmpere(double &a)
|
||||||
|
{
|
||||||
|
long long int val;
|
||||||
|
smlOBISByUnit(val, sc, SML_AMPERE);
|
||||||
|
a = val;
|
||||||
|
smlPow(a, sc);
|
||||||
|
}
|
||||||
106
lib/SMLParser/sml.h
Normal file
106
lib/SMLParser/sml.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#ifndef SML_H
|
||||||
|
#define SML_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SML_START,
|
||||||
|
SML_END,
|
||||||
|
SML_VERSION,
|
||||||
|
SML_NEXT,
|
||||||
|
SML_LISTSTART,
|
||||||
|
SML_LISTEND,
|
||||||
|
SML_LISTEXTENDED,
|
||||||
|
SML_DATA,
|
||||||
|
SML_HDATA,
|
||||||
|
SML_DATAEND,
|
||||||
|
SML_BLOCKSTART,
|
||||||
|
SML_BLOCKEND,
|
||||||
|
SML_CHECKSUM,
|
||||||
|
SML_CHECKSUM_ERROR, /* calculated checksum does not match */
|
||||||
|
SML_UNEXPECTED, /* unexpected byte received */
|
||||||
|
SML_FINAL, /* final state, checksum OK */
|
||||||
|
SML_DATA_SIGNED_INT,
|
||||||
|
SML_DATA_UNSIGNED_INT,
|
||||||
|
SML_DATA_OCTET_STRING,
|
||||||
|
} sml_states_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SML_YEAR = 1,
|
||||||
|
SML_MONTH = 2,
|
||||||
|
SML_WEEK = 3,
|
||||||
|
SML_DAY = 4,
|
||||||
|
SML_HOUR = 5,
|
||||||
|
SML_MIN = 6,
|
||||||
|
SML_SECOND = 7,
|
||||||
|
SML_DEGREE = 8,
|
||||||
|
SML_DEGREE_CELSIUS = 9,
|
||||||
|
SML_CURRENCY = 10,
|
||||||
|
SML_METRE = 11,
|
||||||
|
SML_METRE_PER_SECOND = 12,
|
||||||
|
SML_CUBIC_METRE = 13,
|
||||||
|
SML_CUBIC_METRE_CORRECTED = 14,
|
||||||
|
SML_CUBIC_METRE_PER_HOUR = 15,
|
||||||
|
SML_CUBIC_METRE_PER_HOUR_CORRECTED = 16,
|
||||||
|
SML_CUBIC_METRE_PER_DAY = 17,
|
||||||
|
SML_CUBIC_METRE_PER_DAY_CORRECTED = 18,
|
||||||
|
SML_LITRE = 19,
|
||||||
|
SML_KILOGRAM = 20,
|
||||||
|
SML_NEWTON = 21,
|
||||||
|
SML_NEWTONMETER = 22,
|
||||||
|
SML_PASCAL = 23,
|
||||||
|
SML_BAR = 24,
|
||||||
|
SML_JOULE = 25,
|
||||||
|
SML_JOULE_PER_HOUR = 26,
|
||||||
|
SML_WATT = 27,
|
||||||
|
SML_VOLT_AMPERE = 28,
|
||||||
|
SML_VAR = 29,
|
||||||
|
SML_WATT_HOUR = 30,
|
||||||
|
SML_VOLT_AMPERE_HOUR = 31,
|
||||||
|
SML_VAR_HOUR = 32,
|
||||||
|
SML_AMPERE = 33,
|
||||||
|
SML_COULOMB = 34,
|
||||||
|
SML_VOLT = 35,
|
||||||
|
SML_VOLT_PER_METRE = 36,
|
||||||
|
SML_FARAD = 37,
|
||||||
|
SML_OHM = 38,
|
||||||
|
SML_OHM_METRE = 39,
|
||||||
|
SML_WEBER = 40,
|
||||||
|
SML_TESLA = 41,
|
||||||
|
SML_AMPERE_PER_METRE = 42,
|
||||||
|
SML_HENRY = 43,
|
||||||
|
SML_HERTZ = 44,
|
||||||
|
SML_ACTIVE_ENERGY_METER_CONSTANT_OR_PULSE_VALUE = 45,
|
||||||
|
SML_REACTIVE_ENERGY_METER_CONSTANT_OR_PULSE_VALUE = 46,
|
||||||
|
SML_APPARENT_ENERGY_METER_CONSTANT_OR_PULSE_VALUE = 47,
|
||||||
|
SML_VOLT_SQUARED_HOURS = 48,
|
||||||
|
SML_AMPERE_SQUARED_HOURS = 49,
|
||||||
|
SML_KILOGRAM_PER_SECOND = 50,
|
||||||
|
SML_KELVIN = 52,
|
||||||
|
SML_VOLT_SQUARED_HOUR_METER_CONSTANT_OR_PULSE_VALUE = 53,
|
||||||
|
SML_AMPERE_SQUARED_HOUR_METER_CONSTANT_OR_PULSE_VALUE = 54,
|
||||||
|
SML_METER_CONSTANT_OR_PULSE_VALUE = 55,
|
||||||
|
SML_PERCENTAGE = 56,
|
||||||
|
SML_AMPERE_HOUR = 57,
|
||||||
|
SML_ENERGY_PER_VOLUME = 60,
|
||||||
|
SML_CALORIFIC_VALUE = 61,
|
||||||
|
SML_MOLE_PERCENT = 62,
|
||||||
|
SML_MASS_DENSITY = 63,
|
||||||
|
SML_PASCAL_SECOND = 64,
|
||||||
|
SML_RESERVED = 253,
|
||||||
|
SML_OTHER_UNIT = 254,
|
||||||
|
SML_COUNT = 255
|
||||||
|
} sml_units_t;
|
||||||
|
|
||||||
|
sml_states_t smlState(unsigned char &byte);
|
||||||
|
bool smlOBISCheck(const unsigned char *obis);
|
||||||
|
void smlOBISManufacturer(unsigned char *str, int maxSize);
|
||||||
|
void smlOBISByUnit(long long int &wh, signed char &scaler, sml_units_t unit);
|
||||||
|
|
||||||
|
// Be aware that double on Arduino UNO is just 32 bit
|
||||||
|
void smlOBISWh(double &wh);
|
||||||
|
void smlOBISW(double &w);
|
||||||
|
void smlOBISVolt(double &v);
|
||||||
|
void smlOBISAmpere(double &a);
|
||||||
|
|
||||||
|
#endif
|
||||||
42
lib/SMLParser/smlCrcTable.h
Normal file
42
lib/SMLParser/smlCrcTable.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef SML_CRC_TABLE_H
|
||||||
|
#define SML_CRC_TABLE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef ARDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
static const uint16_t smlCrcTable[256] PROGMEM =
|
||||||
|
#else
|
||||||
|
static const uint16_t smlCrcTable[256] =
|
||||||
|
#endif
|
||||||
|
{0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48,
|
||||||
|
0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, 0x1081, 0x0108,
|
||||||
|
0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9, 0x8D40, 0xBFDB,
|
||||||
|
0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, 0x2102, 0x308B, 0x0210, 0x1399,
|
||||||
|
0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E,
|
||||||
|
0xFAE7, 0xC87C, 0xD9F5, 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E,
|
||||||
|
0x54B5, 0x453C, 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD,
|
||||||
|
0xC974, 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
|
||||||
|
0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, 0x5285,
|
||||||
|
0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, 0xDECD, 0xCF44,
|
||||||
|
0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, 0x6306, 0x728F, 0x4014,
|
||||||
|
0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5,
|
||||||
|
0xA96A, 0xB8E3, 0x8A78, 0x9BF1, 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3,
|
||||||
|
0x242A, 0x16B1, 0x0738, 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862,
|
||||||
|
0x9AF9, 0x8B70, 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E,
|
||||||
|
0xF0B7, 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
|
||||||
|
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, 0x18C1,
|
||||||
|
0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, 0xA50A, 0xB483,
|
||||||
|
0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, 0x2942, 0x38CB, 0x0A50,
|
||||||
|
0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, 0xB58B, 0xA402, 0x9699, 0x8710,
|
||||||
|
0xF3AF, 0xE226, 0xD0BD, 0xC134, 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7,
|
||||||
|
0x6E6E, 0x5CF5, 0x4D7C, 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1,
|
||||||
|
0xA33A, 0xB2B3, 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72,
|
||||||
|
0x3EFB, 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
|
||||||
|
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, 0xE70E,
|
||||||
|
0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, 0x6B46, 0x7ACF,
|
||||||
|
0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, 0xF78F, 0xE606, 0xD49D,
|
||||||
|
0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, 0x7BC7, 0x6A4E, 0x58D5, 0x495C,
|
||||||
|
0x3DE3, 0x2C6A, 0x1EF1, 0x0F78};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -27,12 +27,13 @@ build_unflags =
|
|||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/yubox-node-org/ESPAsyncWebServer
|
https://github.com/yubox-node-org/ESPAsyncWebServer
|
||||||
bblanchon/ArduinoJson @ ^6.21.0
|
bblanchon/ArduinoJson @ ^6.21.1
|
||||||
https://github.com/bertmelis/espMqttClient.git#v1.4.2
|
https://github.com/bertmelis/espMqttClient.git#v1.4.2
|
||||||
nrf24/RF24 @ ^1.4.5
|
nrf24/RF24 @ ^1.4.5
|
||||||
olikraus/U8g2 @ ^2.34.16
|
olikraus/U8g2 @ ^2.34.17
|
||||||
buelowp/sunset @ ^1.1.7
|
buelowp/sunset @ ^1.1.7
|
||||||
https://github.com/coryjfowler/MCP_CAN_lib
|
https://github.com/coryjfowler/MCP_CAN_lib
|
||||||
|
plerup/EspSoftwareSerial@^8.0.1
|
||||||
mobizt/FirebaseJson @ ^3.0.6
|
mobizt/FirebaseJson @ ^3.0.6
|
||||||
|
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
|
|||||||
@ -10,11 +10,14 @@
|
|||||||
#include "SDM.h"
|
#include "SDM.h"
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
|
||||||
PowerMeterClass PowerMeter;
|
PowerMeterClass PowerMeter;
|
||||||
|
|
||||||
SDM sdm(Serial2, 9600, NOT_A_PIN, SERIAL_8N1, SDM_RX_PIN, SDM_TX_PIN);
|
SDM sdm(Serial2, 9600, NOT_A_PIN, SERIAL_8N1, SDM_RX_PIN, SDM_TX_PIN);
|
||||||
|
|
||||||
|
SoftwareSerial inputSerial;
|
||||||
|
|
||||||
void PowerMeterClass::init()
|
void PowerMeterClass::init()
|
||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
@ -29,7 +32,11 @@ void PowerMeterClass::init()
|
|||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
if (config.PowerMeter_Enabled && config.PowerMeter_Source == 0) {
|
if (!config.PowerMeter_Enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.PowerMeter_Source == SOURCE_MQTT) {
|
||||||
if (strlen(config.PowerMeter_MqttTopicPowerMeter1) > 0) {
|
if (strlen(config.PowerMeter_MqttTopicPowerMeter1) > 0) {
|
||||||
MqttSettings.subscribe(config.PowerMeter_MqttTopicPowerMeter1, 0, std::bind(&PowerMeterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
MqttSettings.subscribe(config.PowerMeter_MqttTopicPowerMeter1, 0, std::bind(&PowerMeterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||||
}
|
}
|
||||||
@ -43,16 +50,30 @@ void PowerMeterClass::init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mqttInitDone = true;
|
if(config.PowerMeter_Source == SOURCE_SDM1PH || config.PowerMeter_Source == SOURCE_SDM3PH) {
|
||||||
|
sdm.begin();
|
||||||
|
}
|
||||||
|
|
||||||
sdm.begin();
|
if (config.PowerMeter_Source == SOURCE_HTTP) {
|
||||||
HttpPowerMeter.init();
|
HttpPowerMeter.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.PowerMeter_Source == SOURCE_SML) {
|
||||||
|
pinMode(SML_RX_PIN, INPUT);
|
||||||
|
inputSerial.begin(9600, SWSERIAL_8N1, SML_RX_PIN, -1, false, 128, 95);
|
||||||
|
inputSerial.enableRx(true);
|
||||||
|
inputSerial.enableTx(false);
|
||||||
|
inputSerial.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
mqttInitDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerMeterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total)
|
void PowerMeterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total)
|
||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
if (config.PowerMeter_Enabled && config.PowerMeter_Source != SOURCE_MQTT) {
|
|
||||||
|
if (!config.PowerMeter_Enabled || config.PowerMeter_Source != SOURCE_MQTT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,8 +117,8 @@ void PowerMeterClass::mqtt()
|
|||||||
MqttSettings.publish(topic + "/voltage1", String(_powerMeter1Voltage));
|
MqttSettings.publish(topic + "/voltage1", String(_powerMeter1Voltage));
|
||||||
MqttSettings.publish(topic + "/voltage2", String(_powerMeter2Voltage));
|
MqttSettings.publish(topic + "/voltage2", String(_powerMeter2Voltage));
|
||||||
MqttSettings.publish(topic + "/voltage3", String(_powerMeter3Voltage));
|
MqttSettings.publish(topic + "/voltage3", String(_powerMeter3Voltage));
|
||||||
MqttSettings.publish(topic + "/import", String(_PowerMeterImport));
|
MqttSettings.publish(topic + "/import", String(_powerMeterImport));
|
||||||
MqttSettings.publish(topic + "/export", String(_PowerMeterExport));
|
MqttSettings.publish(topic + "/export", String(_powerMeterExport));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +126,12 @@ void PowerMeterClass::loop()
|
|||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
|
if (config.PowerMeter_Enabled && config.PowerMeter_Source == SOURCE_SML) {
|
||||||
|
if (!smlReadLoop()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!config.PowerMeter_Enabled
|
if (!config.PowerMeter_Enabled
|
||||||
|| (millis() - _lastPowerMeterCheck) < (config.PowerMeter_Interval * 1000)) {
|
|| (millis() - _lastPowerMeterCheck) < (config.PowerMeter_Interval * 1000)) {
|
||||||
return;
|
return;
|
||||||
@ -112,15 +139,15 @@ void PowerMeterClass::loop()
|
|||||||
|
|
||||||
uint8_t _address = config.PowerMeter_SdmAddress;
|
uint8_t _address = config.PowerMeter_SdmAddress;
|
||||||
|
|
||||||
if (config.PowerMeter_Source== SOURCE_SDM1PH) {
|
if (config.PowerMeter_Source == SOURCE_SDM1PH) {
|
||||||
_powerMeter1Power = static_cast<float>(sdm.readVal(SDM_PHASE_1_POWER, _address));
|
_powerMeter1Power = static_cast<float>(sdm.readVal(SDM_PHASE_1_POWER, _address));
|
||||||
_powerMeter2Power = 0.0;
|
_powerMeter2Power = 0.0;
|
||||||
_powerMeter3Power = 0.0;
|
_powerMeter3Power = 0.0;
|
||||||
_powerMeter1Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_1_VOLTAGE, _address));
|
_powerMeter1Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_1_VOLTAGE, _address));
|
||||||
_powerMeter2Voltage = 0.0;
|
_powerMeter2Voltage = 0.0;
|
||||||
_powerMeter3Voltage = 0.0;
|
_powerMeter3Voltage = 0.0;
|
||||||
_PowerMeterImport = static_cast<float>(sdm.readVal(SDM_IMPORT_ACTIVE_ENERGY, _address));
|
_powerMeterImport = static_cast<float>(sdm.readVal(SDM_IMPORT_ACTIVE_ENERGY, _address));
|
||||||
_PowerMeterExport = static_cast<float>(sdm.readVal(SDM_EXPORT_ACTIVE_ENERGY, _address));
|
_powerMeterExport = static_cast<float>(sdm.readVal(SDM_EXPORT_ACTIVE_ENERGY, _address));
|
||||||
_lastPowerMeterUpdate = millis();
|
_lastPowerMeterUpdate = millis();
|
||||||
}
|
}
|
||||||
else if (config.PowerMeter_Source == SOURCE_SDM3PH) {
|
else if (config.PowerMeter_Source == SOURCE_SDM3PH) {
|
||||||
@ -130,8 +157,8 @@ void PowerMeterClass::loop()
|
|||||||
_powerMeter1Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_1_VOLTAGE, _address));
|
_powerMeter1Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_1_VOLTAGE, _address));
|
||||||
_powerMeter2Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_2_VOLTAGE, _address));
|
_powerMeter2Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_2_VOLTAGE, _address));
|
||||||
_powerMeter3Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_3_VOLTAGE, _address));
|
_powerMeter3Voltage = static_cast<float>(sdm.readVal(SDM_PHASE_3_VOLTAGE, _address));
|
||||||
_PowerMeterImport = static_cast<float>(sdm.readVal(SDM_IMPORT_ACTIVE_ENERGY, _address));
|
_powerMeterImport = static_cast<float>(sdm.readVal(SDM_IMPORT_ACTIVE_ENERGY, _address));
|
||||||
_PowerMeterExport = static_cast<float>(sdm.readVal(SDM_EXPORT_ACTIVE_ENERGY, _address));
|
_powerMeterExport = static_cast<float>(sdm.readVal(SDM_EXPORT_ACTIVE_ENERGY, _address));
|
||||||
_lastPowerMeterUpdate = millis();
|
_lastPowerMeterUpdate = millis();
|
||||||
}
|
}
|
||||||
else if (config.PowerMeter_Source == SOURCE_HTTP) {
|
else if (config.PowerMeter_Source == SOURCE_HTTP) {
|
||||||
@ -149,3 +176,24 @@ void PowerMeterClass::loop()
|
|||||||
|
|
||||||
_lastPowerMeterCheck = millis();
|
_lastPowerMeterCheck = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PowerMeterClass::smlReadLoop()
|
||||||
|
{
|
||||||
|
while (inputSerial.available()) {
|
||||||
|
double readVal = 0;
|
||||||
|
unsigned char smlCurrentChar = inputSerial.read();
|
||||||
|
sml_states_t smlCurrentState = smlState(smlCurrentChar);
|
||||||
|
if (smlCurrentState == SML_LISTEND) {
|
||||||
|
for (auto& handler: smlHandlerList) {
|
||||||
|
if (smlOBISCheck(handler.OBIS)) {
|
||||||
|
handler.Fn(readVal);
|
||||||
|
*handler.Arg = readVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (smlCurrentState == SML_FINAL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ $t('firmwareinfo.FirmwareVersion') }}</th>
|
<th>{{ $t('firmwareinfo.FirmwareVersion') }}</th>
|
||||||
<td><a :href="'https://github.com/tbnobody/OpenDTU/commits/' + systemStatus.git_hash"
|
<td><a :href="versionInfoUrl"
|
||||||
target="_blank" v-tooltip :title="$t('firmwareinfo.FirmwareVersionHint')">
|
target="_blank" v-tooltip :title="$t('firmwareinfo.FirmwareVersionHint')">
|
||||||
{{ systemStatus.git_hash }}
|
{{ systemStatus.git_hash }}
|
||||||
</a></td>
|
</a></td>
|
||||||
@ -72,6 +72,12 @@ export default defineComponent({
|
|||||||
return timestampToString(value, true);
|
return timestampToString(value, true);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
versionInfoUrl(): string {
|
||||||
|
if (this.systemStatus.git_is_hash) {
|
||||||
|
return 'https://github.com/tbnobody/OpenDTU/commits/' + this.systemStatus.git_hash;
|
||||||
|
}
|
||||||
|
return 'https://github.com/tbnobody/OpenDTU/releases/tag/' + this.systemStatus.git_hash;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -470,6 +470,7 @@
|
|||||||
"typeSDM1ph": "SDM 1 phase (SDM120/220/230)",
|
"typeSDM1ph": "SDM 1 phase (SDM120/220/230)",
|
||||||
"typeSDM3ph": "SDM 3 phase (SDM72/630)",
|
"typeSDM3ph": "SDM 3 phase (SDM72/630)",
|
||||||
"typeHTTP": "HTTP(S) + JSON",
|
"typeHTTP": "HTTP(S) + JSON",
|
||||||
|
"typeSML": "SML (OBIS 16.7.0)",
|
||||||
"MqttTopicPowerMeter1": "MQTT topic - Stromzähler #1",
|
"MqttTopicPowerMeter1": "MQTT topic - Stromzähler #1",
|
||||||
"MqttTopicPowerMeter2": "MQTT topic - Stromzähler #2 (Optional)",
|
"MqttTopicPowerMeter2": "MQTT topic - Stromzähler #2 (Optional)",
|
||||||
"MqttTopicPowerMeter3": "MQTT topic - Stromzähler #3 (Optional)",
|
"MqttTopicPowerMeter3": "MQTT topic - Stromzähler #3 (Optional)",
|
||||||
|
|||||||
@ -470,6 +470,7 @@
|
|||||||
"typeSDM1ph": "SDM 1 phase (SDM120/220/230)",
|
"typeSDM1ph": "SDM 1 phase (SDM120/220/230)",
|
||||||
"typeSDM3ph": "SDM 3 phase (SDM72/630)",
|
"typeSDM3ph": "SDM 3 phase (SDM72/630)",
|
||||||
"typeHTTP": "HTTP(s) + JSON",
|
"typeHTTP": "HTTP(s) + JSON",
|
||||||
|
"typeSML": "SML (OBIS 16.7.0)",
|
||||||
"MqttTopicPowerMeter1": "MQTT topic - Power meter #1",
|
"MqttTopicPowerMeter1": "MQTT topic - Power meter #1",
|
||||||
"MqttTopicPowerMeter2": "MQTT topic - Power meter #2",
|
"MqttTopicPowerMeter2": "MQTT topic - Power meter #2",
|
||||||
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3",
|
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3",
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export interface SystemStatus {
|
|||||||
sdkversion: string;
|
sdkversion: string;
|
||||||
config_version: string;
|
config_version: string;
|
||||||
git_hash: string;
|
git_hash: string;
|
||||||
|
git_is_hash: boolean;
|
||||||
resetreason_0: string;
|
resetreason_0: string;
|
||||||
resetreason_1: string;
|
resetreason_1: string;
|
||||||
cfgsavecount: number;
|
cfgsavecount: number;
|
||||||
|
|||||||
@ -215,10 +215,11 @@ export default defineComponent({
|
|||||||
dataLoading: true,
|
dataLoading: true,
|
||||||
powerMeterConfigList: {} as PowerMeterConfig,
|
powerMeterConfigList: {} as PowerMeterConfig,
|
||||||
powerMeterSourceList: [
|
powerMeterSourceList: [
|
||||||
{ key: 3, value: this.$t('powermeteradmin.typeHTTP') },
|
|
||||||
{ key: 0, value: this.$t('powermeteradmin.typeMQTT') },
|
{ key: 0, value: this.$t('powermeteradmin.typeMQTT') },
|
||||||
{ key: 1, value: this.$t('powermeteradmin.typeSDM1ph') },
|
{ key: 1, value: this.$t('powermeteradmin.typeSDM1ph') },
|
||||||
{ key: 2, value: this.$t('powermeteradmin.typeSDM3ph') },
|
{ key: 2, value: this.$t('powermeteradmin.typeSDM3ph') },
|
||||||
|
{ key: 3, value: this.$t('powermeteradmin.typeHTTP') },
|
||||||
|
{ key: 4, value: this.$t('powermeteradmin.typeSML') },
|
||||||
],
|
],
|
||||||
alertMessage: "",
|
alertMessage: "",
|
||||||
alertType: "info",
|
alertType: "info",
|
||||||
|
|||||||
@ -51,11 +51,13 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
getUpdateInfo() {
|
getUpdateInfo() {
|
||||||
// If the left char is a "g" the value is the git hash (remove the "g")
|
// If the left char is a "g" the value is the git hash (remove the "g")
|
||||||
this.systemDataList.git_hash = this.systemDataList.git_hash?.substring(0, 1) == 'g' ? this.systemDataList.git_hash?.substring(1) : this.systemDataList.git_hash;
|
this.systemDataList.git_is_hash = this.systemDataList.git_hash?.substring(0, 1) == 'g';
|
||||||
|
this.systemDataList.git_hash = this.systemDataList.git_is_hash ? this.systemDataList.git_hash?.substring(1) : this.systemDataList.git_hash;
|
||||||
|
|
||||||
// Handle format "v0.1-5-gabcdefh"
|
// Handle format "v0.1-5-gabcdefh"
|
||||||
if (this.systemDataList.git_hash.lastIndexOf("-") >= 0) {
|
if (this.systemDataList.git_hash.lastIndexOf("-") >= 0) {
|
||||||
this.systemDataList.git_hash = this.systemDataList.git_hash.substring(this.systemDataList.git_hash.lastIndexOf("-") + 2)
|
this.systemDataList.git_hash = this.systemDataList.git_hash.substring(this.systemDataList.git_hash.lastIndexOf("-") + 2)
|
||||||
|
this.systemDataList.git_is_hash = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchUrl = "https://api.github.com/repos/tbnobody/OpenDTU/compare/"
|
const fetchUrl = "https://api.github.com/repos/tbnobody/OpenDTU/compare/"
|
||||||
|
|||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user