Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
7 changes: 3 additions & 4 deletions bundles/org.openhab.binding.sunsynk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ The SunSynk Inverter has the following channels.
|inverter-grid-voltage |Number:ElectricPotential | R | Grid ac electric-voltage | no |
|inverter-grid-current |Number:ElectricCurrent | R | Grid ac electric-current | no |
|inverter-grid-frequency |Number:Frequency | R | Grid frequency | no |
|inverter-capacity |Number:Power | R | Inverter energy capacity | no |
|inverter-rated-ac-output |Number:Power | R | Inverter energy capacity | no |
|inverter-solar-energy-today |Number:Energy | R | Solar energy generated today | no |
|inverter-solar-energy-month |Number:Energy | R | Solar energy generated this month | no |
|inverter-solar-energy-year |Number:Energy | R | Solar energy generated this year | no |
Expand Down Expand Up @@ -158,8 +158,7 @@ Bridge sunsynk:account:xxx @ "Loft" [email= "user.symbol@domain.", password="som
}
```

You are unlikely to know the plantId, please use the automatic scan to discover and add an inverters, the plantId can be found
in the log or in the inverter Thing configuration.
You are unlikely to know the plantId, please use the automatic scan to discover and add an inverters, the plantId can be found in the log or in the inverter Thing configuration.

#### sunsynk.items

Expand Down Expand Up @@ -203,7 +202,7 @@ Number:Dimensionless BatterySOC "Battery SOC [%s]"
Number:ElectricPotential BatteryGridVoltage "Battery Voltage" {channel="sunsynk:inverter:xxx:1234567R1231234567890:battery-dc-voltage"}
Number:ElectricCurrent BatteryGridCurrent "Battery Current" {channel="sunsynk:inverter:xxx:1234567R1231234567890:battery-dc-current"}
Number:Power BatteryGridPower "Battery Power" {channel="sunsynk:inverter:xxx:1234567R1231234567890:battery-dc-power"}
Number:Power InverterCapacity "Inverter Capacity" {channel="sunsynk:inverter:xxx:1234567R1231234567890:inverter-capacity"}
Number:Power InverterCapacity "Inverter Capacity" {channel="sunsynk:inverter:xxx:1234567R1231234567890:inverter-rated-ac-output"}
Number:Temperature BatteryTemperature "Battery Temperature " {channel="sunsynk:inverter:xxx:1234567R1231234567890:battery-temperature"}
Number:Temperature InverterACTemperature "Inverter AC Temperature" {channel="sunsynk:inverter:xxx:1234567R1231234567890:inverter-ac-temperature"}
Number:Temperature InverterDCTemperature "Inverter DC Temperature" {channel="sunsynk:inverter:xxx:1234567R1231234567890:inverter-dc-temperature"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public class SunSynkBindingConstants {
public static final String CHANNEL_INVERTER_GRID_VOLTAGE = "inverter-grid-voltage";
public static final String CHANNEL_INVERTER_GRID_CURRENT = "inverter-grid-current";
public static final String CHANNEL_INVERTER_GRID_FREQUENCY = "inverter-grid-frequency";
public static final String CHANNEL_INVERTER_CAPACITY = "inverter-capacity";
public static final String CHANNEL_INVERTER_RATED_AC_OUTPUT = "inverter-rated-ac-output";

public static final String CHANNEL_INVERTER_AC_TEMPERATURE = "inverter-ac-temperature";
public static final String CHANNEL_INVERTER_DC_TEMPERATURE = "inverter-dc-temperature";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class AccountController {
private static final int TIMEOUT_IN_MS = 4000;
private final Logger logger = LoggerFactory.getLogger(AccountController.class);
private static final String BEARER_TYPE = "Bearer ";
private static final long EXPIRYSECONDS = 100L; // 100 seconds before expiry
private Client sunAccount = new Client();

public AccountController() {
Expand Down Expand Up @@ -84,7 +85,7 @@ public void authenticate(String username, String password)
public void refreshAccount(String username) throws SunSynkAuthenticateException, SunSynkTokenException {
Long expiresIn = this.sunAccount.getExpiresIn();
Long issuedAt = this.sunAccount.getIssuedAt();
if ((issuedAt + expiresIn) - Instant.now().getEpochSecond() > 100) { // > 100 seconds
if ((issuedAt + expiresIn) - Instant.now().getEpochSecond() > EXPIRYSECONDS) {
logger.debug("Account configuration token not expired.");
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public class DeviceController {
private static final int TIMEOUT_IN_MS = 4000;
private final Logger logger = LoggerFactory.getLogger(DeviceController.class);
private static final String BEARER_TYPE = "Bearer ";
private int successFlags = 0;
// private int successFlags = 0;
private String sn = "";
private String alias = "";
private String plantId = "";
Expand Down Expand Up @@ -123,17 +123,18 @@ public DeviceController(SunSynkInverterConfig config) {
*/
public int sendGetState(boolean batterySettingsUpdate)
throws SunSynkGetStatusException, JsonSyntaxException, SunSynkDeviceControllerException {
int successFlags = 0;
logger.debug("Will get STATE for Inverter {} serial {}", this.alias, this.sn);
successFlags = 0; // tracks the number of good responses
try {
if (!batterySettingsUpdate) { // normally get settings to track changes made by other UIs
getCommonSettings(); // battery charge settings
successFlags = successFlags | getCommonSettings(); // battery charge settings
}
getGridRealTime(); // grid status
getBatteryRealTime(); // battery status
getInverterACDCTemperatures(); // get Inverter temperatures
getRealTimeIn(); // used for solar power now
getSummary(); // used for plant totals
successFlags = successFlags | getGridRealTime(); // grid status
successFlags = successFlags | getBatteryRealTime(); // battery status
successFlags = successFlags | getInverterACDCTemperatures(); // get Inverter temperatures
successFlags = successFlags | getRealTimeIn(); // used for solar power now
successFlags = successFlags | getSummary(); // used for plant totals
} catch (IOException e) {
String message = Objects.requireNonNullElse(e.getMessage(), "unknown error message.");
logger.debug("Failed to send to Inverter API: {}.", message);
Expand Down Expand Up @@ -171,7 +172,8 @@ public PlantSummary getPlantSummary() {
}

@SuppressWarnings("unused")
private void getCommonSettings() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
private int getCommonSettings() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
int resultAPI = 0;
logger.debug("Trying Common Settings");
String response = apiGetMethod(makeURL("common/setting/" + this.sn + "/read", ""), APIdata.staticAccessToken);
logger.trace("Common Settings: {}", response);
Expand All @@ -186,12 +188,14 @@ private void getCommonSettings() throws IOException, JsonSyntaxException, SunSyn
} else {
this.batterySettings = settings;
this.batterySettings.buildLists();
successFlags = successFlags | COMMONSETTINGS;
resultAPI = COMMONSETTINGS;
}
return resultAPI;
}

@SuppressWarnings("unused")
private void getGridRealTime() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
private int getGridRealTime() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
int resultAPI = 0;
logger.debug("Trying Grid Real Time Settings");
String response = apiGetMethod(makeURL("inverter/grid/" + this.sn + "/realtime", "sn=" + this.sn),
APIdata.staticAccessToken);
Expand All @@ -207,12 +211,14 @@ private void getGridRealTime() throws IOException, JsonSyntaxException, SunSynkD
} else {
this.grid = grid;
this.grid.sumVIP();
successFlags = successFlags | GRIDREALTIME;
resultAPI = GRIDREALTIME;
}
return resultAPI;
}

@SuppressWarnings("unused")
private void getBatteryRealTime() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
private int getBatteryRealTime() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
int resultAPI = 0;
logger.debug("Trying Battery Real Time Settings");
String response = apiGetMethod(makeURL("inverter/battery/" + this.sn + "/realtime", "sn=" + this.sn + "&lan"),
APIdata.staticAccessToken);
Expand All @@ -227,13 +233,15 @@ private void getBatteryRealTime() throws IOException, JsonSyntaxException, SunSy
logger.debug("Failed to get battery real time values: {}.", battery.getMsg());
} else {
this.realTimeBattery = battery;
successFlags = successFlags | BATTERYREALTIME;
resultAPI = BATTERYREALTIME;
}
return resultAPI;
}

@SuppressWarnings("unused")
private void getInverterACDCTemperatures()
private int getInverterACDCTemperatures()
throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
int resultAPI = 0;
logger.debug("Trying Temperature History");
String date = getAPIFormatDate();
String response = apiGetMethod(
Expand All @@ -251,13 +259,15 @@ private void getInverterACDCTemperatures()
} else {
this.inverterDayTemperatures = daytemps;
this.inverterDayTemperatures.getLastValue();
successFlags = successFlags | INVERTERDAYTEMPS;
resultAPI = INVERTERDAYTEMPS;
}
return resultAPI;
}

@SuppressWarnings("unused")
private void getRealTimeIn() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException { // Get URL
// response
private int getRealTimeIn() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException { // Get URL
// response
int resultAPI = 0;
logger.debug("Trying Real Time Solar");
String response = apiGetMethod(makeURL("inverter/" + this.sn + "/realtime/input", ""),
APIdata.staticAccessToken);
Expand All @@ -273,12 +283,14 @@ private void getRealTimeIn() throws IOException, JsonSyntaxException, SunSynkDev
} else {
this.realTimeDataIn = realTimeInData;
this.realTimeDataIn.stringEval();
successFlags = successFlags | REALTIMEIN;
resultAPI = REALTIMEIN;
}
return resultAPI;
}

@SuppressWarnings("unused")
private void getSummary() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
private int getSummary() throws IOException, JsonSyntaxException, SunSynkDeviceControllerException {
int resultAPI = 0;
logger.debug("Trying Plant Summary");
String response = apiGetMethod(makeURL("plant/" + this.plantId + "/realtime", "id=" + this.plantId),
APIdata.staticAccessToken);
Expand All @@ -293,8 +305,9 @@ private void getSummary() throws IOException, JsonSyntaxException, SunSynkDevice
logger.debug("Failed to get plant summary values for plant {} : {} .", this.plantName, summary.getMsg());
} else {
this.plantSummary = summary;
successFlags = successFlags | PLANTSUMMARY;
resultAPI = PLANTSUMMARY;
}
return resultAPI;
}

/**
Expand All @@ -311,6 +324,7 @@ public void sendSettings(Settings settings) throws SunSynkSendCommandException {

private void sendCommandToSunSynk(String body) throws SunSynkSendCommandException {
try {
logger.debug("Trying to send commands to inverter: {}", this.sn);
apiPostMethod(makeURL("common/setting/" + this.sn + "/set", ""), body, APIdata.staticAccessToken);
} catch (IOException e) {
String message = Objects.requireNonNullElse(e.getMessage(), "unknown error message");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (thingTypeUID.equals(THING_TYPE_INVERTER)) {
logger.debug("SunSynkHandlerFactory [OCT 2025] created Inverter Handler");
logger.debug("SunSynkHandlerFactory created Inverter Handler");
return new SunSynkInverterHandler(thing);
} else if (thingTypeUID.equals(BRIDGE_TYPE_ACCOUNT)) {
SunSynkAccountHandler handler = new SunSynkAccountHandler((Bridge) thing);
registerAccountDiscoveryService(handler);
logger.debug("SunSynkHandlerFactory [OCT 2025] created Account Handler");
logger.debug("SunSynkHandlerFactory created Account Handler");
return handler;
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ public class SunSynkInverterHandler extends BaseThingHandler {
private @Nullable ScheduledFuture<?> refreshTask;
private boolean batterySettingsUpdated = false;
private SunSynkInverterConfig config = new SunSynkInverterConfig();
private int inverterFlags = DeviceController.COMMONSETTINGS | DeviceController.GRIDREALTIME
| DeviceController.BATTERYREALTIME | DeviceController.INVERTERDAYTEMPS | DeviceController.REALTIMEIN;
private int summaryFlag = DeviceController.PLANTSUMMARY;
private int allFlags = inverterFlags | summaryFlag;
private final int INVERTERFLAGS = DeviceController.GRIDREALTIME | DeviceController.BATTERYREALTIME
| DeviceController.INVERTERDAYTEMPS | DeviceController.REALTIMEIN;
private final int SUMMARYFLAGS = DeviceController.PLANTSUMMARY;
private final int ALLFLAGS = INVERTERFLAGS | SUMMARYFLAGS;
private int statusFlags = 0;
public Settings tempInverterChargeSettings = inverter.tempInverterChargeSettings; // Holds modified
// battery settings.
Expand Down Expand Up @@ -287,18 +287,21 @@ public void refreshStateAndUpdate() {
}
try {
statusFlags = inverter.sendGetState(this.batterySettingsUpdated); // get inverter settings

if ((statusFlags & summaryFlag) != summaryFlag) {
if ((statusFlags & DeviceController.COMMONSETTINGS) != DeviceController.COMMONSETTINGS) {
logger.debug("API call for common settings skipped or failed at plant {} for Inverter {} serial {}.",
this.config.getPlantId(), this.config.getAlias(), this.config.getSerialnumber());
}
if ((statusFlags & SUMMARYFLAGS) != SUMMARYFLAGS) {
logger.debug(
"API call failed at plant {} for Inverter {} serial {} Check plant ID in the inverter Thing configuration.",
this.config.getPlantId(), this.config.getAlias(), this.config.getSerialnumber());
}
if ((statusFlags & inverterFlags) != inverterFlags) {
if ((statusFlags & INVERTERFLAGS) != INVERTERFLAGS) {
logger.debug(
"API calls failed at plant {} for Inverter {} serial {}. Check inverter serial number in the inverter Thing configuration.",
this.config.getPlantId(), this.config.getAlias(), this.config.getSerialnumber());
}
if ((statusFlags & allFlags) == 0) {
if ((statusFlags & ALLFLAGS) == 0) {
logger.debug(
"All API calls failure for plant {}, Inverter {} serial {}. Its likely Sun Synk Connect is down, will retry.",
this.config.getPlantName(), this.config.getAlias(), this.config.getSerialnumber());
Expand Down Expand Up @@ -334,14 +337,16 @@ private void startAutomaticRefresh() {

private void publishChannels() {
logger.debug("Updating Channels");
if ((statusFlags & inverterFlags) == inverterFlags) {
if ((statusFlags & DeviceController.COMMONSETTINGS) == DeviceController.COMMONSETTINGS) {
updateSettings();
}
if ((statusFlags & INVERTERFLAGS) == INVERTERFLAGS) {
updateGrid();
updateBattery();
updateTemperature();
updateSolar();
}
if ((statusFlags & summaryFlag) == summaryFlag) {
if ((statusFlags & SUMMARYFLAGS) == SUMMARYFLAGS) {
updateSummary();
}
}
Expand Down Expand Up @@ -451,7 +456,7 @@ private void updateSummary() {
updateState(CHANNEL_INVERTER_SOLAR_ENERGY_MONTH, new DecimalType(plantSummary.getEmonth()));
updateState(CHANNEL_INVERTER_SOLAR_ENERGY_YEAR, new DecimalType(plantSummary.getEyear()));
updateState(CHANNEL_INVERTER_SOLAR_ENERGY_TOTAL, new DecimalType(plantSummary.getEtotal()));
updateState(CHANNEL_INVERTER_CAPACITY, new DecimalType(plantSummary.getCapacity()));
updateState(CHANNEL_INVERTER_RATED_AC_OUTPUT, new DecimalType(plantSummary.getCapacity()));
updateState(CHANNEL_INVERTER_SOLAR_POWER, new DecimalType(plantSummary.getPac()));
updateState(CHANNEL_INVERTER_SOLAR_EFFICIENCY, new DecimalType(plantSummary.getEfficiency()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ thing-type.sunsynk.inverter.channel.interval-6-grid-charge.label = Interval 6 Gr
thing-type.sunsynk.inverter.channel.interval-6-grid-power-limit.label = Interval 6 Maximum Charge Rate
thing-type.sunsynk.inverter.channel.interval-6-grid-time.label = Interval 6 Start Time
thing-type.sunsynk.inverter.channel.inverter-ac-temperature.label = Inverter AC Temperature
thing-type.sunsynk.inverter.channel.inverter-ac-temperature.description = Temperature of the inverter ac side.
thing-type.sunsynk.inverter.channel.inverter-capacity.label = Inverter Capacity
thing-type.sunsynk.inverter.channel.inverter-capacity.description = Inverter capacity rating.
thing-type.sunsynk.inverter.channel.inverter-ac-temperature.description = Temperature of the inverter AC side.
thing-type.sunsynk.inverter.channel.inverter-dc-temperature.label = Inverter DC Temperature
thing-type.sunsynk.inverter.channel.inverter-dc-temperature.description = Temperature of the inverter dc side.
thing-type.sunsynk.inverter.channel.inverter-grid-current.label = Grid Current
Expand All @@ -63,8 +61,10 @@ thing-type.sunsynk.inverter.channel.inverter-grid-power.label = Grid Power
thing-type.sunsynk.inverter.channel.inverter-grid-power.description = Grid power import/export.
thing-type.sunsynk.inverter.channel.inverter-grid-voltage.label = Grid Voltage
thing-type.sunsynk.inverter.channel.inverter-grid-voltage.description = Measured voltage of the grid.
thing-type.sunsynk.inverter.channel.inverter-rated-ac-output.label = Inverter Rated Output
thing-type.sunsynk.inverter.channel.inverter-rated-ac-output.description = Inverter AC output rating.
thing-type.sunsynk.inverter.channel.inverter-solar-ac-power.label = Solar Power
thing-type.sunsynk.inverter.channel.inverter-solar-ac-power.description = Solar ac power being generated.
thing-type.sunsynk.inverter.channel.inverter-solar-ac-power.description = Solar AC power being generated.
thing-type.sunsynk.inverter.channel.inverter-solar-efficiency.label = Solar Efficiency
thing-type.sunsynk.inverter.channel.inverter-solar-efficiency.description = Efficiency of the solar panels.
thing-type.sunsynk.inverter.channel.inverter-solar-energy-month.label = Solar Energy This Month
Expand Down
Loading
Loading