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 |
- 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
| 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 |
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
Default item=SenecGridVoltagePh3
Default item=SenecGridFrequency
Default item=SenecBatteryVoltage
+ Default item=SenecBatteryChargeMode
}
}
```
*
* @author Steven Schwarznau - Initial contribution
* @author Robert Delbrück - Update for Senec API changes
+ * @author Lukas Pindl - Update for writing to safeChargeMode
*
*/
@NonNullByDefault
*/
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);
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());
* used across the whole binding.
*
* @author Steven Schwarznau - Initial contribution
+ * @author Lukas Pindl - Update for writing to chargeMode
*/
@NonNullByDefault
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";
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";
}
*
* @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 {
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;
+ }
+ }
}
}
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);
}
}
+ 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) {
}
}
+ 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);
* Section is "ENERGY"
*
* @author Steven Schwarznau - Initial Contribution
+ * @author Lukas Pindl - Update for writing to safeChargeMode
*/
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 + "]";
}
}
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
<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"/>
<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">