From 0c7bc3f69d90b633551d886e462d33f251ee54f0 Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Mon, 20 Jan 2025 10:14:30 +0100 Subject: [PATCH] [fronius] Add action to set reserve battery capacity Signed-off-by: Florian Hotze --- bundles/org.openhab.binding.fronius/README.md | 4 ++ .../action/FroniusSymoInverterActions.java | 33 ++++++++++++++++ .../internal/api/FroniusBatteryControl.java | 38 +++++++++++++++++++ .../handler/FroniusSymoInverterHandler.java | 18 +++++++++ .../resources/OH-INF/i18n/fronius.properties | 4 ++ 5 files changed, 97 insertions(+) diff --git a/bundles/org.openhab.binding.fronius/README.md b/bundles/org.openhab.binding.fronius/README.md index d882254880fde..fd62c8aebfeae 100644 --- a/bundles/org.openhab.binding.fronius/README.md +++ b/bundles/org.openhab.binding.fronius/README.md @@ -185,6 +185,8 @@ Once the actions instance has been retrieved, you can invoke the following metho - `forceBatteryCharging(QuantityType power)`: Force the battery to charge with the specified power (removes all battery control schedules first and applies all the time). - `addForcedBatteryChargingSchedule(LocalTime from, LocalTime until, QuantityType power)`: Add a schedule to force the battery to charge with the specified power in the specified time range. - `addForcedBatteryChargingSchedule(ZonedDateTime from, ZonedDateTime until, QuantityType power)`: Add a schedule to force the battery to charge with the specified power in the specified time range. +- `setBackupReservedBatteryCapacity(int percent)`: Set the reserved battery capacity for backup power. +- `setBackupReservedBatteryCapacity(PercentType percent)`: Set the reserved battery capacity for backup power. All methods return a boolean value indicating whether the action was successful. @@ -201,6 +203,8 @@ froniusInverterActions.resetBatteryControl(); froniusInverterActions.addHoldBatteryChargeSchedule(time.toZDT('18:00'), time.toZDT('22:00')); froniusInverterActions.addForcedBatteryChargingSchedule(time.toZDT('22:00'), time.toZDT('23:59'), Quantity('5 kW')); froniusInverterActions.addForcedBatteryChargingSchedule(time.toZDT('00:00'), time.toZDT('06:00'), Quantity('5 kW')); + +froniusInverterActions.setBackupReservedBatteryCapacity(50); ``` ## Full Example diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/action/FroniusSymoInverterActions.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/action/FroniusSymoInverterActions.java index bfdfa325232c8..3a7659f1130c3 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/action/FroniusSymoInverterActions.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/action/FroniusSymoInverterActions.java @@ -23,6 +23,7 @@ import org.openhab.core.automation.annotation.ActionInput; import org.openhab.core.automation.annotation.ActionOutput; import org.openhab.core.automation.annotation.RuleAction; +import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.thing.binding.ThingActions; import org.openhab.core.thing.binding.ThingActionsScope; @@ -97,6 +98,24 @@ public static boolean addForcedBatteryChargingSchedule(ThingActions actions, Zon return addForcedBatteryChargingSchedule(actions, from.toLocalTime(), until.toLocalTime(), power); } + public static boolean setBackupReservedBatteryCapacity(ThingActions actions, int percent) { + if (actions instanceof FroniusSymoInverterActions froniusSymoInverterActions) { + return froniusSymoInverterActions.setBackupReservedBatteryCapacity(percent); + } else { + throw new IllegalArgumentException( + "The 'actions' argument is not an instance of FroniusSymoInverterActions"); + } + } + + public static boolean setBackupReservedBatteryCapacity(ThingActions actions, PercentType percent) { + if (actions instanceof FroniusSymoInverterActions froniusSymoInverterActions) { + return froniusSymoInverterActions.setBackupReservedBatteryCapacity(percent); + } else { + throw new IllegalArgumentException( + "The 'actions' argument is not an instance of FroniusSymoInverterActions"); + } + } + @Override public void setThingHandler(@Nullable ThingHandler handler) { this.handler = (FroniusSymoInverterHandler) handler; @@ -166,4 +185,18 @@ public boolean addForcedBatteryChargingSchedule(ZonedDateTime from, ZonedDateTim QuantityType power) { return addForcedBatteryChargingSchedule(from.toLocalTime(), until.toLocalTime(), power); } + + @RuleAction(label = "@text/actions.backup-reserved-battery-capacity.label", description = "@text/actions.backup-reserved-battery-capacity.description") + public @ActionOutput(type = "boolean", label = "Success") boolean setBackupReservedBatteryCapacity( + @ActionInput(name = "percent", label = "@text/actions.soc.label", description = "@text/actions.soc.description", required = true) int percent) { + FroniusSymoInverterHandler handler = this.handler; + if (handler != null) { + return handler.setBackupReservedBatteryCapacity(new PercentType(percent)); + } + return false; + } + + public boolean setBackupReservedBatteryCapacity(PercentType percent) { + return setBackupReservedBatteryCapacity(percent.intValue()); + } } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/FroniusBatteryControl.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/FroniusBatteryControl.java index 3fc38c76cc387..1e4b40c6a10b8 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/FroniusBatteryControl.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/FroniusBatteryControl.java @@ -18,6 +18,7 @@ import java.net.URI; import java.time.LocalTime; import java.time.format.DateTimeFormatter; +import java.util.Map; import java.util.Properties; import javax.measure.quantity.Power; @@ -32,6 +33,7 @@ import org.openhab.binding.fronius.internal.api.dto.inverter.batterycontrol.TimeOfUseRecords; import org.openhab.binding.fronius.internal.api.dto.inverter.batterycontrol.TimeTableRecord; import org.openhab.binding.fronius.internal.api.dto.inverter.batterycontrol.WeekdaysRecord; +import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; import org.slf4j.Logger; @@ -49,6 +51,8 @@ @NonNullByDefault public class FroniusBatteryControl { private static final String TIME_OF_USE_ENDPOINT = "/config/timeofuse"; + private static final String BATTERIES_ENDPOINT = "/config/batteries"; + private static final String BACKUP_RESERVED_CAPACITY_PARAMETER = "HYB_BACKUP_RESERVED"; private static final Logger LOGGER = LoggerFactory.getLogger(FroniusBatteryControl.class); private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); @@ -64,6 +68,7 @@ public class FroniusBatteryControl { private final String username; private final String password; private final URI timeOfUseUri; + private final URI batteriesUri; public FroniusBatteryControl(HttpClient httpClient, URI baseUri, String username, String password) { this.httpClient = httpClient; @@ -71,6 +76,7 @@ public FroniusBatteryControl(HttpClient httpClient, URI baseUri, String username this.username = username; this.password = password; this.timeOfUseUri = baseUri.resolve(URI.create(TIME_OF_USE_ENDPOINT)); + this.batteriesUri = baseUri.resolve(URI.create(BATTERIES_ENDPOINT)); } /** @@ -208,4 +214,36 @@ ScheduleType.CHARGE_MIN, new TimeTableRecord(from.format(TIME_FORMATTER), until. timeOfUse[timeOfUse.length - 1] = holdCharge; setTimeOfUse(new TimeOfUseRecords(timeOfUse)); } + + /** + * Sets the reserved battery capacity for backup power. + * + * @param percent the reserved battery capacity for backup power + * @throws FroniusCommunicationException when an error occurs during communication with the inverter + * @throws IllegalArgumentException if the percent is not in [10,95] + * @throws FroniusUnauthorizedException when the login failed due to invalid credentials + */ + public void setBackupReservedCapacity(PercentType percent) + throws FroniusCommunicationException, FroniusUnauthorizedException { + if (percent.intValue() < 10 || percent.intValue() > 95) { + throw new IllegalArgumentException("invalid percent value: " + percent + " (must be in [10,95])"); + } + + // Login and get the auth header for the next request + String authHeader = FroniusConfigAuthUtil.login(httpClient, baseUri, username, password, HttpMethod.POST, + batteriesUri.getPath(), API_TIMEOUT); + Properties headers = new Properties(); + headers.put(HttpHeader.AUTHORIZATION.asString(), authHeader); + + // Set the setting + String json = gson.toJson(Map.of(BACKUP_RESERVED_CAPACITY_PARAMETER, percent.intValue())); + String responseString = FroniusHttpUtil.executeUrl(HttpMethod.POST, batteriesUri.toString(), headers, + new ByteArrayInputStream(json.getBytes()), "application/json", API_TIMEOUT); + PostConfigResponse response = gson.fromJson(responseString, PostConfigResponse.class); + if (!response.writeSuccess().contains(BACKUP_RESERVED_CAPACITY_PARAMETER)) { + LOGGER.debug("{}", responseString); + throw new FroniusCommunicationException("Failed to write configuration to inverter"); + } + LOGGER.trace("Backup Reserved Capacity setting set successfully"); + } } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java index c568619333c1e..42ccc08769f28 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java @@ -41,6 +41,7 @@ import org.openhab.binding.fronius.internal.api.dto.powerflow.PowerFlowRealtimeResponse; import org.openhab.binding.fronius.internal.api.dto.powerflow.PowerFlowRealtimeSite; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Thing; @@ -185,6 +186,23 @@ public boolean addForcedBatteryChargingSchedule(LocalTime from, LocalTime until, return false; } + public boolean setBackupReservedBatteryCapacity(PercentType percent) { + FroniusBatteryControl batteryControl = getBatteryControl(); + if (batteryControl != null) { + try { + batteryControl.setBackupReservedCapacity(percent); + return true; + } catch (IllegalArgumentException e) { + logger.warn("Failed to set backup reserved battery capacity: {}", e.getMessage()); + } catch (FroniusCommunicationException e) { + logger.warn("Failed to set backup reserved battery capacity", e); + } catch (FroniusUnauthorizedException e) { + logger.warn("Failed to set backup reserved battery capacity: Invalid username or password"); + } + } + return false; + } + /** * Update the channel from the last data retrieved * diff --git a/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties b/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties index a5c1d08c80839..16a8b8663064e 100644 --- a/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties +++ b/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties @@ -128,9 +128,13 @@ actions.force-battery-charging.label = Force Battery Charging actions.force-battery-charging.description = Force the battery to charge with the specified power actions.add-forced-battery-charging-schedule.label = Add Forced Battery Charging Schedule actions.add-forced-battery-charging-schedule.description = Add a schedule to force the battery to charge with the specified power in the specified time range +actions.backup-reserved-battery-capacity.label = Backup Power Reserved Battery Capacity +actions.backup-reserved-battery-capacity.description = Set the reserved battery capacity for backup power supply actions.from.label = Begin Timestamp actions.from.description = The beginning of the time range actions.until.label = End Timestamp actions.until.description = The (inclusive) end of the time range actions.power.label = Power actions.power.description = The power to charge the battery with +actions.soc.label = State of Charge +actions.soc.description = Battery State of Charge (in percent)