]> git.basschouten.com Git - openhab-addons.git/commitdiff
[SenecHome] Add writeable Charging Modes (#17474)
authorLukas Pindl <36566235+BigFood2307@users.noreply.github.com>
Sat, 5 Oct 2024 14:57:02 +0000 (16:57 +0200)
committerGitHub <noreply@github.com>
Sat, 5 Oct 2024 14:57:02 +0000 (16:57 +0200)
* [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>
bundles/org.openhab.binding.senechome/README.md
bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeApi.java
bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeBindingConstants.java
bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeHandler.java
bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/dto/SenecHomeEnergy.java
bundles/org.openhab.binding.senechome/src/main/resources/OH-INF/i18n/senechome.properties
bundles/org.openhab.binding.senechome/src/main/resources/OH-INF/thing/thing-types.xml

index c188babea896aef6c635209862604a37e33fddf3..4d542b04f3b0a976d02fe26090988480dc6bca52 100644 (file)
@@ -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
     }
 }
 ```
index ccb0246e7a9648a600f47295847a30a963282499..20b8c1528909e2aebd9fcad0db50b1dbf7929ef4 100644 (file)
@@ -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());
index 4bd009b20ba58d42e2aa774e51d3db9e7fa90cd0..d1c520c8e5920bf08da856aa40c8edb8b659fa9d 100644 (file)
@@ -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";
 }
index a5eaf7831b52bf704dc1e760786444ddd48aa950..7f52c51b8025cd7a238774265ddd77e1a1ab5595 100644 (file)
@@ -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);
index f943394127df4f899fa4515b48c3b249aa2e4523..385d2f4752abed52503f6363d9b508530febca33 100644 (file)
@@ -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 + "]";
     }
 }
index bde416149b4be8fbf9999270eadfdbde69ebd2cd..7c5576d996ce6470927e98230e6b9cfedd1e63d7 100644 (file)
@@ -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
index db42197ddb32caeac93c7ce3703b349af4e371d5..40b06ad2cef77854e55b2f3a8c240512368d4e34 100644 (file)
@@ -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"/>
                <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">