[froniuswattpilot] Add channels to control surplus SoC & boost charging (#19256)

* [froniuswattpilot] Add channels to control surplus SoC & boost charging

Signed-off-by: Florian Hotze <dev@florianhotze.com>
This commit is contained in:
Florian Hotze
2025-08-29 10:44:07 +02:00
committed by GitHub
parent 887a842c03
commit 6ab5d848e8
8 changed files with 140 additions and 6 deletions
@@ -36,6 +36,9 @@ After adding it from the inbox, you need to configure the password for accessing
| control#charging-mode | String | RW | The mode of charging: `DEFAULT`, `ECO`, `NEXT_TRIP` |
| control#charging-current | Number:ElectricCurrent | RW | The current to charge with |
| control#pv-surplus-threshold | Number:Power | RW | The PV surplus power at which surplus charging starts |
| control#pv-surplus-soc | Number:Dimensionless | RW | The battery SoC at which PV surplus charging starts |
| control#boost-enabled | Switch | RW | Boost charging in Eco or Next Trip mode by using power from the battery |
| control#boost-soc | Number:Dimensionless | RW | Limit SoC to discharge the battery to when boost is enabled |
| status#charging-state | String | R | Charging state: `NO_CAR`, `CHARGING`, `READY` or `COMPLETE` |
| status#charging-possible | Switch | R | Whether charging is currently possible, e.g. when using ECO mode, too low PV surplus can block charging |
| status#single-phase | Switch | R | Whether the wallbox is currently charging single phase only |
@@ -66,10 +69,13 @@ Thing froniuswattpilot:wattpilot:garage "Wattpilot Garage" [hostname="xxx.xxx.xx
Group Wattpilot_Garage "Wattpilot Garage" ["Equipment"]
// Control
Switch Wattpilot_Garage_Charging_Allowed "Charging Allowed" <BatteryLevel> (Wattpilot_Garage) ["Control"] {channel="froniuswattpilot:wattpilot:garage:control#charging-allowed"}
String Wattpilot_Garage_Charging_Mode "Charging Mode" <BatteryLevel> (Wattpilot_Garage) ["Control"] {channel="froniuswattpilot:wattpilot:garage:control#charging-mode"}
Switch Wattpilot_Garage_Charging_Allowed "Charging Allowed" <BatteryLevel> (Wattpilot_Garage) ["Control", "Enabled"] {channel="froniuswattpilot:wattpilot:garage:control#charging-allowed"}
String Wattpilot_Garage_Charging_Mode "Charging Mode" <BatteryLevel> (Wattpilot_Garage) ["Control", "Mode"] {channel="froniuswattpilot:wattpilot:garage:control#charging-mode"}
Number:ElectricCurrent Wattpilot_Garage_Charging_Current "Charging Current [%d A]" <Energy> (Wattpilot_Garage) ["Setpoint", "Current"] {channel="froniuswattpilot:wattpilot:garage:control#charging-current", unit="A"}
Number:Power Wattpilot_Garage_PV_Surplus_Power_Threshold "PV Surplus Power Threshold [%.1f kW]" <SolarPlant> (Wattpilot_Garage) ["Setpoint", "Power"] {channel="froniuswattpilot:wattpilot:garage:control#pv-surplus-threshold", unit="kW"}
Number:Dimensionless Wattpilot_Garage_PV_Surplus_SoC_Threshold "PV Surplus SoC Threshold [%d %%]" <SolarPlant> (Wattpilot_Garage) ["Setpoint", "Energy"] {channel="froniuswattpilot:wattpilot:garage:control#pv-surplus-soc", unit="%"}
Switch Wattpilot_Garage_Boost_Enabled "Boost Enabled" <BatteryLevel> (Wattpilot_Garage) ["Control", "Enabled"] {channel="froniuswattpilot:wattpilot:garage:control#boost-enabled"}
Number:Dimensionless Wattpilot_Garage_Boost_SoC_Limit "Boost SoC Limit [%d %%]" <BatteryLevel> (Wattpilot_Garage) ["Setpoint", "Energy"] {channel="froniuswattpilot:wattpilot:garage:control#boost-soc", unit="%"}
// Status
Switch Wattpilot_Garage_Charging_Possible "Charging Possible" <BatteryLevel> (Wattpilot_Garage) ["Status"] {channel="froniuswattpilot:wattpilot:garage:status#charging-possible"}
@@ -18,7 +18,7 @@
<dependency>
<groupId>dev.digiried</groupId>
<artifactId>wattpilot4j</artifactId>
<version>2.0.0</version>
<version>2.1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -4,7 +4,7 @@
<feature name="openhab-binding-froniuswattpilot" description="Fronius Wattpilot Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle dependency="true">mvn:dev.digiried/wattpilot4j/2.0.0</bundle>
<bundle dependency="true">mvn:dev.digiried/wattpilot4j/2.1.0</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.froniuswattpilot/${project.version}</bundle>
</feature>
</features>
@@ -43,6 +43,9 @@ public class FroniusWattpilotBindingConstants {
public static final String CHANNEL_CHARGING_MODE = "charging-mode";
public static final String CHANNEL_CHARGING_CURRENT = "charging-current";
public static final String CHANNEL_PV_SURPLUS_THRESHOLD = "pv-surplus-threshold";
public static final String CHANNEL_PV_SURPLUS_SOC = "pv-surplus-soc";
public static final String CHANNEL_BOOST_ENABLED = "boost-enabled";
public static final String CHANNEL_BOOST_SOC = "boost-soc";
// Wattpilot status channels
public static final String CHANNEL_CHARGING_STATE = "charging-state";
@@ -45,16 +45,19 @@ import dev.digiried.wattpilot.WattpilotClient;
import dev.digiried.wattpilot.WattpilotClientListener;
import dev.digiried.wattpilot.WattpilotInfo;
import dev.digiried.wattpilot.WattpilotStatus;
import dev.digiried.wattpilot.commands.SetBoostCommand;
import dev.digiried.wattpilot.commands.SetBoostSoCLimitCommand;
import dev.digiried.wattpilot.commands.SetChargingCurrentCommand;
import dev.digiried.wattpilot.commands.SetChargingModeCommand;
import dev.digiried.wattpilot.commands.SetEnforcedChargingStateCommand;
import dev.digiried.wattpilot.commands.SetSurplusPowerThresholdCommand;
import dev.digiried.wattpilot.commands.SetSurplusSoCThresholdCommand;
import dev.digiried.wattpilot.dto.ChargingMode;
import dev.digiried.wattpilot.dto.EnforcedChargingState;
/**
* The {@link FroniusWattpilotHandler} is responsible for handling commands, which are
* sent to one of the channels.
* sent to one of the channels, and updating the channels with the current states.
*
* @author Florian Hotze - Initial contribution
*/
@@ -71,6 +74,26 @@ public class FroniusWattpilotHandler extends BaseThingHandler implements Wattpil
client.addListener(this);
}
private @Nullable Integer getPercent(Command command) {
switch (command) {
case QuantityType<?> qt -> {
qt = qt.toUnit(Units.PERCENT);
if (qt == null) {
logger.debug("Failed to convert QuantityType to PERCENT!");
return null;
}
return qt.intValue();
}
case DecimalType dt -> {
return dt.intValue();
}
default -> {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
return null;
}
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
String groupId = channelUID.getGroupId();
@@ -133,6 +156,25 @@ public class FroniusWattpilotHandler extends BaseThingHandler implements Wattpil
}
client.sendCommand(new SetSurplusPowerThresholdCommand(watts));
break;
case CHANNEL_PV_SURPLUS_SOC:
Integer surplusSoC = getPercent(command);
if (surplusSoC != null) {
client.sendCommand(new SetSurplusSoCThresholdCommand(surplusSoC));
}
break;
case CHANNEL_BOOST_ENABLED:
if (command instanceof OnOffType oft) {
client.sendCommand(new SetBoostCommand(oft == OnOffType.ON));
} else {
logger.debug("Command has wrong type, OnOffType required!");
}
break;
case CHANNEL_BOOST_SOC:
Integer boostSoc = getPercent(command);
if (boostSoc != null) {
client.sendCommand(new SetBoostSoCLimitCommand(boostSoc));
}
break;
default:
logger.debug("Unknown channel id: {}", channelId);
}
@@ -224,6 +266,7 @@ public class FroniusWattpilotHandler extends BaseThingHandler implements Wattpil
final ThingUID uid = getThing().getUID();
ChannelUID channel;
// generic charging control
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_CONTROL, CHANNEL_CHARGING_ALLOWED);
updateState(channel,
status.getEnforcedChargingState() == EnforcedChargingState.OFF ? OnOffType.OFF : OnOffType.ON);
@@ -234,8 +277,19 @@ public class FroniusWattpilotHandler extends BaseThingHandler implements Wattpil
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_CONTROL, CHANNEL_CHARGING_CURRENT);
updateState(channel, new QuantityType<>(status.getChargingCurrent(), Units.AMPERE));
// PV surplus charging
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_CONTROL, CHANNEL_PV_SURPLUS_THRESHOLD);
updateState(channel, new QuantityType<>(status.getSurplusPowerThreshold(), Units.WATT));
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_CONTROL, CHANNEL_PV_SURPLUS_SOC);
updateState(channel, new DecimalType(status.getSurplusSoCThreshold()));
// boost charging
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_CONTROL, CHANNEL_BOOST_ENABLED);
updateState(channel, status.isBoostEnabled() ? OnOffType.ON : OnOffType.OFF);
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_CONTROL, CHANNEL_BOOST_SOC);
updateState(channel, new DecimalType(status.getBoostSoCLimit()));
}
private void updateChannelsStatus(WattpilotStatus status) {
@@ -47,6 +47,12 @@ channel-group-type.froniuswattpilot.status.label = Charging Status
# channel types
channel-type.froniuswattpilot.boost-enabled.label = Boost Mode
channel-type.froniuswattpilot.boost-enabled.description = Boost charging in Eco or Next Trip mode by using power from the battery
channel-type.froniuswattpilot.boost-enabled.state.option.ON = Boost enabled
channel-type.froniuswattpilot.boost-enabled.state.option.OFF = Boost disabled
channel-type.froniuswattpilot.boost-soc.label = Boost SoC Limit
channel-type.froniuswattpilot.boost-soc.description = Limit SoC to discharge the battery to when boost is enabled
channel-type.froniuswattpilot.charging-allowed.label = Charging Allowed
channel-type.froniuswattpilot.charging-allowed.description = Allow or forbid charging
channel-type.froniuswattpilot.charging-allowed.state.option.ON = Allow charging
@@ -72,5 +78,7 @@ channel-type.froniuswattpilot.charging-state.state.option.NO_CAR = No car connec
channel-type.froniuswattpilot.charging-state.state.option.CHARGING = Charging
channel-type.froniuswattpilot.charging-state.state.option.READY = Ready to charge
channel-type.froniuswattpilot.charging-state.state.option.COMPLETE = Charging completed
channel-type.froniuswattpilot.pv-surplus-soc.label = PV Surplus SoC Threshold
channel-type.froniuswattpilot.pv-surplus-soc.description = The battery SoC at which PV surplus charging starts
channel-type.froniuswattpilot.pv-surplus-threshold.label = PV Surplus Power Threshold
channel-type.froniuswattpilot.pv-surplus-threshold.description = The PV Surplus power at which surplus charging starts
@@ -19,7 +19,7 @@
<properties>
<property name="serialNumber"/>
<property name="firmwareVersion"/>
<property name="thingTypeVersion">1</property>
<property name="thingTypeVersion">2</property>
</properties>
<representation-property>hostname</representation-property>
@@ -46,6 +46,9 @@
<channel id="charging-mode" typeId="charging-mode"/>
<channel id="charging-current" typeId="charging-current"/>
<channel id="pv-surplus-threshold" typeId="pv-surplus-threshold"/>
<channel id="pv-surplus-soc" typeId="pv-surplus-soc"/>
<channel id="boost-enabled" typeId="boost-enabled"/>
<channel id="boost-soc" typeId="boost-soc"/>
</channels>
</channel-group-type>
@@ -120,6 +123,7 @@
<category>Battery</category>
<tags>
<tag>Control</tag>
<tag>Enabled</tag>
</tags>
<state>
<options>
@@ -170,6 +174,45 @@
</tags>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="pv-surplus-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>PV Surplus SoC Threshold</label>
<description>The battery SoC at which PV surplus charging starts</description>
<category>SolarPlant</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="boost-enabled">
<item-type>Switch</item-type>
<label>Boost Mode</label>
<description>Boost charging in Eco or Next Trip mode by using power from the battery</description>
<category>Battery</category>
<tags>
<tag>Control</tag>
<tag>Enabled</tag>
</tags>
<state>
<options>
<option value="ON">Boost enabled</option>
<option value="OFF">Boost disabled</option>
</options>
</state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="boost-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Boost SoC Limit</label>
<description>Limit SoC to discharge the battery to when boost is enabled</description>
<category>Battery</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<!-- Status Channel Types -->
<channel-type id="charging-state">
<item-type>String</item-type>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="froniuswattpilot:wattpilot">
<instruction-set targetVersion="1">
<add-channel groupIds="control" id="pv-surplus-soc">
<type>froniuswattpilot:pv-surplus-soc</type>
</add-channel>
<add-channel groupIds="control" id="boost-enabled">
<type>froniuswattpilot:boost-enabled</type>
</add-channel>
<add-channel groupIds="control" id="boost-soc">
<type>froniuswattpilot:boost-soc</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>