Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
#define POWERMETER_MQTT_MAX_VALUES 3
#define POWERMETER_HTTP_JSON_MAX_VALUES 3

#define ZENDURE_MAX_SERIAL_STRLEN 8
#define ZENDURE_MAX_SERIAL_STRLEN 8
#define ZENDURE_MAX_SERVER_STRLEN 256
#define ZENDURE_MAX_CLIENTID_STRLEN 23
#define ZENDURE_MAX_SECRET_STRLEN 32
#define ZENDURE_MAX_APPKEY_STRLEN ZENDURE_MAX_SERIAL_STRLEN

struct CHANNEL_CONFIG_T {
uint16_t MaxChannelPower;
Expand Down Expand Up @@ -153,6 +157,8 @@ struct POWERLIMITER_INVERTER_CONFIG_T {

enum InverterPowerSource { Battery = 0, Solar = 1, SmartBuffer = 2 };
InverterPowerSource PowerSource;

bool HasPriority;
};
using PowerLimiterInverterConfig = struct POWERLIMITER_INVERTER_CONFIG_T;

Expand Down Expand Up @@ -203,7 +209,16 @@ struct BATTERY_ZENDURE_CONFIG_T {
bool BuzzerEnable;
enum ControlMode : uint8_t { ControlModeFull = 0, ControlModeOnce = 1, ControlModeReadOnly = 2 };
ControlMode ControlMode;
uint8_t ChargeThroughResetLevel;
uint16_t ChargeThroughKeepMinutes;
enum ConnectionType { LocalMqtt = 0, ZendureMqtt = 1, Bluetooth = 2 };
ConnectionType ConnectionType;
char Server[ZENDURE_MAX_SERVER_STRLEN + 1];
uint16_t Port;
char ClientId[ZENDURE_MAX_CLIENTID_STRLEN + 1];
char AppKey[ZENDURE_MAX_APPKEY_STRLEN + 1];
char Secret[ZENDURE_MAX_SECRET_STRLEN + 1];
bool BatteryProtectionEnable;
float MinSoCHysteresis;
};
using BatteryZendureConfig = struct BATTERY_ZENDURE_CONFIG_T;

Expand Down
2 changes: 2 additions & 0 deletions include/PowerLimiterInverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class PowerLimiterInverter {
bool isSolarPowered() const { return _config.PowerSource == PowerLimiterInverterConfig::InverterPowerSource::Solar; }
bool isSmartBufferPowered() const { return _config.PowerSource == PowerLimiterInverterConfig::InverterPowerSource::SmartBuffer; }

bool hasPriority() const { return _config.HasPriority; }

void debug() const;

enum class Eligibility : unsigned {
Expand Down
16 changes: 14 additions & 2 deletions include/battery/zendure/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ namespace Batteries::Zendure {
#define ZENDURE_HYPER2000_A "ja72U0ha"
#define ZENDURE_HYPER2000_B "gDa3tb"

#define ZENDURE_HUB1200_NAME "SolarFlow HUB 1200"
#define ZENDURE_HUB2000_NAME "SolarFlow HUB 2000"
#define ZENDURE_AIO2400_NAME "AIO 2400"
#define ZENDURE_ACE1500_NAME "SolarFlow Ace 1500"
#define ZENDURE_HYPER2000_NAME "SolarFlow Hyper 2000"

#define ZENDURE_MAX_PACKS 4U
#define ZENDURE_REMAINING_TIME_OVERFLOW 59940U

#define ZENDURE_SECONDS_SUNPOSITION 60U
#define ZENDURE_SECONDS_TIMESYNC 3600U
#define ZENDURE_SECONDS_OUTPUT_CALCULATION 30U

#define ZENDURE_LOG_ROOT "log"
#define ZENDURE_LOG_SERIAL "sn"
Expand Down Expand Up @@ -127,8 +134,11 @@ namespace Batteries::Zendure {


#define ZENDURE_REPORT_PROPERTIES "properties"
#define ZENDURE_REPORT_PACK_NUM "packNum"
#define ZENDURE_REPORT_MIN_SOC "minSoc"
#define ZENDURE_REPORT_MAX_SOC "socSet"
#define ZENDURE_REPORT_SERIAL "sn"
#define ZENDURE_REPORT_SOC "electricLevel"
#define ZENDURE_REPORT_INPUT_LIMIT "inputLimit"
#define ZENDURE_REPORT_OUTPUT_LIMIT "outputLimit"
#define ZENDURE_REPORT_INVERSE_MAX_POWER "inverseMaxPower"
Expand Down Expand Up @@ -180,7 +190,7 @@ namespace Batteries::Zendure {
#define ZENDURE_REPORT_LOCAL_STATE "localState" // always 0

#define ZENDURE_REPORT_PACK_DATA "packData"
#define ZENDURE_REPORT_PACK_SERIAL "sn"
#define ZENDURE_REPORT_PACK_SERIAL ZENDURE_REPORT_SERIAL
#define ZENDURE_REPORT_PACK_STATE "state"
#define ZENDURE_REPORT_PACK_POWER "power"
#define ZENDURE_REPORT_PACK_SOC "socLevel"
Expand All @@ -196,6 +206,8 @@ namespace Batteries::Zendure {
#define ZENDURE_PERSISTENT_SETTINGS_LAST_FULL "lastFullEpoch"
#define ZENDURE_PERSISTENT_SETTINGS_LAST_EMPTY "lastEmptyEpoch"
#define ZENDURE_PERSISTENT_SETTINGS_CHARGE_THROUGH "chargeThrough"
#define ZENDURE_PERSISTENT_SETTINGS {ZENDURE_PERSISTENT_SETTINGS_LAST_FULL, ZENDURE_PERSISTENT_SETTINGS_LAST_EMPTY, ZENDURE_PERSISTENT_SETTINGS_CHARGE_THROUGH}
#define ZENDURE_PERSISTENT_SETTINGS_CONTROL_STATE "controlState"
#define ZENDURE_PERSISTENT_SETTINGS_KEEP_EPOCH "keepUntilEpoch"
#define ZENDURE_PERSISTENT_SETTINGS {ZENDURE_PERSISTENT_SETTINGS_LAST_FULL, ZENDURE_PERSISTENT_SETTINGS_LAST_EMPTY, ZENDURE_PERSISTENT_SETTINGS_CHARGE_THROUGH, ZENDURE_PERSISTENT_SETTINGS_CONTROL_STATE, ZENDURE_PERSISTENT_SETTINGS_KEEP_EPOCH}

} // namespace Batteries::Zendure
38 changes: 38 additions & 0 deletions include/battery/zendure/LocalMqttProvider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <battery/zendure/Stats.h>
#include <battery/zendure/Provider.h>

namespace Batteries::Zendure {

class LocalMqttProvider : public ::Batteries::Zendure::Provider {
public:
LocalMqttProvider();
bool init() final;
void deinit() final;

private:
void shutdown() const final;
void timesync() final;
void writeSettings() final;
void processPackDataJson(JsonVariantConst& packDataJson, const String& serial, const uint64_t timestamp) final;

void setBuzzer(bool enable) const;
void setAutoshutdown(bool enable) const;

void setBypassMode(BatteryZendureConfig::BypassMode_t mode) const;
void setTopics(const String& deviceType, const String& deviceId);

void onMqttMessageReport(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);

void onMqttMessageLog(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);

void onMqttMessageTimesync(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);

};

} // namespace Batteries::Zendure
91 changes: 43 additions & 48 deletions include/battery/zendure/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,58 +14,38 @@ namespace Batteries::Zendure {
class Provider : public ::Batteries::Provider {
public:
Provider();
bool init() final;
void deinit() final;
bool init();
void deinit();
void loop() final;
std::shared_ptr<::Batteries::Stats> getStats() const final { return _stats; }
std::shared_ptr<::Batteries::HassIntegration> getHassIntegration() final { return _hassIntegration; }

private:
uint16_t setOutputLimit(uint16_t limit) const;
uint16_t setInverterMax(uint16_t limit) const;
void setBypassMode(BatteryZendureConfig::BypassMode_t mode) const;
void setBuzzer(bool enable) const;
void setAutoshutdown(bool enable) const;

void shutdown() const;

void checkChargeThrough(uint32_t predictHours = 0U);

void timesync();
static String parseVersion(uint32_t version);
uint16_t calcOutputLimit(uint16_t limit) const;
void setTargetSoCs(const float soc_min, const float soc_max);
void writeSettings();

uint32_t _lastUpdate = 0;
std::shared_ptr<Stats> _stats = std::make_shared<Stats>();
std::shared_ptr<HassIntegration> _hassIntegration;

protected:
void processPackData(std::optional<JsonArrayConst>& packData, std::string& logValue, const uint64_t timestamp);
void processProperties(std::optional<JsonObjectConst>& props, const uint64_t timestamp);

void calculatePackStats(const uint64_t timestamp);
void calculateEfficiency();
void calculateFullChargeAge();
void publishProperty(const String& topic, const String& property, const String& value) const;
template<typename... Arg>
void publishProperties(const String& topic, Arg&&... args) const;

void setSoC(const float soc, const uint32_t timestamp = 0, const uint8_t precision = 2);
void setChargeThroughState(const ChargeThroughState value, const bool publish = true);

void rescheduleSunCalc() { _nextSunCalc = 0; }
bool alive() const { return _stats->getAgeSeconds() < ZENDURE_ALIVE_SECONDS; }

void publishPersistentSettings(const char* subtopic, const String& payload);
void setChargeThroughState(const ChargeThroughState value, const bool publish = true);

void setTopics(const String& deviceType, const String& deviceId);
void publishProperty(const String& topic, const String& property, const String& value) const;
template<typename... Arg>
void publishProperties(const String& topic, Arg&&... args) const;

uint32_t _rateFullUpdateMs = 0;
uint64_t _nextFullUpdate = 0;
void onMqttMessagePersistentSettings(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);

uint32_t _rateTimesyncMs = 0;
uint64_t _nextTimesync = 0;
virtual void timesync() = 0;
virtual void shutdown() const = 0;
virtual void writeSettings() = 0;
virtual void processPackDataJson(JsonVariantConst& packDataJson, const String& serial, const uint64_t timestamp) = 0;
virtual void setBypassMode(BatteryZendureConfig::BypassMode_t mode) const = 0;

uint32_t _rateSunCalcMs = 0;
uint64_t _nextSunCalc = 0;
std::shared_ptr<Stats> _stats = std::make_shared<Stats>();
std::shared_ptr<HassIntegration> _hassIntegration;

uint32_t _messageCounter = 0;

Expand All @@ -76,24 +56,39 @@ class Provider : public ::Batteries::Provider {
String _topicWrite = String();
String _topicTimesync = String();
String _topicTimesyncReply = String();
String _topicPersistentSettings = String();
String _topicPersistentSettingsPublish = String();
String _topicPersistentSettingsSubscribe = String();

String _payloadFullUpdate = String();

bool _full_log_supported = false;

void onMqttMessageReport(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);
private:
uint16_t setOutputLimit(uint16_t limit) const;
uint16_t setInverterMax(uint16_t limit) const;
void checkChargeThrough(uint32_t predictHours = 0U);
uint16_t calcOutputLimit(uint16_t limit) const;
void setTargetSoCs(const float soc_min, const float soc_max);

void onMqttMessageLog(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);
void calculateTimeDiff();
void rescheduleSunCalc() { _nextSunCalc = 0; }
void rescheduleOutputCalc() { _nextOutputCalc = 0; }
void publishPersistentSettings(const char* subtopic, const String& payload);
void setControlState(ControlState mode, const bool publish = true);
bool isControlState(ControlState mode) const { return _stats->_controlState == mode; }
void checkBatteryProtection();

void onMqttMessageTimesync(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);
uint32_t _rateFullUpdateMs = 0;
uint64_t _nextFullUpdate = 0;

void onMqttMessagePersistentSettings(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len);
uint32_t _rateTimesyncMs = 0;
uint64_t _nextTimesync = 0;

uint32_t _rateSunCalcMs = 0;
uint64_t _nextSunCalc = 0;

uint32_t _rateOutputCalcMs = 0;
uint64_t _nextOutputCalc = 0;
};

} // namespace Batteries::Zendure
Loading