Skip to content

Commit

Permalink
[SenecHome] Add writeable Charging Modes (#17474)
Browse files Browse the repository at this point in the history
* [senechome] Now accepts commands for active charging

Signed-off-by: Lukas Pindl <lukas.pindl@gmx.net>

* [senechome] Manual update for charging modes

Signed-off-by: Lukas Pindl <lukas.pindl@gmx.net>

* [senechome] docu and spotless

Signed-off-by: Lukas Pindl <lukas.pindl@gmx.net>

* [Senechome] Charge Modes combined into a single channel

Signed-off-by: Lukas Pindl <lukas.pindl@gmx.net>

* [senechome] Apply suggestions from code review

Co-authored-by: lsiepel <leosiepel@gmail.com>
Signed-off-by: Lukas Pindl <36566235+BigFood2307@users.noreply.github.com>

* [senechome] additional review fixes

Signed-off-by: Lukas Pindl <lukas.pindl@gmx.net>

---------

Signed-off-by: Lukas Pindl <lukas.pindl@gmx.net>
Signed-off-by: Lukas Pindl <36566235+BigFood2307@users.noreply.github.com>
Co-authored-by: lsiepel <leosiepel@gmail.com>
  • Loading branch information
BigFood2307 and lsiepel authored Oct 5, 2024
1 parent e655ddb commit 5c99a6c
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 5 deletions.
7 changes: 7 additions & 0 deletions bundles/org.openhab.binding.senechome/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ In addition you can switch off devices if the power consumption is getting highe

Examples: Lights, pool filters, wash machines, ...

Also allows for turning the battery into safe charging mode or storage mode.

## Supported Things

| Thing type id | Name |
Expand All @@ -19,6 +21,7 @@ Examples: Lights, pool filters, wash machines, ...
- not equipped battery packs will return 0 for all ...Pack channels
- currently channels for the first wallbox are implemented (senec could handle 4 wallboxes)
- Senec disables http access at ~30.08.2023
- The chargeMode `STORAGE` also known as Lithium Storage Mode is intended (according to the manual) for disassembly and transport. It is untested if it has any side effects.

## Thing Configuration

Expand Down Expand Up @@ -57,6 +60,8 @@ The property `limitationTresholdValue` is used as threshold for channel `powerLi
| batteryFuelCharge | percent | Fuel charge of your battery (0 - 100%) |
| systemState | | Text describing current action of the senec home system (e.g. CHARGE) |
| systemStateValue | | Value describing current action of the senec home system (e.g. 14) |
| chargeMode | OFF/CHARGE/ | In `CHARGE` mode, the battery will try to fill as quickly as possible |
| | STORAGE | in `STORAGE` mode, the battery will try to reach 25% SOC |
| gridPower | watt | Grid power level, negative for supply, positive values for drawing power |
| gridPowerDraw | watt | Absolute power level of power draw, zero while supplying |
| gridPowerSupply | watt | Absolute power level of power supply, zero while drawing |
Expand Down Expand Up @@ -135,6 +140,7 @@ Number SenecGridVoltagePh2 "Voltage Level on Phase 2 [%d V]" <e
Number SenecGridVoltagePh3 "Voltage Level on Phase 3 [%d V]" <energy> { channel="senechome:senechome:pvbattery:gridVoltagePhase3" }
Number SenecGridFrequency "Grid Frequency [%.2f Hz]" <energy> { channel="senechome:senechome:pvbattery:gridFrequency" }
Number SenecBatteryVoltage "Battery Voltage [%.1f V]" <energy> { channel="senechome:senechome:pvbattery:batteryVoltage" }
String SenecBatteryChargeMode "Battery Charge Mode [%s]" { channel="senechome:senechome:pvbattery:chargeMode" }
```

## Sitemap
Expand Down Expand Up @@ -166,6 +172,7 @@ Text label="Power Grid"{
Default item=SenecGridVoltagePh3
Default item=SenecGridFrequency
Default item=SenecBatteryVoltage
Default item=SenecBatteryChargeMode
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
*
* @author Steven Schwarznau - Initial contribution
* @author Robert Delbrück - Update for Senec API changes
* @author Lukas Pindl - Update for writing to safeChargeMode
*
*/
@NonNullByDefault
Expand Down Expand Up @@ -72,6 +73,38 @@ public void setHostname(String hostname) {
*/
public SenecHomeResponse getStatistics()
throws TimeoutException, ExecutionException, IOException, InterruptedException, JsonSyntaxException {

String dataToSend = gson.toJson(new SenecHomeResponse());
ContentResponse response = postRequest(dataToSend);
return Objects.requireNonNull(gson.fromJson(response.getContentAsString(), SenecHomeResponse.class));
}

/**
* POST json, to lala.cgi of Senec webinterface to set a given parameter
*
* @return boolean, wether or not the request was successful
*/
public boolean setValue(String section, String id, String value) {
String dataToSend = "{\"" + section + "\":{\"" + id + "\":\"" + value + "\"}}";
try {
postRequest(dataToSend);
return true;
} catch (TimeoutException | ExecutionException | IOException | InterruptedException e) {
return false;
}
}

/**
* helper function to handle the actual POST request to the webinterface
*
* @return object of type ContentResponse, the response received to the POST request
* @throws TimeoutException Communication failed (Timeout)
* @throws ExecutionException Communication failed
* @throws IOException Communication failed
* @throws InterruptedException Communication failed (Interrupted)
*/
private ContentResponse postRequest(String dataToSend)
throws TimeoutException, ExecutionException, IOException, InterruptedException {
String location = hostname + "/lala.cgi";
logger.trace("sending request to: {}", location);

Expand All @@ -80,13 +113,11 @@ public SenecHomeResponse getStatistics()
request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString());
ContentResponse response = null;
try {
String dataToSend = gson.toJson(new SenecHomeResponse());
logger.trace("data to send: {}", dataToSend);
response = request.method(HttpMethod.POST).content(new StringContentProvider(dataToSend))
.timeout(15, TimeUnit.SECONDS).send();
if (response.getStatus() == HttpStatus.OK_200) {
String responseString = response.getContentAsString();
return Objects.requireNonNull(gson.fromJson(responseString, SenecHomeResponse.class));
return response;
} else {
logger.trace("Got unexpected response code {}", response.getStatus());
throw new IOException("Got unexpected response code " + response.getStatus());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* used across the whole binding.
*
* @author Steven Schwarznau - Initial contribution
* @author Lukas Pindl - Update for writing to chargeMode
*/
@NonNullByDefault
public class SenecHomeBindingConstants {
Expand Down Expand Up @@ -49,6 +50,7 @@ public class SenecHomeBindingConstants {
public static final String CHANNEL_SENEC_BATTERY_FUEL_CHARGE = "batteryFuelCharge";
public static final String CHANNEL_SENEC_BATTERY_VOLTAGE = "batteryVoltage";
public static final String CHANNEL_SENEC_BATTERY_CURRENT = "batteryCurrent";
public static final String CHANNEL_SENEC_CHARGE_MODE = "chargeMode";

// SenecHomeGrid
public static final String CHANNEL_SENEC_GRID_POWER = "gridPower";
Expand Down Expand Up @@ -107,4 +109,9 @@ public class SenecHomeBindingConstants {
public static final String CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH2 = "wallbox1ChargingCurrentPhase2";
public static final String CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH3 = "wallbox1ChargingCurrentPhase3";
public static final String CHANNEL_SENEC_WALLBOX1_CHARGING_POWER = "wallbox1ChargingPower";

// Charge Mode Definitions
public static final String STATE_SENEC_CHARGE_MODE_OFF = "OFF";
public static final String STATE_SENEC_CHARGE_MODE_CHARGE = "CHARGE";
public static final String STATE_SENEC_CHARGE_MODE_STORAGE = "STORAGE";
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
*
* @author Steven Schwarznau - Initial contribution
* @author Erwin Guib - added more channels, added some convenience methods to reduce code duplication
* @author Lukas Pindl - Update for writing to chargeMode
*/
@NonNullByDefault
public class SenecHomeHandler extends BaseThingHandler {
Expand Down Expand Up @@ -101,7 +102,32 @@ public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Refreshing {}", channelUID);
refresh();
} else {
logger.trace("The SenecHome-Binding is a read-only binding and can not handle commands");
String channelID = channelUID.getId();

logger.trace("Channel: {}", channelID);
switch (channelID) {
case CHANNEL_SENEC_CHARGE_MODE: {
if (command instanceof StringType stringCommand) {
logger.trace("Command: {} ", stringCommand.toString());
if (stringCommand.toString().equals(STATE_SENEC_CHARGE_MODE_OFF)) {
senecHomeApi.setValue("ENERGY", "SAFE_CHARGE_PROHIBIT", "u8_01");
senecHomeApi.setValue("ENERGY", "LI_STORAGE_MODE_STOP", "u8_01");
} else if (stringCommand.toString().equals(STATE_SENEC_CHARGE_MODE_CHARGE)) {
senecHomeApi.setValue("ENERGY", "SAFE_CHARGE_FORCE", "u8_01");
senecHomeApi.setValue("ENERGY", "LI_STORAGE_MODE_STOP", "u8_01");
} else if (stringCommand.toString().equals(STATE_SENEC_CHARGE_MODE_STORAGE)) {
senecHomeApi.setValue("ENERGY", "SAFE_CHARGE_PROHIBIT", "u8_01");
senecHomeApi.setValue("ENERGY", "LI_STORAGE_MODE_START", "u8_01");
}
updateState(channelUID, stringCommand);
}
break;
}
default: {
logger.warn("Received command on unexpected channel: {}", channelID);
break;
}
}
}
}

Expand Down Expand Up @@ -186,6 +212,9 @@ private <Q extends Quantity<Q>> void updateQtyStateIfAvailable(String channel, S
updateQtyState(CHANNEL_SENEC_BATTERY_POWER, response.energy.batteryPower, 2, Units.WATT);
updateQtyState(CHANNEL_SENEC_BATTERY_CURRENT, response.energy.batteryCurrent, 2, Units.AMPERE);
updateQtyState(CHANNEL_SENEC_BATTERY_VOLTAGE, response.energy.batteryVoltage, 2, Units.VOLT);

updateChargeState(CHANNEL_SENEC_CHARGE_MODE, response.energy.safeChargeMode, response.energy.liStorageMode);

updateStringStateFromInt(CHANNEL_SENEC_SYSTEM_STATE, response.energy.systemState,
SenecSystemStatus::descriptionFromCode);
updateDecimalState(CHANNEL_SENEC_SYSTEM_STATE_VALUE, response.energy.systemState);
Expand Down Expand Up @@ -314,6 +343,21 @@ protected void updateStringStateFromInt(String channelName, String senecValue,
}
}

protected void updateChargeState(String channelName, String senecValueCharge, String senecValueStorage) {
Channel channel = getThing().getChannel(channelName);
if (channel != null) {
BigDecimal valueCharge = getSenecValue(senecValueCharge);
BigDecimal valueStorage = getSenecValue(senecValueStorage);
if (valueStorage.intValue() == 1) {
updateState(channel.getUID(), new StringType(STATE_SENEC_CHARGE_MODE_STORAGE));
} else if (valueCharge.intValue() == 1) {
updateState(channel.getUID(), new StringType(STATE_SENEC_CHARGE_MODE_CHARGE));
} else {
updateState(channel.getUID(), new StringType(STATE_SENEC_CHARGE_MODE_OFF));
}
}
}

protected void updateDecimalState(String channelName, String senecValue) {
Channel channel = getThing().getChannel(channelName);
if (channel != null) {
Expand All @@ -322,6 +366,14 @@ protected void updateDecimalState(String channelName, String senecValue) {
}
}

protected void updateSwitchState(String channelName, String senecValue) {
Channel channel = getThing().getChannel(channelName);
if (channel != null) {
BigDecimal value = getSenecValue(senecValue);
updateState(channel.getUID(), OnOffType.from(value.intValue() == 1));
}
}

protected <Q extends Quantity<Q>> void updateQtyState(String channelName, String senecValue, int scale,
Unit<Q> unit) {
updateQtyState(channelName, senecValue, scale, unit, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* Section is "ENERGY"
*
* @author Steven Schwarznau - Initial Contribution
* @author Lukas Pindl - Update for writing to safeChargeMode
*/
public class SenecHomeEnergy implements Serializable {

Expand Down Expand Up @@ -64,11 +65,21 @@ public class SenecHomeEnergy implements Serializable {
*/
public @SerializedName("STAT_STATE") String systemState;

/**
* Safe Charge Mode Running.
*/
public @SerializedName("SAFE_CHARGE_RUNNING") String safeChargeMode;

/**
* Lithium Storage Mode Running.
*/
public @SerializedName("LI_STORAGE_MODE_RUNNING") String liStorageMode;

@Override
public String toString() {
return "SenecHomeEnergy [housePowerConsumption=" + housePowerConsumption + ", inverterPowerGeneration="
+ inverterPowerGeneration + ", batteryPower=" + batteryPower + ", batteryVoltage=" + batteryVoltage
+ ", batteryCurrent=" + batteryCurrent + ", batteryFuelCharge=" + batteryFuelCharge + ", systemState="
+ systemState + "]";
+ systemState + ", safeChargeMode=" + safeChargeMode + ", liStorageMode=" + liStorageMode + "]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ channel-type.senechome.batteryPower.label = Battery Power
channel-type.senechome.batteryTemperature.label = Battery Temperature
channel-type.senechome.batteryVoltage.label = Battery Voltage
channel-type.senechome.caseTemperature.label = Case Temperature
channel-type.senechome.chargeMode.label = Safe Charge Mode
channel-type.senechome.chargeMode.state.option.OFF = Off
channel-type.senechome.chargeMode.state.option.CHARGE = Safe Charge
channel-type.senechome.chargeMode.state.option.STORAGE = Lithium Storage
channel-type.senechome.chargedEnergyPack1.label = Total charged energy battery pack 1
channel-type.senechome.chargedEnergyPack2.label = Total charged energy battery pack 2
channel-type.senechome.chargedEnergyPack3.label = Total charged energy battery pack 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<channel id="batteryFuelCharge" typeId="batteryFuelCharge"/>
<channel id="systemState" typeId="systemState"/>
<channel id="systemStateValue" typeId="systemStateValue"/>
<channel id="chargeMode" typeId="chargeMode"/>

<!-- SenecHomeGrid -->
<channel id="gridPower" typeId="gridPower"/>
Expand Down Expand Up @@ -242,6 +243,18 @@
<category>Number</category>
<state readOnly="true" pattern="%d"/>
</channel-type>
<channel-type id="chargeMode">
<item-type>String</item-type>
<label>Safe Charge Mode</label>
<category>Text</category>
<state readOnly="false" pattern="%s">
<options>
<option value="OFF">Off</option>
<option value="CHARGE">Safe Charge</option>
<option value="STORAGE">Lithium Storage</option>
</options>
</state>
</channel-type>


<channel-type id="gridPower">
Expand Down

0 comments on commit 5c99a6c

Please sign in to comment.