mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[SenecHome] Add writeable Charging Modes (#17474)
* [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>
This commit is contained in:
parent
e655ddb65d
commit
5c99a6c19c
@ -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 |
|
||||
@ -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
|
||||
|
||||
@ -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 |
|
||||
@ -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
|
||||
@ -166,6 +172,7 @@ Text label="Power Grid"{
|
||||
Default item=SenecGridVoltagePh3
|
||||
Default item=SenecGridFrequency
|
||||
Default item=SenecBatteryVoltage
|
||||
Default item=SenecBatteryChargeMode
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -40,6 +40,7 @@ import com.google.gson.JsonSyntaxException;
|
||||
*
|
||||
* @author Steven Schwarznau - Initial contribution
|
||||
* @author Robert Delbrück - Update for Senec API changes
|
||||
* @author Lukas Pindl - Update for writing to safeChargeMode
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@ -72,6 +73,38 @@ public class SenecHomeApi {
|
||||
*/
|
||||
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);
|
||||
|
||||
@ -80,13 +113,11 @@ public class SenecHomeApi {
|
||||
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());
|
||||
|
@ -20,6 +20,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Steven Schwarznau - Initial contribution
|
||||
* @author Lukas Pindl - Update for writing to chargeMode
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SenecHomeBindingConstants {
|
||||
@ -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";
|
||||
@ -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";
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ import com.google.gson.JsonParseException;
|
||||
*
|
||||
* @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 {
|
||||
@ -101,7 +102,32 @@ public class SenecHomeHandler extends BaseThingHandler {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,6 +212,9 @@ public class SenecHomeHandler extends BaseThingHandler {
|
||||
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);
|
||||
@ -314,6 +343,21 @@ public class SenecHomeHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -322,6 +366,14 @@ public class SenecHomeHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -23,6 +23,7 @@ import com.google.gson.annotations.SerializedName;
|
||||
* Section is "ENERGY"
|
||||
*
|
||||
* @author Steven Schwarznau - Initial Contribution
|
||||
* @author Lukas Pindl - Update for writing to safeChargeMode
|
||||
*/
|
||||
public class SenecHomeEnergy implements Serializable {
|
||||
|
||||
@ -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 + "]";
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"/>
|
||||
@ -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">
|
||||
|
Loading…
Reference in New Issue
Block a user