diff --git a/include/BatteryStats.h b/include/BatteryStats.h index 36eed06a3..204690f9d 100644 --- a/include/BatteryStats.h +++ b/include/BatteryStats.h @@ -86,6 +86,41 @@ class PylontechBatteryStats : public BatteryStats { bool _chargeImmediately; }; +class DalyBatteryStats : public BatteryStats { + friend class DalyBms; + + public: + void getLiveViewData(JsonVariant& root) const final; + void mqttPublish() const final; + + private: + void setManufacturer(String&& m) { _manufacturer = std::move(m); } + void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = millis(); } + void setLastUpdate(uint32_t ts) { _lastUpdate = ts; } + + float _voltage; + float _current; + float _temperature; + float _maxCellmV; + uint8_t _maxCellVNum; + float _minCellmV; + uint8_t _minCellVNum; + float _cellDiff; + std::string _state; + uint8_t _numberOfCells; + uint8_t _numOfTempSensors; + uint8_t _chargeState; + uint8_t _loadState; + bool _chargeFetState; + bool _dischargeFetState; + int _bmsHeartBeat; + float _resCapacityAh; + int _bmsCycles; + float _cellVmV[48]; + bool _cellBalanceActive; + bool _connectionState; +}; + class JkBmsBatteryStats : public BatteryStats { public: void getLiveViewData(JsonVariant& root) const final { diff --git a/include/DalyBms.h b/include/DalyBms.h new file mode 100644 index 000000000..37764235d --- /dev/null +++ b/include/DalyBms.h @@ -0,0 +1,203 @@ +/* +DALY2MQTT Project +https://github.com/softwarecrash/DALY2MQTT +*/ +#include "SoftwareSerial.h" +#include "Battery.h" +#ifndef DALY_BMS_UART_H +#define DALY_BMS_UART_H + +#define XFER_BUFFER_LENGTH 13 +#define MIN_NUMBER_CELLS 1 +#define MAX_NUMBER_CELLS 48 +#define MIN_NUMBER_TEMP_SENSORS 1 +#define MAX_NUMBER_TEMP_SENSORS 16 + +#define START_BYTE 0xA5; // Start byte +#define HOST_ADRESS 0x40; // Host address +#define FRAME_LENGTH 0x08; // Length +#define ERRORCOUNTER 10 //number of try befor clear data + +//time in ms for delay the bms requests, to fast brings connection error + +#define DELAYTINME 100 + +class DalyBms : public BatteryProvider { +public: + bool init(bool verboseLogging) final; + void deinit() final; + void loop() final; + std::shared_ptr getStats() const final { return _stats; } + + unsigned long previousTime = 0; + byte requestCounter = 0; + String failCodeArr; + + enum COMMAND + { + CELL_THRESHOLDS = 0x59, + PACK_THRESHOLDS = 0x5A, + VOUT_IOUT_SOC = 0x90, + MIN_MAX_CELL_VOLTAGE = 0x91, + MIN_MAX_TEMPERATURE = 0x92, + DISCHARGE_CHARGE_MOS_STATUS = 0x93, + STATUS_INFO = 0x94, + CELL_VOLTAGES = 0x95, + CELL_TEMPERATURE = 0x96, + CELL_BALANCE_STATE = 0x97, + FAILURE_CODES = 0x98, + DISCHRG_FET = 0xD9, + CHRG_FET = 0xDA, + BMS_RESET = 0x00, + READ_SOC = 0x61, //read the time and soc + SET_SOC = 0x21, //set the time and soc + //END = 0xD8, + //after request the pc soft hangs a 0xD8 as last request, its empty, dont know what it means? + }; + + + /** + * @brief Gets Voltage, Current, and SOC measurements from the BMS + * @return True on successful aquisition, false otherwise + */ + bool getPackMeasurements(); + + /** + * @brief Gets the pack temperature from the min and max of all the available temperature sensors + * @details Populates tempMax, tempMax, and tempAverage in the "get" struct + * @return True on successful aquisition, false otherwise + */ + bool getPackTemp(); + + /** + * @brief Returns the highest and lowest individual cell voltage, and which cell is highest/lowest + * @details Voltages are returned as floats with milliVolt precision (3 decimal places) + * @return True on successful aquisition, false otherwise + */ + bool getMinMaxCellVoltage(); + + /** + * @brief Get the general Status Info + * + */ + bool getStatusInfo(); + + /** + * @brief Get Cell Voltages + * + */ + bool getCellVoltages(); + + /** + * @brief + * set the Discharging MOS State + */ + bool setDischargeMOS(bool sw); + + /** + * @brief set the Charging MOS State + * + */ + bool setChargeMOS(bool sw); + + /** + * @brief set the SOC + * + */ + bool setSOC(float sw); + + /** + * @brief Read the charge and discharge MOS States + * + */ + bool getDischargeChargeMosStatus(); + + /** + * @brief Reseting The BMS + * @details Reseting the BMS and let it restart + */ + bool setBmsReset(); + + /** + * @brief return the state of connection to the BMS + * @details returns the following value for different connection state + * -3 - could not open serial port + * -2 - no data recived or wrong crc, check connection + * -1 - working and collecting data, please wait + * 0 - All data recived with correct crc, idleing + * + * now changed to bool, only true if data avaible, false when no connection + */ + bool getState(); + +private: + bool _verboseLogging = true; + std::shared_ptr _stats = + std::make_shared(); + + bool getStaticData = false; + unsigned int errorCounter = 0; + unsigned int requestCount = 0; + uint32_t _lastRequest = 0; + uint8_t _pollInterval = 5; + unsigned int commandQueue[5] = {0x100, 0x100, 0x100, 0x100, 0x100}; + /** + * @brief send the command id, and return true if data complete read or false by crc error + * @details calculates the checksum and sends the command over the specified serial connection + */ + bool requestData(COMMAND cmdID, unsigned int frameAmount); + + /** + * @brief Sends a complete packet with the specified command + * @details calculates the checksum and sends the command over the specified serial connection + */ + bool sendCommand(COMMAND cmdID); + + /** + * @brief command queue + */ + bool sendQueueAdd(COMMAND cmdID); + + /** + * @brief Send the command ID to the BMS + * @details + * @return True on success, false on failure + */ + bool receiveBytes(void); + + /** + * @brief Validates the checksum in the RX Buffer + * @return true if checksum matches, false otherwise + */ + bool validateChecksum(); + + /** + * @brief Prints out the contense of the RX buffer + * @details Useful for debugging + */ + void barfRXBuffer(); + + /** + * @brief Clear all data from the Get struct + * @details when wrong or missing data comes in it need sto be cleared + */ + void clearGet(); + + /** + * @brief Buffer used to transmit data to the BMS + * @details Populated primarily in the "Init()" function, see the readme for more info + */ + uint8_t my_txBuffer[XFER_BUFFER_LENGTH]; + + /** + * @brief Buffer filled with data from the BMS + */ + uint8_t my_rxBuffer[XFER_BUFFER_LENGTH]; + + +uint8_t my_rxFrameBuffer[XFER_BUFFER_LENGTH*12]; +uint8_t frameBuff[12][XFER_BUFFER_LENGTH]; +unsigned int frameCount; +}; + +#endif // DALY_BMS_UART_H diff --git a/src/Battery.cpp b/src/Battery.cpp index 381fdc952..9880d3a38 100644 --- a/src/Battery.cpp +++ b/src/Battery.cpp @@ -3,6 +3,7 @@ #include "MessageOutput.h" #include "PylontechCanReceiver.h" #include "JkBmsController.h" +#include "DalyBms.h" #include "VictronSmartShunt.h" #include "MqttBattery.h" @@ -61,6 +62,10 @@ void BatteryClass::updateSettings() _upProvider = std::make_unique(); if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; } break; + case 4: + _upProvider = std::make_unique(); + if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; } + break; default: MessageOutput.printf("Unknown battery provider: %d\r\n", config.Battery.Provider); break; diff --git a/src/BatteryStats.cpp b/src/BatteryStats.cpp index 606a372fd..2e8e455b5 100644 --- a/src/BatteryStats.cpp +++ b/src/BatteryStats.cpp @@ -99,6 +99,28 @@ void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const addLiveViewAlarm(root, "bmsInternal", _alarmBmsInternal); } +void DalyBatteryStats::getLiveViewData(JsonVariant& root) const +{ + BatteryStats::getLiveViewData(root); + + if (_connectionState==true){ + // values go into the "Status" card of the web application + addLiveViewValue(root, "voltage", _voltage, "V", 2); + addLiveViewValue(root, "current", _current, "A", 1); + addLiveViewValue(root, "temperature", _temperature, "°C", 1); + addLiveViewTextValue(root, "status", _state); + addLiveViewValue(root, "cellMaxVoltage", _maxCellmV, "mV", 0); + addLiveViewValue(root, "cellMinVoltage", _minCellmV, "mV", 0); + addLiveViewValue(root, "cellDiffVoltage", _cellDiff, "mV", 0); + addLiveViewTextValue(root, "chargeEnabled", (_chargeFetState?"yes":"no")); + addLiveViewTextValue(root, "dischargeEnabled", (_dischargeFetState?"yes":"no")); + addLiveViewValue(root, "chargeCycles", _bmsCycles,"",0); + addLiveViewValue(root, "remainingAh", _resCapacityAh,"Ah",1); + } else { + + } +} + void JkBmsBatteryStats::getJsonData(JsonVariant& root, bool verbose) const { BatteryStats::getLiveViewData(root); @@ -250,6 +272,34 @@ void PylontechBatteryStats::mqttPublish() const MqttSettings.publish(F("battery/charging/chargeImmediately"), String(_chargeImmediately)); } +void DalyBatteryStats::mqttPublish() const +{ + BatteryStats::mqttPublish(); + + MqttSettings.publish(F("battery/soc"), String(_SoC)); + MqttSettings.publish(F("battery/voltage"), String(_voltage)); + MqttSettings.publish(F("battery/current"), String(_current)); + MqttSettings.publish(F("battery/temperature"), String(_temperature)); + MqttSettings.publish(F("battery/minCellmV"), String(_minCellmV)); + MqttSettings.publish(F("battery/minCellmVNum"), String(_minCellVNum)); + MqttSettings.publish(F("battery/maxCellmV"), String(_maxCellmV)); + MqttSettings.publish(F("battery/maxCellmVNum"), String(_maxCellVNum)); + MqttSettings.publish(F("battery/cellmVDrift"), String(_cellDiff)); + MqttSettings.publish(F("battery/status"), _state.c_str()); + MqttSettings.publish(F("battery/chargeFetState"), String(_chargeFetState)); + MqttSettings.publish(F("battery/dischargeFetState"), String(_dischargeFetState)); + MqttSettings.publish(F("battery/numberOfCells"), String(_numberOfCells)); + MqttSettings.publish(F("battery/numOfTempSensors"), String(_numOfTempSensors)); + MqttSettings.publish(F("battery/chargeState"), String(_cellDiff)); + MqttSettings.publish(F("battery/loadState"), String(_loadState)); + MqttSettings.publish(F("battery/cycles"), String(_bmsCycles)); + MqttSettings.publish(F("battery/remainingAh"), String(_resCapacityAh)); + MqttSettings.publish(F("battery/bmsHeartBeat"), String(_bmsHeartBeat)); + for (uint8_t c = 0; c<_numberOfCells;c++){ + MqttSettings.publish("battery/cell_" + String(c), String(_cellVmV[c])); + } +} + void JkBmsBatteryStats::mqttPublish() const { BatteryStats::mqttPublish(); diff --git a/src/DalyBms.cpp b/src/DalyBms.cpp new file mode 100644 index 000000000..5bb2695a6 --- /dev/null +++ b/src/DalyBms.cpp @@ -0,0 +1,542 @@ +#include "DalyBms.h" +#include "Configuration.h" +#include "MessageOutput.h" +#include "PinMapping.h" +#include "Battery.h" + +HardwareSerial DalyHwSerial(2); + +bool DalyBms::init(bool verboseLogging) +{ + _verboseLogging = verboseLogging; + + const PinMapping_t& pin = PinMapping.get(); + MessageOutput.printf("[Daly BMS] rx = %d, tx = %d, wake = %d\r\n", + pin.battery_rx, pin.battery_tx, pin.battery_txen); + + if (pin.battery_rx < 0 || pin.battery_tx < 0) { + MessageOutput.println("[Daly BMS] Invalid RX/TX pin config"); + return false; + } + + DalyHwSerial.begin(9600, SERIAL_8N1, pin.battery_rx, pin.battery_tx); + DalyHwSerial.flush(); + + memset(this->my_txBuffer, 0x00, XFER_BUFFER_LENGTH); + clearGet(); + _stats->_state = "DalyOffline"; + _stats->_connectionState = false; + + return true; +} + +void DalyBms::deinit() +{ + DalyHwSerial.end(); +} + +void DalyBms::loop() +{ + if (millis() > _lastRequest + _pollInterval * 1000 + 250) { + + if (millis() - previousTime >= DELAYTINME) + { + _stats->setManufacturer("Daly BMS"); + MessageOutput.printf("[Daly BMS] Request counter = %d\r\n", requestCounter ); + switch (requestCounter) + { + case 0: + // requestCounter = sendCommand() ? (requestCounter + 1) : 0; + requestCounter++; + break; + case 1: + if (getPackMeasurements()) + { + _stats->_connectionState = true; + errorCounter = 0; + requestCounter++; + _stats->setLastUpdate(millis()); + } + else + { + requestCounter = 0; + if (errorCounter < ERRORCOUNTER) + { + errorCounter++; + } + else + { + errorCounter = 0; + //requestCallback(); + clearGet(); + _stats -> _connectionState = false; + _stats -> _state = "DalyOffline"; + } + } + break; + case 2: + requestCounter = getMinMaxCellVoltage() ? (requestCounter + 1) : 0; + break; + case 3: + requestCounter = getPackTemp() ? (requestCounter + 1) : 0; + break; + case 4: + requestCounter = getDischargeChargeMosStatus() ? (requestCounter + 1) : 0; + break; + case 5: + requestCounter = getStatusInfo() ? (requestCounter + 1) : 0; + break; + case 6: + requestCounter = getCellVoltages() ? (requestCounter + 1) : 0; + break; + default: + requestCounter = 1; + break; + } + previousTime = millis(); + } + } +} + +bool DalyBms::getPackMeasurements() // 0x90 +{ + if (!this->requestData(COMMAND::VOUT_IOUT_SOC, 1)) + { + MessageOutput.println(" Receive failed, V, I, & SOC values won't be modified!\n"); + clearGet(); + return false; + } + else + // check if packCurrent in range + if (((float)(((this->frameBuff[0][8] << 8) | this->frameBuff[0][9]) - 30000) / 10.0f) == -3000.f) + { + MessageOutput.println(" Receive failed, pack Current not in range. values won't be modified!\n"); + return false; + } + else + // check if SOC in range + if (((float)((this->frameBuff[0][10] << 8) | this->frameBuff[0][11]) / 10.0f) > 100.f) + { + MessageOutput.println(" Receive failed,SOC out of range. values won't be modified!\n"); + return false; + } + + // Pull the relevent values out of the buffer + _stats->_voltage = ((float)((this->frameBuff[0][4] << 8) | this->frameBuff[0][5]) / 10.0f); + _stats->_current = ((float)(((this->frameBuff[0][8] << 8) | this->frameBuff[0][9]) - 30000) / 10.0f); + _stats->_SoC = ((float)((this->frameBuff[0][10] << 8) | this->frameBuff[0][11]) / 10.0f); + //MessageOutput.println(" " + (String)get.packVoltage + "V, " + (String)get.packCurrent + "A, " + (String)get.packSOC + "SOC"); + return true; +} + +bool DalyBms::getMinMaxCellVoltage() // 0x91 +{ + if (!this->requestData(COMMAND::MIN_MAX_CELL_VOLTAGE, 1)) + { + MessageOutput.printf(" Receive failed, min/max cell values won't be modified!\n"); + return false; + } + + _stats -> _maxCellmV = (float)((this->frameBuff[0][4] << 8) | this->frameBuff[0][5]); + _stats -> _maxCellVNum = this->frameBuff[0][6]; + _stats -> _minCellmV = (float)((this->frameBuff[0][7] << 8) | this->frameBuff[0][8]); + _stats -> _minCellVNum = this->frameBuff[0][9]; + _stats -> _cellDiff = ( _stats -> _maxCellmV - _stats -> _minCellmV); + + return true; +} + +bool DalyBms::getPackTemp() // 0x92 +{ + if (!this->requestData(COMMAND::MIN_MAX_TEMPERATURE, 1)) + { + MessageOutput.printf(" Receive failed, Temp values won't be modified!\n"); + return false; + } + _stats -> _temperature = ((this->frameBuff[0][4] - 40) + (this->frameBuff[0][6] - 40)) / 2; + + return true; +} + +bool DalyBms::getDischargeChargeMosStatus() // 0x93 +{ + if (!this->requestData(COMMAND::DISCHARGE_CHARGE_MOS_STATUS, 1)) + { + MessageOutput.printf(" Receive failed, Charge / discharge mos Status won't be modified!\n"); + return false; + } + + switch (this->frameBuff[0][4]) + { + case 0: + _stats -> _state = "DalyStationary"; + break; + case 1: + _stats -> _state = "DalyCharge"; + break; + case 2: + _stats -> _state = "DalyDischarge"; + break; + } + + if (this->frameBuff[0][5]==0){ + _stats -> _chargeFetState = true; + } else { + _stats -> _chargeFetState = false; + } + if (this->frameBuff[0][6]==0){ + _stats -> _dischargeFetState = true; + } else { + _stats -> _dischargeFetState = true; + } + _stats -> _bmsHeartBeat = this->frameBuff[0][7]; + char msgbuff[16]; + float tmpAh = (((uint32_t)frameBuff[0][8] << 0x18) | ((uint32_t)frameBuff[0][9] << 0x10) | ((uint32_t)frameBuff[0][10] << 0x08) | (uint32_t)frameBuff[0][11]) * 0.001; + dtostrf(tmpAh, 3, 1, msgbuff); + _stats -> _resCapacityAh = atof(msgbuff); + + return true; +} + +bool DalyBms::getStatusInfo() // 0x94 +{ + if (!this->requestData(COMMAND::STATUS_INFO, 1)) + { + MessageOutput.printf(" Receive failed, Status info won't be modified!\n"); + return false; + } + + _stats -> _numberOfCells = this->frameBuff[0][4]; + _stats -> _numOfTempSensors = this->frameBuff[0][5]; + _stats -> _chargeState = this->frameBuff[0][6]; + _stats -> _loadState = this->frameBuff[0][7]; + _stats -> _bmsCycles = ((uint16_t)this->frameBuff[0][9] << 0x08) | (uint16_t)this->frameBuff[0][10]; + + return true; +} + +bool DalyBms::getCellVoltages() // 0x95 +{ + unsigned int cellNo = 0; // start with cell no. 1 + + // Check to make sure we have a valid number of cells + if (_stats -> _numberOfCells < MIN_NUMBER_CELLS && _stats -> _numberOfCells >= MAX_NUMBER_CELLS) + { + return false; + } + + if (this->requestData(COMMAND::CELL_VOLTAGES, (unsigned int)ceil(_stats -> _numberOfCells / 3.0))) + { + for (size_t k = 0; k < (unsigned int)ceil(_stats -> _numberOfCells / 3.0); k++) // test for bug #67 + { + for (size_t i = 0; i < 3; i++) + { + _stats -> _cellVmV[cellNo] = (this->frameBuff[k][5 + i + i] << 8) | this->frameBuff[k][6 + i + i]; + cellNo++; + if (cellNo >= _stats -> _numberOfCells) + break; + } + } + return true; + } + else + { + return false; + } +} + +bool DalyBms::setDischargeMOS(bool sw) // 0xD9 0x80 First Byte 0x01=ON 0x00=OFF +{ + if (sw) + { + MessageOutput.println("Attempting to switch discharge MOSFETs on"); + // Set the first byte of the data payload to 1, indicating that we want to switch on the MOSFET + requestCounter = 0; + this->my_txBuffer[4] = 0x01; + this->sendCommand(COMMAND::DISCHRG_FET); + } + else + { + MessageOutput.println("Attempting to switch discharge MOSFETs off"); + requestCounter = 0; + this->sendCommand(COMMAND::DISCHRG_FET); + } + if (!this->receiveBytes()) + { + MessageOutput.printf(" No response from BMS! Can't verify MOSFETs switched.\n"); + return false; + } + + return true; +} + +bool DalyBms::setChargeMOS(bool sw) // 0xDA 0x80 First Byte 0x01=ON 0x00=OFF +{ + if (sw == true) + { + MessageOutput.println("Attempting to switch charge MOSFETs on"); + // Set the first byte of the data payload to 1, indicating that we want to switch on the MOSFET + requestCounter = 0; + this->my_txBuffer[4] = 0x01; + this->sendCommand(COMMAND::CHRG_FET); + } + else + { + MessageOutput.println("Attempting to switch charge MOSFETs off"); + requestCounter = 0; + this->sendCommand(COMMAND::CHRG_FET); + } + + if (!this->receiveBytes()) + { + MessageOutput.println(" No response from BMS! Can't verify MOSFETs switched.\n"); + return false; + } + + return true; +} + +bool DalyBms::setBmsReset() // 0x00 Reset the BMS +{ + requestCounter = 0; + this->sendCommand(COMMAND::BMS_RESET); + + if (!this->receiveBytes()) + { + MessageOutput.printf(" Send failed, can't verify BMS was reset!\n"); + return false; + } + + return true; +} + +bool DalyBms::setSOC(float val) // 0x21 last two byte is SOC +{ + if (val >= 0 && val <= 100) + { + requestCounter = 0; + + MessageOutput.println(" Attempting to read the SOC"); + // try read with 0x61 + this->sendCommand(COMMAND::READ_SOC); + if (!this->receiveBytes()) + { + // if 0x61 fails, write fake timestamp + MessageOutput.println(" Attempting to set the SOC with fake RTC data"); + + this->my_txBuffer[5] = 0x17; // year + this->my_txBuffer[6] = 0x01; // month + this->my_txBuffer[7] = 0x01; // day + this->my_txBuffer[8] = 0x01; // hour + this->my_txBuffer[9] = 0x01; // minute + } + else + { + MessageOutput.println(" Attempting to set the SOC with RTC data from BMS"); + for (size_t i = 5; i <= 9; i++) + { + this->my_txBuffer[i] = this->my_rxBuffer[i]; + } + } + uint16_t value = (val * 10); + this->my_txBuffer[10] = (value & 0xFF00) >> 8; + this->my_txBuffer[11] = (value & 0x00FF); + this->sendCommand(COMMAND::SET_SOC); + + if (!this->receiveBytes()) + { + MessageOutput.printf(" No response from BMS! Can't verify SOC.\n"); + return false; + } + else + { + return true; + } + } + return false; +} + +bool DalyBms::getState() // Function to return the state of connection +{ + return _stats->_connectionState; +} + +//---------------------------------------------------------------------- +// Private Functions +//---------------------------------------------------------------------- + +bool DalyBms::requestData(COMMAND cmdID, unsigned int frameAmount) // new function to request global data +{ + // Clear out the buffers + memset(this->my_rxFrameBuffer, 0x00, sizeof(this->my_rxFrameBuffer)); + memset(this->frameBuff, 0x00, sizeof(this->frameBuff)); + memset(this->my_txBuffer, 0x00, XFER_BUFFER_LENGTH); + //--------------send part-------------------- + uint8_t txChecksum = 0x00; // transmit checksum buffer + unsigned int byteCounter = 0; // bytecounter for incomming data + // prepare the frame with static data and command ID + this->my_txBuffer[0] = START_BYTE; + this->my_txBuffer[1] = HOST_ADRESS; + this->my_txBuffer[2] = cmdID; + this->my_txBuffer[3] = FRAME_LENGTH; + + // Calculate the checksum + for (uint8_t i = 0; i <= 11; i++) + { + txChecksum += this->my_txBuffer[i]; + } + // put it on the frame + this->my_txBuffer[12] = txChecksum; + + // send the packet + DalyHwSerial.write(this->my_txBuffer, XFER_BUFFER_LENGTH); + // first wait for transmission end + DalyHwSerial.flush(); + //------------------------------------------- + + //-----------Recive Part--------------------- + /*uint8_t rxByteNum = */ DalyHwSerial.readBytes(this->my_rxFrameBuffer, XFER_BUFFER_LENGTH * frameAmount); + for (size_t i = 0; i < frameAmount; i++) + { + for (size_t j = 0; j < XFER_BUFFER_LENGTH; j++) + { + this->frameBuff[i][j] = this->my_rxFrameBuffer[byteCounter]; + byteCounter++; + } + + uint8_t rxChecksum = 0x00; + for (int k = 0; k < XFER_BUFFER_LENGTH - 1; k++) + { + rxChecksum += this->frameBuff[i][k]; + } + char debugBuff[128]; + sprintf(debugBuff, "[Command: 0x%2X][CRC Rec: %2X][CRC Calc: %2X]", cmdID, rxChecksum, this->frameBuff[i][XFER_BUFFER_LENGTH - 1]); + + if (rxChecksum != this->frameBuff[i][XFER_BUFFER_LENGTH - 1]) + { + MessageOutput.println(" CRC FAIL"); + return false; + } + if (rxChecksum == 0) + { + MessageOutput.println(" NO DATA"); + return false; + } + if (this->frameBuff[i][1] >= 0x20) + { + MessageOutput.println(" BMS SLEEPING"); + return false; + } + } + return true; +} + +bool DalyBms::sendQueueAdd(COMMAND cmdID) +{ + + for (size_t i = 0; i < sizeof commandQueue / sizeof commandQueue[0]; i++) // run over the queue array + { + if (commandQueue[i] == 0x100) // search the next free slot for command + { + commandQueue[i] = cmdID; // put in the command + break; + } + } + + return true; +} + +bool DalyBms::sendCommand(COMMAND cmdID) +{ + + uint8_t checksum = 0; + do // clear all incoming serial to avoid data collision + { + char t __attribute__((unused)) = DalyHwSerial.read(); // war auskommentiert, zum testen an + + } while (DalyHwSerial.read() > 0); + + // prepare the frame with static data and command ID + this->my_txBuffer[0] = START_BYTE; + this->my_txBuffer[1] = HOST_ADRESS; + this->my_txBuffer[2] = cmdID; + this->my_txBuffer[3] = FRAME_LENGTH; + + // Calculate the checksum + for (uint8_t i = 0; i <= 11; i++) + { + checksum += this->my_txBuffer[i]; + } + // put it on the frame + this->my_txBuffer[12] = checksum; + MessageOutput.println(); + MessageOutput.println(checksum, HEX); + + DalyHwSerial.write(this->my_txBuffer, XFER_BUFFER_LENGTH); + // fix the sleep Bug + // first wait for transmission end + DalyHwSerial.flush(); + + // after send clear the transmit buffer + memset(this->my_txBuffer, 0x00, XFER_BUFFER_LENGTH); + requestCounter = 0; // reset the request queue that we get actual data + return true; +} + +bool DalyBms::receiveBytes(void) +{ + // Clear out the input buffer + memset(this->my_rxBuffer, 0x00, XFER_BUFFER_LENGTH); + memset(this->frameBuff, 0x00, sizeof(this->frameBuff)); + + // Read bytes from the specified serial interface + uint8_t rxByteNum = DalyHwSerial.readBytes(this->my_rxBuffer, XFER_BUFFER_LENGTH); + + // Make sure we got the correct number of bytes + if (rxByteNum != XFER_BUFFER_LENGTH) + { + MessageOutput.println(" Error: Received the wrong number of bytes! Expected 13, got "); + MessageOutput.println(rxByteNum, DEC); + this->barfRXBuffer(); + return false; + } + + if (!validateChecksum()) + { + MessageOutput.println(" Error: Checksum failed!"); + this->barfRXBuffer(); + + return false; + } + + return true; +} + +bool DalyBms::validateChecksum() +{ + uint8_t checksum = 0x00; + + for (int i = 0; i < XFER_BUFFER_LENGTH - 1; i++) + { + checksum += this->my_rxBuffer[i]; + } + MessageOutput.println(" CRC: Calc.: " + (String)checksum + " Rec.: " + (String)this->my_rxBuffer[XFER_BUFFER_LENGTH - 1]); + // Compare the calculated checksum to the real checksum (the last received byte) + return (checksum == this->my_rxBuffer[XFER_BUFFER_LENGTH - 1]); +} + +void DalyBms::barfRXBuffer(void) +{ + MessageOutput.println(" RX Buffer: ["); + for (int i = 0; i < XFER_BUFFER_LENGTH; i++) + { + MessageOutput.println(",0x" + (String)this->my_rxBuffer[i]); + } + MessageOutput.println("]"); +} + +void DalyBms::clearGet(void) +{ + _stats -> _connectionState = false; + _stats -> _state = "DalyOffline"; // charge/discharge status (0 stationary ,1 charge ,2 discharge) +} diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index ae442ec2a..2f6de8acc 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -623,6 +623,7 @@ "ProviderJkBmsSerial": "Jikong (JK) BMS per serieller Verbindung", "ProviderMqtt": "State of Charge (SoC) Wert aus MQTT Broker", "ProviderVictron": "Victron SmartShunt per VE.Direct Schnittstelle", + "ProviderDalyBmsSerial": "Daly BMS per serieller Verbindung", "MqttConfiguration": "MQTT Einstellungen", "MqttTopic": "SoC-Wert Topic", "JkBmsConfiguration": "JK BMS Einstellungen", @@ -806,7 +807,7 @@ "Close": "Schließen", "SetVoltageLimit": "Spannungslimit:", "SetCurrentLimit": "Stromlimit:", - "CurrentLimit": "Aktuelles Limit: " + "CurrentLimit": "Aktuelles Limit: " }, "acchargeradmin": { "ChargerSettings": "AC Ladegerät Einstellungen", @@ -852,6 +853,8 @@ "cellAvgVoltage": "Durchschnittliche Zellspannung", "cellMaxVoltage": "Höchste Zellspannung", "cellDiffVoltage": "Zellspannungsdifferenz", + "cellMaxVoltageNum": "Zelle mit höchst. Spannung", + "cellMinVoltageNum": "Zelle mit nied. Spannung", "balancingActive": "Ausgleichen aktiv", "issues": "Meldungen", "noIssues": "Keine Meldungen", @@ -889,6 +892,11 @@ "bmsInternal": "BMS intern", "chargeCycles": "Ladezyklen", "chargedEnergy": "Geladene Energie", - "dischargedEnergy": "Entladene Energie" + "dischargedEnergy": "Entladene Energie", + "DalyStationary": "Standby", + "DalyOffline": "Offline", + "DalyCharge": "Laden", + "DalyDischarge": "Entladen", + "remainingAh": "Verbleibende Kapazität" } } diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 668e095ee..a19f75fa4 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -632,6 +632,7 @@ "ProviderJkBmsSerial": "Jikong (JK) BMS using serial connection", "ProviderMqtt": "State of Charge (SoC) value from MQTT broker", "ProviderVictron": "Victron SmartShunt using VE.Direct interface", + "ProviderDalyBmsSerial": "Daly BMS using serial connection", "MqttConfiguration": "MQTT Settings", "MqttTopic": "SoC value topic", "JkBmsConfiguration": "JK BMS Settings", @@ -816,7 +817,7 @@ "Close": "close", "SetVoltageLimit": "Voltage limit:", "SetCurrentLimit": "Current limit:", - "CurrentLimit": "Current limit:" + "CurrentLimit": "Current limit:" }, "acchargeradmin": { "ChargerSettings": "AC Charger Settings", @@ -899,6 +900,11 @@ "bmsInternal": "BMS internal", "chargeCycles": "Charge cycles", "chargedEnergy": "Charged energy", - "dischargedEnergy": "Discharged energy" + "dischargedEnergy": "Discharged energy", + "DalyStationary": "Standby", + "DalyOffline": "Offline", + "DalyCharge": "Charge", + "DalyDischarge": "Discharge", + "remainingAh": "Remaining capacity" } } diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 7c845c741..9be2d532a 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -1,861 +1,866 @@ -{ - "menu": { - "LiveView": "Direct", - "Settings": "Paramètres", - "NetworkSettings": "Réseau", - "NTPSettings": "Heure locale", - "MQTTSettings": "MQTT", - "InverterSettings": "Onduleurs", - "SecuritySettings": "Sécurité", - "DTUSettings": "DTU", - "DeviceManager": "Périphériques", - "VedirectSettings": "VE.Direct", - "PowerMeterSettings": "Power Meter", - "BatterySettings": "Battery", - "AcChargerSettings": "AC Charger", - "ConfigManagement": "Gestion de la configuration", - "FirmwareUpgrade": "Mise à jour du firmware", - "DeviceReboot": "Redémarrage de l'appareil", - "Info": "Informations", - "System": "Système", - "Network": "Réseau", - "NTP": "NTP", - "MQTT": "MQTT", - "Console": "Console", - "Vedirect": "VE.Direct", - "About": "A propos", - "Logout": "Déconnexion", - "Login": "Connexion" - }, - "base": { - "Yes": "Oui", - "No": "Non", - "VerboseLogging": "Journalisation Détaillée", - "Seconds": "Secondes", - "Loading": "Chargement...", - "Reload": "Reload", - "Cancel": "Annuler", - "Save": "Sauvegarder", - "Refreshing": "Refreshing", - "Pull": "Pull down to refresh", - "Release": "Release to refresh", - "Close": "Fermer" - }, - "localeswitcher": { - "Dark": "Sombre", - "Light": "Clair", - "Auto": "Auto" - }, - "apiresponse": { - "1001": "Paramètres enregistrés !", - "1002": "Aucune valeur trouvée !", - "1003": "Données trop importantes !", - "1004": "Échec de l'analyse des données !", - "1005": "Certaines valeurs sont manquantes !", - "1006": "Write failed!", - "2001": "Le numéro de série ne peut pas être nul !", - "2002": "L'intervalle de sondage doit être supérieur à zéro !", - "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!", - "2005": "Invalid country selection !", - "3001": "Rien n'a été supprimé !", - "3002": "Configuration réinitialisée. Redémarrage maintenant...", - "4001": "@:apiresponse.2001", - "4002": "Le nom doit comporter entre 1 et {max} caractères !", - "4003": "Seulement {max} onduleurs sont supportés !", - "4004": "Onduleur créé !", - "4005": "Identifiant spécifié invalide !", - "4006": "Réglage du montant maximal de canaux invalide !", - "4007": "Onduleur modifié !", - "4008": "Onduleur supprimé !", - "4009": "Inverter order saved!", - "5001": "@:apiresponse.2001", - "5002": "La limite doit être comprise entre 1 et {max} !", - "5003": "Type spécifié invalide !", - "5004": "Onduleur spécifié invalide !", - "6001": "Redémarrage déclenché !", - "6002": "Redémarrage annulé !", - "7001": "Le nom du serveur MQTT doit comporter entre 1 et {max} caractères !", - "7002": "Le nom d'utilisateur ne doit pas comporter plus de {max} caractères !", - "7003": "Le mot de passe ne doit pas comporter plus de {max} caractères !", - "7004": "Le sujet ne doit pas comporter plus de {max} caractères !", - "7005": "Le sujet ne doit pas contenir d'espace !", - "7006": "Le sujet doit se terminer par une barre oblique (/) !", - "7007": "Le port doit être un nombre entre 1 et 65535 !", - "7008": "Le certificat ne doit pas comporter plus de {max} caractères !", - "7009": "Le sujet LWT ne doit pas comporter plus de {max} caractères !", - "7010": "Le sujet LWT ne doit pas contenir de caractères d'espacement !", - "7011": "La valeur LWT en ligne ne doit pas dépasser {max} caractères !", - "7012": "La valeur LWT hors ligne ne doit pas dépasser {max} caractères !", - "7013": "L'intervalle de publication doit être un nombre compris entre {min} et {max} !", - "7014": "Le sujet Hass ne doit pas dépasser {max} caractères !", - "7015": "Le sujet Hass ne doit pas contenir d'espace !", - "7016": "LWT QOS ne doit pas être supérieur à {max}!", - "8001": "L'adresse IP n'est pas valide !", - "8002": "Le masque de réseau n'est pas valide !", - "8003": "La passerelle n'est pas valide !", - "8004": "L'adresse IP du serveur DNS primaire n'est pas valide !", - "8005": "L'adresse IP du serveur DNS secondaire n'est pas valide !", - "8006": "La valeur du délai d'attente du point d'accès administratif n'est pas valide !", - "9001": "Le serveur NTP doit avoir une longueur comprise entre 1 et {max} caractères !", - "9002": "Le fuseau horaire doit comporter entre 1 et {max} caractères !", - "9003": "La description du fuseau horaire doit comporter entre 1 et {max} caractères !", - "9004": "L'année doit être un nombre compris entre {min} et {max} !", - "9005": "Le mois doit être un nombre compris entre {min} et {max} !", - "9006": "Le jour doit être un nombre compris entre {min} et {max} !", - "9007": "Les heures doivent être un nombre compris entre {min} et {max} !", - "9008": "Les minutes doivent être un nombre compris entre {min} et {max} !", - "9009": "Les secondes doivent être un nombre compris entre {min} et {max} !", - "9010": "Heure mise à jour !", - "10001": "Le mot de passe doit comporter entre 8 et {max} caractères !", - "10002": "Authentification réussie !", - "11001": "@:apiresponse.2001", - "11002": "@:apiresponse:5004", - "12001": "Le profil doit comporter entre 1 et {max} caractères !" - }, - "home": { - "LiveData": "Données en direct", - "SerialNumber": "Numéro de série : ", - "CurrentLimit": "Limite de courant : ", - "DataAge": "Âge des données : ", - "Seconds": "{val} secondes", - "ShowSetInverterLimit": "Afficher / Régler la limite de l'onduleur", - "TurnOnOff": "Allumer / Eteindre l'onduleur", - "ShowInverterInfo": "Afficher les informations sur l'onduleur", - "ShowEventlog": "Afficher le journal des événements", - "UnreadMessages": "messages non lus", - "Loading": "@:base.Loading", - "EventLog": "Journal des événements", - "InverterInfo": "Informations sur l'onduleur", - "LimitSettings": "Paramètres de la limite", - "LastLimitSetStatus": "Statut de la dernière limite fixée", - "SetLimit": "Fixer la limite", - "Relative": "Relative (%)", - "Absolute": "Absolue (W)", - "LimitHint": "Astuce : Si vous définissez la limite en valeur absolue, l'affichage de la valeur actuelle ne sera mis à jour qu'après environ 4 minutes.", - "SetPersistent": "Fixer une limite persistante", - "SetNonPersistent": "Fixer une limite non persistante", - "PowerSettings": "Paramètres d'alimentation", - "LastPowerSetStatus": "État du dernier réglage de l'alimentation", - "TurnOn": "Allumer", - "TurnOff": "Eteindre", - "Restart": "Redémarrer", - "Failure": "Échec", - "Pending": "En attente", - "Ok": "OK", - "Unknown": "Inconnu", - "ShowGridProfile": "Show Grid Profile", - "GridProfile": "Grid Profile" - }, - "vedirecthome": { - "SerialNumber": "Serial Number: ", - "FirmwareNumber": "Firmware Number: ", - "DataAge": "Data Age: ", - "Seconds": "{val} seconds", - "DeviceInfo": "Device Info", - "Property": "Property", - "Value": "Value", - "Unit": "Unit", - "LoadOutputState": "Load output state", - "StateOfOperation": "State of operation", - "TrackerOperationMode": "Tracker operation mode", - "OffReason": "Off reason", - "ErrorCode": "Error code", - "DaySequenceNumber": "Day sequence number (0..364)", - "Battery": "Output (Battery)", - "output": { - "P": "Power (calculated)", - "V": "Voltage", - "I": "Current", - "E": "Efficiency (calculated)" - }, - "Panel": "Input (Solar Panels)", - "input": { - "PPV": "Power", - "VPV": "Voltage", - "IPV": "Current (calculated)", - "YieldToday": "Yield today", - "YieldYesterday": "Yield yesterday", - "YieldTotal": "Yield total (user resettable counter)", - "MaximumPowerToday": "Maximum power today", - "MaximumPowerYesterday": "Maximum power yesterday" - }, - "PowerLimiterState": "Power limiter state [off (charging), solar passthrough, on battery]" - }, - "eventlog": { - "Start": "Départ", - "Stop": "Arrêt", - "Id": "ID", - "Message": "Message" - }, - "devinfo": { - "NoInfo": "Aucune information disponible", - "NoInfoLong": "N'a pas reçu de données valides de l'onduleur jusqu'à présent. J'essaie toujours...", - "UnknownModel": "Modèle inconnu ! Veuillez signaler le \"Numéro d'article matériel\" et le modèle (par exemple, HM-350) comme un problème ici.", - "Serial": "Serial", - "ProdYear": "Production Year", - "ProdWeek": "Production Week", - "Model": "Modèle", - "DetectedMaxPower": "Puissance maximale détectée", - "BootloaderVersion": "Version du bootloader", - "FirmwareVersion": "Version du firmware", - "FirmwareBuildDate": "Date de création du firmware", - "HardwarePartNumber": "Numéro d'article matériel", - "HardwareVersion": "Version du matériel" - }, - "gridprofile": { - "NoInfo": "@:devinfo.NoInfo", - "NoInfoLong": "@:devinfo.NoInfoLong", - "Name": "Name", - "Version": "Version", - "Enabled": "@:wifistationinfo.Enabled", - "Disabled": "@:wifistationinfo.Disabled", - "GridprofileSupport": "Support the development", - "GridprofileSupportLong": "Please see here for further information." - }, - "systeminfo": { - "SystemInfo": "Informations sur le système", - "VersionError": "Erreur de récupération des informations de version", - "VersionNew": "Nouvelle version disponible ! Montrer les changements !", - "VersionOk": "À jour !" - }, - "firmwareinfo": { - "FirmwareInformation": "Informations sur le firmware", - "Hostname": "Nom d'hôte", - "SdkVersion": "Version du SDK", - "ConfigVersion": "Version de la configuration", - "FirmwareVersion": "Version du firmware / Hash Git", - "PioEnv": "PIO Environment", - "FirmwareVersionHint": "Cliquez ici pour afficher des informations sur votre version actuelle", - "FirmwareUpdate": "Mise à jour du firmware", - "FirmwareUpdateHint": "Cliquez ici pour voir les changements entre votre version et la dernière version", - "FrmwareUpdateAllow": "En activant le contrôle de mise à jour, une demande est envoyée à GitHub.com à chaque fois que la page est consultée afin de récupérer la dernière version disponible. Si tu n'es pas d'accord, laisse cette fonction désactivée.", - "ResetReason0": "Raison de la réinitialisation CPU 0", - "ResetReason1": "Raison de la réinitialisation CPU 1", - "ConfigSaveCount": "Nombre d'enregistrements de la configuration", - "Uptime": "Durée de fonctionnement", - "UptimeValue": "0 jour {time} | 1 jour {time} | {count} jours {time}" - }, - "hardwareinfo": { - "HardwareInformation": "Informations sur le matériel", - "ChipModel": "Modèle de puce", - "ChipRevision": "Révision de la puce", - "ChipCores": "Nombre de cœurs", - "CpuFrequency": "Fréquence du CPU", - "Mhz": "MHz" - }, - "memoryinfo": { - "MemoryInformation": "Informations sur la mémoire", - "Type": "Type", - "Usage": "Utilisation", - "Free": "Libre", - "Used": "Utilisée", - "Size": "Taille", - "Heap": "Heap", - "LittleFs": "LittleFs", - "Sketch": "Sketch" - }, - "heapdetails": { - "HeapDetails": "Heap Details", - "TotalFree": "Total free", - "LargestFreeBlock": "Biggest contiguous free block", - "MaxUsage": "Maximum usage since start", - "Fragmentation": "Level of fragmentation" - }, - "radioinfo": { - "RadioInformation": "Informations sur la radio", - "Status": "{module} Statut", - "ChipStatus": "{module} État de la puce", - "ChipType": "{module} Type de puce", - "Connected": "connectée", - "NotConnected": "non connectée", - "Configured": "configurée", - "NotConfigured": "non configurée", - "Unknown": "Inconnue" - }, - "networkinfo": { - "NetworkInformation": "Informations sur le réseau" - }, - "wifistationinfo": { - "WifiStationInfo": "Informations sur le WiFi (Station)", - "Status": "Statut", - "Enabled": "activé", - "Disabled": "désactivé", - "Ssid": "SSID", - "Bssid": "BSSID", - "Quality": "Qualité", - "Rssi": "RSSI" - }, - "wifiapinfo": { - "WifiApInfo": "Informations sur le WiFi (Point d'accès)", - "Status": "@:wifistationinfo.Status", - "Enabled": "@:wifistationinfo.Enabled", - "Disabled": "@:wifistationinfo.Disabled", - "Ssid": "@:wifistationinfo.Ssid", - "Stations": "# Stations" - }, - "interfacenetworkinfo": { - "NetworkInterface": "Interface réseau ({iface})", - "Hostname": "@:firmwareinfo.Hostname", - "IpAddress": "Adresse IP", - "Netmask": "Masque de réseau", - "DefaultGateway": "Passerelle par défaut", - "Dns": "DNS {num}", - "MacAddress": "Addresse MAC" - }, - "interfaceapinfo": { - "NetworkInterface": "Interface réseau (Point d'accès)", - "IpAddress": "@:interfacenetworkinfo.IpAddress", - "MacAddress": "@:interfacenetworkinfo.MacAddress" - }, - "ntpinfo": { - "NtpInformation": "Informations sur le NTP", - "ConfigurationSummary": "Résumé de la configuration", - "Server": "Serveur", - "Timezone": "Fuseau horaire", - "TimezoneDescription": "Description du fuseau horaire", - "CurrentTime": "Heure actuelle", - "Status": "Statut", - "Synced": "synchronisée", - "NotSynced": "pas synchronisée", - "LocalTime": "Heure locale", - "Sunrise": "Lever du soleil", - "Sunset": "Coucher du soleil", - "NotAvailable": "Not Available", - "Mode": "Mode", - "Day": "Jour", - "Night": "Nuit" - }, - "mqttinfo": { - "MqttInformation": "MQTT Information", - "ConfigurationSummary": "@:ntpinfo.ConfigurationSummary", - "Status": "@:ntpinfo.Status", - "Enabled": "Activé", - "Disabled": "Désactivé", - "Server": "@:ntpinfo.Server", - "Port": "Port", - "Username": "Nom d'utilisateur", - "BaseTopic": "Sujet de base", - "PublishInterval": "Intervalle de publication", - "Seconds": "{sec} secondes", - "CleanSession": "CleanSession Flag", - "Retain": "Conserver", - "Tls": "TLS", - "RootCertifcateInfo": "Informations sur le certificat de l'autorité de certification racine", - "TlsCertLogin": "Connexion avec un certificat TLS", - "ClientCertifcateInfo": "Informations sur le certificat du client", - "HassSummary": "Résumé de la configuration de la découverte automatique du MQTT de Home Assistant", - "Expire": "Expiration", - "IndividualPanels": "Panneaux individuels", - "RuntimeSummary": "Résumé du temps de fonctionnement", - "ConnectionStatus": "État de la connexion", - "Connected": "connecté", - "Disconnected": "déconnecté" - }, - "vedirectinfo": { - "VedirectInformation" : "VE.Direct Info", - "ConfigurationSummary": "@:ntpinfo.ConfigurationSummary", - "Status": "@:ntpinfo.Status", - "Enabled": "@:mqttinfo.Enabled", - "Disabled": "@:mqttinfo.Disabled", - "VerboseLogging": "@:base.VerboseLogging", - "UpdatesOnly": "@:vedirectadmin.UpdatesOnly", - "UpdatesEnabled": "@:mqttinfo.Enabled", - "UpdatesDisabled": "@:mqttinfo.Disabled" - }, - "console": { - "Console": "Console", - "VirtualDebugConsole": "Console de débogage", - "EnableAutoScroll": "Activer le défilement automatique", - "ClearConsole": "Vider la console", - "CopyToClipboard": "Copier dans le presse-papiers" - }, - "inverterchannelinfo": { - "String": "Ligne {num}", - "Phase": "Phase {num}", - "General": "General" - }, - "invertertotalinfo": { - "InverterTotalYieldTotal": "Onduleurs rendement total", - "InverterTotalYieldDay": "Onduleurs rendement du jour", - "InverterTotalPower": "Onduleurs puissance de l'installation", - "MpptTotalYieldTotal": "MPPT rendement total", - "MpptTotalYieldDay": "MPPT rendement du jour", - "MpptTotalPower": "MPPT puissance de l'installation", - "BatterySoc": "State of charge", - "HomePower": "Grid Power", - "HuaweiPower": "Huawei AC Power" - }, - "inverterchannelproperty": { - "Power": "Puissance", - "Voltage": "Tension", - "Current": "Courant", - "Power DC": "Puissance continue", - "YieldDay": "Rendement du jour", - "YieldTotal": "Rendement total", - "Frequency": "Fréquence", - "Temperature": "Température", - "PowerFactor": "Facteur de puissance", - "ReactivePower": "Puissance réactive", - "Efficiency": "Efficacité", - "Irradiation": "Irradiation" - }, - "maintenancereboot": { - "DeviceReboot": "Redémarrage de l'appareil", - "PerformReboot": "Effectuer un redémarrage", - "Reboot": "Redémarrer !", - "Cancel": "@:base.Cancel", - "RebootOpenDTU": "Redémarrer OpenDTU", - "RebootQuestion": "Voulez-vous vraiment redémarrer l'appareil ?", - "RebootHint": "Astuce : Normalement, il n'est pas nécessaire de procéder à un redémarrage manuel. OpenDTU effectue automatiquement tout redémarrage nécessaire (par exemple, après une mise à jour du firmware). Les paramètres sont également adoptés sans redémarrage. Si vous devez redémarrer en raison d'une erreur, veuillez envisager de la signaler à l'adresse suivante Github." - }, - "dtuadmin": { - "DtuSettings": "Paramètres du DTU", - "DtuConfiguration": "Configuration du DTU", - "Serial": "Numéro de série", - "SerialHint": "L'onduleur et le DTU ont tous deux un numéro de série. Le numéro de série du DTU est généré de manière aléatoire lors du premier démarrage et ne doit normalement pas être modifié.", - "PollInterval": "Intervalle de sondage", - "VerboseLogging": "@:base.VerboseLogging", - "Seconds": "Secondes", - "NrfPaLevel": "NRF24 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.", - "CmtPaLevelHint": "Used for HMS/HMT-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.", - "CmtCountry": "CMT2300A 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:", - "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 allowed range in your selected region/country. Make sure that this selection does not violate any local regulations.", - "MHz": "{mhz} MHz", - "dBm": "{dbm} dBm", - "Min": "Minimum ({db} dBm)", - "Low": "Bas ({db} dBm)", - "High": "Haut ({db} dBm)", - "Max": "Maximum ({db} dBm)" - }, - "securityadmin": { - "SecuritySettings": "Paramètres de sécurité", - "AdminPassword": "Mot de passe administrateur", - "Password": "Mot de passe", - "RepeatPassword": "Répéter le mot de passe", - "PasswordHint": "Astuce : Le mot de passe administrateur est utilisé pour accéder à cette interface web (utilisateur 'admin'), mais aussi pour se connecter à l'appareil en mode AP. Il doit comporter de 8 à 64 caractères.", - "Permissions": "Autorisations", - "ReadOnly": "Autoriser l'accès en lecture seule à l'interface web sans mot de passe" - }, - "ntpadmin": { - "NtpSettings": "Paramètres NTP", - "NtpConfiguration": "Configuration du protocole NTP", - "TimeServer": "Serveur horaire", - "TimeServerHint": "La valeur par défaut convient tant que OpenDTU a un accès direct à Internet.", - "Timezone": "Fuseau horaire", - "TimezoneConfig": "Configuration du fuseau horaire", - "LocationConfiguration": "Géolocalisation", - "Longitude": "Longitude", - "Latitude": "Latitude", - "SunSetType": "Sunset type", - "SunSetTypeHint": "Affects the day/night calculation. It can take up to one minute until the new type will be applied.", - "OFFICIAL": "Standard dawn (90.8°)", - "NAUTICAL": "Nautical dawn (102°)", - "CIVIL": "Civil dawn (96°)", - "ASTONOMICAL": "Astronomical dawn (108°)", - "ManualTimeSynchronization": "Synchronisation manuelle de l'heure", - "CurrentOpenDtuTime": "Heure actuelle de l'OpenDTU", - "CurrentLocalTime": "Heure locale actuelle", - "SynchronizeTime": "Synchroniser l'heure", - "SynchronizeTimeHint": "Astuce : Vous pouvez utiliser la synchronisation horaire manuelle pour définir l'heure actuelle d'OpenDTU si aucun serveur NTP n'est disponible. Mais attention, en cas de mise sous tension, l'heure est perdue. Notez également que la précision de l'heure sera faussée, car elle ne peut pas être resynchronisée régulièrement et le microcontrôleur ESP32 ne dispose pas d'une horloge temps réel." - }, - "networkadmin": { - "NetworkSettings": "Paramètres réseau", - "WifiConfiguration": "Configuration du réseau WiFi", - "WifiSsid": "SSID", - "WifiPassword": "Mot de passe", - "Hostname": "Nom d'hôte", - "HostnameHint": "Astuce : Le texte %06X sera remplacé par les 6 derniers chiffres de l'ESP ChipID au format hexadécimal.", - "EnableDhcp": "Activer le DHCP", - "StaticIpConfiguration": "Configuration de l'IP statique", - "IpAddress": "Adresse IP", - "Netmask": "Masque de réseau", - "DefaultGateway": "Passerelle par défaut", - "Dns": "Serveur DNS {num}", - "AdminAp": "Configuration du réseau WiFi (Point d'accès)", - "ApTimeout": "Délai d'attente du point d'accès", - "ApTimeoutHint": "Durée pendant laquelle le point d'accès reste ouvert. Une valeur de 0 signifie infini.", - "Minutes": "minutes", - "EnableMdns": "Activer mDNS", - "MdnsSettings": "mDNS Settings" - }, - "mqttadmin": { - "MqttSettings": "Paramètres MQTT", - "MqttConfiguration": "Configuration du système MQTT", - "EnableMqtt": "Activer le MQTT", - "VerboseLogging": "@:base.VerboseLogging", - "EnableHass": "Activer la découverte automatique du MQTT de Home Assistant", - "MqttBrokerParameter": "Paramètre du Broker MQTT", - "Hostname": "Nom d'hôte", - "HostnameHint": "Nom d'hôte ou adresse IP", - "Port": "Port", - "Username": "Nom d'utilisateur", - "UsernameHint": "Nom d'utilisateur, laisser vide pour une connexion anonyme", - "Password": "Mot de passe:", - "PasswordHint": "Mot de passe, laissez vide pour une connexion anonyme", - "BaseTopic": "Sujet de base", - "BaseTopicHint": "Sujet de base, qui sera ajouté en préambule à tous les sujets publiés (par exemple, inverter/).", - "PublishInterval": "Intervalle de publication", - "Seconds": "secondes", - "CleanSession": "Enable CleanSession flag", - "EnableRetain": "Activation du maintien", - "EnableTls": "Activer le TLS", - "RootCa": "Certificat CA-Root (par défaut Letsencrypt)", - "TlsCertLoginEnable": "Activer la connexion par certificat TLS", - "ClientCert": "Certificat client TLS:", - "ClientKey": "Clé client TLS:", - "LwtParameters": "Paramètres LWT", - "LwtTopic": "Sujet LWT", - "LwtTopicHint": "Sujet LWT, sera ajouté comme sujet de base", - "LwtOnline": "Message en ligne de LWT", - "LwtOnlineHint": "Message qui sera publié sur le sujet LWT lorsqu'il sera en ligne", - "LwtOffline": "Message hors ligne de LWT", - "LwtOfflineHint": "Message qui sera publié sur le sujet LWT lorsqu'il sera hors ligne", - "LwtQos": "QoS (Quality of Service):", - "QOS0": "0 (Au maximum une fois)", - "QOS1": "1 (Au moins une fois)", - "QOS2": "2 (Exactement une fois)", - "HassParameters": "Paramètres de découverte automatique MQTT de Home Assistant", - "HassPrefixTopic": "Préfixe du sujet", - "HassPrefixTopicHint": "Le préfixe de découverte du sujet", - "HassRetain": "Activer du maintien", - "HassExpire": "Activer l'expiration", - "HassIndividual": "Panneaux individuels" - }, - "vedirectadmin": { - "VedirectSettings": "VE.Direct Settings", - "VedirectConfiguration": "VE.Direct Configuration", - "EnableVedirect": "Enable VE.Direct", - "VedirectParameter": "VE.Direct Parameter", - "VerboseLogging": "@:base.VerboseLogging", - "UpdatesOnly": "Publish values to MQTT only when they change" - }, - "batteryadmin": { - "BatterySettings": "Battery Settings", - "BatteryConfiguration": "General Interface Settings", - "EnableBattery": "Enable Interface", - "VerboseLogging": "@:base.VerboseLogging", - "Provider": "Data Provider", - "ProviderPylontechCan": "Pylontech using CAN bus", - "ProviderJkBmsSerial": "Jikong (JK) BMS using serial connection", - "ProviderMqtt": "State of Charge (SoC) value from MQTT broker", - "ProviderVictron": "Victron SmartShunt using VE.Direct interface", - "MqttConfiguration": "MQTT Settings", - "MqttTopic": "SoC value topic", - "JkBmsConfiguration": "JK BMS Settings", - "JkBmsInterface": "Interface Type", - "JkBmsInterfaceUart": "TTL-UART on MCU", - "JkBmsInterfaceTransceiver": "RS-485 Transceiver on MCU", - "PollingInterval": "Polling Interval", - "Seconds": "@:base.Seconds" - }, - "inverteradmin": { - "InverterSettings": "Paramètres des onduleurs", - "AddInverter": "Ajouter un nouvel onduleur", - "Serial": "Numéro de série", - "Name": "Nom", - "Add": "Ajouter", - "AddHint": " Astuce : Vous pouvez définir des paramètres supplémentaires après avoir créé l'onduleur. Utilisez l'icône du stylo dans la liste des onduleurs.", - "InverterList": "Liste des onduleurs", - "Status": "État", - "Send": "Envoyer", - "Receive": "Recevoir", - "StatusHint": "Astuce : L'onduleur est alimenté par son entrée courant continu. S'il n'y a pas de soleil, l'onduleur est éteint, mais les requêtes peuvent toujours être envoyées.", - "Type": "Type", - "Action": "Action", - "SaveOrder": "Save order", - "DeleteInverter": "Supprimer l'onduleur", - "EditInverter": "Modifier l'onduleur", - "General": "Général", - "String": "Ligne", - "Advanced": "Advanced", - "InverterSerial": "Numéro de série de l'onduleur", - "InverterName": "Nom de l'onduleur :", - "InverterNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour votre onduleur.", - "InverterStatus": "Recevoir / Envoyer", - "PollEnable": "Interroger les données de l'onduleur", - "PollEnableNight": "Interroger les données de l'onduleur la nuit", - "CommandEnable": "Envoyer des commandes", - "CommandEnableNight": "Envoyer des commandes la nuit", - "StringName": "Nom de la ligne {num}:", - "StringNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour le port respectif de votre onduleur.", - "StringMaxPower": "Puissance maximale de la ligne {num}:", - "StringMaxPowerHint": "Entrez la puissance maximale des panneaux solaires connectés.", - "StringYtOffset": "Décalage du rendement total de la ligne {num} :", - "StringYtOffsetHint": "Ce décalage est appliqué à la valeur de rendement total lue sur le variateur. Il peut être utilisé pour mettre le rendement total du variateur à zéro si un variateur usagé est utilisé.", - "InverterHint": "*) Entrez le Wp du canal pour calculer l'irradiation.", - "ReachableThreshold": "Reachable Threshold:", - "ReachableThresholdHint": "Defines how many requests are allowed to fail until the inverter is treated is not reachable.", - "ZeroRuntime": "Zero runtime data", - "ZeroRuntimeHint": "Zero runtime data (no yield data) if inverter becomes unreachable.", - "ZeroDay": "Zero daily yield at midnight", - "ZeroDayHint": "This only works if the inverter is unreachable. If data is read from the inverter, it's values will be used. (Reset only occours on power cycle)", - "Cancel": "@:base.Cancel", - "Save": "@:base.Save", - "DeleteMsg": "Êtes-vous sûr de vouloir supprimer l'onduleur \"{name}\" avec le numéro de série \"{serial}\" ?", - "Delete": "Supprimer", - "YieldDayCorrection": "Yield Day Correction", - "YieldDayCorrectionHint": "Sum up daily yield even if the inverter is restarted. Value will be reset at midnight" - }, - "configadmin": { - "ConfigManagement": "Gestion de la configuration", - "BackupHeader": "Sauvegarder le fichier de configuration", - "BackupConfig": "Fichier de configuration", - "Backup": "Sauvegarder", - "Restore": "Restaurer", - "NoFileSelected": "Aucun fichier sélectionné", - "RestoreHeader": "Restaurer le fichier de configuration", - "Back": "Retour", - "UploadSuccess": "Succès du téléversement", - "RestoreHint": "Note : Cette opération remplace le fichier de configuration par la configuration restaurée et redémarre OpenDTU pour appliquer tous les paramètres.", - "ResetHeader": "Effectuer une réinitialisation d'usine", - "FactoryResetButton": "Restaurer les paramètres d'usine", - "ResetHint": "Note : Cliquez sur \"Restaurer les paramètres d'usine\" pour restaurer et initialiser les paramètres d'usine par défaut et redémarrer.", - "FactoryReset": "Remise à zéro", - "ResetMsg": "Êtes-vous sûr de vouloir supprimer la configuration actuelle et réinitialiser tous les paramètres à leurs valeurs par défaut ?", - "ResetConfirm": "Remise à zéro !", - "Cancel": "@:base.Cancel" - }, - "powerlimiteradmin": { - "PowerLimiterSettings": "Power Limiter Settings", - "PowerLimiterConfiguration": "Power Limiter Configuration", - "General": "General", - "Enable": "Enable", - "VerboseLogging": "@:base.VerboseLogging", - "EnableSolarPassthrough": "Enable Solar-Passthrough", - "SolarPassthroughLosses": "(Full) Solar Passthrough Losses:", - "SolarPassthroughLossesInfo": "Hint: Line losses are to be expected when transferring energy from the solar charge controller to the inverter. These losses can be taken into account to prevent the battery from gradually discharging in (full) solar passthrough mode. The power limit to be set on the inverter is additionally reduced by this factor after taking its efficiency into account.", - "BatteryDrainStrategy": "Battery drain strategy", - "BatteryDrainWhenFull": "Empty when full", - "BatteryDrainAtNight": "Empty at night", - "SolarpassthroughInfo": "When the sun is shining, this setting enables the sychronization of the inverter limit with the current solar power of the Victron MPPT charger. This optimizes battery degradation and loses.", - "InverterId": "Inverter ID", - "InverterIdHint": "Select proper inverter ID where battery is connected to.", - "InverterChannelId": "Channel ID", - "InverterChannelIdHint": "Select proper channel where battery is connected to.", - "TargetPowerConsumption": "Target power consumption from grid", - "TargetPowerConsumptionHint": "Set the grid power consumption the limiter tries to achieve.", - "TargetPowerConsumptionHysteresis": "Hysteresis for calculated power limit", - "TargetPowerConsumptionHysteresisHint": "Only send a newly calculated power limit to the inverter if the absolute difference to the last sent power limit matches or exceeds this amount.", - "LowerPowerLimit": "Lower power limit", - "UpperPowerLimit": "Upper power limit", - "PowerMeters": "Power meter", - "MqttTopicPowerMeter1": "MQTT topic - Power meter #1", - "MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (optional)", - "MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)", - "BatterySocStartThreshold": "Battery SoC - Start threshold", - "BatterySocStopThreshold": "Battery SoC - Stop threshold", - "BatterySocSolarPassthroughStartThreshold": "Battery SoC - Start threshold for full solar passthrough", - "BatterySocSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) if battery SOC is over this limit. Use this if you like to supply excess power to the grid when battery is full", - "VoltageStartThreshold": "DC Voltage - Start threshold", - "VoltageStopThreshold": "DC Voltage - Stop threshold", - "VoltageSolarPassthroughStartThreshold": "DC Voltage - Start threshold for full solar passthrough", - "VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough", - "VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.", - "VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor", - "BatterySocInfo": "Hint: The battery SoC (State of Charge) values can only be used if the battery communication interface is enabled. If the battery has not reported any SoC updates in the last minute, the voltage thresholds will be used as fallback.", - "InverterIsBehindPowerMeter": "Inverter is behind Power meter", - "Battery": "DC / Battery", - "VoltageLoadCorrectionInfo": "Hint: When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor)." - }, - "login": { - "Login": "Connexion", - "SystemLogin": "Connexion au système", - "Username": "Nom d'utilisateur", - "UsernameRequired": "Le nom d'utilisateur est requis", - "Password": "Mot de passe", - "PasswordRequired": "Le mot de passe est requis", - "LoginButton": "Connexion" - }, - "firmwareupgrade": { - "FirmwareUpgrade": "Mise à jour du firmware", - "Loading": "@:base.Loading", - "OtaError": "Erreur OTA", - "Back": "Retour", - "Retry": "Réessayer", - "OtaStatus": "Statut OTA", - "OtaSuccess": "Le téléchargement du firmware a réussi. L'appareil a été redémarré automatiquement. Lorsque l'appareil est à nouveau accessible, l'interface est automatiquement rechargée.", - "FirmwareUpload": "Téléversement du firmware", - "UploadProgress": "Progression du téléversement" - }, - "about": { - "AboutOpendtu": "À propos d'OpenDTU", - "ProjectOrigin": "Origine du projet", - "ProjectOriginBody1": "Ce projet a été démarré suite à cette discussion (Mikrocontroller.net).", - "ProjectOriginBody2": "Le protocole Hoymiles a été décrypté grâce aux efforts volontaires de nombreux participants. OpenDTU, entre autres, a été développé sur la base de ce travail. Le projet est sous licence Open Source (GNU General Public License version 2).", - "ProjectOriginBody3": "Le logiciel a été développé au mieux de nos connaissances et de nos convictions. Néanmoins, aucune responsabilité ne peut être acceptée en cas de dysfonctionnement ou de perte de garantie de l'onduleur.", - "ProjectOriginBody4": "OpenDTU est disponible gratuitement. Si vous avez payé pour le logiciel, vous avez probablement été arnaqué.", - "NewsUpdates": "Actualités et mises à jour", - "NewsUpdatesBody": "Les nouvelles mises à jour peuvent être trouvées sur Github.", - "ErrorReporting": "Rapport d'erreurs", - "ErrorReportingBody": "Veuillez signaler les problèmes en utilisant la fonction fournie par Github.", - "Discussion": "Discussion", - "DiscussionBody": "Discutez avec nous sur Discord ou sur Github." - }, - "hints": { - "RadioProblem": "Impossible de se connecter à un module radio configuré.. Veuillez vérifier le câblage.", - "TimeSync": "L'horloge n'a pas encore été synchronisée. Sans une horloge correctement réglée, aucune demande n'est adressée à l'onduleur. Ceci est normal peu de temps après le démarrage. Cependant, après un temps de fonctionnement plus long (>1 minute), cela indique que le serveur NTP n'est pas accessible.", - "TimeSyncLink": "Veuillez vérifier vos paramètres horaires.", - "DefaultPassword": "Vous utilisez le mot de passe par défaut pour l'interface Web et le point d'accès d'urgence. Ceci est potentiellement non sécurisé.", - "DefaultPasswordLink": "Merci de changer le mot de passe." - }, - "deviceadmin": { - "DeviceManager": "Gestionnaire de périphériques", - "ParseError": "Erreur d'analyse dans 'pin_mapping.json': {error}", - "PinAssignment": "Paramètres de connexion", - "SelectedProfile": "Profil sélectionné", - "DefaultProfile": "(Réglages par défaut)", - "ProfileHint": "Votre appareil peut cesser de répondre si vous sélectionnez un profil incompatible. Dans ce cas, vous devez effectuer une suppression via l'interface série.", - "Display": "Affichage", - "PowerSafe": "Activer l'économiseur d'énergie", - "PowerSafeHint": "Eteindre l'écran si aucun onduleur n'est en production.", - "Screensaver": "Activer l'écran de veille", - "ScreensaverHint": "Déplacez un peu l'écran à chaque mise à jour pour éviter le phénomène de brûlure. (Utile surtout pour les écrans OLED)", - "DiagramMode": "Diagram mode:", - "off": "Off", - "small": "Small", - "fullscreen": "Fullscreen", - "DiagramDuration": "Diagram duration:", - "DiagramDurationHint": "The time period which is shown in the diagram.", - "Seconds": "Seconds", - "Contrast": "Contraste ({contrast}):", - "Rotation": "Rotation:", - "rot0": "Pas de rotation", - "rot90": "Rotation de 90 degrés", - "rot180": "Rotation de 180 degrés", - "rot270": "Rotation de 270 degrés", - "DisplayLanguage": "Langue d'affichage", - "en": "Anglais", - "de": "Allemand", - "fr": "Français", - "Leds": "LEDs", - "EqualBrightness": "Même luminosité:", - "LedBrightness": "LED {led} luminosité ({brightness}):" - }, - "pininfo": { - "PinOverview": "Vue d'ensemble des connexions", - "Category": "Catégorie", - "Name": "Nom", - "ValueSelected": "Sélectionné", - "ValueActive": "Activé" - }, - "huawei": { - "DataAge": "Data Age: ", - "Seconds": " {val} seconds", - "Input": "Input", - "Output": "Output", - "Property": "Property", - "Value": "Value", - "Unit": "Unit", - "input_voltage": "Input voltage", - "input_current": "Input current", - "input_power": "Input power", - "input_temp": "Input temperature", - "efficiency": "Efficiency", - "output_voltage": "Output voltage", - "output_current": "Output current", - "max_output_current": "Maximum output current", - "output_power": "Output power", - "output_temp": "Output temperature", - "ShowSetLimit": "Show / Set Huawei Limit", - "LimitSettings": "Limit Settings", - "SetOffline": "Set limit, CAN bus not connected", - "SetOnline": "Set limit, CAN bus connected", - "LimitHint": "Hint: CAN bus not connected voltage limit is 48V-58.5V.", - "Close": "close", - "SetVoltageLimit": "Voltage limit:", - "SetCurrentLimit": "Current limit:", - "CurrentLimit": "Current limit:" - }, - "acchargeradmin": { - "ChargerSettings": "AC Charger Settings", - "Configuration": "AC Charger Configuration", - "EnableHuawei": "Enable Huawei R4850G2 on CAN Bus Interface", - "CanControllerFrequency": "CAN controller quarz frequency", - "EnableAutoPower": "Automatic power control", - "Limits": "Limits", - "VoltageLimit": "Charge Voltage limit", - "enableVoltageLimit": "Re-enable voltage limit", - "enableVoltageLimitHint": "Automatic power control is disabled if the output voltage is higher then this value and if the output power drops below the minimum output power limit (set below).\nAutomatic power control is re-enabled if the battery voltage drops below the value set in this field.", - "lowerPowerLimit": "Minimum output power", - "upperPowerLimit": "Maximum output power", - "Seconds": "@:base.Seconds" - }, - "battery": { - "battery": "Battery", - "DataAge": "Data Age: ", - "Seconds": " {val} seconds", - "status": "Status", - "Property": "Property", - "yes": "@:base.Yes", - "no": "@:base.No", - "Value": "Value", - "Unit": "Unit", - "SoC": "State of Charge", - "stateOfHealth": "State of Health", - "voltage": "Voltage", - "current": "Current", - "power": "Power", - "temperature": "Temperature", - "bmsTemp": "BMS temperature", - "chargeVoltage": "Requested charge voltage", - "chargeCurrentLimitation": "Charge current limit", - "dischargeCurrentLimitation": "Discharge current limit", - "chargeEnabled": "Charging possible", - "dischargeEnabled": "Discharging possible", - "chargeImmediately": "Immediate charging requested", - "cells": "Cells", - "batOneTemp": "Battery temperature 1", - "batTwoTemp": "Battery temperature 2", - "cellMinVoltage": "Minimum cell voltage", - "cellAvgVoltage": "Average cell voltage", - "cellMaxVoltage": "Maximum cell voltage", - "cellDiffVoltage": "Cell voltage difference", - "balancingActive": "Balancing active", - "issues": "Issues", - "noIssues": "No Issues", - "issueName": "Name", - "issueType": "Type", - "alarm": "Alarm", - "warning": "Warning", - "JkBmsIssueLowCapacity": "Low Capacity", - "JkBmsIssueBmsOvertemperature": "BMS overtemperature", - "JkBmsIssueChargingOvervoltage": "Overvoltage (sum of all cells)", - "JkBmsIssueDischargeUndervoltage": "Undervoltage (sum of all cells)", - "JkBmsIssueBatteryOvertemperature": "Battery overtemperature", - "JkBmsIssueChargingOvercurrent": "Overcurrent (Charging)", - "JkBmsIssueDischargeOvercurrent": "Overcurrent (Discharging)", - "JkBmsIssueCellVoltageDifference": "Cell voltage difference too high", - "JkBmsIssueBatteryBoxOvertemperature": "Battery (box?) overtemperature", - "JkBmsIssueBatteryUndertemperature": "Battery undertemperature", - "JkBmsIssueCellOvervoltage": "Overvoltage (single cell)", - "JkBmsIssueCellUndervoltage": "Undervoltage (single cell)", - "JkBmsIssueAProtect": "AProtect (meaning?)", - "JkBmsIssueBProtect": "BProtect (meaning?)", - "highCurrentDischarge": "High current (discharge)", - "overCurrentDischarge": "Overcurrent (discharge)", - "highCurrentCharge": "High current (charge)", - "overCurrentCharge": "Overcurrent (charge)", - "lowTemperature": "Low temperature", - "underTemperature": "Undertemperature", - "highTemperature": "High temperature", - "overTemperature": "Overtemperature", - "lowVoltage": "Low voltage", - "lowSOC": "Low state of charge", - "underVoltage": "Undervoltage", - "highVoltage": "High voltage", - "overVoltage": "Overvoltage", - "bmsInternal": "BMS internal", - "chargeCycles": "Charge cycles", - "chargedEnergy": "Charged energy", - "dischargedEnergy": "Discharged energy" - } -} +{ + "menu": { + "LiveView": "Direct", + "Settings": "Paramètres", + "NetworkSettings": "Réseau", + "NTPSettings": "Heure locale", + "MQTTSettings": "MQTT", + "InverterSettings": "Onduleurs", + "SecuritySettings": "Sécurité", + "DTUSettings": "DTU", + "DeviceManager": "Périphériques", + "VedirectSettings": "VE.Direct", + "PowerMeterSettings": "Power Meter", + "BatterySettings": "Battery", + "AcChargerSettings": "AC Charger", + "ConfigManagement": "Gestion de la configuration", + "FirmwareUpgrade": "Mise à jour du firmware", + "DeviceReboot": "Redémarrage de l'appareil", + "Info": "Informations", + "System": "Système", + "Network": "Réseau", + "NTP": "NTP", + "MQTT": "MQTT", + "Console": "Console", + "Vedirect": "VE.Direct", + "About": "A propos", + "Logout": "Déconnexion", + "Login": "Connexion" + }, + "base": { + "Yes": "Oui", + "No": "Non", + "VerboseLogging": "Journalisation Détaillée", + "Seconds": "Secondes", + "Loading": "Chargement...", + "Reload": "Reload", + "Cancel": "Annuler", + "Save": "Sauvegarder", + "Refreshing": "Refreshing", + "Pull": "Pull down to refresh", + "Release": "Release to refresh", + "Close": "Fermer" + }, + "localeswitcher": { + "Dark": "Sombre", + "Light": "Clair", + "Auto": "Auto" + }, + "apiresponse": { + "1001": "Paramètres enregistrés !", + "1002": "Aucune valeur trouvée !", + "1003": "Données trop importantes !", + "1004": "Échec de l'analyse des données !", + "1005": "Certaines valeurs sont manquantes !", + "1006": "Write failed!", + "2001": "Le numéro de série ne peut pas être nul !", + "2002": "L'intervalle de sondage doit être supérieur à zéro !", + "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!", + "2005": "Invalid country selection !", + "3001": "Rien n'a été supprimé !", + "3002": "Configuration réinitialisée. Redémarrage maintenant...", + "4001": "@:apiresponse.2001", + "4002": "Le nom doit comporter entre 1 et {max} caractères !", + "4003": "Seulement {max} onduleurs sont supportés !", + "4004": "Onduleur créé !", + "4005": "Identifiant spécifié invalide !", + "4006": "Réglage du montant maximal de canaux invalide !", + "4007": "Onduleur modifié !", + "4008": "Onduleur supprimé !", + "4009": "Inverter order saved!", + "5001": "@:apiresponse.2001", + "5002": "La limite doit être comprise entre 1 et {max} !", + "5003": "Type spécifié invalide !", + "5004": "Onduleur spécifié invalide !", + "6001": "Redémarrage déclenché !", + "6002": "Redémarrage annulé !", + "7001": "Le nom du serveur MQTT doit comporter entre 1 et {max} caractères !", + "7002": "Le nom d'utilisateur ne doit pas comporter plus de {max} caractères !", + "7003": "Le mot de passe ne doit pas comporter plus de {max} caractères !", + "7004": "Le sujet ne doit pas comporter plus de {max} caractères !", + "7005": "Le sujet ne doit pas contenir d'espace !", + "7006": "Le sujet doit se terminer par une barre oblique (/) !", + "7007": "Le port doit être un nombre entre 1 et 65535 !", + "7008": "Le certificat ne doit pas comporter plus de {max} caractères !", + "7009": "Le sujet LWT ne doit pas comporter plus de {max} caractères !", + "7010": "Le sujet LWT ne doit pas contenir de caractères d'espacement !", + "7011": "La valeur LWT en ligne ne doit pas dépasser {max} caractères !", + "7012": "La valeur LWT hors ligne ne doit pas dépasser {max} caractères !", + "7013": "L'intervalle de publication doit être un nombre compris entre {min} et {max} !", + "7014": "Le sujet Hass ne doit pas dépasser {max} caractères !", + "7015": "Le sujet Hass ne doit pas contenir d'espace !", + "7016": "LWT QOS ne doit pas être supérieur à {max}!", + "8001": "L'adresse IP n'est pas valide !", + "8002": "Le masque de réseau n'est pas valide !", + "8003": "La passerelle n'est pas valide !", + "8004": "L'adresse IP du serveur DNS primaire n'est pas valide !", + "8005": "L'adresse IP du serveur DNS secondaire n'est pas valide !", + "8006": "La valeur du délai d'attente du point d'accès administratif n'est pas valide !", + "9001": "Le serveur NTP doit avoir une longueur comprise entre 1 et {max} caractères !", + "9002": "Le fuseau horaire doit comporter entre 1 et {max} caractères !", + "9003": "La description du fuseau horaire doit comporter entre 1 et {max} caractères !", + "9004": "L'année doit être un nombre compris entre {min} et {max} !", + "9005": "Le mois doit être un nombre compris entre {min} et {max} !", + "9006": "Le jour doit être un nombre compris entre {min} et {max} !", + "9007": "Les heures doivent être un nombre compris entre {min} et {max} !", + "9008": "Les minutes doivent être un nombre compris entre {min} et {max} !", + "9009": "Les secondes doivent être un nombre compris entre {min} et {max} !", + "9010": "Heure mise à jour !", + "10001": "Le mot de passe doit comporter entre 8 et {max} caractères !", + "10002": "Authentification réussie !", + "11001": "@:apiresponse.2001", + "11002": "@:apiresponse:5004", + "12001": "Le profil doit comporter entre 1 et {max} caractères !" + }, + "home": { + "LiveData": "Données en direct", + "SerialNumber": "Numéro de série : ", + "CurrentLimit": "Limite de courant : ", + "DataAge": "Âge des données : ", + "Seconds": "{val} secondes", + "ShowSetInverterLimit": "Afficher / Régler la limite de l'onduleur", + "TurnOnOff": "Allumer / Eteindre l'onduleur", + "ShowInverterInfo": "Afficher les informations sur l'onduleur", + "ShowEventlog": "Afficher le journal des événements", + "UnreadMessages": "messages non lus", + "Loading": "@:base.Loading", + "EventLog": "Journal des événements", + "InverterInfo": "Informations sur l'onduleur", + "LimitSettings": "Paramètres de la limite", + "LastLimitSetStatus": "Statut de la dernière limite fixée", + "SetLimit": "Fixer la limite", + "Relative": "Relative (%)", + "Absolute": "Absolue (W)", + "LimitHint": "Astuce : Si vous définissez la limite en valeur absolue, l'affichage de la valeur actuelle ne sera mis à jour qu'après environ 4 minutes.", + "SetPersistent": "Fixer une limite persistante", + "SetNonPersistent": "Fixer une limite non persistante", + "PowerSettings": "Paramètres d'alimentation", + "LastPowerSetStatus": "État du dernier réglage de l'alimentation", + "TurnOn": "Allumer", + "TurnOff": "Eteindre", + "Restart": "Redémarrer", + "Failure": "Échec", + "Pending": "En attente", + "Ok": "OK", + "Unknown": "Inconnu", + "ShowGridProfile": "Show Grid Profile", + "GridProfile": "Grid Profile" + }, + "vedirecthome": { + "SerialNumber": "Serial Number: ", + "FirmwareNumber": "Firmware Number: ", + "DataAge": "Data Age: ", + "Seconds": "{val} seconds", + "DeviceInfo": "Device Info", + "Property": "Property", + "Value": "Value", + "Unit": "Unit", + "LoadOutputState": "Load output state", + "StateOfOperation": "State of operation", + "TrackerOperationMode": "Tracker operation mode", + "OffReason": "Off reason", + "ErrorCode": "Error code", + "DaySequenceNumber": "Day sequence number (0..364)", + "Battery": "Output (Battery)", + "output": { + "P": "Power (calculated)", + "V": "Voltage", + "I": "Current", + "E": "Efficiency (calculated)" + }, + "Panel": "Input (Solar Panels)", + "input": { + "PPV": "Power", + "VPV": "Voltage", + "IPV": "Current (calculated)", + "YieldToday": "Yield today", + "YieldYesterday": "Yield yesterday", + "YieldTotal": "Yield total (user resettable counter)", + "MaximumPowerToday": "Maximum power today", + "MaximumPowerYesterday": "Maximum power yesterday" + }, + "PowerLimiterState": "Power limiter state [off (charging), solar passthrough, on battery]" + }, + "eventlog": { + "Start": "Départ", + "Stop": "Arrêt", + "Id": "ID", + "Message": "Message" + }, + "devinfo": { + "NoInfo": "Aucune information disponible", + "NoInfoLong": "N'a pas reçu de données valides de l'onduleur jusqu'à présent. J'essaie toujours...", + "UnknownModel": "Modèle inconnu ! Veuillez signaler le \"Numéro d'article matériel\" et le modèle (par exemple, HM-350) comme un problème ici.", + "Serial": "Serial", + "ProdYear": "Production Year", + "ProdWeek": "Production Week", + "Model": "Modèle", + "DetectedMaxPower": "Puissance maximale détectée", + "BootloaderVersion": "Version du bootloader", + "FirmwareVersion": "Version du firmware", + "FirmwareBuildDate": "Date de création du firmware", + "HardwarePartNumber": "Numéro d'article matériel", + "HardwareVersion": "Version du matériel" + }, + "gridprofile": { + "NoInfo": "@:devinfo.NoInfo", + "NoInfoLong": "@:devinfo.NoInfoLong", + "Name": "Name", + "Version": "Version", + "Enabled": "@:wifistationinfo.Enabled", + "Disabled": "@:wifistationinfo.Disabled", + "GridprofileSupport": "Support the development", + "GridprofileSupportLong": "Please see here for further information." + }, + "systeminfo": { + "SystemInfo": "Informations sur le système", + "VersionError": "Erreur de récupération des informations de version", + "VersionNew": "Nouvelle version disponible ! Montrer les changements !", + "VersionOk": "À jour !" + }, + "firmwareinfo": { + "FirmwareInformation": "Informations sur le firmware", + "Hostname": "Nom d'hôte", + "SdkVersion": "Version du SDK", + "ConfigVersion": "Version de la configuration", + "FirmwareVersion": "Version du firmware / Hash Git", + "PioEnv": "PIO Environment", + "FirmwareVersionHint": "Cliquez ici pour afficher des informations sur votre version actuelle", + "FirmwareUpdate": "Mise à jour du firmware", + "FirmwareUpdateHint": "Cliquez ici pour voir les changements entre votre version et la dernière version", + "FrmwareUpdateAllow": "En activant le contrôle de mise à jour, une demande est envoyée à GitHub.com à chaque fois que la page est consultée afin de récupérer la dernière version disponible. Si tu n'es pas d'accord, laisse cette fonction désactivée.", + "ResetReason0": "Raison de la réinitialisation CPU 0", + "ResetReason1": "Raison de la réinitialisation CPU 1", + "ConfigSaveCount": "Nombre d'enregistrements de la configuration", + "Uptime": "Durée de fonctionnement", + "UptimeValue": "0 jour {time} | 1 jour {time} | {count} jours {time}" + }, + "hardwareinfo": { + "HardwareInformation": "Informations sur le matériel", + "ChipModel": "Modèle de puce", + "ChipRevision": "Révision de la puce", + "ChipCores": "Nombre de cœurs", + "CpuFrequency": "Fréquence du CPU", + "Mhz": "MHz" + }, + "memoryinfo": { + "MemoryInformation": "Informations sur la mémoire", + "Type": "Type", + "Usage": "Utilisation", + "Free": "Libre", + "Used": "Utilisée", + "Size": "Taille", + "Heap": "Heap", + "LittleFs": "LittleFs", + "Sketch": "Sketch" + }, + "heapdetails": { + "HeapDetails": "Heap Details", + "TotalFree": "Total free", + "LargestFreeBlock": "Biggest contiguous free block", + "MaxUsage": "Maximum usage since start", + "Fragmentation": "Level of fragmentation" + }, + "radioinfo": { + "RadioInformation": "Informations sur la radio", + "Status": "{module} Statut", + "ChipStatus": "{module} État de la puce", + "ChipType": "{module} Type de puce", + "Connected": "connectée", + "NotConnected": "non connectée", + "Configured": "configurée", + "NotConfigured": "non configurée", + "Unknown": "Inconnue" + }, + "networkinfo": { + "NetworkInformation": "Informations sur le réseau" + }, + "wifistationinfo": { + "WifiStationInfo": "Informations sur le WiFi (Station)", + "Status": "Statut", + "Enabled": "activé", + "Disabled": "désactivé", + "Ssid": "SSID", + "Bssid": "BSSID", + "Quality": "Qualité", + "Rssi": "RSSI" + }, + "wifiapinfo": { + "WifiApInfo": "Informations sur le WiFi (Point d'accès)", + "Status": "@:wifistationinfo.Status", + "Enabled": "@:wifistationinfo.Enabled", + "Disabled": "@:wifistationinfo.Disabled", + "Ssid": "@:wifistationinfo.Ssid", + "Stations": "# Stations" + }, + "interfacenetworkinfo": { + "NetworkInterface": "Interface réseau ({iface})", + "Hostname": "@:firmwareinfo.Hostname", + "IpAddress": "Adresse IP", + "Netmask": "Masque de réseau", + "DefaultGateway": "Passerelle par défaut", + "Dns": "DNS {num}", + "MacAddress": "Addresse MAC" + }, + "interfaceapinfo": { + "NetworkInterface": "Interface réseau (Point d'accès)", + "IpAddress": "@:interfacenetworkinfo.IpAddress", + "MacAddress": "@:interfacenetworkinfo.MacAddress" + }, + "ntpinfo": { + "NtpInformation": "Informations sur le NTP", + "ConfigurationSummary": "Résumé de la configuration", + "Server": "Serveur", + "Timezone": "Fuseau horaire", + "TimezoneDescription": "Description du fuseau horaire", + "CurrentTime": "Heure actuelle", + "Status": "Statut", + "Synced": "synchronisée", + "NotSynced": "pas synchronisée", + "LocalTime": "Heure locale", + "Sunrise": "Lever du soleil", + "Sunset": "Coucher du soleil", + "NotAvailable": "Not Available", + "Mode": "Mode", + "Day": "Jour", + "Night": "Nuit" + }, + "mqttinfo": { + "MqttInformation": "MQTT Information", + "ConfigurationSummary": "@:ntpinfo.ConfigurationSummary", + "Status": "@:ntpinfo.Status", + "Enabled": "Activé", + "Disabled": "Désactivé", + "Server": "@:ntpinfo.Server", + "Port": "Port", + "Username": "Nom d'utilisateur", + "BaseTopic": "Sujet de base", + "PublishInterval": "Intervalle de publication", + "Seconds": "{sec} secondes", + "CleanSession": "CleanSession Flag", + "Retain": "Conserver", + "Tls": "TLS", + "RootCertifcateInfo": "Informations sur le certificat de l'autorité de certification racine", + "TlsCertLogin": "Connexion avec un certificat TLS", + "ClientCertifcateInfo": "Informations sur le certificat du client", + "HassSummary": "Résumé de la configuration de la découverte automatique du MQTT de Home Assistant", + "Expire": "Expiration", + "IndividualPanels": "Panneaux individuels", + "RuntimeSummary": "Résumé du temps de fonctionnement", + "ConnectionStatus": "État de la connexion", + "Connected": "connecté", + "Disconnected": "déconnecté" + }, + "vedirectinfo": { + "VedirectInformation" : "VE.Direct Info", + "ConfigurationSummary": "@:ntpinfo.ConfigurationSummary", + "Status": "@:ntpinfo.Status", + "Enabled": "@:mqttinfo.Enabled", + "Disabled": "@:mqttinfo.Disabled", + "VerboseLogging": "@:base.VerboseLogging", + "UpdatesOnly": "@:vedirectadmin.UpdatesOnly", + "UpdatesEnabled": "@:mqttinfo.Enabled", + "UpdatesDisabled": "@:mqttinfo.Disabled" + }, + "console": { + "Console": "Console", + "VirtualDebugConsole": "Console de débogage", + "EnableAutoScroll": "Activer le défilement automatique", + "ClearConsole": "Vider la console", + "CopyToClipboard": "Copier dans le presse-papiers" + }, + "inverterchannelinfo": { + "String": "Ligne {num}", + "Phase": "Phase {num}", + "General": "General" + }, + "invertertotalinfo": { + "InverterTotalYieldTotal": "Onduleurs rendement total", + "InverterTotalYieldDay": "Onduleurs rendement du jour", + "InverterTotalPower": "Onduleurs puissance de l'installation", + "MpptTotalYieldTotal": "MPPT rendement total", + "MpptTotalYieldDay": "MPPT rendement du jour", + "MpptTotalPower": "MPPT puissance de l'installation", + "BatterySoc": "State of charge", + "HomePower": "Grid Power", + "HuaweiPower": "Huawei AC Power" + }, + "inverterchannelproperty": { + "Power": "Puissance", + "Voltage": "Tension", + "Current": "Courant", + "Power DC": "Puissance continue", + "YieldDay": "Rendement du jour", + "YieldTotal": "Rendement total", + "Frequency": "Fréquence", + "Temperature": "Température", + "PowerFactor": "Facteur de puissance", + "ReactivePower": "Puissance réactive", + "Efficiency": "Efficacité", + "Irradiation": "Irradiation" + }, + "maintenancereboot": { + "DeviceReboot": "Redémarrage de l'appareil", + "PerformReboot": "Effectuer un redémarrage", + "Reboot": "Redémarrer !", + "Cancel": "@:base.Cancel", + "RebootOpenDTU": "Redémarrer OpenDTU", + "RebootQuestion": "Voulez-vous vraiment redémarrer l'appareil ?", + "RebootHint": "Astuce : Normalement, il n'est pas nécessaire de procéder à un redémarrage manuel. OpenDTU effectue automatiquement tout redémarrage nécessaire (par exemple, après une mise à jour du firmware). Les paramètres sont également adoptés sans redémarrage. Si vous devez redémarrer en raison d'une erreur, veuillez envisager de la signaler à l'adresse suivante Github." + }, + "dtuadmin": { + "DtuSettings": "Paramètres du DTU", + "DtuConfiguration": "Configuration du DTU", + "Serial": "Numéro de série", + "SerialHint": "L'onduleur et le DTU ont tous deux un numéro de série. Le numéro de série du DTU est généré de manière aléatoire lors du premier démarrage et ne doit normalement pas être modifié.", + "PollInterval": "Intervalle de sondage", + "VerboseLogging": "@:base.VerboseLogging", + "Seconds": "Secondes", + "NrfPaLevel": "NRF24 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.", + "CmtPaLevelHint": "Used for HMS/HMT-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.", + "CmtCountry": "CMT2300A 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:", + "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 allowed range in your selected region/country. Make sure that this selection does not violate any local regulations.", + "MHz": "{mhz} MHz", + "dBm": "{dbm} dBm", + "Min": "Minimum ({db} dBm)", + "Low": "Bas ({db} dBm)", + "High": "Haut ({db} dBm)", + "Max": "Maximum ({db} dBm)" + }, + "securityadmin": { + "SecuritySettings": "Paramètres de sécurité", + "AdminPassword": "Mot de passe administrateur", + "Password": "Mot de passe", + "RepeatPassword": "Répéter le mot de passe", + "PasswordHint": "Astuce : Le mot de passe administrateur est utilisé pour accéder à cette interface web (utilisateur 'admin'), mais aussi pour se connecter à l'appareil en mode AP. Il doit comporter de 8 à 64 caractères.", + "Permissions": "Autorisations", + "ReadOnly": "Autoriser l'accès en lecture seule à l'interface web sans mot de passe" + }, + "ntpadmin": { + "NtpSettings": "Paramètres NTP", + "NtpConfiguration": "Configuration du protocole NTP", + "TimeServer": "Serveur horaire", + "TimeServerHint": "La valeur par défaut convient tant que OpenDTU a un accès direct à Internet.", + "Timezone": "Fuseau horaire", + "TimezoneConfig": "Configuration du fuseau horaire", + "LocationConfiguration": "Géolocalisation", + "Longitude": "Longitude", + "Latitude": "Latitude", + "SunSetType": "Sunset type", + "SunSetTypeHint": "Affects the day/night calculation. It can take up to one minute until the new type will be applied.", + "OFFICIAL": "Standard dawn (90.8°)", + "NAUTICAL": "Nautical dawn (102°)", + "CIVIL": "Civil dawn (96°)", + "ASTONOMICAL": "Astronomical dawn (108°)", + "ManualTimeSynchronization": "Synchronisation manuelle de l'heure", + "CurrentOpenDtuTime": "Heure actuelle de l'OpenDTU", + "CurrentLocalTime": "Heure locale actuelle", + "SynchronizeTime": "Synchroniser l'heure", + "SynchronizeTimeHint": "Astuce : Vous pouvez utiliser la synchronisation horaire manuelle pour définir l'heure actuelle d'OpenDTU si aucun serveur NTP n'est disponible. Mais attention, en cas de mise sous tension, l'heure est perdue. Notez également que la précision de l'heure sera faussée, car elle ne peut pas être resynchronisée régulièrement et le microcontrôleur ESP32 ne dispose pas d'une horloge temps réel." + }, + "networkadmin": { + "NetworkSettings": "Paramètres réseau", + "WifiConfiguration": "Configuration du réseau WiFi", + "WifiSsid": "SSID", + "WifiPassword": "Mot de passe", + "Hostname": "Nom d'hôte", + "HostnameHint": "Astuce : Le texte %06X sera remplacé par les 6 derniers chiffres de l'ESP ChipID au format hexadécimal.", + "EnableDhcp": "Activer le DHCP", + "StaticIpConfiguration": "Configuration de l'IP statique", + "IpAddress": "Adresse IP", + "Netmask": "Masque de réseau", + "DefaultGateway": "Passerelle par défaut", + "Dns": "Serveur DNS {num}", + "AdminAp": "Configuration du réseau WiFi (Point d'accès)", + "ApTimeout": "Délai d'attente du point d'accès", + "ApTimeoutHint": "Durée pendant laquelle le point d'accès reste ouvert. Une valeur de 0 signifie infini.", + "Minutes": "minutes", + "EnableMdns": "Activer mDNS", + "MdnsSettings": "mDNS Settings" + }, + "mqttadmin": { + "MqttSettings": "Paramètres MQTT", + "MqttConfiguration": "Configuration du système MQTT", + "EnableMqtt": "Activer le MQTT", + "VerboseLogging": "@:base.VerboseLogging", + "EnableHass": "Activer la découverte automatique du MQTT de Home Assistant", + "MqttBrokerParameter": "Paramètre du Broker MQTT", + "Hostname": "Nom d'hôte", + "HostnameHint": "Nom d'hôte ou adresse IP", + "Port": "Port", + "Username": "Nom d'utilisateur", + "UsernameHint": "Nom d'utilisateur, laisser vide pour une connexion anonyme", + "Password": "Mot de passe:", + "PasswordHint": "Mot de passe, laissez vide pour une connexion anonyme", + "BaseTopic": "Sujet de base", + "BaseTopicHint": "Sujet de base, qui sera ajouté en préambule à tous les sujets publiés (par exemple, inverter/).", + "PublishInterval": "Intervalle de publication", + "Seconds": "secondes", + "CleanSession": "Enable CleanSession flag", + "EnableRetain": "Activation du maintien", + "EnableTls": "Activer le TLS", + "RootCa": "Certificat CA-Root (par défaut Letsencrypt)", + "TlsCertLoginEnable": "Activer la connexion par certificat TLS", + "ClientCert": "Certificat client TLS:", + "ClientKey": "Clé client TLS:", + "LwtParameters": "Paramètres LWT", + "LwtTopic": "Sujet LWT", + "LwtTopicHint": "Sujet LWT, sera ajouté comme sujet de base", + "LwtOnline": "Message en ligne de LWT", + "LwtOnlineHint": "Message qui sera publié sur le sujet LWT lorsqu'il sera en ligne", + "LwtOffline": "Message hors ligne de LWT", + "LwtOfflineHint": "Message qui sera publié sur le sujet LWT lorsqu'il sera hors ligne", + "LwtQos": "QoS (Quality of Service):", + "QOS0": "0 (Au maximum une fois)", + "QOS1": "1 (Au moins une fois)", + "QOS2": "2 (Exactement une fois)", + "HassParameters": "Paramètres de découverte automatique MQTT de Home Assistant", + "HassPrefixTopic": "Préfixe du sujet", + "HassPrefixTopicHint": "Le préfixe de découverte du sujet", + "HassRetain": "Activer du maintien", + "HassExpire": "Activer l'expiration", + "HassIndividual": "Panneaux individuels" + }, + "vedirectadmin": { + "VedirectSettings": "VE.Direct Settings", + "VedirectConfiguration": "VE.Direct Configuration", + "EnableVedirect": "Enable VE.Direct", + "VedirectParameter": "VE.Direct Parameter", + "VerboseLogging": "@:base.VerboseLogging", + "UpdatesOnly": "Publish values to MQTT only when they change" + }, + "batteryadmin": { + "BatterySettings": "Battery Settings", + "BatteryConfiguration": "General Interface Settings", + "EnableBattery": "Enable Interface", + "VerboseLogging": "@:base.VerboseLogging", + "Provider": "Data Provider", + "ProviderPylontechCan": "Pylontech using CAN bus", + "ProviderJkBmsSerial": "Jikong (JK) BMS using serial connection", + "ProviderMqtt": "State of Charge (SoC) value from MQTT broker", + "ProviderVictron": "Victron SmartShunt using VE.Direct interface", + "ProviderDalyBmsSerial": "Daly BMS using serial connection", + "MqttConfiguration": "MQTT Settings", + "MqttTopic": "SoC value topic", + "JkBmsConfiguration": "JK BMS Settings", + "JkBmsInterface": "Interface Type", + "JkBmsInterfaceUart": "TTL-UART on MCU", + "JkBmsInterfaceTransceiver": "RS-485 Transceiver on MCU", + "PollingInterval": "Polling Interval", + "Seconds": "@:base.Seconds" + }, + "inverteradmin": { + "InverterSettings": "Paramètres des onduleurs", + "AddInverter": "Ajouter un nouvel onduleur", + "Serial": "Numéro de série", + "Name": "Nom", + "Add": "Ajouter", + "AddHint": " Astuce : Vous pouvez définir des paramètres supplémentaires après avoir créé l'onduleur. Utilisez l'icône du stylo dans la liste des onduleurs.", + "InverterList": "Liste des onduleurs", + "Status": "État", + "Send": "Envoyer", + "Receive": "Recevoir", + "StatusHint": "Astuce : L'onduleur est alimenté par son entrée courant continu. S'il n'y a pas de soleil, l'onduleur est éteint, mais les requêtes peuvent toujours être envoyées.", + "Type": "Type", + "Action": "Action", + "SaveOrder": "Save order", + "DeleteInverter": "Supprimer l'onduleur", + "EditInverter": "Modifier l'onduleur", + "General": "Général", + "String": "Ligne", + "Advanced": "Advanced", + "InverterSerial": "Numéro de série de l'onduleur", + "InverterName": "Nom de l'onduleur :", + "InverterNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour votre onduleur.", + "InverterStatus": "Recevoir / Envoyer", + "PollEnable": "Interroger les données de l'onduleur", + "PollEnableNight": "Interroger les données de l'onduleur la nuit", + "CommandEnable": "Envoyer des commandes", + "CommandEnableNight": "Envoyer des commandes la nuit", + "StringName": "Nom de la ligne {num}:", + "StringNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour le port respectif de votre onduleur.", + "StringMaxPower": "Puissance maximale de la ligne {num}:", + "StringMaxPowerHint": "Entrez la puissance maximale des panneaux solaires connectés.", + "StringYtOffset": "Décalage du rendement total de la ligne {num} :", + "StringYtOffsetHint": "Ce décalage est appliqué à la valeur de rendement total lue sur le variateur. Il peut être utilisé pour mettre le rendement total du variateur à zéro si un variateur usagé est utilisé.", + "InverterHint": "*) Entrez le Wp du canal pour calculer l'irradiation.", + "ReachableThreshold": "Reachable Threshold:", + "ReachableThresholdHint": "Defines how many requests are allowed to fail until the inverter is treated is not reachable.", + "ZeroRuntime": "Zero runtime data", + "ZeroRuntimeHint": "Zero runtime data (no yield data) if inverter becomes unreachable.", + "ZeroDay": "Zero daily yield at midnight", + "ZeroDayHint": "This only works if the inverter is unreachable. If data is read from the inverter, it's values will be used. (Reset only occours on power cycle)", + "Cancel": "@:base.Cancel", + "Save": "@:base.Save", + "DeleteMsg": "Êtes-vous sûr de vouloir supprimer l'onduleur \"{name}\" avec le numéro de série \"{serial}\" ?", + "Delete": "Supprimer", + "YieldDayCorrection": "Yield Day Correction", + "YieldDayCorrectionHint": "Sum up daily yield even if the inverter is restarted. Value will be reset at midnight" + }, + "configadmin": { + "ConfigManagement": "Gestion de la configuration", + "BackupHeader": "Sauvegarder le fichier de configuration", + "BackupConfig": "Fichier de configuration", + "Backup": "Sauvegarder", + "Restore": "Restaurer", + "NoFileSelected": "Aucun fichier sélectionné", + "RestoreHeader": "Restaurer le fichier de configuration", + "Back": "Retour", + "UploadSuccess": "Succès du téléversement", + "RestoreHint": "Note : Cette opération remplace le fichier de configuration par la configuration restaurée et redémarre OpenDTU pour appliquer tous les paramètres.", + "ResetHeader": "Effectuer une réinitialisation d'usine", + "FactoryResetButton": "Restaurer les paramètres d'usine", + "ResetHint": "Note : Cliquez sur \"Restaurer les paramètres d'usine\" pour restaurer et initialiser les paramètres d'usine par défaut et redémarrer.", + "FactoryReset": "Remise à zéro", + "ResetMsg": "Êtes-vous sûr de vouloir supprimer la configuration actuelle et réinitialiser tous les paramètres à leurs valeurs par défaut ?", + "ResetConfirm": "Remise à zéro !", + "Cancel": "@:base.Cancel" + }, + "powerlimiteradmin": { + "PowerLimiterSettings": "Power Limiter Settings", + "PowerLimiterConfiguration": "Power Limiter Configuration", + "General": "General", + "Enable": "Enable", + "VerboseLogging": "@:base.VerboseLogging", + "EnableSolarPassthrough": "Enable Solar-Passthrough", + "SolarPassthroughLosses": "(Full) Solar Passthrough Losses:", + "SolarPassthroughLossesInfo": "Hint: Line losses are to be expected when transferring energy from the solar charge controller to the inverter. These losses can be taken into account to prevent the battery from gradually discharging in (full) solar passthrough mode. The power limit to be set on the inverter is additionally reduced by this factor after taking its efficiency into account.", + "BatteryDrainStrategy": "Battery drain strategy", + "BatteryDrainWhenFull": "Empty when full", + "BatteryDrainAtNight": "Empty at night", + "SolarpassthroughInfo": "When the sun is shining, this setting enables the sychronization of the inverter limit with the current solar power of the Victron MPPT charger. This optimizes battery degradation and loses.", + "InverterId": "Inverter ID", + "InverterIdHint": "Select proper inverter ID where battery is connected to.", + "InverterChannelId": "Channel ID", + "InverterChannelIdHint": "Select proper channel where battery is connected to.", + "TargetPowerConsumption": "Target power consumption from grid", + "TargetPowerConsumptionHint": "Set the grid power consumption the limiter tries to achieve.", + "TargetPowerConsumptionHysteresis": "Hysteresis for calculated power limit", + "TargetPowerConsumptionHysteresisHint": "Only send a newly calculated power limit to the inverter if the absolute difference to the last sent power limit matches or exceeds this amount.", + "LowerPowerLimit": "Lower power limit", + "UpperPowerLimit": "Upper power limit", + "PowerMeters": "Power meter", + "MqttTopicPowerMeter1": "MQTT topic - Power meter #1", + "MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (optional)", + "MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)", + "BatterySocStartThreshold": "Battery SoC - Start threshold", + "BatterySocStopThreshold": "Battery SoC - Stop threshold", + "BatterySocSolarPassthroughStartThreshold": "Battery SoC - Start threshold for full solar passthrough", + "BatterySocSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) if battery SOC is over this limit. Use this if you like to supply excess power to the grid when battery is full", + "VoltageStartThreshold": "DC Voltage - Start threshold", + "VoltageStopThreshold": "DC Voltage - Stop threshold", + "VoltageSolarPassthroughStartThreshold": "DC Voltage - Start threshold for full solar passthrough", + "VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough", + "VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.", + "VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor", + "BatterySocInfo": "Hint: The battery SoC (State of Charge) values can only be used if the battery communication interface is enabled. If the battery has not reported any SoC updates in the last minute, the voltage thresholds will be used as fallback.", + "InverterIsBehindPowerMeter": "Inverter is behind Power meter", + "Battery": "DC / Battery", + "VoltageLoadCorrectionInfo": "Hint: When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor)." + }, + "login": { + "Login": "Connexion", + "SystemLogin": "Connexion au système", + "Username": "Nom d'utilisateur", + "UsernameRequired": "Le nom d'utilisateur est requis", + "Password": "Mot de passe", + "PasswordRequired": "Le mot de passe est requis", + "LoginButton": "Connexion" + }, + "firmwareupgrade": { + "FirmwareUpgrade": "Mise à jour du firmware", + "Loading": "@:base.Loading", + "OtaError": "Erreur OTA", + "Back": "Retour", + "Retry": "Réessayer", + "OtaStatus": "Statut OTA", + "OtaSuccess": "Le téléchargement du firmware a réussi. L'appareil a été redémarré automatiquement. Lorsque l'appareil est à nouveau accessible, l'interface est automatiquement rechargée.", + "FirmwareUpload": "Téléversement du firmware", + "UploadProgress": "Progression du téléversement" + }, + "about": { + "AboutOpendtu": "À propos d'OpenDTU", + "ProjectOrigin": "Origine du projet", + "ProjectOriginBody1": "Ce projet a été démarré suite à cette discussion (Mikrocontroller.net).", + "ProjectOriginBody2": "Le protocole Hoymiles a été décrypté grâce aux efforts volontaires de nombreux participants. OpenDTU, entre autres, a été développé sur la base de ce travail. Le projet est sous licence Open Source (GNU General Public License version 2).", + "ProjectOriginBody3": "Le logiciel a été développé au mieux de nos connaissances et de nos convictions. Néanmoins, aucune responsabilité ne peut être acceptée en cas de dysfonctionnement ou de perte de garantie de l'onduleur.", + "ProjectOriginBody4": "OpenDTU est disponible gratuitement. Si vous avez payé pour le logiciel, vous avez probablement été arnaqué.", + "NewsUpdates": "Actualités et mises à jour", + "NewsUpdatesBody": "Les nouvelles mises à jour peuvent être trouvées sur Github.", + "ErrorReporting": "Rapport d'erreurs", + "ErrorReportingBody": "Veuillez signaler les problèmes en utilisant la fonction fournie par Github.", + "Discussion": "Discussion", + "DiscussionBody": "Discutez avec nous sur Discord ou sur Github." + }, + "hints": { + "RadioProblem": "Impossible de se connecter à un module radio configuré.. Veuillez vérifier le câblage.", + "TimeSync": "L'horloge n'a pas encore été synchronisée. Sans une horloge correctement réglée, aucune demande n'est adressée à l'onduleur. Ceci est normal peu de temps après le démarrage. Cependant, après un temps de fonctionnement plus long (>1 minute), cela indique que le serveur NTP n'est pas accessible.", + "TimeSyncLink": "Veuillez vérifier vos paramètres horaires.", + "DefaultPassword": "Vous utilisez le mot de passe par défaut pour l'interface Web et le point d'accès d'urgence. Ceci est potentiellement non sécurisé.", + "DefaultPasswordLink": "Merci de changer le mot de passe." + }, + "deviceadmin": { + "DeviceManager": "Gestionnaire de périphériques", + "ParseError": "Erreur d'analyse dans 'pin_mapping.json': {error}", + "PinAssignment": "Paramètres de connexion", + "SelectedProfile": "Profil sélectionné", + "DefaultProfile": "(Réglages par défaut)", + "ProfileHint": "Votre appareil peut cesser de répondre si vous sélectionnez un profil incompatible. Dans ce cas, vous devez effectuer une suppression via l'interface série.", + "Display": "Affichage", + "PowerSafe": "Activer l'économiseur d'énergie", + "PowerSafeHint": "Eteindre l'écran si aucun onduleur n'est en production.", + "Screensaver": "Activer l'écran de veille", + "ScreensaverHint": "Déplacez un peu l'écran à chaque mise à jour pour éviter le phénomène de brûlure. (Utile surtout pour les écrans OLED)", + "DiagramMode": "Diagram mode:", + "off": "Off", + "small": "Small", + "fullscreen": "Fullscreen", + "DiagramDuration": "Diagram duration:", + "DiagramDurationHint": "The time period which is shown in the diagram.", + "Seconds": "Seconds", + "Contrast": "Contraste ({contrast}):", + "Rotation": "Rotation:", + "rot0": "Pas de rotation", + "rot90": "Rotation de 90 degrés", + "rot180": "Rotation de 180 degrés", + "rot270": "Rotation de 270 degrés", + "DisplayLanguage": "Langue d'affichage", + "en": "Anglais", + "de": "Allemand", + "fr": "Français", + "Leds": "LEDs", + "EqualBrightness": "Même luminosité:", + "LedBrightness": "LED {led} luminosité ({brightness}):" + }, + "pininfo": { + "PinOverview": "Vue d'ensemble des connexions", + "Category": "Catégorie", + "Name": "Nom", + "ValueSelected": "Sélectionné", + "ValueActive": "Activé" + }, + "huawei": { + "DataAge": "Data Age: ", + "Seconds": " {val} seconds", + "Input": "Input", + "Output": "Output", + "Property": "Property", + "Value": "Value", + "Unit": "Unit", + "input_voltage": "Input voltage", + "input_current": "Input current", + "input_power": "Input power", + "input_temp": "Input temperature", + "efficiency": "Efficiency", + "output_voltage": "Output voltage", + "output_current": "Output current", + "max_output_current": "Maximum output current", + "output_power": "Output power", + "output_temp": "Output temperature", + "ShowSetLimit": "Show / Set Huawei Limit", + "LimitSettings": "Limit Settings", + "SetOffline": "Set limit, CAN bus not connected", + "SetOnline": "Set limit, CAN bus connected", + "LimitHint": "Hint: CAN bus not connected voltage limit is 48V-58.5V.", + "Close": "close", + "SetVoltageLimit": "Voltage limit:", + "SetCurrentLimit": "Current limit:", + "CurrentLimit": "Current limit:" + }, + "acchargeradmin": { + "ChargerSettings": "AC Charger Settings", + "Configuration": "AC Charger Configuration", + "EnableHuawei": "Enable Huawei R4850G2 on CAN Bus Interface", + "CanControllerFrequency": "CAN controller quarz frequency", + "EnableAutoPower": "Automatic power control", + "Limits": "Limits", + "VoltageLimit": "Charge Voltage limit", + "enableVoltageLimit": "Re-enable voltage limit", + "enableVoltageLimitHint": "Automatic power control is disabled if the output voltage is higher then this value and if the output power drops below the minimum output power limit (set below).\nAutomatic power control is re-enabled if the battery voltage drops below the value set in this field.", + "lowerPowerLimit": "Minimum output power", + "upperPowerLimit": "Maximum output power", + "Seconds": "@:base.Seconds" + }, + "battery": { + "battery": "Battery", + "DataAge": "Data Age: ", + "Seconds": " {val} seconds", + "status": "Status", + "Property": "Property", + "yes": "@:base.Yes", + "no": "@:base.No", + "Value": "Value", + "Unit": "Unit", + "SoC": "State of Charge", + "stateOfHealth": "State of Health", + "voltage": "Voltage", + "current": "Current", + "power": "Power", + "temperature": "Temperature", + "bmsTemp": "BMS temperature", + "chargeVoltage": "Requested charge voltage", + "chargeCurrentLimitation": "Charge current limit", + "dischargeCurrentLimitation": "Discharge current limit", + "chargeEnabled": "Charging possible", + "dischargeEnabled": "Discharging possible", + "chargeImmediately": "Immediate charging requested", + "cells": "Cells", + "batOneTemp": "Battery temperature 1", + "batTwoTemp": "Battery temperature 2", + "cellMinVoltage": "Minimum cell voltage", + "cellAvgVoltage": "Average cell voltage", + "cellMaxVoltage": "Maximum cell voltage", + "cellDiffVoltage": "Cell voltage difference", + "balancingActive": "Balancing active", + "issues": "Issues", + "noIssues": "No Issues", + "issueName": "Name", + "issueType": "Type", + "alarm": "Alarm", + "warning": "Warning", + "JkBmsIssueLowCapacity": "Low Capacity", + "JkBmsIssueBmsOvertemperature": "BMS overtemperature", + "JkBmsIssueChargingOvervoltage": "Overvoltage (sum of all cells)", + "JkBmsIssueDischargeUndervoltage": "Undervoltage (sum of all cells)", + "JkBmsIssueBatteryOvertemperature": "Battery overtemperature", + "JkBmsIssueChargingOvercurrent": "Overcurrent (Charging)", + "JkBmsIssueDischargeOvercurrent": "Overcurrent (Discharging)", + "JkBmsIssueCellVoltageDifference": "Cell voltage difference too high", + "JkBmsIssueBatteryBoxOvertemperature": "Battery (box?) overtemperature", + "JkBmsIssueBatteryUndertemperature": "Battery undertemperature", + "JkBmsIssueCellOvervoltage": "Overvoltage (single cell)", + "JkBmsIssueCellUndervoltage": "Undervoltage (single cell)", + "JkBmsIssueAProtect": "AProtect (meaning?)", + "JkBmsIssueBProtect": "BProtect (meaning?)", + "highCurrentDischarge": "High current (discharge)", + "overCurrentDischarge": "Overcurrent (discharge)", + "highCurrentCharge": "High current (charge)", + "overCurrentCharge": "Overcurrent (charge)", + "lowTemperature": "Low temperature", + "underTemperature": "Undertemperature", + "highTemperature": "High temperature", + "overTemperature": "Overtemperature", + "lowVoltage": "Low voltage", + "lowSOC": "Low state of charge", + "underVoltage": "Undervoltage", + "highVoltage": "High voltage", + "overVoltage": "Overvoltage", + "bmsInternal": "BMS internal", + "chargeCycles": "Charge cycles", + "chargedEnergy": "Charged energy", + "dischargedEnergy": "Discharged energy", + "DalyStationary": "Standby", + "DalyOffline": "Offline", + "DalyCharge": "Charge", + "DalyDischarge": "Discharge" + } +} diff --git a/webapp/src/views/BatteryAdminView.vue b/webapp/src/views/BatteryAdminView.vue index 88b67df4b..eb3da4435 100644 --- a/webapp/src/views/BatteryAdminView.vue +++ b/webapp/src/views/BatteryAdminView.vue @@ -98,6 +98,7 @@ export default defineComponent({ { key: 1, value: 'JkBmsSerial' }, { key: 2, value: 'Mqtt' }, { key: 3, value: 'Victron' }, + { key: 4, value: 'DalyBmsSerial' }, ], jkBmsInterfaceTypeList: [ { key: 0, value: 'Uart' }, diff --git a/webapp/vite.config.ts b/webapp/vite.config.ts index 34af8312b..f664d032b 100644 --- a/webapp/vite.config.ts +++ b/webapp/vite.config.ts @@ -14,7 +14,7 @@ let proxy_target; try { proxy_target = require('./vite.user.ts').proxy_target; } catch (error) { - proxy_target = '192.168.20.110'; + proxy_target = '192.168.188.125'; } // https://vitejs.dev/config/ diff --git a/webapp_dist/js/app.js.gz b/webapp_dist/js/app.js.gz index 8d177806b..300eb8817 100644 Binary files a/webapp_dist/js/app.js.gz and b/webapp_dist/js/app.js.gz differ