diff --git a/Dockerfile.dev b/Dockerfile.dev index aecf7ae..5704253 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM debian:stretch +FROM debian:bullseye RUN apt update && apt install -y \ curl \ diff --git a/config/inverter.conf b/config/inverter.conf index 21e6a2f..e2646d2 100644 --- a/config/inverter.conf +++ b/config/inverter.conf @@ -5,7 +5,7 @@ # /dev/ttyUSB0 if a USB<>Serial, # /dev/hidraw0 if you're connecting via the USB port on the inverter. -device=/dev/ttyUSB0 +device=/dev/hidraw0 # How many times per hour is the program going to run... # This is used to calculate the PV & Load Watt Hours between runs... @@ -20,23 +20,6 @@ amperage_factor=1.0 # This allows you to modify the wattage in case the inverter is giving an incorrect # reading compared to measurement tools. Normally this will remain '1' -watt_factor=1.01 +#watt_factor=1.01 +watt_factor=1.0 - -# The following settings allow you to modify runtime buffers. -# N.B. These values may not be applicable to all inverter types, as such you will -# need to run docker exec -it voltronic-mqtt bash -c '/opt/inverter-cli/bin/inverter_poller -d -1' -# or simply inverter_poller -d -1 to check for warnings or errors -# mentioned in https://github.com/ned-kelly/docker-voltronic-homeassistant/issues/5 - -# This allows you to modify the buffersize for the qpiri command -qpiri=98 - -# This allows you to modify the buffersize for the qpiws command -qpiws=36 - -# This allows you to modify the buffersize for the qmod command -qmod=5 - -# This allows you to modify the buffersize for the qpigs command -qpigs=110 diff --git a/sources/healthcheck b/sources/healthcheck index 476093f..b79ab57 100755 --- a/sources/healthcheck +++ b/sources/healthcheck @@ -1,9 +1,9 @@ #!/bin/bash -PROC=`ps cax | grep -E "mqtt-subscriber|mosquitto_sub|watch" | awk '{print $5}' | sort -u | wc -l` +PROC=`ps cax | grep -E "mqtt-subscriber|mosquitto_sub" | awk '{print $5}' | sort -u | wc -l` -if [ "$PROC" -eq "3" ] ; then +if [ "$PROC" -eq "2" ] ; then exit 0 else exit 99 -fi \ No newline at end of file +fi diff --git a/sources/inverter-cli/inverter.cpp b/sources/inverter-cli/inverter.cpp index 45711f4..1957c26 100644 --- a/sources/inverter-cli/inverter.cpp +++ b/sources/inverter-cli/inverter.cpp @@ -9,16 +9,12 @@ #include #include -cInverter::cInverter(std::string devicename, int qpiri, int qpiws, int qmod, int qpigs) { +cInverter::cInverter(std::string devicename) { device = devicename; status1[0] = 0; status2[0] = 0; warnings[0] = 0; mode = 0; - qpiri = qpiri; - qpiws = qpiws; - qmod = qmod; - qpigs = qpigs; } string *cInverter::GetQpigsStatus() { @@ -68,19 +64,39 @@ int cInverter::GetMode() { return result; } -bool cInverter::query(const char *cmd, int replysize) { +#define HEX(x) (x < 10 ? ('0' + x) : ('a' + x - 10)) +char *cInverter::escape_strn(unsigned char *str, int n) { + int j=0; + + for (int i=0; i> 4; + unsigned char x2 = str[i] & 0x0f; + escaped_buf[j++] = '\\'; + escaped_buf[j++] = 'x'; + escaped_buf[j++] = HEX(x1); + escaped_buf[j++] = HEX(x2); + } + } + + escaped_buf[j] = '\0'; + return escaped_buf; +} + +bool cInverter::query(const char *cmd) { time_t started; int fd; - int i=0, n; + int i=0, n, write_failed=0; fd = open(this->device.data(), O_RDWR | O_NONBLOCK); if (fd == -1) { - lprintf("INVERTER: Unable to open device file (errno=%d %s)", errno, strerror(errno)); + lprintf("Unable to open device file (errno=%d %s)", errno, strerror(errno)); sleep(5); return false; } - // Once connected, set the baud rate and other serial config (Don't rely on this being correct on the system by default...) speed_t baud = B2400; @@ -105,21 +121,44 @@ bool cInverter::query(const char *cmd, int replysize) { uint16_t crc = cal_crc_half((uint8_t*)cmd, strlen(cmd)); n = strlen(cmd); memcpy(&buf, cmd, n); - lprintf("INVERTER: Current CRC: %X %X", crc >> 8, crc & 0xff); + lprintf("%s: command CRC: %X %X", cmd, crc >> 8, crc & 0xff); buf[n++] = crc >> 8; buf[n++] = crc & 0xff; - buf[n++] = 0x0d; + buf[n++] = 0x0d; // '\r' + buf[n+1] = '\0'; // see workaround below + + // send a command + int chunk_size = 8; + for (int offset = 0; offset < n; usleep(50000)) { + int left = n - offset; + int towrite = left > chunk_size ? chunk_size : left; + // WORKAROUND: For some reason, writing 1 byte causes it to error. + // However, since we padded with '\0' above, we can give it 2 instead. + // I don't know of any 6 (+ 2*CRC + '\r') byte commands to test it on + // but this at least gets it to return NAK. + if (towrite == 1) towrite = 2; + lprintf("%s: write offset %d, writing %d", cmd, offset, towrite); + ssize_t written = write(fd, &buf[offset], towrite); + if (written > 0) + offset += written; + else { + lprintf("%s: write failed (written=%d, errno=%d: %s)", cmd, written, errno, strerror(errno)); + write_failed=1; + break; + } + lprintf("%s: %d bytes to write, %d bytes written", cmd, n, offset); + } - //send a command - write(fd, &buf, n); + // reads tend to be in multiple of 8 chars with NULL padding as necessary + char *startbuf = (char *)&buf[0]; + char *endbuf = 0; time(&started); - do { - n = read(fd, (void*)buf+i, replysize-i); + n = read(fd, &buf[i], 120-i); if (n < 0) { if (time(NULL) - started > 2) { - lprintf("INVERTER: %s read timeout", cmd); + lprintf("%s: read timeout", cmd); break; } else { usleep(10); @@ -127,32 +166,45 @@ bool cInverter::query(const char *cmd, int replysize) { } } + endbuf = (char *)memrchr((void *)&buf[i], 0x0d, n); i += n; - } while (i: %s", cmd, buf); + return false; + } - if (buf[0]!='(' || buf[replysize-1]!=0x0d) { - lprintf("INVERTER: %s: incorrect start/stop bytes. Buffer: %s", cmd, buf); - return false; - } + int replysize = endbuf - startbuf + 1; + + // proper response, check CRC + if (buf[0]=='(' && buf[replysize-1]==0x0d) { if (!(CheckCRC(buf, replysize))) { - lprintf("INVERTER: %s: CRC Failed! Reply size: %d Buffer: %s", cmd, replysize, buf); + lprintf("%s: CRC failed!", cmd); return false; } - - buf[i-3] = '\0'; //nullterminating on first CRC byte - lprintf("INVERTER: %s: %d bytes read: %s", cmd, i, buf); - - lprintf("INVERTER: %s query finished", cmd); - return true; - } else { - lprintf("INVERTER: %s reply too short (%d bytes)", cmd, i); + replysize -= 3; + buf[replysize] = '\0'; // null terminating on first CRC byte + } + else { + lprintf("%s: incorrect start/stop bytes", cmd); return false; } + + lprintf("%s: %d bytes in payload", cmd, replysize); + lprintf("%s: query finished", cmd); + return true; } void cInverter::poll() { @@ -163,7 +215,8 @@ void cInverter::poll() { // Reading mode if (!ups_qmod_changed) { - if (query("QMOD", qmod)) { + if (query("QMOD") && + strcmp((char *)&buf[1], "NAK") != 0) { SetMode(buf[1]); ups_qmod_changed = true; } @@ -171,7 +224,8 @@ void cInverter::poll() { // reading status (QPIGS) if (!ups_qpigs_changed) { - if (query("QPIGS", qpigs)) { + if (query("QPIGS") && + strcmp((char *)&buf[1], "NAK") != 0) { m.lock(); strcpy(status1, (const char*)buf+1); m.unlock(); @@ -181,7 +235,8 @@ void cInverter::poll() { // Reading QPIRI status if (!ups_qpiri_changed) { - if (query("QPIRI", qpiri)) { + if (query("QPIRI") && + strcmp((char *)&buf[1], "NAK") != 0) { m.lock(); strcpy(status2, (const char*)buf+1); m.unlock(); @@ -191,7 +246,8 @@ void cInverter::poll() { // Get any device warnings... if (!ups_qpiws_changed) { - if (query("QPIWS", qpiws)) { + if (query("QPIWS") && + strcmp((char *)&buf[1], "NAK") != 0) { m.lock(); strcpy(warnings, (const char*)buf+1); m.unlock(); @@ -205,7 +261,7 @@ void cInverter::poll() { void cInverter::ExecuteCmd(const string cmd) { // Sending any command raw - if (query(cmd.data(), 7)) { + if (query(cmd.data())) { m.lock(); strcpy(status2, (const char*)buf+1); m.unlock(); diff --git a/sources/inverter-cli/inverter.h b/sources/inverter-cli/inverter.h index 3501b6b..89e1322 100644 --- a/sources/inverter-cli/inverter.h +++ b/sources/inverter-cli/inverter.h @@ -7,7 +7,8 @@ using namespace std; class cInverter { - unsigned char buf[1024]; //internal work buffer + unsigned char buf[1024]; // internal work buffer + char escaped_buf[4096]; // screen-printable version of above char warnings[1024]; char status1[1024]; @@ -19,11 +20,12 @@ class cInverter { void SetMode(char newmode); bool CheckCRC(unsigned char *buff, int len); - bool query(const char *cmd, int replysize); + bool query(const char *cmd); uint16_t cal_crc_half(uint8_t *pin, uint8_t len); + char *escape_strn(unsigned char *str, int n); public: - cInverter(std::string devicename, int qpiri, int qpiws, int qmod, int qpigs); + cInverter(std::string devicename); void poll(); void runMultiThread() { std::thread t1(&cInverter::poll, this); diff --git a/sources/inverter-cli/main.cpp b/sources/inverter-cli/main.cpp index 7650fab..9a8e23a 100644 --- a/sources/inverter-cli/main.cpp +++ b/sources/inverter-cli/main.cpp @@ -43,7 +43,6 @@ string devicename; int runinterval; float ampfactor; float wattfactor; -int qpiri, qpiws, qmod, qpigs; // --------------------------------------- @@ -91,14 +90,6 @@ void getSettingsFile(string filename) { attemptAddSetting(&wattfactor, linepart2); else if(linepart1 == "watt_factor") attemptAddSetting(&wattfactor, linepart2); - else if(linepart1 == "qpiri") - attemptAddSetting(&qpiri, linepart2); - else if(linepart1 == "qpiws") - attemptAddSetting(&qpiws, linepart2); - else if(linepart1 == "qmod") - attemptAddSetting(&qmod, linepart2); - else if(linepart1 == "qpigs") - attemptAddSetting(&qpigs, linepart2); else continue; } @@ -170,7 +161,7 @@ int main(int argc, char* argv[]) { if(cmdArgs.cmdOptionExists("-1") || cmdArgs.cmdOptionExists("--run-once")) { runOnce = true; } - lprintf("INVERTER: Debug set"); + lprintf("Debug set"); // Get the rest of the settings from the conf file if( access( "./inverter.conf", F_OK ) != -1 ) { // file exists @@ -180,7 +171,7 @@ int main(int argc, char* argv[]) { } bool ups_status_changed(false); - ups = new cInverter(devicename,qpiri,qpiws,qmod,qpigs); + ups = new cInverter(devicename); // Logic to send 'raw commands' to the inverter.. if (!rawcmd.empty()) { @@ -197,7 +188,7 @@ int main(int argc, char* argv[]) { int mode = ups->GetMode(); if (mode) - lprintf("INVERTER: Mode Currently set to: %d", mode); + lprintf("Mode Currently set to: %d", mode); ups_status_changed = false; } @@ -216,15 +207,15 @@ int main(int argc, char* argv[]) { if (reply1 && reply2 && warnings) { // Parse and display values - sscanf(reply1->c_str(), "%f %f %f %f %d %d %d %d %f %d %d %d %f %f %f %d %s", &voltage_grid, &freq_grid, &voltage_out, &freq_out, &load_va, &load_watt, &load_percent, &voltage_bus, &voltage_batt, &batt_charge_current, &batt_capacity, &temp_heatsink, &pv_input_current, &pv_input_voltage, &scc_voltage, &batt_discharge_current, &device_status); + sscanf(reply1->c_str(), "%f %f %f %f %d %d %d %d %f %d %d %d %f %f %f %d %s", &voltage_grid, &freq_grid, &voltage_out, &freq_out, &load_va, &load_watt, &load_percent, &voltage_bus, &voltage_batt, &batt_charge_current, &batt_capacity, &temp_heatsink, &pv_input_current, &pv_input_voltage, &scc_voltage, &batt_discharge_current, (char *)&device_status); sscanf(reply2->c_str(), "%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d - %d %d %d %f", &grid_voltage_rating, &grid_current_rating, &out_voltage_rating, &out_freq_rating, &out_current_rating, &out_va_rating, &out_watt_rating, &batt_rating, &batt_recharge_voltage, &batt_under_voltage, &batt_bulk_voltage, &batt_float_voltage, &batt_type, &max_grid_charge_current, &max_charge_current, &in_voltage_range, &out_source_priority, &charger_source_priority, &machine_type, &topology, &out_mode, &batt_redischarge_voltage); // There appears to be a discrepancy in actual DMM measured current vs what the meter is // telling me it's getting, so lets add a variable we can multiply/divide by to adjust if // needed. This should be set in the config so it can be changed without program recompile. if (debugFlag) { - printf("INVERTER: ampfactor from config is %.2f\n", ampfactor); - printf("INVERTER: wattfactor from config is %.2f\n", wattfactor); + printf("ampfactor from config is %.2f\n", ampfactor); + printf("wattfactor from config is %.2f\n", wattfactor); } pv_input_current = pv_input_current * ampfactor; @@ -283,7 +274,7 @@ int main(int argc, char* argv[]) { if(runOnce) { // Do once and exit instead of loop endlessly - lprintf("INVERTER: All queries complete, exiting loop."); + lprintf("All queries complete, exiting loop."); exit(0); } } diff --git a/sources/inverter-cli/tools.cpp b/sources/inverter-cli/tools.cpp index 95311a0..8b3244c 100644 --- a/sources/inverter-cli/tools.cpp +++ b/sources/inverter-cli/tools.cpp @@ -28,7 +28,7 @@ void lprintf(const char *format, ...) { buf[strlen(buf)-1] = 0; //connect with args - snprintf(fmt, sizeof(fmt), "%s %s\n", buf, format); + snprintf(fmt, sizeof(fmt), "%s INVERTER: %s\n", buf, format); //put on screen: va_start(ap, format); @@ -71,4 +71,4 @@ int print_help() { printf(" PEx / PDx (Enable/disable backlight)\n\n"); return 1; -} \ No newline at end of file +} diff --git a/sources/inverter-mqtt/entrypoint.sh b/sources/inverter-mqtt/entrypoint.sh index a459cd3..39fdd64 100755 --- a/sources/inverter-mqtt/entrypoint.sh +++ b/sources/inverter-mqtt/entrypoint.sh @@ -1,15 +1,21 @@ #!/bin/bash -export TERM=xterm + +UNBUFFER='stdbuf -i0 -oL -eL' # stty -F /dev/ttyUSB0 2400 raw -# Init the mqtt server for the first time, then every 5 minutes -# This will re-create the auto-created topics in the MQTT server if HA is restarted... +# Init the mqtt server. This creates the config topics in the MQTT server +# that the MQTT integration uses to create entities in HA. + +# broker using persistence (default HA config) +$UNBUFFER /opt/inverter-mqtt/mqtt-init.sh -watch -n 300 /opt/inverter-mqtt/mqtt-init.sh > /dev/null 2>&1 & +# broker not using persistence +#(while :; do $UNBUFFER /opt/inverter-mqtt/mqtt-init.sh; sleep 300; done) & -# Run the MQTT Subscriber process in the background (so that way we can change the configuration on the inverter from home assistant) -/opt/inverter-mqtt/mqtt-subscriber.sh & +# Run the MQTT subscriber process in the background (so that way we can change +# the configuration on the inverter from home assistant). +$UNBUFFER /opt/inverter-mqtt/mqtt-subscriber.sh & -# execute exactly every 30 seconds... -watch -n 30 /opt/inverter-mqtt/mqtt-push.sh > /dev/null 2>&1 +# Push poller updates every 30 seconds. +while :; do $UNBUFFER /opt/inverter-mqtt/mqtt-push.sh; sleep 30; done diff --git a/sources/inverter-mqtt/mqtt-init.sh b/sources/inverter-mqtt/mqtt-init.sh index e9e6197..f1dcb5a 100755 --- a/sources/inverter-mqtt/mqtt-init.sh +++ b/sources/inverter-mqtt/mqtt-init.sh @@ -15,6 +15,7 @@ registerTopic () { -p $MQTT_PORT \ -u "$MQTT_USERNAME" \ -P "$MQTT_PASSWORD" \ + -r \ -t "$MQTT_TOPIC/sensor/"$MQTT_DEVICENAME"_$1/config" \ -m "{ \"name\": \""$MQTT_DEVICENAME"_$1\", @@ -30,6 +31,7 @@ registerInverterRawCMD () { -p $MQTT_PORT \ -u "$MQTT_USERNAME" \ -P "$MQTT_PASSWORD" \ + -r \ -t "$MQTT_TOPIC/sensor/$MQTT_DEVICENAME/config" \ -m "{ \"name\": \""$MQTT_DEVICENAME"\", @@ -52,7 +54,7 @@ registerTopic "Load_watt" "W" "chart-bell-curve" registerTopic "Load_watthour" "Wh" "chart-bell-curve" registerTopic "Load_va" "VA" "chart-bell-curve" registerTopic "Bus_voltage" "V" "details" -registerTopic "Heatsink_temperature" "" "details" +registerTopic "Heatsink_temperature" "C" "thermometer" registerTopic "Battery_capacity" "%" "battery-outline" registerTopic "Battery_voltage" "V" "battery-outline" registerTopic "Battery_charge_current" "A" "current-dc" diff --git a/sources/inverter-mqtt/mqtt-push.sh b/sources/inverter-mqtt/mqtt-push.sh index 5b1002c..ac9b103 100755 --- a/sources/inverter-mqtt/mqtt-push.sh +++ b/sources/inverter-mqtt/mqtt-push.sh @@ -1,28 +1,14 @@ #!/bin/bash -INFLUX_ENABLED=`cat /etc/inverter/mqtt.json | jq '.influx.enabled' -r` - -pushMQTTData () { - MQTT_SERVER=`cat /etc/inverter/mqtt.json | jq '.server' -r` - MQTT_PORT=`cat /etc/inverter/mqtt.json | jq '.port' -r` - MQTT_TOPIC=`cat /etc/inverter/mqtt.json | jq '.topic' -r` - MQTT_DEVICENAME=`cat /etc/inverter/mqtt.json | jq '.devicename' -r` - MQTT_USERNAME=`cat /etc/inverter/mqtt.json | jq '.username' -r` - MQTT_PASSWORD=`cat /etc/inverter/mqtt.json | jq '.password' -r` - mosquitto_pub \ - -h $MQTT_SERVER \ - -p $MQTT_PORT \ - -u "$MQTT_USERNAME" \ - -P "$MQTT_PASSWORD" \ - -t "$MQTT_TOPIC/sensor/"$MQTT_DEVICENAME"_$1" \ - -m "$2" - - if [[ $INFLUX_ENABLED == "true" ]] ; then - pushInfluxData $1 $2 - fi -} +MQTT_SERVER=`cat /etc/inverter/mqtt.json | jq '.server' -r` +MQTT_PORT=`cat /etc/inverter/mqtt.json | jq '.port' -r` +MQTT_TOPIC=`cat /etc/inverter/mqtt.json | jq '.topic' -r` +MQTT_DEVICENAME=`cat /etc/inverter/mqtt.json | jq '.devicename' -r` +MQTT_USERNAME=`cat /etc/inverter/mqtt.json | jq '.username' -r` +MQTT_PASSWORD=`cat /etc/inverter/mqtt.json | jq '.password' -r` -pushInfluxData () { +INFLUX_ENABLED=`cat /etc/inverter/mqtt.json | jq '.influx.enabled' -r` +if [[ $INFLUX_ENABLED == "true" ]] ; then INFLUX_HOST=`cat /etc/inverter/mqtt.json | jq '.influx.host' -r` INFLUX_USERNAME=`cat /etc/inverter/mqtt.json | jq '.influx.username' -r` INFLUX_PASSWORD=`cat /etc/inverter/mqtt.json | jq '.influx.password' -r` @@ -30,113 +16,38 @@ pushInfluxData () { INFLUX_PREFIX=`cat /etc/inverter/mqtt.json | jq '.influx.prefix' -r` INFLUX_DATABASE=`cat /etc/inverter/mqtt.json | jq '.influx.database' -r` INFLUX_MEASUREMENT_NAME=`cat /etc/inverter/mqtt.json | jq '.influx.namingMap.'$1'' -r` +fi + +pushMQTTData () { + if [ -n "$2" ]; then + mosquitto_pub \ + -h $MQTT_SERVER \ + -p $MQTT_PORT \ + -u "$MQTT_USERNAME" \ + -P "$MQTT_PASSWORD" \ + -r \ + -t "$MQTT_TOPIC/sensor/"$MQTT_DEVICENAME"_$1" \ + -m "$2" - curl -i -XPOST "$INFLUX_HOST/write?db=$INFLUX_DATABASE&precision=s" -u "$INFLUX_USERNAME:$INFLUX_PASSWORD" --data-binary "$INFLUX_PREFIX,device=$INFLUX_DEVICE $INFLUX_MEASUREMENT_NAME=$2" + if [[ $INFLUX_ENABLED == "true" ]] ; then + pushInfluxData "$1" "$2" + fi + fi } -INVERTER_DATA=`timeout 10 /opt/inverter-cli/bin/inverter_poller -1` - -##################################################################################### - -Inverter_mode=`echo $INVERTER_DATA | jq '.Inverter_mode' -r` - - # 1 = Power_On, 2 = Standby, 3 = Line, 4 = Battery, 5 = Fault, 6 = Power_Saving, 7 = Unknown - -[ ! -z "$Inverter_mode" ] && pushMQTTData "Inverter_mode" "$Inverter_mode" - -AC_grid_voltage=`echo $INVERTER_DATA | jq '.AC_grid_voltage' -r` -[ ! -z "$AC_grid_voltage" ] && pushMQTTData "AC_grid_voltage" "$AC_grid_voltage" - -AC_grid_frequency=`echo $INVERTER_DATA | jq '.AC_grid_frequency' -r` -[ ! -z "$AC_grid_frequency" ] && pushMQTTData "AC_grid_frequency" "$AC_grid_frequency" - -AC_out_voltage=`echo $INVERTER_DATA | jq '.AC_out_voltage' -r` -[ ! -z "$AC_out_voltage" ] && pushMQTTData "AC_out_voltage" "$AC_out_voltage" - -AC_out_frequency=`echo $INVERTER_DATA | jq '.AC_out_frequency' -r` -[ ! -z "$AC_out_frequency" ] && pushMQTTData "AC_out_frequency" "$AC_out_frequency" - -PV_in_voltage=`echo $INVERTER_DATA | jq '.PV_in_voltage' -r` -[ ! -z "$PV_in_voltage" ] && pushMQTTData "PV_in_voltage" "$PV_in_voltage" - -PV_in_current=`echo $INVERTER_DATA | jq '.PV_in_current' -r` -[ ! -z "$PV_in_current" ] && pushMQTTData "PV_in_current" "$PV_in_current" - -PV_in_watts=`echo $INVERTER_DATA | jq '.PV_in_watts' -r` -[ ! -z "$PV_in_watts" ] && pushMQTTData "PV_in_watts" "$PV_in_watts" - -PV_in_watthour=`echo $INVERTER_DATA | jq '.PV_in_watthour' -r` -[ ! -z "$PV_in_watthour" ] && pushMQTTData "PV_in_watthour" "$PV_in_watthour" - -SCC_voltage=`echo $INVERTER_DATA | jq '.SCC_voltage' -r` -[ ! -z "$SCC_voltage" ] && pushMQTTData "SCC_voltage" "$SCC_voltage" - -Load_pct=`echo $INVERTER_DATA | jq '.Load_pct' -r` -[ ! -z "$Load_pct" ] && pushMQTTData "Load_pct" "$Load_pct" - -Load_watt=`echo $INVERTER_DATA | jq '.Load_watt' -r` -[ ! -z "$Load_watt" ] && pushMQTTData "Load_watt" "$Load_watt" - -Load_watthour=`echo $INVERTER_DATA | jq '.Load_watthour' -r` -[ ! -z "$Load_watthour" ] && pushMQTTData "Load_watthour" "$Load_watthour" - -Load_va=`echo $INVERTER_DATA | jq '.Load_va' -r` -[ ! -z "$Load_va" ] && pushMQTTData "Load_va" "$Load_va" - -Bus_voltage=`echo $INVERTER_DATA | jq '.Bus_voltage' -r` -[ ! -z "$Bus_voltage" ] && pushMQTTData "Bus_voltage" "$Bus_voltage" - -Heatsink_temperature=`echo $INVERTER_DATA | jq '.Heatsink_temperature' -r` -[ ! -z "$Heatsink_temperature" ] && pushMQTTData "Heatsink_temperature" "$Heatsink_temperature" - -Battery_capacity=`echo $INVERTER_DATA | jq '.Battery_capacity' -r` -[ ! -z "$Battery_capacity" ] && pushMQTTData "Battery_capacity" "$Battery_capacity" - -Battery_voltage=`echo $INVERTER_DATA | jq '.Battery_voltage' -r` -[ ! -z "$Battery_voltage" ] && pushMQTTData "Battery_voltage" "$Battery_voltage" - -Battery_charge_current=`echo $INVERTER_DATA | jq '.Battery_charge_current' -r` -[ ! -z "$Battery_charge_current" ] && pushMQTTData "Battery_charge_current" "$Battery_charge_current" - -Battery_discharge_current=`echo $INVERTER_DATA | jq '.Battery_discharge_current' -r` -[ ! -z "$Battery_discharge_current" ] && pushMQTTData "Battery_discharge_current" "$Battery_discharge_current" - -Load_status_on=`echo $INVERTER_DATA | jq '.Load_status_on' -r` -[ ! -z "$Load_status_on" ] && pushMQTTData "Load_status_on" "$Load_status_on" - -SCC_charge_on=`echo $INVERTER_DATA | jq '.SCC_charge_on' -r` -[ ! -z "$SCC_charge_on" ] && pushMQTTData "SCC_charge_on" "$SCC_charge_on" - -AC_charge_on=`echo $INVERTER_DATA | jq '.AC_charge_on' -r` -[ ! -z "$AC_charge_on" ] && pushMQTTData "AC_charge_on" "$AC_charge_on" - -Battery_recharge_voltage=`echo $INVERTER_DATA | jq '.Battery_recharge_voltage' -r` -[ ! -z "$Battery_recharge_voltage" ] && pushMQTTData "Battery_recharge_voltage" "$Battery_recharge_voltage" - -Battery_under_voltage=`echo $INVERTER_DATA | jq '.Battery_under_voltage' -r` -[ ! -z "$Battery_under_voltage" ] && pushMQTTData "Battery_under_voltage" "$Battery_under_voltage" - -Battery_bulk_voltage=`echo $INVERTER_DATA | jq '.Battery_bulk_voltage' -r` -[ ! -z "$Battery_bulk_voltage" ] && pushMQTTData "Battery_bulk_voltage" "$Battery_bulk_voltage" - -Battery_float_voltage=`echo $INVERTER_DATA | jq '.Battery_float_voltage' -r` -[ ! -z "$Battery_float_voltage" ] && pushMQTTData "Battery_float_voltage" "$Battery_float_voltage" - -Max_grid_charge_current=`echo $INVERTER_DATA | jq '.Max_grid_charge_current' -r` -[ ! -z "$Max_grid_charge_current" ] && pushMQTTData "Max_grid_charge_current" "$Max_grid_charge_current" - -Max_charge_current=`echo $INVERTER_DATA | jq '.Max_charge_current' -r` -[ ! -z "$Max_charge_current" ] && pushMQTTData "Max_charge_current" "$Max_charge_current" +pushInfluxData () { + curl -i -XPOST "$INFLUX_HOST/write?db=$INFLUX_DATABASE&precision=s" -u "$INFLUX_USERNAME:$INFLUX_PASSWORD" --data-binary "$INFLUX_PREFIX,device=$INFLUX_DEVICE $INFLUX_MEASUREMENT_NAME=$2" +} -Out_source_priority=`echo $INVERTER_DATA | jq '.Out_source_priority' -r` -[ ! -z "$Out_source_priority" ] && pushMQTTData "Out_source_priority" "$Out_source_priority" +############################################################################### -Charger_source_priority=`echo $INVERTER_DATA | jq '.Charger_source_priority' -r` -[ ! -z "$Charger_source_priority" ] && pushMQTTData "Charger_source_priority" "$Charger_source_priority" +# Inverter modes: 1 = Power_On, 2 = Standby, 3 = Line, 4 = Battery, 5 = Fault, 6 = Power_Saving, 7 = Unknown -Battery_redischarge_voltage=`echo $INVERTER_DATA | jq '.Battery_redischarge_voltage' -r` -[ ! -z "$Battery_redischarge_voltage" ] && pushMQTTData "Battery_redischarge_voltage" "$Battery_redischarge_voltage" +POLLER_JSON=$(timeout 10 /opt/inverter-cli/bin/inverter_poller -1) +BASH_HASH=$(echo $POLLER_JSON | jq -r '. | to_entries | .[] | @sh "[\(.key)]=\(.value)"') +eval "declare -A INVERTER_DATA=($BASH_HASH)" -Warnings=`echo $INVERTER_DATA | jq '.Warnings' -r` -[ ! -z "$Warnings" ] && pushMQTTData "Warnings" "$Warnings" +for key in "${!INVERTER_DATA[@]}"; do + pushMQTTData "$key" "${INVERTER_DATA[$key]}" +done diff --git a/sources/inverter-mqtt/mqtt-subscriber.sh b/sources/inverter-mqtt/mqtt-subscriber.sh index f30ce27..0f39e9b 100755 --- a/sources/inverter-mqtt/mqtt-subscriber.sh +++ b/sources/inverter-mqtt/mqtt-subscriber.sh @@ -7,10 +7,21 @@ MQTT_DEVICENAME=`cat /etc/inverter/mqtt.json | jq '.devicename' -r` MQTT_USERNAME=`cat /etc/inverter/mqtt.json | jq '.username' -r` MQTT_PASSWORD=`cat /etc/inverter/mqtt.json | jq '.password' -r` -while read rawcmd; -do +function subscribe () { + mosquitto_sub -h $MQTT_SERVER -p $MQTT_PORT -u "$MQTT_USERNAME" -P "$MQTT_PASSWORD" -t "$MQTT_TOPIC/sensor/$MQTT_DEVICENAME" -q 1 +} - echo "Incoming request send: [$rawcmd] to inverter." - /opt/inverter-cli/bin/inverter_poller -r $rawcmd; +function reply () { + mosquitto_pub -h $MQTT_SERVER -p $MQTT_PORT -u "$MQTT_USERNAME" -P "$MQTT_PASSWORD" -t "$MQTT_TOPIC/sensor/${MQTT_DEVICENAME}/reply" -q 1 -m "$*" +} -done < <(mosquitto_sub -h $MQTT_SERVER -p $MQTT_PORT -u "$MQTT_USERNAME" -P "$MQTT_PASSWORD" -t "$MQTT_TOPIC/sensor/$MQTT_DEVICENAME" -q 1) +subscribe | while read rawcmd; do + echo "[$(date +%F+%T)] Incoming request send: [$rawcmd] to inverter." + for attempt in $(seq 3); do + REPLY=$(/opt/inverter-cli/bin/inverter_poller -r $rawcmd) + echo "[$(date +%F+%T)] $REPLY" + reply "[$rawcmd] [Attempt $attempt] [$REPLY]" + [ "$REPLY" = "Reply: ACK" ] && break + [ "$attempt" != "3" ] && sleep 1 + done +done