Feature: Support HMS/HMT inverters in different countries with different frequency bands

Thanks to @Fribur, @homeautomation2022 and @stefan123t
This commit is contained in:
Thomas Basler 2024-01-14 00:16:43 +01:00
parent 188c4688ab
commit c20caf8097
18 changed files with 188 additions and 60 deletions

View File

@ -129,6 +129,7 @@ struct CONFIG_T {
struct { struct {
int8_t PaLevel; int8_t PaLevel;
uint32_t Frequency; uint32_t Frequency;
uint8_t CountryMode;
} Cmt; } Cmt;
} Dtu; } Dtu;

View File

@ -13,4 +13,5 @@ private:
void onDtuAdminPost(AsyncWebServerRequest* request); void onDtuAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server; AsyncWebServer* _server;
bool _performReload = false;
}; };

View File

@ -15,6 +15,7 @@ enum WebApiError {
DtuPollZero, DtuPollZero,
DtuInvalidPowerLevel, DtuInvalidPowerLevel,
DtuInvalidCmtFrequency, DtuInvalidCmtFrequency,
DtuInvalidCmtCountry,
ConfigBase = 3000, ConfigBase = 3000,
ConfigNotDeleted, ConfigNotDeleted,

View File

@ -84,6 +84,7 @@
#define DTU_NRF_PA_LEVEL 0U #define DTU_NRF_PA_LEVEL 0U
#define DTU_CMT_PA_LEVEL 0 #define DTU_CMT_PA_LEVEL 0
#define DTU_CMT_FREQUENCY 865000000U #define DTU_CMT_FREQUENCY 865000000U
#define DTU_CMT_COUNTRY_MODE 0U
#define MQTT_HASS_ENABLED false #define MQTT_HASS_ENABLED false
#define MQTT_HASS_EXPIRE true #define MQTT_HASS_EXPIRE true

View File

@ -91,8 +91,6 @@
#include "cmt2300a_defs.h" #include "cmt2300a_defs.h"
#include <stdint.h> #include <stdint.h>
#define CMT_BASE_FREQ_860 860000000
/* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */ /* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */
static uint8_t g_cmt2300aCmtBank_860[CMT2300A_CMT_BANK_SIZE] = { static uint8_t g_cmt2300aCmtBank_860[CMT2300A_CMT_BANK_SIZE] = {
0x00, 0x00,

View File

@ -91,8 +91,6 @@
#include "cmt2300a_defs.h" #include "cmt2300a_defs.h"
#include <stdint.h> #include <stdint.h>
#define CMT_BASE_FREQ_900 900000000
/* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */ /* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */
static uint8_t g_cmt2300aCmtBank_900[CMT2300A_CMT_BANK_SIZE] = { static uint8_t g_cmt2300aCmtBank_900[CMT2300A_CMT_BANK_SIZE] = {
0x00, 0x00,

View File

@ -243,16 +243,9 @@ bool CMT2300A::rxFifoAvailable()
) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG); ) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG);
} }
uint32_t CMT2300A::getBaseFrequency() uint32_t CMT2300A::getBaseFrequency() const
{ {
switch (_frequencyBand) { return getBaseFrequency(_frequencyBand);
case FrequencyBand_t::BAND_900:
return CMT_BASE_FREQ_900;
break;
default:
return CMT_BASE_FREQ_860;
break;
}
} }
FrequencyBand_t CMT2300A::getFrequencyBand() const FrequencyBand_t CMT2300A::getFrequencyBand() const

View File

@ -7,6 +7,9 @@
#define FH_OFFSET 100 // value * CMT2300A_ONE_STEP_SIZE = channel frequency offset #define FH_OFFSET 100 // value * CMT2300A_ONE_STEP_SIZE = channel frequency offset
#define CMT_SPI_SPEED 4000000 // 4 MHz #define CMT_SPI_SPEED 4000000 // 4 MHz
#define CMT_BASE_FREQ_900 900000000
#define CMT_BASE_FREQ_860 860000000
enum FrequencyBand_t { enum FrequencyBand_t {
BAND_860, BAND_860,
BAND_900, BAND_900,
@ -91,7 +94,18 @@ public:
bool rxFifoAvailable(); bool rxFifoAvailable();
uint32_t getBaseFrequency(); uint32_t getBaseFrequency() const;
static constexpr uint32_t getBaseFrequency(FrequencyBand_t band)
{
switch (band) {
case FrequencyBand_t::BAND_900:
return CMT_BASE_FREQ_900;
break;
default:
return CMT_BASE_FREQ_860;
break;
}
}
FrequencyBand_t getFrequencyBand() const; FrequencyBand_t getFrequencyBand() const;
void setFrequencyBand(const FrequencyBand_t mode); void setFrequencyBand(const FrequencyBand_t mode);

View File

@ -8,17 +8,15 @@
#include <FunctionalInterrupt.h> #include <FunctionalInterrupt.h>
#include <frozen/map.h> #include <frozen/map.h>
struct CountryFrequencyDefinition_t {
FrequencyBand_t Band;
uint32_t Freq_Legal_Min;
uint32_t Freq_Legal_Max;
uint32_t Freq_Default;
uint32_t Freq_StartUp;
};
constexpr CountryFrequencyDefinition_t make_value(FrequencyBand_t Band, uint32_t Freq_Legal_Min, uint32_t Freq_Legal_Max, uint32_t Freq_Default, uint32_t Freq_StartUp) constexpr CountryFrequencyDefinition_t make_value(FrequencyBand_t Band, uint32_t Freq_Legal_Min, uint32_t Freq_Legal_Max, uint32_t Freq_Default, uint32_t Freq_StartUp)
{ {
CountryFrequencyDefinition_t v = { Band, Freq_Legal_Min, Freq_Legal_Max, Freq_Default, Freq_StartUp }; // frequency can not be lower than actual initailized base freq + 250000
uint32_t minFrequency = CMT2300A::getBaseFrequency(Band) + HoymilesRadio_CMT::getChannelWidth();
// =923500, 0xFF does not work
uint32_t maxFrequency = CMT2300A::getBaseFrequency(Band) + 0xFE * HoymilesRadio_CMT::getChannelWidth();
CountryFrequencyDefinition_t v = { Band, minFrequency, maxFrequency, Freq_Legal_Min, Freq_Legal_Max, Freq_Default, Freq_StartUp };
return v; return v;
} }
@ -54,6 +52,25 @@ uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t frequency) con
return (frequency - _radio->getBaseFrequency()) / getChannelWidth(); // frequency to channel return (frequency - _radio->getBaseFrequency()) / getChannelWidth(); // frequency to channel
} }
std::vector<CountryFrequencyList_t> HoymilesRadio_CMT::getCountryFrequencyList()
{
std::vector<CountryFrequencyList_t> v;
for (const auto& [key, value] : countryDefinition) {
CountryFrequencyList_t s;
s.mode = key;
s.definition.Band = value.Band;
s.definition.Freq_Default = value.Freq_Default;
s.definition.Freq_StartUp = value.Freq_StartUp;
s.definition.Freq_Min = value.Freq_Min;
s.definition.Freq_Max = value.Freq_Max;
s.definition.Freq_Legal_Max = value.Freq_Legal_Max;
s.definition.Freq_Legal_Min = value.Freq_Legal_Min;
v.push_back(s);
}
return v;
}
bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_frequency) bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_frequency)
{ {
const uint8_t toChannel = getChannelFromFrequency(to_frequency); const uint8_t toChannel = getChannelFromFrequency(to_frequency);
@ -207,19 +224,12 @@ bool HoymilesRadio_CMT::isConnected() const
uint32_t HoymilesRadio_CMT::getMinFrequency() const uint32_t HoymilesRadio_CMT::getMinFrequency() const
{ {
// frequency can not be lower than actual initailized base freq + 250000 return countryDefinition.at(_countryMode).Freq_Min;
return _radio->getBaseFrequency() + getChannelWidth();
} }
uint32_t HoymilesRadio_CMT::getMaxFrequency() const uint32_t HoymilesRadio_CMT::getMaxFrequency() const
{ {
// =923500, 0xFF does not work return countryDefinition.at(_countryMode).Freq_Max;
return _radio->getBaseFrequency() + 0xFE * getChannelWidth();
}
uint32_t HoymilesRadio_CMT::getChannelWidth()
{
return FH_OFFSET * CMT2300A_ONE_STEP_SIZE;
} }
CountryModeId_t HoymilesRadio_CMT::getCountryMode() const CountryModeId_t HoymilesRadio_CMT::getCountryMode() const

View File

@ -8,6 +8,7 @@
#include <cmt2300wrapper.h> #include <cmt2300wrapper.h>
#include <memory> #include <memory>
#include <queue> #include <queue>
#include <vector>
// number of fragments hold in buffer // number of fragments hold in buffer
#define FRAGMENT_BUFFER_SIZE 30 #define FRAGMENT_BUFFER_SIZE 30
@ -20,6 +21,22 @@ enum CountryModeId_t {
MODE_EU, MODE_EU,
MODE_US, MODE_US,
MODE_BR, MODE_BR,
CountryModeId_Max
};
struct CountryFrequencyDefinition_t {
FrequencyBand_t Band;
uint32_t Freq_Min;
uint32_t Freq_Max;
uint32_t Freq_Legal_Min;
uint32_t Freq_Legal_Max;
uint32_t Freq_Default;
uint32_t Freq_StartUp;
};
struct CountryFrequencyList_t {
CountryModeId_t mode;
CountryFrequencyDefinition_t definition;
}; };
class HoymilesRadio_CMT : public HoymilesRadio { class HoymilesRadio_CMT : public HoymilesRadio {
@ -34,7 +51,10 @@ public:
uint32_t getMinFrequency() const; uint32_t getMinFrequency() const;
uint32_t getMaxFrequency() const; uint32_t getMaxFrequency() const;
static uint32_t getChannelWidth(); static constexpr uint32_t getChannelWidth()
{
return FH_OFFSET * CMT2300A_ONE_STEP_SIZE;
}
CountryModeId_t getCountryMode() const; CountryModeId_t getCountryMode() const;
void setCountryMode(CountryModeId_t mode); void setCountryMode(CountryModeId_t mode);
@ -44,6 +64,8 @@ public:
uint32_t getFrequencyFromChannel(const uint8_t channel) const; uint32_t getFrequencyFromChannel(const uint8_t channel) const;
uint8_t getChannelFromFrequency(const uint32_t frequency) const; uint8_t getChannelFromFrequency(const uint32_t frequency) const;
std::vector<CountryFrequencyList_t> getCountryFrequencyList();
private: private:
void ARDUINO_ISR_ATTR handleInt1(); void ARDUINO_ISR_ATTR handleInt1();
void ARDUINO_ISR_ATTR handleInt2(); void ARDUINO_ISR_ATTR handleInt2();

View File

@ -95,6 +95,7 @@ bool ConfigurationClass::write()
dtu["nrf_pa_level"] = config.Dtu.Nrf.PaLevel; dtu["nrf_pa_level"] = config.Dtu.Nrf.PaLevel;
dtu["cmt_pa_level"] = config.Dtu.Cmt.PaLevel; dtu["cmt_pa_level"] = config.Dtu.Cmt.PaLevel;
dtu["cmt_frequency"] = config.Dtu.Cmt.Frequency; dtu["cmt_frequency"] = config.Dtu.Cmt.Frequency;
dtu["cmt_country_mode"] = config.Dtu.Cmt.CountryMode;
JsonObject security = doc.createNestedObject("security"); JsonObject security = doc.createNestedObject("security");
security["password"] = config.Security.Password; security["password"] = config.Security.Password;
@ -263,6 +264,7 @@ bool ConfigurationClass::read()
config.Dtu.Nrf.PaLevel = dtu["nrf_pa_level"] | DTU_NRF_PA_LEVEL; config.Dtu.Nrf.PaLevel = dtu["nrf_pa_level"] | DTU_NRF_PA_LEVEL;
config.Dtu.Cmt.PaLevel = dtu["cmt_pa_level"] | DTU_CMT_PA_LEVEL; config.Dtu.Cmt.PaLevel = dtu["cmt_pa_level"] | DTU_CMT_PA_LEVEL;
config.Dtu.Cmt.Frequency = dtu["cmt_frequency"] | DTU_CMT_FREQUENCY; config.Dtu.Cmt.Frequency = dtu["cmt_frequency"] | DTU_CMT_FREQUENCY;
config.Dtu.Cmt.CountryMode = dtu["cmt_country_mode"] | DTU_CMT_COUNTRY_MODE;
JsonObject security = doc["security"]; JsonObject security = doc["security"];
strlcpy(config.Security.Password, security["password"] | ACCESS_POINT_PASSWORD, sizeof(config.Security.Password)); strlcpy(config.Security.Password, security["password"] | ACCESS_POINT_PASSWORD, sizeof(config.Security.Password));

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2023 Thomas Basler and others * Copyright (C) 2023-2024 Thomas Basler and others
*/ */
#include "InverterSettings.h" #include "InverterSettings.h"
#include "Configuration.h" #include "Configuration.h"
@ -45,6 +45,8 @@ void InverterSettingsClass::init(Scheduler& scheduler)
if (PinMapping.isValidCmt2300Config()) { if (PinMapping.isValidCmt2300Config()) {
Hoymiles.initCMT(pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3); Hoymiles.initCMT(pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3);
MessageOutput.println(F(" Setting country mode... "));
Hoymiles.getRadioCmt()->setCountryMode(static_cast<CountryModeId_t>(config.Dtu.Cmt.CountryMode));
MessageOutput.println(F(" Setting CMT target frequency... ")); MessageOutput.println(F(" Setting CMT target frequency... "));
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency); Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022-2023 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_dtu.h" #include "WebApi_dtu.h"
#include "Configuration.h" #include "Configuration.h"
@ -21,6 +21,18 @@ void WebApiDtuClass::init(AsyncWebServer& server)
void WebApiDtuClass::loop() void WebApiDtuClass::loop()
{ {
if (_performReload) {
// Execute stuff in main thread to avoid busy SPI bus
CONFIG_T& config = Configuration.get();
Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel);
Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel);
Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial);
Hoymiles.getRadioCmt()->setDtuSerial(config.Dtu.Serial);
Hoymiles.getRadioCmt()->setCountryMode(static_cast<CountryModeId_t>(config.Dtu.Cmt.CountryMode));
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
Hoymiles.setPollInterval(config.Dtu.PollInterval);
_performReload = false;
}
} }
void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request) void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
@ -45,10 +57,20 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
root["cmt_enabled"] = Hoymiles.getRadioCmt()->isInitialized(); root["cmt_enabled"] = Hoymiles.getRadioCmt()->isInitialized();
root["cmt_palevel"] = config.Dtu.Cmt.PaLevel; root["cmt_palevel"] = config.Dtu.Cmt.PaLevel;
root["cmt_frequency"] = config.Dtu.Cmt.Frequency; root["cmt_frequency"] = config.Dtu.Cmt.Frequency;
root["cmt_min_freq"] = Hoymiles.getRadioCmt()->getMinFrequency(); root["cmt_country"] = config.Dtu.Cmt.CountryMode;
root["cmt_max_freq"] = Hoymiles.getRadioCmt()->getMaxFrequency();
root["cmt_chan_width"] = Hoymiles.getRadioCmt()->getChannelWidth(); root["cmt_chan_width"] = Hoymiles.getRadioCmt()->getChannelWidth();
auto data = root.createNestedArray("country_def");
auto countryDefs = Hoymiles.getRadioCmt()->getCountryFrequencyList();
for (const auto& definition : countryDefs) {
auto obj = data.createNestedObject();
obj["freq_default"] = definition.definition.Freq_Default;
obj["freq_min"] = definition.definition.Freq_Min;
obj["freq_max"] = definition.definition.Freq_Max;
obj["freq_legal_min"] = definition.definition.Freq_Legal_Min;
obj["freq_legal_max"] = definition.definition.Freq_Legal_Max;
}
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }
@ -96,7 +118,8 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
&& root.containsKey("pollinterval") && root.containsKey("pollinterval")
&& root.containsKey("nrf_palevel") && root.containsKey("nrf_palevel")
&& root.containsKey("cmt_palevel") && root.containsKey("cmt_palevel")
&& root.containsKey("cmt_frequency"))) { && root.containsKey("cmt_frequency")
&& root.containsKey("cmt_country"))) {
retMsg["message"] = "Values are missing!"; retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing; retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength(); response->setLength();
@ -136,14 +159,23 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
return; return;
} }
if (root["cmt_frequency"].as<uint32_t>() < Hoymiles.getRadioCmt()->getMinFrequency() if (root["cmt_country"].as<uint8_t>() >= CountryModeId_t::CountryModeId_Max) {
|| root["cmt_frequency"].as<uint32_t>() > Hoymiles.getRadioCmt()->getMaxFrequency() retMsg["message"] = "Invalid country setting!";
|| root["cmt_frequency"].as<uint32_t>() % 250 > 0) { retMsg["code"] = WebApiError::DtuInvalidCmtCountry;
response->setLength();
request->send(response);
return;
}
auto FrequencyDefinition = Hoymiles.getRadioCmt()->getCountryFrequencyList()[root["cmt_country"].as<CountryModeId_t>()].definition;
if (root["cmt_frequency"].as<uint32_t>() < FrequencyDefinition.Freq_Min
|| root["cmt_frequency"].as<uint32_t>() > FrequencyDefinition.Freq_Max
|| root["cmt_frequency"].as<uint32_t>() % Hoymiles.getRadioCmt()->getChannelWidth() > 0) {
retMsg["message"] = "Invalid CMT frequency setting!"; retMsg["message"] = "Invalid CMT frequency setting!";
retMsg["code"] = WebApiError::DtuInvalidCmtFrequency; retMsg["code"] = WebApiError::DtuInvalidCmtFrequency;
retMsg["param"]["min"] = Hoymiles.getRadioCmt()->getMinFrequency(); retMsg["param"]["min"] = FrequencyDefinition.Freq_Min;
retMsg["param"]["max"] = Hoymiles.getRadioCmt()->getMaxFrequency(); retMsg["param"]["max"] = FrequencyDefinition.Freq_Max;
response->setLength(); response->setLength();
request->send(response); request->send(response);
return; return;
@ -157,16 +189,12 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as<uint8_t>(); config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as<uint8_t>();
config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as<int8_t>(); config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as<int8_t>();
config.Dtu.Cmt.Frequency = root["cmt_frequency"].as<uint32_t>(); config.Dtu.Cmt.Frequency = root["cmt_frequency"].as<uint32_t>();
config.Dtu.Cmt.CountryMode = root["cmt_country"].as<CountryModeId_t>();
WebApi.writeConfig(retMsg); WebApi.writeConfig(retMsg);
response->setLength(); response->setLength();
request->send(response); request->send(response);
Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel); _performReload = true;
Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel);
Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial);
Hoymiles.getRadioCmt()->setDtuSerial(config.Dtu.Serial);
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
Hoymiles.setPollInterval(config.Dtu.PollInterval);
} }

View File

@ -47,6 +47,7 @@
"2002": "Das Abfraginterval muss größer als 0 sein!", "2002": "Das Abfraginterval muss größer als 0 sein!",
"2003": "Ungültige Sendeleistung angegeben!", "2003": "Ungültige Sendeleistung angegeben!",
"2004": "Die Frequenz muss zwischen {min} und {max} kHz liegen und ein vielfaches von 250kHz betragen!", "2004": "Die Frequenz muss zwischen {min} und {max} kHz liegen und ein vielfaches von 250kHz betragen!",
"2005": "Ungültige Landesauswahl!",
"3001": "Nichts gelöscht!", "3001": "Nichts gelöscht!",
"3002": "Konfiguration zurückgesetzt. Starte jetzt neu...", "3002": "Konfiguration zurückgesetzt. Starte jetzt neu...",
"4001": "@:apiresponse.2001", "4001": "@:apiresponse.2001",
@ -359,9 +360,14 @@
"CmtPaLevel": "CMT2300A Sendeleistung:", "CmtPaLevel": "CMT2300A Sendeleistung:",
"NrfPaLevelHint": "Verwendet für HM-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.", "NrfPaLevelHint": "Verwendet für HM-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.",
"CmtPaLevelHint": "Verwendet für HMS/HMT-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.", "CmtPaLevelHint": "Verwendet für HMS/HMT-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.",
"CmtCountry": "Region/Land:",
"CmtCountryHint": "Jedes Land hat unterschiedliche Frequenzzuteilungen.",
"country_0": "Europa ({min}MHz - {max}MHz)",
"country_1": "Nordamerika ({min}MHz - {max}MHz)",
"country_2": "Brasilien ({min}MHz - {max}MHz)",
"CmtFrequency": "CMT2300A Frequenz:", "CmtFrequency": "CMT2300A Frequenz:",
"CmtFrequencyHint": "Stelle sicher, dass du nur Frequenzen verwendet werden welche im entsprechenden Land erlaubt sind! Nach einer Frequenzänderung kann es bis zu 15min dauern bis eine Verbindung hergestellt wird.", "CmtFrequencyHint": "Stelle sicher, dass du nur Frequenzen verwendet werden welche im entsprechenden Land erlaubt sind! Nach einer Frequenzänderung kann es bis zu 15min dauern bis eine Verbindung hergestellt wird.",
"CmtFrequencyWarning": "Die ausgewählte Frequenz befindet außerhalb des in der EU zugelassenen Bereiches. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.", "CmtFrequencyWarning": "Die gewählte Frequenz liegt außerhalb des zulässigen Bereichs in der gewählten Region/dem Land. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.",
"MHz": "{mhz} MHz", "MHz": "{mhz} MHz",
"dBm": "{dbm} dBm", "dBm": "{dbm} dBm",
"Min": "Minimum ({db} dBm)", "Min": "Minimum ({db} dBm)",

View File

@ -47,6 +47,7 @@
"2002": "Poll interval must be greater zero!", "2002": "Poll interval must be greater zero!",
"2003": "Invalid power level setting!", "2003": "Invalid power level setting!",
"2004": "The frequency must be set between {min} and {max} kHz and must be a multiple of 250kHz!", "2004": "The frequency must be set between {min} and {max} kHz and must be a multiple of 250kHz!",
"2005": "Invalid country selection!",
"3001": "Not deleted anything!", "3001": "Not deleted anything!",
"3002": "Configuration resettet. Rebooting now...", "3002": "Configuration resettet. Rebooting now...",
"4001": "@:apiresponse.2001", "4001": "@:apiresponse.2001",
@ -359,9 +360,14 @@
"CmtPaLevel": "CMT2300A Transmitting power:", "CmtPaLevel": "CMT2300A Transmitting power:",
"NrfPaLevelHint": "Used for HM-Inverters. Make sure your power supply is stable enough before increasing the transmit power.", "NrfPaLevelHint": "Used for HM-Inverters. Make sure your power supply is stable enough before increasing the transmit power.",
"CmtPaLevelHint": "Used for HMS/HMT-Inverters. Make sure your power supply is stable enough before increasing the transmit power.", "CmtPaLevelHint": "Used for HMS/HMT-Inverters. Make sure your power supply is stable enough before increasing the transmit power.",
"CmtCountry": "Region/Country:",
"CmtCountryHint": "Each country has different frequency allocations.",
"country_0": "Europe ({min}MHz - {max}MHz)",
"country_1": "North America ({min}MHz - {max}MHz)",
"country_2": "Brazil ({min}MHz - {max}MHz)",
"CmtFrequency": "CMT2300A Frequency:", "CmtFrequency": "CMT2300A Frequency:",
"CmtFrequencyHint": "Make sure to only use frequencies that are allowed in the respective country! After a frequency change, it can take up to 15min until a connection is established.", "CmtFrequencyHint": "Make sure to only use frequencies that are allowed in the respective country! After a frequency change, it can take up to 15min until a connection is established.",
"CmtFrequencyWarning": "The selected frequency is outside the range allowed in the EU. Make sure that this selection does not violate any local regulations.", "CmtFrequencyWarning": "The selected frequency is outside the allowed range in your selected region/country. Make sure that this selection does not violate any local regulations.",
"MHz": "{mhz} MHz", "MHz": "{mhz} MHz",
"dBm": "{dbm} dBm", "dBm": "{dbm} dBm",
"Min": "Minimum ({db} dBm)", "Min": "Minimum ({db} dBm)",

View File

@ -47,6 +47,7 @@
"2002": "L'intervalle de sondage doit être supérieur à zéro !", "2002": "L'intervalle de sondage doit être supérieur à zéro !",
"2003": "Réglage du niveau de puissance invalide !", "2003": "Réglage du niveau de puissance invalide !",
"2004": "The frequency must be set between {min} and {max} kHz and must be a multiple of 250kHz!", "2004": "The frequency must be set between {min} and {max} kHz and must be a multiple of 250kHz!",
"2005": "Invalid country selection !",
"3001": "Rien n'a été supprimé !", "3001": "Rien n'a été supprimé !",
"3002": "Configuration réinitialisée. Redémarrage maintenant...", "3002": "Configuration réinitialisée. Redémarrage maintenant...",
"4001": "@:apiresponse.2001", "4001": "@:apiresponse.2001",
@ -359,9 +360,14 @@
"CmtPaLevel": "CMT2300A Niveau de puissance d'émission", "CmtPaLevel": "CMT2300A Niveau de puissance d'émission",
"NrfPaLevelHint": "Used for HM-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.", "NrfPaLevelHint": "Used for HM-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.",
"CmtPaLevelHint": "Used for HMS/HMT-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.", "CmtPaLevelHint": "Used for HMS/HMT-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.",
"CmtCountry": "Region/Country:",
"CmtCountryHint": "Each country has different frequency allocations.",
"country_0": "Europe ({min}MHz - {max}MHz)",
"country_1": "North America ({min}MHz - {max}MHz)",
"country_2": "Brazil ({min}MHz - {max}MHz)",
"CmtFrequency": "CMT2300A Frequency:", "CmtFrequency": "CMT2300A Frequency:",
"CmtFrequencyHint": "Make sure to only use frequencies that are allowed in the respective country! After a frequency change, it can take up to 15min until a connection is established.", "CmtFrequencyHint": "Make sure to only use frequencies that are allowed in the respective country! After a frequency change, it can take up to 15min until a connection is established.",
"CmtFrequencyWarning": "The selected frequency is outside the range allowed in the EU. Make sure that this selection does not violate any local regulations.", "CmtFrequencyWarning": "The selected frequency is outside the allowed range in your selected region/country. Make sure that this selection does not violate any local regulations.",
"MHz": "{mhz} MHz", "MHz": "{mhz} MHz",
"dBm": "{dbm} dBm", "dBm": "{dbm} dBm",
"Min": "Minimum ({db} dBm)", "Min": "Minimum ({db} dBm)",

View File

@ -1,3 +1,11 @@
export interface CountryDef {
freq_default: number;
freq_min: number;
freq_max: number;
freq_legal_min: number;
freq_legal_max: number;
}
export interface DtuConfig { export interface DtuConfig {
serial: number; serial: number;
pollinterval: number; pollinterval: number;
@ -6,7 +14,7 @@ export interface DtuConfig {
cmt_enabled: boolean; cmt_enabled: boolean;
cmt_palevel: number; cmt_palevel: number;
cmt_frequency: number; cmt_frequency: number;
cmt_min_freq: number; cmt_country: number;
cmt_max_freq: number; country_def: Array<CountryDef>;
cmt_chan_width: number; cmt_chan_width: number;
} }

View File

@ -47,6 +47,20 @@
</div> </div>
</div> </div>
<div class="row mb-3" v-if="dtuConfigList.cmt_enabled">
<label for="inputCmtCountry" class="col-sm-2 col-form-label">
{{ $t('dtuadmin.CmtCountry') }}
<BIconInfoCircle v-tooltip :title="$t('dtuadmin.CmtCountryHint')" />
</label>
<div class="col-sm-10">
<select id="inputCmtCountry" class="form-select" v-model="dtuConfigList.cmt_country">
<option v-for="(country, index) in dtuConfigList.country_def" :key="index" :value="index">
{{ $t(`dtuadmin.country_` + index, {min: country.freq_min / 1e6, max: country.freq_max / 1e6}) }}
</option>
</select>
</div>
</div>
<div class="row mb-3" v-if="dtuConfigList.cmt_enabled"> <div class="row mb-3" v-if="dtuConfigList.cmt_enabled">
<label for="cmtFrequency" class="col-sm-2 col-form-label"> <label for="cmtFrequency" class="col-sm-2 col-form-label">
{{ $t('dtuadmin.CmtFrequency') }} {{ $t('dtuadmin.CmtFrequency') }}
@ -56,12 +70,12 @@
<div class="input-group mb-3"> <div class="input-group mb-3">
<input type="range" class="form-control form-range" <input type="range" class="form-control form-range"
v-model="dtuConfigList.cmt_frequency" v-model="dtuConfigList.cmt_frequency"
:min="dtuConfigList.cmt_min_freq" :max="dtuConfigList.cmt_max_freq" :step="dtuConfigList.cmt_chan_width" :min="cmtMinFrequency" :max="cmtMaxFrequency" :step="dtuConfigList.cmt_chan_width"
id="cmtFrequency" aria-describedby="basic-addon2" id="cmtFrequency" aria-describedby="basic-addon2"
style="height: unset;" /> style="height: unset;" />
<span class="input-group-text" id="basic-addon2">{{ cmtFrequencyText }}</span> <span class="input-group-text" id="basic-addon2">{{ cmtFrequencyText }}</span>
</div> </div>
<div class="alert alert-danger" role="alert" v-html="$t('dtuadmin.CmtFrequencyWarning')" v-if="cmtIsOutOfEu"></div> <div class="alert alert-danger" role="alert" v-html="$t('dtuadmin.CmtFrequencyWarning')" v-if="cmtIsOutOfLegalRange"></div>
</div> </div>
</div> </div>
@ -116,8 +130,25 @@ export default defineComponent({
cmtPaLevelText() { cmtPaLevelText() {
return this.$t("dtuadmin.dBm", { dbm: this.$n(this.dtuConfigList.cmt_palevel * 1) }); return this.$t("dtuadmin.dBm", { dbm: this.$n(this.dtuConfigList.cmt_palevel * 1) });
}, },
cmtIsOutOfEu() { cmtMinFrequency() {
return this.dtuConfigList.cmt_frequency < 863000000 || this.dtuConfigList.cmt_frequency > 870000000; return this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_min;
},
cmtMaxFrequency() {
return this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_max;
},
cmtIsOutOfLegalRange() {
return this.dtuConfigList.cmt_frequency < this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_legal_min
|| this.dtuConfigList.cmt_frequency > this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_legal_max;
}
},
watch: {
'dtuConfigList.cmt_country'(newValue, oldValue) {
// Don't do anything on initial load (then oldValue equals undefined)
if (oldValue != undefined) {
this.$nextTick(() => {
this.dtuConfigList.cmt_frequency = this.dtuConfigList.country_def[newValue].freq_default;
});
}
} }
}, },
methods: { methods: {