Added settings to pause polling/sending commands in general and at night
This commit is contained in:
parent
b319c78dc1
commit
cd99ab8e42
@ -40,6 +40,10 @@ struct CHANNEL_CONFIG_T {
|
|||||||
struct INVERTER_CONFIG_T {
|
struct INVERTER_CONFIG_T {
|
||||||
uint64_t Serial;
|
uint64_t Serial;
|
||||||
char Name[INV_MAX_NAME_STRLEN + 1];
|
char Name[INV_MAX_NAME_STRLEN + 1];
|
||||||
|
bool Poll_Enable;
|
||||||
|
bool Poll_Enable_Night;
|
||||||
|
bool Command_Enable;
|
||||||
|
bool Command_Enable_Night;
|
||||||
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
15
include/InverterSettings.h
Normal file
15
include/InverterSettings.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class InverterSettingsClass {
|
||||||
|
public:
|
||||||
|
void init();
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t _lastUpdate = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern InverterSettingsClass InverterSettings;
|
||||||
@ -96,6 +96,10 @@ bool ConfigurationClass::write()
|
|||||||
JsonObject inv = inverters.createNestedObject();
|
JsonObject inv = inverters.createNestedObject();
|
||||||
inv["serial"] = config.Inverter[i].Serial;
|
inv["serial"] = config.Inverter[i].Serial;
|
||||||
inv["name"] = config.Inverter[i].Name;
|
inv["name"] = config.Inverter[i].Name;
|
||||||
|
inv["poll_enable"] = config.Inverter[i].Poll_Enable;
|
||||||
|
inv["poll_enable_night"] = config.Inverter[i].Poll_Enable_Night;
|
||||||
|
inv["command_enable"] = config.Inverter[i].Command_Enable;
|
||||||
|
inv["command_enable_night"] = config.Inverter[i].Command_Enable_Night;
|
||||||
|
|
||||||
JsonArray channel = inv.createNestedArray("channel");
|
JsonArray channel = inv.createNestedArray("channel");
|
||||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||||
@ -230,6 +234,11 @@ bool ConfigurationClass::read()
|
|||||||
config.Inverter[i].Serial = inv["serial"] | 0ULL;
|
config.Inverter[i].Serial = inv["serial"] | 0ULL;
|
||||||
strlcpy(config.Inverter[i].Name, inv["name"] | "", sizeof(config.Inverter[i].Name));
|
strlcpy(config.Inverter[i].Name, inv["name"] | "", sizeof(config.Inverter[i].Name));
|
||||||
|
|
||||||
|
config.Inverter[i].Poll_Enable = inv["poll_enable"] | true;
|
||||||
|
config.Inverter[i].Poll_Enable_Night = inv["poll_enable_night"] | true;
|
||||||
|
config.Inverter[i].Command_Enable = inv["command_enable"] | true;
|
||||||
|
config.Inverter[i].Command_Enable_Night = inv["command_enable_night"] | true;
|
||||||
|
|
||||||
JsonArray channel = inv["channel"];
|
JsonArray channel = inv["channel"];
|
||||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||||
config.Inverter[i].channel[c].MaxChannelPower = channel[c]["max_power"] | 0;
|
config.Inverter[i].channel[c].MaxChannelPower = channel[c]["max_power"] | 0;
|
||||||
|
|||||||
82
src/InverterSettings.cpp
Normal file
82
src/InverterSettings.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
#include "InverterSettings.h"
|
||||||
|
#include "Configuration.h"
|
||||||
|
#include "MessageOutput.h"
|
||||||
|
#include "PinMapping.h"
|
||||||
|
#include "SunPosition.h"
|
||||||
|
#include <Hoymiles.h>
|
||||||
|
|
||||||
|
InverterSettingsClass InverterSettings;
|
||||||
|
|
||||||
|
void InverterSettingsClass::init()
|
||||||
|
{
|
||||||
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
const PinMapping_t& pin = PinMapping.get();
|
||||||
|
|
||||||
|
// Initialize inverter communication
|
||||||
|
MessageOutput.print(F("Initialize Hoymiles interface... "));
|
||||||
|
if (PinMapping.isValidNrf24Config()) {
|
||||||
|
SPIClass* spiClass = new SPIClass(HSPI);
|
||||||
|
spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs);
|
||||||
|
Hoymiles.setMessageOutput(&MessageOutput);
|
||||||
|
Hoymiles.init(spiClass, pin.nrf24_en, pin.nrf24_irq);
|
||||||
|
|
||||||
|
MessageOutput.println(F(" Setting radio PA level... "));
|
||||||
|
Hoymiles.getRadio()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel);
|
||||||
|
|
||||||
|
MessageOutput.println(F(" Setting DTU serial... "));
|
||||||
|
Hoymiles.getRadio()->setDtuSerial(config.Dtu_Serial);
|
||||||
|
|
||||||
|
MessageOutput.println(F(" Setting poll interval... "));
|
||||||
|
Hoymiles.setPollInterval(config.Dtu_PollInterval);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||||
|
if (config.Inverter[i].Serial > 0) {
|
||||||
|
MessageOutput.print(F(" Adding inverter: "));
|
||||||
|
MessageOutput.print(config.Inverter[i].Serial, HEX);
|
||||||
|
MessageOutput.print(F(" - "));
|
||||||
|
MessageOutput.print(config.Inverter[i].Name);
|
||||||
|
auto inv = Hoymiles.addInverter(
|
||||||
|
config.Inverter[i].Name,
|
||||||
|
config.Inverter[i].Serial);
|
||||||
|
|
||||||
|
if (inv != nullptr) {
|
||||||
|
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||||
|
inv->Statistics()->setStringMaxPower(c, config.Inverter[i].channel[c].MaxChannelPower);
|
||||||
|
inv->Statistics()->setChannelFieldOffset(TYPE_DC, static_cast<ChannelNum_t>(c), FLD_YT, config.Inverter[i].channel[c].YieldTotalOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MessageOutput.println(F(" done"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MessageOutput.println(F("done"));
|
||||||
|
} else {
|
||||||
|
MessageOutput.println(F("Invalid pin config"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InverterSettingsClass::loop()
|
||||||
|
{
|
||||||
|
if (millis() - _lastUpdate > SUNPOS_UPDATE_INTERVAL) {
|
||||||
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||||
|
auto const& inv_cfg = config.Inverter[i];
|
||||||
|
if (inv_cfg.Serial == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto inv = Hoymiles.getInverterBySerial(inv_cfg.Serial);
|
||||||
|
if (inv == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inv->setEnablePolling(inv_cfg.Poll_Enable && (SunPosition.isDayPeriod() || inv_cfg.Poll_Enable_Night));
|
||||||
|
inv->setEnableCommands(inv_cfg.Command_Enable && (SunPosition.isDayPeriod() || inv_cfg.Command_Enable_Night));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Hoymiles.loop();
|
||||||
|
}
|
||||||
@ -51,6 +51,10 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
|||||||
((uint32_t)((config.Inverter[i].Serial >> 32) & 0xFFFFFFFF)),
|
((uint32_t)((config.Inverter[i].Serial >> 32) & 0xFFFFFFFF)),
|
||||||
((uint32_t)(config.Inverter[i].Serial & 0xFFFFFFFF)));
|
((uint32_t)(config.Inverter[i].Serial & 0xFFFFFFFF)));
|
||||||
obj[F("serial")] = buffer;
|
obj[F("serial")] = buffer;
|
||||||
|
obj[F("poll_enable")] = config.Inverter[i].Poll_Enable;
|
||||||
|
obj[F("poll_enable_night")] = config.Inverter[i].Poll_Enable_Night;
|
||||||
|
obj[F("command_enable")] = config.Inverter[i].Command_Enable;
|
||||||
|
obj[F("command_enable_night")] = config.Inverter[i].Command_Enable_Night;
|
||||||
|
|
||||||
auto inv = Hoymiles.getInverterBySerial(config.Inverter[i].Serial);
|
auto inv = Hoymiles.getInverterBySerial(config.Inverter[i].Serial);
|
||||||
uint8_t max_channels;
|
uint8_t max_channels;
|
||||||
@ -270,6 +274,11 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
|||||||
inverter.channel[arrayCount].MaxChannelPower = channel[F("max_power")].as<uint16_t>();
|
inverter.channel[arrayCount].MaxChannelPower = channel[F("max_power")].as<uint16_t>();
|
||||||
inverter.channel[arrayCount].YieldTotalOffset = channel[F("yield_total_offset")].as<float>();
|
inverter.channel[arrayCount].YieldTotalOffset = channel[F("yield_total_offset")].as<float>();
|
||||||
strncpy(inverter.channel[arrayCount].Name, channel[F("name")] | "", sizeof(inverter.channel[arrayCount].Name));
|
strncpy(inverter.channel[arrayCount].Name, channel[F("name")] | "", sizeof(inverter.channel[arrayCount].Name));
|
||||||
|
inverter.Poll_Enable = root[F("poll_enable")] | true;
|
||||||
|
inverter.Poll_Enable_Night = root[F("poll_enable_night")] | true;
|
||||||
|
inverter.Command_Enable = root[F("command_enable")] | true;
|
||||||
|
inverter.Command_Enable_Night = root[F("command_enable_night")] | true;
|
||||||
|
|
||||||
arrayCount++;
|
arrayCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,6 +306,8 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (inv != nullptr) {
|
if (inv != nullptr) {
|
||||||
|
inv->setEnablePolling(inverter.Poll_Enable);
|
||||||
|
inv->setEnableCommands(inverter.Command_Enable);
|
||||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||||
inv->Statistics()->setStringMaxPower(c, inverter.channel[c].MaxChannelPower);
|
inv->Statistics()->setStringMaxPower(c, inverter.channel[c].MaxChannelPower);
|
||||||
inv->Statistics()->setChannelFieldOffset(TYPE_DC, static_cast<ChannelNum_t>(c), FLD_YT, inverter.channel[c].YieldTotalOffset);
|
inv->Statistics()->setChannelFieldOffset(TYPE_DC, static_cast<ChannelNum_t>(c), FLD_YT, inverter.channel[c].YieldTotalOffset);
|
||||||
|
|||||||
45
src/main.cpp
45
src/main.cpp
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "Display_Graphic.h"
|
#include "Display_Graphic.h"
|
||||||
|
#include "InverterSettings.h"
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
#include "MqttHandleDtu.h"
|
#include "MqttHandleDtu.h"
|
||||||
#include "MqttHandleHass.h"
|
#include "MqttHandleHass.h"
|
||||||
@ -17,7 +18,6 @@
|
|||||||
#include "WebApi.h"
|
#include "WebApi.h"
|
||||||
#include "defaults.h"
|
#include "defaults.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Hoymiles.h>
|
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
@ -126,53 +126,14 @@ void setup()
|
|||||||
}
|
}
|
||||||
MessageOutput.println(F("done"));
|
MessageOutput.println(F("done"));
|
||||||
|
|
||||||
// Initialize inverter communication
|
InverterSettings.init();
|
||||||
MessageOutput.print(F("Initialize Hoymiles interface... "));
|
|
||||||
if (PinMapping.isValidNrf24Config()) {
|
|
||||||
SPIClass* spiClass = new SPIClass(HSPI);
|
|
||||||
spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs);
|
|
||||||
Hoymiles.setMessageOutput(&MessageOutput);
|
|
||||||
Hoymiles.init(spiClass, pin.nrf24_en, pin.nrf24_irq);
|
|
||||||
|
|
||||||
MessageOutput.println(F(" Setting radio PA level... "));
|
|
||||||
Hoymiles.getRadio()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel);
|
|
||||||
|
|
||||||
MessageOutput.println(F(" Setting DTU serial... "));
|
|
||||||
Hoymiles.getRadio()->setDtuSerial(config.Dtu_Serial);
|
|
||||||
|
|
||||||
MessageOutput.println(F(" Setting poll interval... "));
|
|
||||||
Hoymiles.setPollInterval(config.Dtu_PollInterval);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
|
||||||
if (config.Inverter[i].Serial > 0) {
|
|
||||||
MessageOutput.print(F(" Adding inverter: "));
|
|
||||||
MessageOutput.print(config.Inverter[i].Serial, HEX);
|
|
||||||
MessageOutput.print(F(" - "));
|
|
||||||
MessageOutput.print(config.Inverter[i].Name);
|
|
||||||
auto inv = Hoymiles.addInverter(
|
|
||||||
config.Inverter[i].Name,
|
|
||||||
config.Inverter[i].Serial);
|
|
||||||
|
|
||||||
if (inv != nullptr) {
|
|
||||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
|
||||||
inv->Statistics()->setStringMaxPower(c, config.Inverter[i].channel[c].MaxChannelPower);
|
|
||||||
inv->Statistics()->setChannelFieldOffset(TYPE_DC, static_cast<ChannelNum_t>(c), FLD_YT, config.Inverter[i].channel[c].YieldTotalOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MessageOutput.println(F(" done"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MessageOutput.println(F("done"));
|
|
||||||
} else {
|
|
||||||
MessageOutput.println(F("Invalid pin config"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
NetworkSettings.loop();
|
NetworkSettings.loop();
|
||||||
yield();
|
yield();
|
||||||
Hoymiles.loop();
|
InverterSettings.loop();
|
||||||
yield();
|
yield();
|
||||||
MqttHandleDtu.loop();
|
MqttHandleDtu.loop();
|
||||||
yield();
|
yield();
|
||||||
|
|||||||
@ -408,6 +408,11 @@
|
|||||||
"InverterSerial": "Wechselrichter Seriennummer:",
|
"InverterSerial": "Wechselrichter Seriennummer:",
|
||||||
"InverterName": "Wechselrichter Name:",
|
"InverterName": "Wechselrichter Name:",
|
||||||
"InverterNameHint": "Hier kann ein eigener Namen für den Wechselrichter angeben werden.",
|
"InverterNameHint": "Hier kann ein eigener Namen für den Wechselrichter angeben werden.",
|
||||||
|
"InverterStatus": "Empfangen / senden",
|
||||||
|
"PollEnable": "Daten abrufen",
|
||||||
|
"PollEnableNight": "Daten auch nachts abrufen",
|
||||||
|
"CommandEnable": "Befehle senden",
|
||||||
|
"CommandEnableNight": "Befehle auch nachts senden",
|
||||||
"StringName": "Name String {num}:",
|
"StringName": "Name String {num}:",
|
||||||
"StringNameHint": "Hier kann ein eigener Name für den entsprechenden Port des Wechselrichters angegeben werden.",
|
"StringNameHint": "Hier kann ein eigener Name für den entsprechenden Port des Wechselrichters angegeben werden.",
|
||||||
"StringMaxPower": "Max. Leistung String {num}:",
|
"StringMaxPower": "Max. Leistung String {num}:",
|
||||||
|
|||||||
@ -408,6 +408,11 @@
|
|||||||
"InverterSerial": "Inverter Serial:",
|
"InverterSerial": "Inverter Serial:",
|
||||||
"InverterName": "Inverter Name:",
|
"InverterName": "Inverter Name:",
|
||||||
"InverterNameHint": "Here you can specify a custom name for your inverter.",
|
"InverterNameHint": "Here you can specify a custom name for your inverter.",
|
||||||
|
"InverterStatus": "Receive / Send",
|
||||||
|
"PollEnable": "Poll inverter data",
|
||||||
|
"PollEnableNight": "Poll inverter data at night",
|
||||||
|
"CommandEnable": "Send commands",
|
||||||
|
"CommandEnableNight": "Send commands at night",
|
||||||
"StringName": "Name string {num}:",
|
"StringName": "Name string {num}:",
|
||||||
"StringNameHint": "Here you can specify a custom name for the respective port of your inverter.",
|
"StringNameHint": "Here you can specify a custom name for the respective port of your inverter.",
|
||||||
"StringMaxPower": "Max power string {num}:",
|
"StringMaxPower": "Max power string {num}:",
|
||||||
|
|||||||
@ -408,6 +408,11 @@
|
|||||||
"InverterSerial": "Numéro de série de l'onduleur",
|
"InverterSerial": "Numéro de série de l'onduleur",
|
||||||
"InverterName": "Nom de l'onduleur :",
|
"InverterName": "Nom de l'onduleur :",
|
||||||
"InverterNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour votre onduleur.",
|
"InverterNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour votre onduleur.",
|
||||||
|
"InverterStatus": "Receive / Send",
|
||||||
|
"PollEnable": "Poll inverter data",
|
||||||
|
"PollEnableNight": "Poll inverter data at night",
|
||||||
|
"CommandEnable": "Send commands",
|
||||||
|
"CommandEnableNight": "Send commands at night",
|
||||||
"StringName": "Nom de la ligne {num}:",
|
"StringName": "Nom de la ligne {num}:",
|
||||||
"StringNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour le port respectif de votre onduleur.",
|
"StringNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour le port respectif de votre onduleur.",
|
||||||
"StringMaxPower": "Puissance maximale de la ligne {num}:",
|
"StringMaxPower": "Puissance maximale de la ligne {num}:",
|
||||||
|
|||||||
@ -74,6 +74,21 @@
|
|||||||
</label>
|
</label>
|
||||||
<input v-model="selectedInverterData.name" type="text" id="inverter-name"
|
<input v-model="selectedInverterData.name" type="text" id="inverter-name"
|
||||||
class="form-control" maxlength="31" />
|
class="form-control" maxlength="31" />
|
||||||
|
|
||||||
|
<CardElement :text="$t('inverteradmin.InverterStatus')" addSpace>
|
||||||
|
<InputElement :label="$t('inverteradmin.PollEnable')"
|
||||||
|
v-model="selectedInverterData.poll_enable"
|
||||||
|
type="checkbox" wide />
|
||||||
|
<InputElement :label="$t('inverteradmin.PollEnableNight')"
|
||||||
|
v-model="selectedInverterData.poll_enable_night"
|
||||||
|
type="checkbox" wide/>
|
||||||
|
<InputElement :label="$t('inverteradmin.CommandEnable')"
|
||||||
|
v-model="selectedInverterData.command_enable"
|
||||||
|
type="checkbox" wide/>
|
||||||
|
<InputElement :label="$t('inverteradmin.CommandEnableNight')"
|
||||||
|
v-model="selectedInverterData.command_enable_night"
|
||||||
|
type="checkbox" wide/>
|
||||||
|
</CardElement>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="(max, index) in selectedInverterData.channel" :key="`${index}`">
|
<div v-for="(max, index) in selectedInverterData.channel" :key="`${index}`">
|
||||||
@ -168,6 +183,7 @@
|
|||||||
import BasePage from '@/components/BasePage.vue';
|
import BasePage from '@/components/BasePage.vue';
|
||||||
import BootstrapAlert from "@/components/BootstrapAlert.vue";
|
import BootstrapAlert from "@/components/BootstrapAlert.vue";
|
||||||
import CardElement from '@/components/CardElement.vue';
|
import CardElement from '@/components/CardElement.vue';
|
||||||
|
import InputElement from '@/components/InputElement.vue';
|
||||||
import { authHeader, handleResponse } from '@/utils/authentication';
|
import { authHeader, handleResponse } from '@/utils/authentication';
|
||||||
import * as bootstrap from 'bootstrap';
|
import * as bootstrap from 'bootstrap';
|
||||||
import {
|
import {
|
||||||
@ -188,6 +204,10 @@ declare interface Inverter {
|
|||||||
serial: number;
|
serial: number;
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
poll_enable: boolean;
|
||||||
|
poll_enable_night: boolean;
|
||||||
|
command_enable: boolean;
|
||||||
|
command_enable_night: boolean;
|
||||||
channel: Array<Channel>;
|
channel: Array<Channel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +226,7 @@ export default defineComponent({
|
|||||||
BIconInfoCircle,
|
BIconInfoCircle,
|
||||||
BIconPencil,
|
BIconPencil,
|
||||||
BIconTrash,
|
BIconTrash,
|
||||||
|
InputElement,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user