]> git.basschouten.com Git - openhab-addons.git/commitdiff
[homewizard] Add current, voltage and failure channels (#16995)
authorlsiepel <leosiepel@gmail.com>
Mon, 7 Oct 2024 06:11:04 +0000 (08:11 +0200)
committerGitHub <noreply@github.com>
Mon, 7 Oct 2024 06:11:04 +0000 (08:11 +0200)
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
27 files changed:
bundles/org.openhab.binding.homewizard/README.md
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/DataPayload.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardBindingConstants.java
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardDeviceHandler.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardEnergySocketHandler.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardHandlerFactory.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandler.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardStatefulDeviceHandler.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardWaterMeterHandler.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/StatePayload.java [deleted file]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/DataPayload.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/StatePayload.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardDeviceHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardEnergySocketHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardP1MeterHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardStatefulDeviceHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardWaterMeterHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/i18n/homewizard.properties
bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/update/instructions.xml [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerMock.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/DataUtil.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/P1PayloadTest.java [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response-empty.json [new file with mode: 0644]
bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response.json [new file with mode: 0644]

index 93e2d84813b220c42f496184da1c50fa67c1fec7..f76ef561f4dbc2532483f9d95a32dcd40779a0b2 100644 (file)
@@ -48,23 +48,33 @@ For DSMR5 meters this is generally once per second, for older versions the frequ
 
 ## Channels
 
-| Channel ID             | Item Type                 | Description                                                                                |Available|
-|------------------------|---------------------------|--------------------------------------------------------------------------------------------|---------|
-| total_energy_import_t1 | Number:Energy             | The most recently reported total imported energy in kWh by counter 1.                      | P,E     |
-| total_energy_import_t2 | Number:Energy             | The most recently reported total imported energy in kWh by counter 2.                      | P       |
-| total_energy_export_t1 | Number:Energy             | The most recently reported total exported energy in kWh by counter 1.                      | P,E     |
-| total_energy_export_t2 | Number:Energy             | The most recently reported total exported energy in kWh by counter 2.                      | P       |
-| active_power           | Number:Power              | The current net total power in W. It will be below 0 if power is currently being exported. | P,E     |
-| active_power_l1        | Number:Power              | The current net total power in W for phase 1.                                              | P       |
-| active_power_l2        | Number:Power              | The current net total power in W for phase 2.                                              | P       |
-| active_power_l3        | Number:Power              | The current net total power in W for phase 3.                                              | P       |
-| total_gas              | Number:Volume             | The most recently reported total imported gas in m^3.                                      | P       |
-| gas_timestamp          | DateTime                  | The time stamp of the total_gas measurement.                                               | P       |
-| total_water            | Number:Volume             | Total water used.                                                                          | W       |
-| current_water          | Number:VolumetricFlowRate | Current water usage.                                                                       | W       |
-| power_switch           | Switch                    | Controls the power switch of the socket.                                                   | E       |
-| power_lock             | Switch                    | Controls the lock of the power switch (un/locking both the API and the physical button)    | E       |
-| ring_brightness        | Number:Dimensionless      | Controls the brightness of the ring on the socket                                          | E       |
+| Channel ID             | Item Type                 | Description                                                                                | Available |
+|------------------------|---------------------------|--------------------------------------------------------------------------------------------|-----------|
+| active_current         | Number:ElectricCurrent    | The combined current in A vor all phases                                                   | P,E       |
+| active_current_l1      | Number:ElectricCurrent    | The active current in A for phase 1.                                                       | P         |
+| active_current_l2      | Number:ElectricCurrent    | The active current in A for phase 2.                                                       | P         |
+| active_current_l3      | Number:ElectricCurrent    | The active current in A for phase 3.                                                       | P         |
+| active_power           | Number:Power              | The current net total power in W. It will be below 0 if power is currently being exported. | P,E       |
+| active_power_l1        | Number:Power              | The current net total power in W for phase 1.                                              | P         |
+| active_power_l2        | Number:Power              | The current net total power in W for phase 2.                                              | P         |
+| active_power_l3        | Number:Power              | The current net total power in W for phase 3.                                              | P         |
+| active_voltage         | Number:ElectricPotential  | The active voltage in V                                                                    | P         |
+| active_voltage_l1      | Number:ElectricPotential  | The active voltage in V for phase 1.                                                       | P         |
+| active_voltage_l2      | Number:ElectricPotential  | The active voltage in V for phase 2.                                                       | P         |
+| active_voltage_l3      | Number:ElectricPotential  | The active voltage in V for phase 3.                                                       | P         |
+| total_energy_import_t1 | Number:Energy             | The most recently reported total imported energy in kWh by counter 1.                      | P,E       |
+| total_energy_import_t2 | Number:Energy             | The most recently reported total imported energy in kWh by counter 2.                      | P         |
+| total_energy_export_t1 | Number:Energy             | The most recently reported total exported energy in kWh by counter 1.                      | P,E       |
+| total_energy_export_t2 | Number:Energy             | The most recently reported total exported energy in kWh by counter 2.                      | P         |
+| total_gas              | Number:Volume             | The most recently reported total imported gas in m^3.                                      | P         |
+| gas_timestamp          | DateTime                  | The time stamp of the total_gas measurement.                                               | P         |
+| total_water            | Number:Volume             | Total water used.                                                                          | W         |
+| current_water          | Number:VolumetricFlowRate | Current water usage.                                                                       | W         |
+| power_failures         | Number                    | The count of long power failures.                                                          | P         |
+| long_power_failures    | Number                    | the count of any power failures.                                                           | P         |
+| power_switch           | Switch                    | Controls the power switch of the socket.                                                   | E         |
+| power_lock             | Switch                    | Controls the lock of the power switch (un/locking both the API and the physical button)    | E         |
+| ring_brightness        | Number:Dimensionless      | Controls the brightness of the ring on the socket                                          | E         |
 
 ## Full Example
 
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/DataPayload.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/DataPayload.java
deleted file mode 100644 (file)
index c1bc9e6..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Class that provides storage for the json objects obtained from HomeWizard devices.
- *
- * @author DaniĆ«l van Os - Initial contribution
- *
- */
-@NonNullByDefault
-public class DataPayload {
-    private int smrVersion = 0;
-    private String meterModel = "";
-    private String wifiSsid = "";
-    private int wifiStrength = 0;
-
-    @SerializedName("total_power_import_t1_kwh")
-    private double totalEnergyImportT1Kwh;
-    @SerializedName("total_power_import_t2_kwh")
-    private double totalEnergyImportT2Kwh;
-    @SerializedName("total_power_export_t1_kwh")
-    private double totalEnergyExportT1Kwh;
-    @SerializedName("total_power_export_t2_kwh")
-    private double totalEnergyExportT2Kwh;
-
-    private double activePowerW;
-    private double activePowerL1W;
-    private double activePowerL2W;
-    private double activePowerL3W;
-    private double totalGasM3;
-    private long gasTimestamp = 0;
-
-    @SerializedName("total_liter_m3")
-    private double totalWaterM3;
-    @SerializedName("active_liter_lpm")
-    private double currentWaterLPM;
-
-    /**
-     * Getter for the smart meter version
-     *
-     * @return The most recent smart meter version obtained from the API
-     */
-    public int getSmrVersion() {
-        return smrVersion;
-    }
-
-    /**
-     * Setter for the smart meter version
-     *
-     * @param smrVersion The smart meter version to set
-     */
-    public void setSmrVersion(int smrVersion) {
-        this.smrVersion = smrVersion;
-    }
-
-    /**
-     * Getter for the meter model
-     *
-     * @return meter model
-     */
-    public String getMeterModel() {
-        return meterModel;
-    }
-
-    /**
-     * Setter for the meter model
-     *
-     * @param meterModel meter model
-     */
-    public void setMeterModel(String meterModel) {
-        this.meterModel = meterModel;
-    }
-
-    /**
-     * Getter for the meter's wifi ssid
-     *
-     * @return the meter's wifi sid
-     */
-    public String getWifiSsid() {
-        return wifiSsid;
-    }
-
-    /**
-     * Setter for the wifi ssid
-     *
-     * @param wifiSsid wifi ssid
-     */
-    public void setWifiSsid(String wifiSsid) {
-        this.wifiSsid = wifiSsid;
-    }
-
-    /**
-     * Getter for the wifi rssi
-     *
-     * @return wifi rssi
-     */
-    public int getWifiStrength() {
-        return wifiStrength;
-    }
-
-    /**
-     * Setter for the wifi rssi
-     *
-     * @param wifiStrength wifi rssi
-     */
-    public void setWifiStrength(int wifiStrength) {
-        this.wifiStrength = wifiStrength;
-    }
-
-    /**
-     * Getter for the total imported energy on counter 1
-     *
-     * @return total imported energy on counter 1
-     */
-    public double getTotalEnergyImportT1Kwh() {
-        return totalEnergyImportT1Kwh;
-    }
-
-    /**
-     * Setter for the total imported energy on counter 1
-     *
-     * @param totalEnergyImportT1Kwh total imported energy on counter 1
-     */
-    public void setTotalEnergyImportT1Kwh(double totalEnergyImportT1Kwh) {
-        this.totalEnergyImportT1Kwh = totalEnergyImportT1Kwh;
-    }
-
-    /**
-     * Getter for the total imported energy on counter 2
-     *
-     * @return total imported energy on counter 2
-     */
-    public double getTotalEnergyImportT2Kwh() {
-        return totalEnergyImportT2Kwh;
-    }
-
-    /**
-     * Setter for the total imported energy on counter 2
-     *
-     * @param totalEnergyImportT2Kwh
-     */
-    public void setTotalEnergyImportT2Kwh(double totalEnergyImportT2Kwh) {
-        this.totalEnergyImportT2Kwh = totalEnergyImportT2Kwh;
-    }
-
-    /**
-     * Getter for the total exported energy on counter 1
-     *
-     * @return total exported energy on counter 1
-     */
-    public double getTotalEnergyExportT1Kwh() {
-        return totalEnergyExportT1Kwh;
-    }
-
-    /**
-     * Setter for the total exported energy on counter 1
-     *
-     * @param totalEnergyExportT1Kwh
-     */
-    public void setTotalEnergyExportT1Kwh(double totalEnergyExportT1Kwh) {
-        this.totalEnergyExportT1Kwh = totalEnergyExportT1Kwh;
-    }
-
-    /**
-     * Getter for the total exported energy on counter 2
-     *
-     * @return total exported energy on counter 2
-     */
-    public double getTotalEnergyExportT2Kwh() {
-        return totalEnergyExportT2Kwh;
-    }
-
-    /**
-     * Setter for the total exported energy on counter 2
-     *
-     * @param totalEnergyExportT2Kwh
-     */
-    public void setTotalEnergyExportT2Kwh(double totalEnergyExportT2Kwh) {
-        this.totalEnergyExportT2Kwh = totalEnergyExportT2Kwh;
-    }
-
-    /**
-     * Getter for the current active total power
-     *
-     * @return current active total power
-     */
-    public double getActivePowerW() {
-        return activePowerW;
-    }
-
-    /**
-     * Setter for the current active total power
-     *
-     * @param activePowerW
-     */
-    public void setActivePowerW(double activePowerW) {
-        this.activePowerW = activePowerW;
-    }
-
-    /**
-     * Getter for the current active total power on phase 1
-     *
-     * @return current active total power on phase 1
-     */
-    public double getActivePowerL1W() {
-        return activePowerL1W;
-    }
-
-    /**
-     * Setter for the current active power on phase 1
-     *
-     * @param activePowerL1W current active total power on phase 1
-     */
-    public void setActivePowerL1W(double activePowerL1W) {
-        this.activePowerL1W = activePowerL1W;
-    }
-
-    /**
-     * Getter for the current active total power on phase 2
-     *
-     * @return current active total power on phase 2
-     */
-    public double getActivePowerL2W() {
-        return activePowerL2W;
-    }
-
-    /**
-     * Setter for the current active power on phase 2
-     *
-     * @param activePowerL2W current active total power on phase 2
-     */
-    public void setActivePowerL2W(double activePowerL2W) {
-        this.activePowerL2W = activePowerL2W;
-    }
-
-    /**
-     * Getter for the current active total power on phase 3
-     *
-     * @return current active total power on phase 3
-     */
-    public double getActivePowerL3W() {
-        return activePowerL3W;
-    }
-
-    /**
-     * Setter for the current active power on phase 3
-     *
-     * @param activePowerL3W current active total power on phase 3
-     */
-    public void setActivePowerL3W(double activePowerL3W) {
-        this.activePowerL3W = activePowerL3W;
-    }
-
-    /**
-     * Getter for the total imported gas volume
-     *
-     * @return total imported gas volume
-     */
-    public double getTotalGasM3() {
-        return totalGasM3;
-    }
-
-    /**
-     * Setter for the total imported gas volume
-     *
-     * @param totalGasM3 total imported gas volume
-     */
-    public void setTotalGasM3(double totalGasM3) {
-        this.totalGasM3 = totalGasM3;
-    }
-
-    /**
-     * Getter for the time stamp of the last gas update
-     *
-     * @return time stamp of the last gas update
-     */
-    public long getGasTimestamp() {
-        return gasTimestamp;
-    }
-
-    /**
-     * Setter for the time stamp of the last gas update
-     *
-     * @param gasTimestamp time stamp of the last gas update
-     */
-    public void setGasTimestamp(long gasTimestamp) {
-        this.gasTimestamp = gasTimestamp;
-    }
-
-    /**
-     * Getter for the total imported water volume
-     *
-     * @return total imported water volume
-     */
-    public double getTotalWaterM3() {
-        return totalWaterM3;
-    }
-
-    /**
-     * Setter for the total imported water volume
-     *
-     * @param totalWaterM3 total imported water volume
-     */
-    public void setTotalWaterM3(double totalWaterM3) {
-        this.totalWaterM3 = totalWaterM3;
-    }
-
-    /**
-     * Getter for the current water flow
-     *
-     * @return current water flow
-     */
-    public double getCurrentWaterLPM() {
-        return currentWaterLPM;
-    }
-
-    /**
-     * Setter for the current water flow
-     *
-     * @param currentWaterLPM current water flow
-     */
-    public void setCurrentWaterLPM(double currentWaterLPM) {
-        this.currentWaterLPM = currentWaterLPM;
-    }
-
-    @Override
-    public String toString() {
-        return String.format(
-                """
-                        Data [smrVersion: %d meterModel: %s wifiSsid: %s wifiStrength: %d"
-                        totalEnergyImportT1Kwh: %f totalEnergyImportT2Kwh: %f totalEnergyExportT1Kwh: %f totalEnergyExportT2Kwh: %f"
-                        activePowerW: %f activePowerL1W: %f activePowerL2W: %f activePowerL3W: %f totalGasM3: %f gasTimestamp: %.0f"
-                        totalWaterM3: %f currentWaterLPM: %f]
-                        """,
-                smrVersion, meterModel, wifiSsid, wifiStrength, totalEnergyImportT1Kwh, totalEnergyImportT2Kwh,
-                totalEnergyExportT1Kwh, totalEnergyExportT2Kwh, activePowerW, activePowerL1W, activePowerL2W,
-                activePowerL3W, totalGasM3, gasTimestamp, totalWaterM3, currentWaterLPM);
-    }
-}
index 3943f2bed36c4bc0f732c3e46d885c5a82fc49c9..8b28b204c890fd05bf597967a6cbe4cb510137ea 100644 (file)
@@ -32,16 +32,27 @@ public class HomeWizardBindingConstants {
     public static final ThingTypeUID THING_TYPE_WATERMETER = new ThingTypeUID(BINDING_ID, "watermeter");
 
     // List of all Channel ids
-    public static final String CHANNEL_ENERGY_IMPORT_T1 = "total_energy_import_t1";
-    public static final String CHANNEL_ENERGY_IMPORT_T2 = "total_energy_import_t2";
-    public static final String CHANNEL_ENERGY_EXPORT_T1 = "total_energy_export_t1";
-    public static final String CHANNEL_ENERGY_EXPORT_T2 = "total_energy_export_t2";
+    public static final String CHANNEL_ACTIVE_CURRENT = "active_current";
+    public static final String CHANNEL_ACTIVE_CURRENT_L1 = "active_current_l1";
+    public static final String CHANNEL_ACTIVE_CURRENT_L2 = "active_current_l2";
+    public static final String CHANNEL_ACTIVE_CURRENT_L3 = "active_current_l3";
     public static final String CHANNEL_ACTIVE_POWER = "active_power";
     public static final String CHANNEL_ACTIVE_POWER_L1 = "active_power_l1";
     public static final String CHANNEL_ACTIVE_POWER_L2 = "active_power_l2";
     public static final String CHANNEL_ACTIVE_POWER_L3 = "active_power_l3";
-    public static final String CHANNEL_TOTAL_GAS = "total_gas";
+    public static final String CHANNEL_ACTIVE_VOLTAGE = "active_voltage";
+    public static final String CHANNEL_ACTIVE_VOLTAGE_L1 = "active_voltage_l1";
+    public static final String CHANNEL_ACTIVE_VOLTAGE_L2 = "active_voltage_l2";
+    public static final String CHANNEL_ACTIVE_VOLTAGE_L3 = "active_voltage_l3";
+    public static final String CHANNEL_POWER_FAILURES = "power_failures";
+    public static final String CHANNEL_LONG_POWER_FAILURES = "long_power_failures";
+    public static final String CHANNEL_ENERGY_IMPORT_T1 = "total_energy_import_t1";
+    public static final String CHANNEL_ENERGY_IMPORT_T2 = "total_energy_import_t2";
+    public static final String CHANNEL_ENERGY_EXPORT_T1 = "total_energy_export_t1";
+    public static final String CHANNEL_ENERGY_EXPORT_T2 = "total_energy_export_t2";
+
     public static final String CHANNEL_GAS_TIMESTAMP = "gas_timestamp";
+    public static final String CHANNEL_GAS_TOTAL = "total_gas";
     public static final String CHANNEL_TOTAL_WATER = "total_water";
     public static final String CHANNEL_CURRENT_WATER = "current_water";
 
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardDeviceHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardDeviceHandler.java
deleted file mode 100644 (file)
index 9e9f74f..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import java.io.IOException;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.io.net.http.HttpUtil;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BaseThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.FieldNamingPolicy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-/**
- * The {@link HomeWizardDeviceHandler} is a base class for all
- * HomeWizard devices. It provides configuration and polling of
- * data from a device. It also processes common data.
- * 
- * @author DaniĆ«l van Os - Initial contribution
- */
-@NonNullByDefault
-public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
-
-    protected final Logger logger = LoggerFactory.getLogger(HomeWizardDeviceHandler.class);
-    protected final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
-            .create();
-
-    private HomeWizardConfiguration config = new HomeWizardConfiguration();
-    private @Nullable ScheduledFuture<?> pollingJob;
-
-    protected String apiURL = "";
-
-    /**
-     * Constructor
-     *
-     * @param thing The thing to handle
-     */
-    public HomeWizardDeviceHandler(Thing thing) {
-        super(thing);
-    }
-
-    /**
-     * If a host has been specified start polling it
-     */
-    @Override
-    public void initialize() {
-        config = getConfigAs(HomeWizardConfiguration.class);
-        if (configure()) {
-            pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, TimeUnit.SECONDS);
-        }
-    }
-
-    /**
-     * Check the current configuration
-     *
-     * @return true if the configuration is ok to start polling, false otherwise
-     */
-    private boolean configure() {
-        if (config.ipAddress.trim().isEmpty()) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
-                    "Missing ipAddress/host configuration");
-            return false;
-        } else {
-            updateStatus(ThingStatus.UNKNOWN);
-            apiURL = String.format("http://%s/api/v1/", config.ipAddress.trim());
-            return true;
-        }
-    }
-
-    /**
-     * dispose: stop the poller
-     */
-    @Override
-    public void dispose() {
-        var job = pollingJob;
-        if (job != null) {
-            job.cancel(true);
-        }
-        pollingJob = null;
-    }
-
-    /**
-     * Device specific handling of the returned data payload.
-     *
-     * @param payload The data parsed from the data Json file
-     */
-    protected abstract void handleDataPayload(DataPayload payload);
-
-    /**
-     *
-     */
-    protected void pollData() {
-        final String dataResult;
-
-        try {
-            dataResult = HttpUtil.executeUrl("GET", apiURL + "data", 30000);
-        } catch (IOException e) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                    String.format("Unable to query device data: %s", e.getMessage()));
-            return;
-        }
-
-        if (dataResult.trim().isEmpty()) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device returned empty data");
-            return;
-        }
-
-        DataPayload dataPayload = gson.fromJson(dataResult, DataPayload.class);
-        if (dataPayload == null) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                    "Unable to parse data response from device");
-            return;
-        }
-
-        if ("".equals(dataPayload.getWifiSsid())) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Results from API are empty");
-            return;
-        }
-
-        updateStatus(ThingStatus.ONLINE);
-        handleDataPayload(dataPayload);
-    }
-
-    /**
-     * The actual polling loop
-     */
-    protected void pollingCode() {
-        pollData();
-    }
-}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardEnergySocketHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardEnergySocketHandler.java
deleted file mode 100644 (file)
index 72c844f..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.RefreshType;
-
-/**
- * The {@link HomeWizardEnergySocketHandler} implements functionality to handle a HomeWizard EnergySocket.
- *
- * @author DaniĆ«l van Os - Initial contribution
- */
-@NonNullByDefault
-public class HomeWizardEnergySocketHandler extends HomeWizardStatefulDeviceHandler {
-
-    /**
-     * Constructor
-     *
-     * @param thing The thing to handle
-     */
-    public HomeWizardEnergySocketHandler(Thing thing) {
-        super(thing);
-    }
-
-    /**
-     * Converts a brightness value (0..255) to a percentage.
-     *
-     * @param brightness The brightness to convert.
-     * @return brightness percentage
-     */
-    private int brightnessToPercentage(int brightness) {
-        return (int) (100.0 * brightness / 255.0 + 0.5);
-    }
-
-    /**
-     * Converts a percentage to a brightness value (0..255)
-     *
-     * @param percentage The percentage to convert.
-     * @return brightness value
-     */
-    private int percentageToBrightness(String percentage) {
-        return (int) (Double.valueOf(percentage) * 255.0 / 100.0 + 0.5);
-    }
-
-    /**
-     * Handle incoming commands.
-     *
-     * Power on/off, Power lock/unlock and Ring brightness are supported.
-     */
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-        if (command instanceof RefreshType) {
-            // For now I prefer not updating immediately above firing a full update request for each channel
-            return;
-        }
-
-        StatePayload result = null;
-
-        /*
-         * The returned payloads below only contain the modified value, so each has it's own
-         * call to updateState instead of just calling handleStatePayload() with the returned
-         * payload.
-         */
-
-        switch (channelUID.getId()) {
-            case HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS: {
-                result = sendStateCommand(
-                        String.format("{\"brightness\": %d}", percentageToBrightness(command.toFullString())));
-                if (result != null) {
-                    updateState(HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
-                            new PercentType(brightnessToPercentage(result.getBrightness())));
-                }
-                break;
-            }
-            case HomeWizardBindingConstants.CHANNEL_POWER_SWITCH: {
-                boolean onOff = command.equals(OnOffType.ON);
-                result = sendStateCommand(String.format("{\"power_on\": %b}", onOff));
-                if (result != null) {
-                    updateState(HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(result.getPowerOn()));
-                }
-                break;
-            }
-            case HomeWizardBindingConstants.CHANNEL_POWER_LOCK: {
-                boolean onOff = command.equals(OnOffType.ON);
-                result = sendStateCommand(String.format("{\"switch_lock\": %b}", onOff));
-                if (result != null) {
-                    updateState(HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(result.getSwitchLock()));
-                }
-                break;
-            }
-            default:
-                logger.warn("Should handle {} {}", channelUID.getIdWithoutGroup(), command);
-                break;
-        }
-    }
-
-    /**
-     * Device specific handling of the returned payload.
-     *
-     * @param payload The data parsed from the Json file
-     */
-    @Override
-    protected void handleDataPayload(DataPayload payload) {
-        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
-                new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
-        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
-                new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
-        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
-                new QuantityType<>(payload.getActivePowerW(), Units.WATT));
-    }
-
-    @Override
-    protected void handleStatePayload(StatePayload payload) {
-        updateState(HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(payload.getPowerOn()));
-        updateState(HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(payload.getSwitchLock()));
-        updateState(HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
-                new PercentType(brightnessToPercentage(payload.getBrightness())));
-    }
-}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardHandlerFactory.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardHandlerFactory.java
deleted file mode 100644 (file)
index 57e6b80..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import static org.openhab.binding.homewizard.internal.HomeWizardBindingConstants.*;
-
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.binding.BaseThingHandlerFactory;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerFactory;
-import org.osgi.service.component.annotations.Component;
-
-/**
- * The {@link HomeWizardHandlerFactory} is responsible for creating things and thing
- * handlers.
- *
- * @author DaniĆ«l van Os - Initial contribution
- */
-@NonNullByDefault
-@Component(configurationPid = "binding.homewizard", service = ThingHandlerFactory.class)
-public class HomeWizardHandlerFactory extends BaseThingHandlerFactory {
-
-    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_P1_METER,
-            THING_TYPE_ENERGY_SOCKET, THING_TYPE_WATERMETER);
-
-    @Override
-    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
-        return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
-    }
-
-    @Override
-    protected @Nullable ThingHandler createHandler(Thing thing) {
-        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
-
-        if (THING_TYPE_P1_METER.equals(thingTypeUID)) {
-            return new HomeWizardP1MeterHandler(thing);
-        }
-
-        if (THING_TYPE_ENERGY_SOCKET.equals(thingTypeUID)) {
-            return new HomeWizardEnergySocketHandler(thing);
-        }
-
-        if (THING_TYPE_WATERMETER.equals(thingTypeUID)) {
-            return new HomeWizardWaterMeterHandler(thing);
-        }
-
-        return null;
-    }
-}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandler.java
deleted file mode 100644 (file)
index 3f8785c..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import java.time.DateTimeException;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.types.Command;
-
-/**
- * The {@link HomeWizardP1MeterHandler} implements functionality to handle a HomeWizard P1 Meter.
- *
- * @author DaniĆ«l van Os - Initial contribution
- */
-@NonNullByDefault
-public class HomeWizardP1MeterHandler extends HomeWizardDeviceHandler {
-
-    private String meterModel = "";
-    private int meterVersion = 0;
-
-    /**
-     * Constructor
-     *
-     * @param thing The thing to handle
-     */
-    public HomeWizardP1MeterHandler(Thing thing) {
-        super(thing);
-    }
-
-    /**
-     * Not listening to any commands.
-     */
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-    }
-
-    /**
-     * Device specific handling of the returned payload.
-     *
-     * @param payload The data parsed from the Json file
-     */
-    @Override
-    protected void handleDataPayload(DataPayload payload) {
-        if (!meterModel.equals(payload.getMeterModel())) {
-            meterModel = payload.getMeterModel();
-            updateProperty(HomeWizardBindingConstants.PROPERTY_METER_MODEL, meterModel);
-        }
-
-        if (meterVersion != payload.getSmrVersion()) {
-            meterVersion = payload.getSmrVersion();
-            updateProperty(HomeWizardBindingConstants.PROPERTY_METER_VERSION, String.format("%d", meterVersion));
-        }
-
-        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
-                new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
-        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2,
-                new QuantityType<>(payload.getTotalEnergyImportT2Kwh(), Units.KILOWATT_HOUR));
-        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
-                new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
-        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2,
-                new QuantityType<>(payload.getTotalEnergyExportT2Kwh(), Units.KILOWATT_HOUR));
-
-        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
-                new QuantityType<>(payload.getActivePowerW(), Units.WATT));
-        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1,
-                new QuantityType<>(payload.getActivePowerL1W(), Units.WATT));
-        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2,
-                new QuantityType<>(payload.getActivePowerL2W(), Units.WATT));
-        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3,
-                new QuantityType<>(payload.getActivePowerL3W(), Units.WATT));
-
-        // If no data from the gas meter is present, the json value will be null, which means gson ignores it,
-        // leaving the value in the payload object at 0.
-        long dtv = payload.getGasTimestamp();
-        if (dtv > 0) {
-            updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
-                    new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
-
-            // 210119164000
-            int seconds = (int) (dtv % 100);
-
-            dtv /= 100;
-            int minutes = (int) (dtv % 100);
-
-            dtv /= 100;
-            int hours = (int) (dtv % 100);
-
-            dtv /= 100;
-            int day = (int) (dtv % 100);
-
-            dtv /= 100;
-            int month = (int) (dtv % 100);
-
-            dtv /= 100;
-            int year = (int) (dtv + 2000);
-
-            try {
-                DateTimeType dtt = new DateTimeType(
-                        ZonedDateTime.of(year, month, day, hours, minutes, seconds, 0, ZoneId.systemDefault()));
-                updateState(HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP, dtt);
-                updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
-                        new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
-            } catch (DateTimeException e) {
-                logger.warn("Unable to parse Gas timestamp: {}", payload.getGasTimestamp());
-            }
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardStatefulDeviceHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardStatefulDeviceHandler.java
deleted file mode 100644 (file)
index 11197b7..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.io.net.http.HttpUtil;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-
-/**
- * The {@link HomeWizardStatefulDeviceHandler} extends the base class
- * to provide support for devices that also have a 'state' interface.
- * This interface can be used to query and control the state of a device.
- *
- * @author DaniĆ«l van Os - Initial contribution
- */
-@NonNullByDefault
-public abstract class HomeWizardStatefulDeviceHandler extends HomeWizardDeviceHandler {
-
-    /**
-     * Constructor
-     *
-     * @param thing The thing to handle
-     */
-    public HomeWizardStatefulDeviceHandler(Thing thing) {
-        super(thing);
-    }
-
-    /**
-     * Device specific handling of the returned state payload.
-     *
-     * @param payload The data parsed from the state Json file
-     */
-    protected abstract void handleStatePayload(StatePayload payload);
-
-    protected void pollState() {
-        final String stateResult;
-
-        try {
-            stateResult = HttpUtil.executeUrl("GET", apiURL + "state", 30000);
-        } catch (IOException e) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                    String.format("Unable to query device state: %s", e.getMessage()));
-            return;
-        }
-
-        if (stateResult.trim().isEmpty()) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device returned empty state");
-            return;
-        }
-
-        StatePayload statePayload = gson.fromJson(stateResult, StatePayload.class);
-        if (statePayload == null) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                    "Unable to parse state response from device");
-            return;
-        }
-
-        handleStatePayload(statePayload);
-    }
-
-    /**
-     * Sends a command to the state interface of the device.
-     *
-     * @param command The command to send.
-     */
-    protected @Nullable StatePayload sendStateCommand(String command) {
-        try (InputStream is = new ByteArrayInputStream(command.getBytes())) {
-            String updatedState = HttpUtil.executeUrl("PUT", apiURL + "state", is, "application/json", 30000);
-            return gson.fromJson(updatedState, StatePayload.class);
-        } catch (IOException e) {
-            logger.warn("Failed to send command {} to {}", command, apiURL + "state");
-            return null;
-        }
-    }
-
-    /*
-     * This overrides the original polling loop by including a request for the current state..
-     */
-    @Override
-    protected void pollingCode() {
-        pollData();
-        pollState();
-    }
-}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardWaterMeterHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardWaterMeterHandler.java
deleted file mode 100644 (file)
index 5fcc2ac..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.types.Command;
-
-/**
- * The {@link HomeWizardWaterMeterHandler} implements functionality to handle a HomeWizard Watermeter.
- *
- * @author DaniĆ«l van Os - Initial contribution
- */
-@NonNullByDefault
-public class HomeWizardWaterMeterHandler extends HomeWizardDeviceHandler {
-
-    /**
-     * Constructor
-     *
-     * @param thing The thing to handle
-     */
-    public HomeWizardWaterMeterHandler(Thing thing) {
-        super(thing);
-    }
-
-    /**
-     * Not listening to any commands.
-     */
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-    }
-
-    /**
-     * Device specific handling of the returned payload.
-     *
-     * @param payload The data parsed from the Json file
-     */
-    @Override
-    protected void handleDataPayload(DataPayload payload) {
-        updateState(HomeWizardBindingConstants.CHANNEL_CURRENT_WATER,
-                new QuantityType<>(payload.getCurrentWaterLPM(), Units.LITRE_PER_MINUTE));
-        updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_WATER,
-                new QuantityType<>(payload.getTotalWaterM3(), SIUnits.CUBIC_METRE));
-    }
-}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/StatePayload.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/StatePayload.java
deleted file mode 100644 (file)
index 8235dd3..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.homewizard.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Class that provides storage for the json object obtained from the HomeWizard device State API
- *
- * @author DaniĆ«l van Os - Initial contribution
- *
- */
-@NonNullByDefault
-public class StatePayload {
-    @SerializedName("power_on")
-    private boolean powerOn;
-    @SerializedName("switch_lock")
-    private boolean switchLock;
-    private int brightness = 0;
-
-    /**
-     * Getter for the power_on field
-     *
-     * @return true if the device is currently on, false if it is off
-     */
-    public boolean getPowerOn() {
-        return powerOn;
-    }
-
-    /**
-     * Setter for the power_on field
-     *
-     * @param powerOn true to turn the device on, false to turn it off
-     */
-    public void setPowerOn(boolean powerOn) {
-        this.powerOn = powerOn;
-    }
-
-    /**
-     * Getter for the switch_lock field
-     *
-     * @return true if the device currently locked, false if it is not
-     */
-    public boolean getSwitchLock() {
-        return switchLock;
-    }
-
-    /**
-     * Setter for the power_on field
-     *
-     * @param switchLock true to lock the device, false to unlock it
-     */
-    public void setSwitchLock(boolean switchLock) {
-        this.switchLock = switchLock;
-    }
-
-    /**
-     * Getter for the ring brightness
-     *
-     * @return ring brightness percentage
-     */
-    public int getBrightness() {
-        return brightness;
-    }
-
-    /**
-     * Setter for the ring brightness
-     *
-     * @param brightness ring brightness
-     */
-    public void setBrightness(int brightness) {
-        this.brightness = brightness;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("State [power_on: %b switch_lock: %b brightness: %d]", powerOn, switchLock, brightness);
-    }
-}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/DataPayload.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/DataPayload.java
new file mode 100644 (file)
index 0000000..e6154be
--- /dev/null
@@ -0,0 +1,356 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.dto;
+
+import java.time.DateTimeException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Class that provides storage for the json objects obtained from HomeWizard devices.
+ *
+ * @author DaniĆ«l van Os - Initial contribution
+ * @author Leo Siepel - Clean-up and additional fields
+ *
+ */
+@NonNullByDefault
+public class DataPayload {
+    private int smrVersion = 0;
+    private String meterModel = "";
+    private String wifiSsid = "";
+    private int wifiStrength = 0;
+
+    @SerializedName("total_power_import_t1_kwh")
+    private double totalEnergyImportT1Kwh;
+    @SerializedName("total_power_import_t2_kwh")
+    private double totalEnergyImportT2Kwh;
+    @SerializedName("total_power_export_t1_kwh")
+    private double totalEnergyExportT1Kwh;
+    @SerializedName("total_power_export_t2_kwh")
+    private double totalEnergyExportT2Kwh;
+
+    private int activePowerW;
+    private int activePowerL1W;
+    private int activePowerL2W;
+    private int activePowerL3W;
+    private double totalGasM3;
+    private long gasTimestamp = 0;
+
+    @SerializedName("any_power_fail_count")
+    private int anyPowerFailCount;
+
+    @SerializedName("long_power_fail_count")
+    private int longPowerFailCount;
+
+    @SerializedName("active_voltage_v")
+    private int activeVoltage;
+    @SerializedName("active_voltage_l1_v")
+    private int activeVoltageL1;
+    @SerializedName("active_voltage_l2_v")
+    private int activeVoltageL2;
+    @SerializedName("active_voltage_l3_v")
+    private int activeVoltageL3;
+
+    @SerializedName("active_current_a")
+    private double activeCurrent;
+    @SerializedName("active_current_l1_a")
+    private double activeCurrentL1;
+    @SerializedName("active_current_l2_a")
+    private double activeCurrentL2;
+    @SerializedName("active_current_l3_a")
+    private double activeCurrentL3;
+
+    @SerializedName("total_liter_m3")
+    private double totalWaterM3;
+    @SerializedName("active_liter_lpm")
+    private double currentWaterLPM;
+
+    /**
+     * Getter for the smart meter version
+     *
+     * @return The most recent smart meter version obtained from the API
+     */
+    public int getSmrVersion() {
+        return smrVersion;
+    }
+
+    /**
+     * Getter for the meter model
+     *
+     * @return meter model
+     */
+    public String getMeterModel() {
+        return meterModel;
+    }
+
+    /**
+     * Getter for the meter's wifi ssid
+     *
+     * @return the meter's wifi sid
+     */
+    public String getWifiSsid() {
+        return wifiSsid;
+    }
+
+    /**
+     * Getter for the wifi rssi
+     *
+     * @return wifi rssi
+     */
+    public int getWifiStrength() {
+        return wifiStrength;
+    }
+
+    /**
+     * Getter for the total imported energy on counter 1
+     *
+     * @return total imported energy on counter 1
+     */
+    public double getTotalEnergyImportT1Kwh() {
+        return totalEnergyImportT1Kwh;
+    }
+
+    /**
+     * Getter for the total imported energy on counter 2
+     *
+     * @return total imported energy on counter 2
+     */
+    public double getTotalEnergyImportT2Kwh() {
+        return totalEnergyImportT2Kwh;
+    }
+
+    /**
+     * Getter for the total exported energy on counter 1
+     *
+     * @return total exported energy on counter 1
+     */
+    public double getTotalEnergyExportT1Kwh() {
+        return totalEnergyExportT1Kwh;
+    }
+
+    /**
+     * Getter for the total exported energy on counter 2
+     *
+     * @return total exported energy on counter 2
+     */
+    public double getTotalEnergyExportT2Kwh() {
+        return totalEnergyExportT2Kwh;
+    }
+
+    /**
+     * Getter for the count of any power failures
+     *
+     * @return count of any power failures
+     */
+    public int getAnyPowerFailCount() {
+        return anyPowerFailCount;
+    }
+
+    /**
+     * Getter for the count of long power failures
+     *
+     * @return count of long power failures
+     */
+    public int getLongPowerFailCount() {
+        return longPowerFailCount;
+    }
+
+    /**
+     * Getter for the active voltage
+     *
+     * @return current active voltage
+     */
+    public int getActiveVoltage() {
+        return activeVoltage;
+    }
+
+    /**
+     * Getter for the active voltage on phase 1
+     *
+     * @return active voltage on phase 1
+     */
+    public int getActiveVoltageL1() {
+        return activeVoltageL1;
+    }
+
+    /**
+     * Getter for the active voltage on phase 2
+     *
+     * @return active voltage on phase 2
+     */
+    public int getActiveVoltageL2() {
+        return activeVoltageL2;
+    }
+
+    /**
+     * Getter for the active voltage on phase 3
+     *
+     * @return active voltage on phase 3
+     */
+    public int getActiveVoltageL3() {
+        return activeVoltageL3;
+    }
+
+    /**
+     * Getter for the active current (sum of all phases)
+     *
+     * @return active current (all phases)
+     */
+    public double getActiveCurrent() {
+        return activeCurrent;
+    }
+
+    /**
+     * Getter for the active current on phase 1
+     *
+     * @return active current on phase 1
+     */
+    public double getActiveCurrentL1() {
+        return activeCurrentL1;
+    }
+
+    /**
+     * Getter for the active current on phase 2
+     *
+     * @return active current on phase 2
+     */
+    public double getActiveCurrentL2() {
+        return activeCurrentL2;
+    }
+
+    /**
+     * Getter for the active current on phase 3
+     *
+     * @return active current on phase 3
+     */
+    public double getActiveCurrentL3() {
+        return activeCurrentL3;
+    }
+
+    /**
+     * Getter for the current active total power
+     *
+     * @return current active total power
+     */
+    public int getActivePowerW() {
+        return activePowerW;
+    }
+
+    /**
+     * Getter for the current active total power on phase 2
+     *
+     * @return current active total power on phase 2
+     */
+    public int getActivePowerL1W() {
+        return activePowerL1W;
+    }
+
+    /**
+     * Getter for the current active total power on phase 2
+     *
+     * @return current active total power on phase 2
+     */
+    public int getActivePowerL2W() {
+        return activePowerL2W;
+    }
+
+    /**
+     * Getter for the current active total power on phase 3
+     *
+     * @return current active total power on phase 3
+     */
+    public int getActivePowerL3W() {
+        return activePowerL3W;
+    }
+
+    /**
+     * Getter for the total imported gas volume
+     *
+     * @return total imported gas volume
+     */
+    public double getTotalGasM3() {
+        return totalGasM3;
+    }
+
+    /**
+     * Getter for the time stamp of the last gas update
+     * 
+     * @param zoneId The time zone id for the return value, falls back to systemDefault() when null
+     * @return time stamp of the last gas update as ZonedDateTime
+     * @throws DateTimeException When the method fails to create a ZonedDateTime
+     */
+    public @Nullable ZonedDateTime getGasTimestamp(@Nullable ZoneId zoneId) throws DateTimeException {
+        ZoneId timeZoneId = zoneId == null ? ZoneId.systemDefault() : zoneId;
+        long dtv = gasTimestamp;
+        if (dtv < 1) {
+            return null;
+        }
+
+        // 210119164000
+        int seconds = (int) (dtv % 100);
+
+        dtv /= 100;
+        int minutes = (int) (dtv % 100);
+
+        dtv /= 100;
+        int hours = (int) (dtv % 100);
+
+        dtv /= 100;
+        int day = (int) (dtv % 100);
+
+        dtv /= 100;
+        int month = (int) (dtv % 100);
+
+        dtv /= 100;
+        int year = (int) (dtv + 2000);
+
+        return ZonedDateTime.of(year, month, day, hours, minutes, seconds, 0, timeZoneId);
+    }
+
+    /**
+     * Getter for the total imported water volume
+     *
+     * @return total imported water volume
+     */
+    public double getTotalWaterM3() {
+        return totalWaterM3;
+    }
+
+    /**
+     * Getter for the current water flow
+     *
+     * @return current water flow
+     */
+    public double getCurrentWaterLPM() {
+        return currentWaterLPM;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(
+                """
+                        Data [smrVersion: %d meterModel: %s wifiSsid: %s wifiStrength: %d"
+                        totalEnergyImportT1Kwh: %f totalEnergyImportT2Kwh: %f totalEnergyExportT1Kwh: %f totalEnergyExportT2Kwh: %f"
+                        activePowerW: %f activePowerL1W: %f activePowerL2W: %f activePowerL3W: %f totalGasM3: %f gasTimestamp: %.0f"
+                        totalWaterM3: %f currentWaterLPM: %f]
+                        """,
+                smrVersion, meterModel, wifiSsid, wifiStrength, totalEnergyImportT1Kwh, totalEnergyImportT2Kwh,
+                totalEnergyExportT1Kwh, totalEnergyExportT2Kwh, activePowerW, activePowerL1W, activePowerL2W,
+                activePowerL3W, totalGasM3, gasTimestamp, totalWaterM3, currentWaterLPM);
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/StatePayload.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/StatePayload.java
new file mode 100644 (file)
index 0000000..76d3102
--- /dev/null
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Class that provides storage for the json object obtained from the HomeWizard device State API
+ *
+ * @author DaniĆ«l van Os - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class StatePayload {
+    @SerializedName("power_on")
+    private boolean powerOn;
+    @SerializedName("switch_lock")
+    private boolean switchLock;
+    private int brightness = 0;
+
+    /**
+     * Getter for the power_on field
+     *
+     * @return true if the device is currently on, false if it is off
+     */
+    public boolean getPowerOn() {
+        return powerOn;
+    }
+
+    /**
+     * Setter for the power_on field
+     *
+     * @param powerOn true to turn the device on, false to turn it off
+     */
+    public void setPowerOn(boolean powerOn) {
+        this.powerOn = powerOn;
+    }
+
+    /**
+     * Getter for the switch_lock field
+     *
+     * @return true if the device currently locked, false if it is not
+     */
+    public boolean getSwitchLock() {
+        return switchLock;
+    }
+
+    /**
+     * Setter for the power_on field
+     *
+     * @param switchLock true to lock the device, false to unlock it
+     */
+    public void setSwitchLock(boolean switchLock) {
+        this.switchLock = switchLock;
+    }
+
+    /**
+     * Getter for the ring brightness
+     *
+     * @return ring brightness percentage
+     */
+    public int getBrightness() {
+        return brightness;
+    }
+
+    /**
+     * Setter for the ring brightness
+     *
+     * @param brightness ring brightness
+     */
+    public void setBrightness(int brightness) {
+        this.brightness = brightness;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("State [power_on: %b switch_lock: %b brightness: %d]", powerOn, switchLock, brightness);
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardDeviceHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardDeviceHandler.java
new file mode 100644 (file)
index 0000000..984ca85
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.handler;
+
+import java.io.IOException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.homewizard.internal.HomeWizardConfiguration;
+import org.openhab.binding.homewizard.internal.dto.DataPayload;
+import org.openhab.core.io.net.http.HttpUtil;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * The {@link HomeWizardDeviceHandler} is a base class for all
+ * HomeWizard devices. It provides configuration and polling of
+ * data from a device. It also processes common data.
+ * 
+ * @author DaniĆ«l van Os - Initial contribution
+ */
+@NonNullByDefault
+public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
+
+    protected final Logger logger = LoggerFactory.getLogger(HomeWizardDeviceHandler.class);
+    protected final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+            .create();
+
+    protected ScheduledExecutorService executorService = this.scheduler;
+    private HomeWizardConfiguration config = new HomeWizardConfiguration();
+    private @Nullable ScheduledFuture<?> pollingJob;
+
+    protected String apiURL = "";
+
+    /**
+     * Constructor
+     *
+     * @param thing The thing to handle
+     */
+    public HomeWizardDeviceHandler(Thing thing) {
+        super(thing);
+    }
+
+    /**
+     * If a host has been specified start polling it
+     */
+    @Override
+    public void initialize() {
+        config = getConfigAs(HomeWizardConfiguration.class);
+        if (configure()) {
+            pollingJob = executorService.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay,
+                    TimeUnit.SECONDS);
+        }
+    }
+
+    /**
+     * Check the current configuration
+     *
+     * @return true if the configuration is ok to start polling, false otherwise
+     */
+    private boolean configure() {
+        if (config.ipAddress.isBlank()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "Missing ipAddress/host configuration");
+            return false;
+        } else {
+            updateStatus(ThingStatus.UNKNOWN);
+            apiURL = String.format("http://%s/api/v1/", config.ipAddress.trim());
+            return true;
+        }
+    }
+
+    /**
+     * dispose: stop the poller
+     */
+    @Override
+    public void dispose() {
+        var job = pollingJob;
+        if (job != null) {
+            job.cancel(true);
+        }
+        pollingJob = null;
+    }
+
+    /**
+     * Device specific handling of the returned data payload.
+     *
+     * @param payload The data parsed from the data Json file
+     */
+    protected abstract void handleDataPayload(DataPayload payload);
+
+    /**
+     * @return json response from the remote server
+     * @throws IOException
+     */
+    public String getData() throws IOException {
+        return HttpUtil.executeUrl("GET", apiURL + "data", 30000);
+    }
+
+    /**
+     *
+     */
+    protected void pollData() {
+        final String dataResult;
+
+        try {
+            dataResult = getData();
+        } catch (IOException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    String.format("Unable to query device data: %s", e.getMessage()));
+            return;
+        }
+
+        if (dataResult.trim().isEmpty()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device returned empty data");
+            return;
+        }
+
+        DataPayload dataPayload = gson.fromJson(dataResult, DataPayload.class);
+        if (dataPayload == null) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    "Unable to parse data response from device");
+            return;
+        }
+
+        if ("".equals(dataPayload.getWifiSsid())) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Results from API are empty");
+            return;
+        }
+
+        updateStatus(ThingStatus.ONLINE);
+        handleDataPayload(dataPayload);
+    }
+
+    /**
+     * The actual polling loop
+     */
+    protected void pollingCode() {
+        pollData();
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardEnergySocketHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardEnergySocketHandler.java
new file mode 100644 (file)
index 0000000..aaac22c
--- /dev/null
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
+import org.openhab.binding.homewizard.internal.dto.DataPayload;
+import org.openhab.binding.homewizard.internal.dto.StatePayload;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+
+/**
+ * The {@link HomeWizardEnergySocketHandler} implements functionality to handle a HomeWizard EnergySocket.
+ *
+ * @author DaniĆ«l van Os - Initial contribution
+ */
+@NonNullByDefault
+public class HomeWizardEnergySocketHandler extends HomeWizardStatefulDeviceHandler {
+
+    /**
+     * Constructor
+     *
+     * @param thing The thing to handle
+     * @param timeZoneProvider The TimeZoneProvider
+     */
+    public HomeWizardEnergySocketHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
+        super(thing, timeZoneProvider);
+    }
+
+    /**
+     * Converts a brightness value (0..255) to a percentage.
+     *
+     * @param brightness The brightness to convert.
+     * @return brightness percentage
+     */
+    private int brightnessToPercentage(int brightness) {
+        return (int) (100.0 * brightness / 255.0 + 0.5);
+    }
+
+    /**
+     * Converts a percentage to a brightness value (0..255)
+     *
+     * @param percentage The percentage to convert.
+     * @return brightness value
+     */
+    private int percentageToBrightness(String percentage) {
+        return (int) (Double.valueOf(percentage) * 255.0 / 100.0 + 0.5);
+    }
+
+    /**
+     * Handle incoming commands.
+     *
+     * Power on/off, Power lock/unlock and Ring brightness are supported.
+     */
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if (command instanceof RefreshType) {
+            // For now I prefer not updating immediately above firing a full update request for each channel
+            return;
+        }
+
+        StatePayload result = null;
+
+        /*
+         * The returned payloads below only contain the modified value, so each has it's own
+         * call to updateState instead of just calling handleStatePayload() with the returned
+         * payload.
+         */
+
+        switch (channelUID.getId()) {
+            case HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS: {
+                result = sendStateCommand(
+                        String.format("{\"brightness\": %d}", percentageToBrightness(command.toFullString())));
+                if (result != null) {
+                    updateState(HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
+                            new PercentType(brightnessToPercentage(result.getBrightness())));
+                }
+                break;
+            }
+            case HomeWizardBindingConstants.CHANNEL_POWER_SWITCH: {
+                boolean onOff = command.equals(OnOffType.ON);
+                result = sendStateCommand(String.format("{\"power_on\": %b}", onOff));
+                if (result != null) {
+                    updateState(HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(result.getPowerOn()));
+                }
+                break;
+            }
+            case HomeWizardBindingConstants.CHANNEL_POWER_LOCK: {
+                boolean onOff = command.equals(OnOffType.ON);
+                result = sendStateCommand(String.format("{\"switch_lock\": %b}", onOff));
+                if (result != null) {
+                    updateState(HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(result.getSwitchLock()));
+                }
+                break;
+            }
+            default:
+                logger.warn("Should handle {} {}", channelUID.getIdWithoutGroup(), command);
+                break;
+        }
+    }
+
+    /**
+     * Device specific handling of the returned payload.
+     *
+     * @param payload The data parsed from the Json file
+     */
+    @Override
+    protected void handleDataPayload(DataPayload payload) {
+        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
+                new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
+        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
+                new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
+                new QuantityType<>(payload.getActivePowerW(), Units.WATT));
+    }
+
+    @Override
+    protected void handleStatePayload(StatePayload payload) {
+        updateState(HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(payload.getPowerOn()));
+        updateState(HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(payload.getSwitchLock()));
+        updateState(HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
+                new PercentType(brightnessToPercentage(payload.getBrightness())));
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardHandlerFactory.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardHandlerFactory.java
new file mode 100644 (file)
index 0000000..2eb4a88
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.handler;
+
+import static org.openhab.binding.homewizard.internal.HomeWizardBindingConstants.*;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link HomeWizardHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author DaniĆ«l van Os - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.homewizard", service = ThingHandlerFactory.class)
+public class HomeWizardHandlerFactory extends BaseThingHandlerFactory {
+
+    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_P1_METER,
+            THING_TYPE_ENERGY_SOCKET, THING_TYPE_WATERMETER);
+
+    private final TimeZoneProvider timeZoneProvider;
+
+    @Activate
+    public HomeWizardHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider) {
+        this.timeZoneProvider = timeZoneProvider;
+    }
+
+    @Override
+    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+        return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+    }
+
+    @Override
+    protected @Nullable ThingHandler createHandler(Thing thing) {
+        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+        if (THING_TYPE_P1_METER.equals(thingTypeUID)) {
+            return new HomeWizardP1MeterHandler(thing, timeZoneProvider);
+        }
+
+        if (THING_TYPE_ENERGY_SOCKET.equals(thingTypeUID)) {
+            return new HomeWizardEnergySocketHandler(thing, timeZoneProvider);
+        }
+
+        if (THING_TYPE_WATERMETER.equals(thingTypeUID)) {
+            return new HomeWizardWaterMeterHandler(thing, timeZoneProvider);
+        }
+
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardP1MeterHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardP1MeterHandler.java
new file mode 100644 (file)
index 0000000..7419ba4
--- /dev/null
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.handler;
+
+import java.time.DateTimeException;
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
+import org.openhab.binding.homewizard.internal.dto.DataPayload;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+
+/**
+ * The {@link HomeWizardP1MeterHandler} implements functionality to handle a HomeWizard P1 Meter.
+ *
+ * @author DaniĆ«l van Os - Initial contribution
+ */
+@NonNullByDefault
+public class HomeWizardP1MeterHandler extends HomeWizardDeviceHandler {
+
+    private String meterModel = "";
+    private int meterVersion = 0;
+    private TimeZoneProvider timeZoneProvider;
+
+    /**
+     * Constructor
+     *
+     * @param thing The thing to handle
+     * @param timeZoneProvider The TimeZoneProvider
+     */
+    public HomeWizardP1MeterHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
+        super(thing);
+        this.timeZoneProvider = timeZoneProvider;
+    }
+
+    /**
+     * Not listening to any commands.
+     */
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+    }
+
+    /**
+     * Device specific handling of the returned payload.
+     *
+     * @param payload The data parsed from the Json file
+     */
+    @Override
+    protected void handleDataPayload(DataPayload payload) {
+        if (!meterModel.equals(payload.getMeterModel())) {
+            meterModel = payload.getMeterModel();
+            updateProperty(HomeWizardBindingConstants.PROPERTY_METER_MODEL, meterModel);
+        }
+
+        if (meterVersion != payload.getSmrVersion()) {
+            meterVersion = payload.getSmrVersion();
+            updateProperty(HomeWizardBindingConstants.PROPERTY_METER_VERSION, String.format("%d", meterVersion));
+        }
+
+        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
+                new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
+        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2,
+                new QuantityType<>(payload.getTotalEnergyImportT2Kwh(), Units.KILOWATT_HOUR));
+        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
+                new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
+        updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2,
+                new QuantityType<>(payload.getTotalEnergyExportT2Kwh(), Units.KILOWATT_HOUR));
+
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
+                new QuantityType<>(payload.getActivePowerW(), Units.WATT));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1,
+                new QuantityType<>(payload.getActivePowerL1W(), Units.WATT));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2,
+                new QuantityType<>(payload.getActivePowerL2W(), Units.WATT));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3,
+                new QuantityType<>(payload.getActivePowerL3W(), Units.WATT));
+
+        updateState(HomeWizardBindingConstants.CHANNEL_POWER_FAILURES, new DecimalType(payload.getAnyPowerFailCount()));
+        updateState(HomeWizardBindingConstants.CHANNEL_LONG_POWER_FAILURES,
+                new DecimalType(payload.getLongPowerFailCount()));
+
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT,
+                new QuantityType<>(payload.getActiveCurrent(), Units.AMPERE));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L1,
+                new QuantityType<>(payload.getActiveCurrentL1(), Units.AMPERE));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L2,
+                new QuantityType<>(payload.getActiveCurrentL2(), Units.AMPERE));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L3,
+                new QuantityType<>(payload.getActiveCurrentL3(), Units.AMPERE));
+
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE,
+                new QuantityType<>(payload.getActiveVoltage(), Units.VOLT));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L1,
+                new QuantityType<>(payload.getActiveVoltageL1(), Units.VOLT));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L2,
+                new QuantityType<>(payload.getActiveVoltageL2(), Units.VOLT));
+        updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L3,
+                new QuantityType<>(payload.getActiveVoltageL3(), Units.VOLT));
+
+        ZonedDateTime gasTimestamp;
+        try {
+            gasTimestamp = payload.getGasTimestamp(timeZoneProvider.getTimeZone());
+        } catch (DateTimeException e) {
+            logger.warn("Unable to parse Gas timestamp: {}", e.getMessage());
+            gasTimestamp = null;
+        }
+        if (gasTimestamp != null) {
+            updateState(HomeWizardBindingConstants.CHANNEL_GAS_TOTAL,
+                    new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
+            updateState(HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP, new DateTimeType(gasTimestamp));
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardStatefulDeviceHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardStatefulDeviceHandler.java
new file mode 100644 (file)
index 0000000..9afd528
--- /dev/null
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.handler;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.homewizard.internal.dto.StatePayload;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.io.net.http.HttpUtil;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+
+/**
+ * The {@link HomeWizardStatefulDeviceHandler} extends the base class
+ * to provide support for devices that also have a 'state' interface.
+ * This interface can be used to query and control the state of a device.
+ *
+ * @author DaniĆ«l van Os - Initial contribution
+ */
+@NonNullByDefault
+public abstract class HomeWizardStatefulDeviceHandler extends HomeWizardP1MeterHandler {
+
+    /**
+     * Constructor
+     *
+     * @param thing The thing to handle
+     * @param timeZoneProvider The TimeZoneProvider
+     */
+    public HomeWizardStatefulDeviceHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
+        super(thing, timeZoneProvider);
+    }
+
+    /**
+     * Device specific handling of the returned state payload.
+     *
+     * @param payload The data parsed from the state Json file
+     */
+    protected abstract void handleStatePayload(StatePayload payload);
+
+    protected void pollState() {
+        final String stateResult;
+
+        try {
+            stateResult = HttpUtil.executeUrl("GET", apiURL + "state", 30000);
+        } catch (IOException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    String.format("Unable to query device state: %s", e.getMessage()));
+            return;
+        }
+
+        if (stateResult.trim().isEmpty()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device returned empty state");
+            return;
+        }
+
+        StatePayload statePayload = gson.fromJson(stateResult, StatePayload.class);
+        if (statePayload == null) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    "Unable to parse state response from device");
+            return;
+        }
+
+        handleStatePayload(statePayload);
+    }
+
+    /**
+     * Sends a command to the state interface of the device.
+     *
+     * @param command The command to send.
+     */
+    protected @Nullable StatePayload sendStateCommand(String command) {
+        try (InputStream is = new ByteArrayInputStream(command.getBytes())) {
+            String updatedState = HttpUtil.executeUrl("PUT", apiURL + "state", is, "application/json", 30000);
+            return gson.fromJson(updatedState, StatePayload.class);
+        } catch (IOException e) {
+            logger.warn("Failed to send command {} to {}", command, apiURL + "state");
+            return null;
+        }
+    }
+
+    /*
+     * This overrides the original polling loop by including a request for the current state..
+     */
+    @Override
+    protected void pollingCode() {
+        pollData();
+        pollState();
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardWaterMeterHandler.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardWaterMeterHandler.java
new file mode 100644 (file)
index 0000000..fd46bdd
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
+import org.openhab.binding.homewizard.internal.dto.DataPayload;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+
+/**
+ * The {@link HomeWizardWaterMeterHandler} implements functionality to handle a HomeWizard Watermeter.
+ *
+ * @author DaniĆ«l van Os - Initial contribution
+ */
+@NonNullByDefault
+public class HomeWizardWaterMeterHandler extends HomeWizardP1MeterHandler {
+
+    /**
+     * Constructor
+     *
+     * @param thing The thing to handle
+     * @param timeZoneProvider The TimeZoneProvider
+     */
+    public HomeWizardWaterMeterHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
+        super(thing, timeZoneProvider);
+    }
+
+    /**
+     * Not listening to any commands.
+     */
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+    }
+
+    /**
+     * Device specific handling of the returned payload.
+     *
+     * @param payload The data parsed from the Json file
+     */
+    @Override
+    protected void handleDataPayload(DataPayload payload) {
+        updateState(HomeWizardBindingConstants.CHANNEL_CURRENT_WATER,
+                new QuantityType<>(payload.getCurrentWaterLPM(), Units.LITRE_PER_MINUTE));
+        updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_WATER,
+                new QuantityType<>(payload.getTotalWaterM3(), SIUnits.CUBIC_METRE));
+    }
+}
index 867755a84c4d39582b3d8bee1c044d3fe49b8487..2d38c10234dfeba394bfcda4745093a2710fd7e3 100644 (file)
@@ -9,6 +9,17 @@ thing-type.homewizard.energy_socket.label = HomeWizard Energysocket
 thing-type.homewizard.energy_socket.description = This thing provides the measurement data that is available through the http interface of a HomeWizard Energysocket.
 thing-type.homewizard.p1_wifi_meter.label = HomeWizard Wi-Fi P1 Meter
 thing-type.homewizard.p1_wifi_meter.description = This thing provides the measurement data that is available through the http interface of the HomeWizard Wi-Fi P1 Meter.
+thing-type.homewizard.p1_wifi_meter.channel.active_current.label = Current
+thing-type.homewizard.p1_wifi_meter.channel.active_current.description = The sum of the current for all phases
+thing-type.homewizard.p1_wifi_meter.channel.active_current_l1.label = Current L1
+thing-type.homewizard.p1_wifi_meter.channel.active_current_l2.label = Current L2
+thing-type.homewizard.p1_wifi_meter.channel.active_current_l3.label = Current L3
+thing-type.homewizard.p1_wifi_meter.channel.active_voltage.label = Active Voltage
+thing-type.homewizard.p1_wifi_meter.channel.active_voltage_l1.label = Active Voltage L1
+thing-type.homewizard.p1_wifi_meter.channel.active_voltage_l2.label = Active Voltage L2
+thing-type.homewizard.p1_wifi_meter.channel.active_voltage_l3.label = Active Voltage L3
+thing-type.homewizard.p1_wifi_meter.channel.long_power_failures.label = Long Power Failures
+thing-type.homewizard.p1_wifi_meter.channel.long_power_failures.description = This channel provides the count of long power failures.
 thing-type.homewizard.watermeter.label = HomeWizard Wi-Fi Watermeter
 thing-type.homewizard.watermeter.description = This thing provides the measurement data that is available through the http interface of a HomeWizard Watermeter.
 
@@ -41,6 +52,8 @@ channel-type.homewizard.current_water.label = Current Water Rate
 channel-type.homewizard.current_water.description = This channel provides the most recently reported current water usage in liters per minute.
 channel-type.homewizard.gas_timestamp.label = Gas Update Time Stamp
 channel-type.homewizard.gas_timestamp.description = This channel provides the time stamp of the total_gas measurement.
+channel-type.homewizard.power_failures.label = Power Failures
+channel-type.homewizard.power_failures.description = This channel provides the count of any type of power failure.
 channel-type.homewizard.power_lock.label = Power Lock
 channel-type.homewizard.power_lock.description = This channel provides access to the power lock of the Energysocket
 channel-type.homewizard.power_switch.label = Power Switch
index 14982a41851119102417b25d7c324c95ca539603..2b26551e2d73b10a9f043e8768fccba01b07448c 100644 (file)
@@ -4,27 +4,60 @@
        xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
        xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
 
-
        <thing-type id="p1_wifi_meter">
                <label>HomeWizard Wi-Fi P1 Meter</label>
                <description>This thing provides the measurement data that is available through the http interface of the HomeWizard
                        Wi-Fi P1 Meter.</description>
 
                <channels>
-                       <channel id="total_energy_import_t1" typeId="total_energy_import_t1"/>
-                       <channel id="total_energy_import_t2" typeId="total_energy_import_t2"/>
-                       <channel id="total_energy_export_t1" typeId="total_energy_export_t1"/>
-                       <channel id="total_energy_export_t2" typeId="total_energy_export_t2"/>
+                       <channel id="active_current" typeId="system.electric-current">
+                               <label>Current</label>
+                               <description>The sum of the current for all phases</description>
+                       </channel>
+                       <channel id="active_current_l1" typeId="system.electric-current">
+                               <label>Current L1</label>
+                       </channel>
+                       <channel id="active_current_l2" typeId="system.electric-current">
+                               <label>Current L2</label>
+                       </channel>
+                       <channel id="active_current_l3" typeId="system.electric-current">
+                               <label>Current L3</label>
+                       </channel>
                        <channel id="active_power" typeId="active_power"/>
                        <channel id="active_power_l1" typeId="active_power_l1"/>
                        <channel id="active_power_l2" typeId="active_power_l2"/>
                        <channel id="active_power_l3" typeId="active_power_l3"/>
+                       <channel id="active_voltage" typeId="system.electric-voltage">
+                               <label>Active Voltage</label>
+                       </channel>
+                       <channel id="active_voltage_l1" typeId="system.electric-voltage">
+                               <label>Active Voltage L1</label>
+                       </channel>
+                       <channel id="active_voltage_l2" typeId="system.electric-voltage">
+                               <label>Active Voltage L2</label>
+                       </channel>
+                       <channel id="active_voltage_l3" typeId="system.electric-voltage">
+                               <label>Active Voltage L3</label>
+                       </channel>
+
+                       <channel id="power_failures" typeId="power_failures"/>
+                       <channel id="long_power_failures" typeId="power_failures">
+                               <label>Long Power Failures</label>
+                               <description>This channel provides the count of long power failures.</description>
+                       </channel>
+
+                       <channel id="total_energy_import_t1" typeId="total_energy_import_t1"/>
+                       <channel id="total_energy_import_t2" typeId="total_energy_import_t2"/>
+                       <channel id="total_energy_export_t1" typeId="total_energy_export_t1"/>
+                       <channel id="total_energy_export_t2" typeId="total_energy_export_t2"/>
+
                        <channel id="total_gas" typeId="total_gas"/>
                        <channel id="gas_timestamp" typeId="gas_timestamp"/>
                </channels>
 
                <properties>
                        <property name="meterModel">Unknown</property>
+                       <property name="thingTypeVersion">1</property>
                </properties>
 
                <config-description>
 
        </thing-type>
 
+
+       <channel-type id="power_failures">
+               <item-type>Number</item-type>
+               <label>Power Failures</label>
+               <description>This channel provides the count of any type of power failure.</description>
+       </channel-type>
+
        <channel-type id="total_energy_import_t1">
                <item-type>Number:Energy</item-type>
                <label>Total Imported Energy Counter 1</label>
diff --git a/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/update/instructions.xml
new file mode 100644 (file)
index 0000000..168fc51
--- /dev/null
@@ -0,0 +1,57 @@
+<?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="homewizard:p1_wifi_meter">
+
+               <instruction-set targetVersion="1">
+                       <add-channel id="active_voltage">
+                               <type>system:electric-voltage</type>
+                       </add-channel>
+                       <add-channel id="active_voltage_l1">
+                               <type>system:electric-voltage</type>
+                       </add-channel>
+                       <add-channel id="active_voltage_l2">
+                               <type>system:electric-voltage</type>
+                       </add-channel>
+                       <add-channel id="active_voltage_l3">
+                               <type>system:electric-voltage</type>
+                       </add-channel>
+                       <add-channel id="active_current">
+                               <type>system:electric-current</type>
+                       </add-channel>
+                       <add-channel id="active_current_l1">
+                               <type>system:electric-current</type>
+                       </add-channel>
+                       <add-channel id="active_current_l2">
+                               <type>system:electric-current</type>
+                       </add-channel>
+                       <add-channel id="active_current_l3">
+                               <type>system:electric-current</type>
+                       </add-channel>
+                       <add-channel id="power_failures">
+                               <type>homewizard:power_failures</type>
+                       </add-channel>
+                       <add-channel id="long_power_failures">
+                               <type>homewizard:power_failures</type>
+                               <label>Long Power Failures</label>
+                               <description>This channel provides the count of long power failures.</description>
+                       </add-channel>
+                       <add-channel id="total_energy_import_t1">
+                               <type>homewizard:total_energy_import_t1</type>
+                       </add-channel>
+                       <add-channel id="total_energy_import_t2">
+                               <type>homewizard:total_energy_import_t2</type>
+                       </add-channel>
+                       <add-channel id="total_energy_export_t1">
+                               <type>homewizard:total_energy_export_t1</type>
+                       </add-channel>
+                       <add-channel id="total_energy_export_t2">
+                               <type>homewizard:total_energy_export_t2</type>
+                       </add-channel>
+               </instruction-set>
+
+       </thing-type>
+
+</update:update-descriptions>
diff --git a/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerMock.java b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerMock.java
new file mode 100644 (file)
index 0000000..224a367
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.openhab.binding.homewizard.internal.handler.HomeWizardP1MeterHandler;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.thing.Thing;
+
+/**
+ * The {@link HomeWizardP1MeterHandlerMock} is responsible for mocking {@link HomeWizardP1MeterHandler}
+ * 
+ * @author Leo Siepel - Initial contribution
+ */
+@NonNullByDefault
+public class HomeWizardP1MeterHandlerMock extends HomeWizardP1MeterHandler {
+
+    public HomeWizardP1MeterHandlerMock(Thing thing, TimeZoneProvider timeZoneProvider) {
+        super(thing, timeZoneProvider);
+
+        executorService = Mockito.mock(ScheduledExecutorService.class);
+        doAnswer((InvocationOnMock invocation) -> {
+            ((Runnable) invocation.getArguments()[0]).run();
+            return null;
+        }).when(executorService).scheduleWithFixedDelay(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class));
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerTest.java b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerTest.java
new file mode 100644 (file)
index 0000000..30fb17e
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.openhab.binding.homewizard.internal.dto.DataUtil;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.types.State;
+
+/**
+ * Tests for the HomeWizard Handler
+ *
+ * @author Leo Siepel - Initial contribution
+ */
+@NonNullByDefault
+public class HomeWizardP1MeterHandlerTest {
+
+    private static final Configuration CONFIG = createConfig();
+
+    private static Configuration createConfig() {
+        final Configuration config = new Configuration();
+        config.put("ipAddress", "1.2.3.4");
+        return config;
+    }
+
+    private static Thing mockThing() {
+        final Thing thing = mock(Thing.class);
+        when(thing.getUID())
+                .thenReturn(new ThingUID(HomeWizardBindingConstants.THING_TYPE_P1_METER, "homewizard-test-thing"));
+        when(thing.getConfiguration()).thenReturn(CONFIG);
+
+        final List<Channel> channelList = Arrays.asList(
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L1), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L3), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L1), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L2), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_POWER_FAILURES), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_LONG_POWER_FAILURES), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP), //
+                mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GAS_TOTAL));
+
+        when(thing.getChannels()).thenReturn(channelList);
+        return thing;
+    }
+
+    private static Channel mockChannel(final ThingUID thingId, final String channelId) {
+        final Channel channel = Mockito.mock(Channel.class);
+        when(channel.getUID()).thenReturn(new ChannelUID(thingId, channelId));
+        return channel;
+    }
+
+    private static HomeWizardP1MeterHandlerMock createAndInitHandler(final ThingHandlerCallback callback,
+            final Thing thing) {
+        final TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
+        doReturn(ZoneId.systemDefault()).when(timeZoneProvider).getTimeZone();
+        final HomeWizardP1MeterHandlerMock handler = spy(new HomeWizardP1MeterHandlerMock(thing, timeZoneProvider));
+
+        try {
+            doReturn(DataUtil.fromFile("response.json")).when(handler).getData();
+        } catch (IOException e) {
+            assertFalse(true);
+        }
+
+        handler.setCallback(callback);
+        handler.initialize();
+        return handler;
+    }
+
+    private static State getState(final int input) {
+        return new DecimalType(input);
+    }
+
+    private static State getState(final int input, Unit<?> unit) {
+        return new QuantityType<>(input, unit);
+    }
+
+    private static State getState(final double input, Unit<?> unit) {
+        return new QuantityType<>(input, unit);
+    }
+
+    @Test
+    public void testUpdateChannels() {
+        final Thing thing = mockThing();
+        final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
+        final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing);
+
+        try {
+            verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
+            verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
+
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT),
+                    getState(567.0, Units.AMPERE));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L1),
+                    getState(-4.0, Units.AMPERE));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L2),
+                    getState(2.0, Units.AMPERE));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_CURRENT_L3),
+                    getState(333.0, Units.AMPERE));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER),
+                    getState(-543, Units.WATT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1),
+                    getState(-676, Units.WATT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2),
+                    getState(133, Units.WATT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3),
+                    getState(18, Units.WATT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE),
+                    getState(220, Units.VOLT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L1),
+                    getState(221, Units.VOLT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L2),
+                    getState(222, Units.VOLT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ACTIVE_VOLTAGE_L3),
+                    getState(223, Units.VOLT));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1),
+                    getState(8874.0, Units.KILOWATT_HOUR));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2),
+                    getState(7788.0, Units.KILOWATT_HOUR));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1),
+                    getState(10830.511, Units.KILOWATT_HOUR));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2),
+                    getState(2948.827, Units.KILOWATT_HOUR));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_POWER_FAILURES), getState(7));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_LONG_POWER_FAILURES),
+                    getState(2));
+            verify(callback).stateUpdated(
+                    new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP),
+                    new DateTimeType(ZonedDateTime.of(2021, 6, 06, 14, 0, 10, 0, ZoneId.systemDefault())));
+            verify(callback).stateUpdated(new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GAS_TOTAL),
+                    getState(2569.646, SIUnits.CUBIC_METRE));
+        } finally {
+            handler.dispose();
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/DataUtil.java b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/DataUtil.java
new file mode 100644 (file)
index 0000000..d0396e8
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.dto;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * Utility class for working with test data in unit tests
+ *
+ * @author Leo Siepel - Initial contribution
+ */
+@NonNullByDefault
+public class DataUtil {
+
+    private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+            .create();
+
+    @SuppressWarnings("null")
+    public static Reader openDataReader(String fileName) throws FileNotFoundException {
+        String packagePath = (DataUtil.class.getPackage().getName()).replace(".", "/");
+        String filePath = "src/test/resources/" + packagePath + "/" + fileName;
+
+        InputStream inputStream = new FileInputStream(filePath);
+        return new InputStreamReader(inputStream, StandardCharsets.UTF_8);
+    }
+
+    public <T> T fromJson(String fileName, Type typeOfT) throws IOException {
+        try (Reader reader = openDataReader(fileName)) {
+            return gson.fromJson(reader, typeOfT);
+        }
+    }
+
+    @SuppressWarnings("null")
+    public static String fromFile(String fileName) throws IOException {
+        try (Reader reader = openDataReader(fileName)) {
+            return new BufferedReader(reader).lines().parallel().collect(Collectors.joining("\n"));
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/P1PayloadTest.java b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/P1PayloadTest.java
new file mode 100644 (file)
index 0000000..cf7bdbb
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.homewizard.internal.dto;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+import java.io.IOException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests deserialization of HomeWizard API responses from JSON.
+ *
+ * @author Leo Siepel - Initial contribution
+ */
+@NonNullByDefault
+public class P1PayloadTest {
+
+    private static final DataUtil DATA_UTIL = new DataUtil();
+
+    @Test
+    public void deserializeResponse() throws IOException {
+        DataPayload key = DATA_UTIL.fromJson("response.json", DataPayload.class);
+        assertThat(key, is(notNullValue()));
+
+        assertThat(key.getActiveCurrent(), is(567.0));
+        assertThat(key.getActiveCurrentL1(), is(-4.0));
+        assertThat(key.getActiveCurrentL2(), is(2.0));
+        assertThat(key.getActiveCurrentL3(), is(333.0));
+        assertThat(key.getActivePowerW(), is(-543));
+        assertThat(key.getActivePowerL1W(), is(-676));
+        assertThat(key.getActivePowerL2W(), is(133));
+        assertThat(key.getActivePowerL3W(), is(18));
+        assertThat(key.getActiveVoltage(), is(220));
+        assertThat(key.getActiveVoltageL1(), is(221));
+        assertThat(key.getActiveVoltageL2(), is(222));
+        assertThat(key.getActiveVoltageL3(), is(223));
+        assertThat(key.getTotalEnergyExportT1Kwh(), is(8874.0));
+        assertThat(key.getTotalEnergyExportT2Kwh(), is(7788.0));
+        assertThat(key.getTotalEnergyImportT1Kwh(), is(10830.511));
+        assertThat(key.getTotalEnergyImportT2Kwh(), is(2948.827));
+        assertThat(key.getAnyPowerFailCount(), is(7));
+        assertThat(key.getLongPowerFailCount(), is(2));
+        assertThat(key.getGasTimestamp(ZoneId.systemDefault()),
+                is(ZonedDateTime.of(2021, 6, 06, 14, 0, 10, 0, ZoneId.systemDefault())));
+        assertThat(key.getTotalGasM3(), is(2569.646));
+
+        assertThat(key.getMeterModel(), is("ISKRA  2M550T-101"));
+        assertThat(key.getSmrVersion(), is(50));
+        assertThat(key.getWifiSsid(), is("My Wi-Fi"));
+        assertThat(key.getWifiStrength(), is(100));
+    }
+
+    @Test
+    public void deserializeResponseEmpty() throws IOException {
+        DataPayload key = DATA_UTIL.fromJson("response-empty.json", DataPayload.class);
+        assertThat(key, is(notNullValue()));
+
+        assertThat(key.getActiveCurrent(), is(0.0));
+        assertThat(key.getActiveCurrentL1(), is(0.0));
+        assertThat(key.getActiveCurrentL2(), is(0.0));
+        assertThat(key.getActiveCurrentL3(), is(0.0));
+        assertThat(key.getActivePowerW(), is(0));
+        assertThat(key.getActivePowerL1W(), is(0));
+        assertThat(key.getActivePowerL2W(), is(0));
+        assertThat(key.getActivePowerL3W(), is(0));
+        assertThat(key.getActiveVoltage(), is(0));
+        assertThat(key.getActiveVoltageL1(), is(0));
+        assertThat(key.getActiveVoltageL2(), is(0));
+        assertThat(key.getActiveVoltageL3(), is(0));
+        assertThat(key.getAnyPowerFailCount(), is(0));
+        assertThat(key.getLongPowerFailCount(), is(0));
+        assertThat(key.getTotalEnergyExportT1Kwh(), is(0.0));
+        assertThat(key.getTotalEnergyExportT2Kwh(), is(0.0));
+        assertThat(key.getTotalEnergyImportT1Kwh(), is(0.0));
+        assertThat(key.getTotalEnergyImportT2Kwh(), is(0.0));
+        assertThat(key.getGasTimestamp(ZoneId.systemDefault()), is(nullValue()));
+        assertThat(key.getTotalGasM3(), is(0.0));
+
+        assertThat(key.getMeterModel(), is(""));
+        assertThat(key.getSmrVersion(), is(0));
+        assertThat(key.getWifiSsid(), is(""));
+        assertThat(key.getWifiStrength(), is(0));
+    }
+}
diff --git a/bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response-empty.json b/bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response-empty.json
new file mode 100644 (file)
index 0000000..7a73a41
--- /dev/null
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response.json b/bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response.json
new file mode 100644 (file)
index 0000000..5844ea3
--- /dev/null
@@ -0,0 +1,56 @@
+{
+    "wifi_ssid": "My Wi-Fi",
+    "wifi_strength": 100,
+    "smr_version": 50,
+    "meter_model": "ISKRA  2M550T-101",
+    "unique_id": "00112233445566778899AABBCCDDEEFF",
+    "active_tariff": 2,
+    "total_power_import_kwh": 13779.338,
+    "total_power_import_t1_kwh": 10830.511,
+    "total_power_import_t2_kwh": 2948.827,
+    "total_power_export_kwh": 8877,
+    "total_power_export_t1_kwh": 8874,
+    "total_power_export_t2_kwh": 7788,
+    "active_power_w": -543,
+    "active_power_l1_w": -676,
+    "active_power_l2_w": 133,
+    "active_power_l3_w": 18,
+    "active_current_a": 567,
+    "active_current_l1_a": -4,
+    "active_current_l2_a": 2,
+    "active_current_l3_a": 333,
+    "active_voltage_v": 220,
+    "active_voltage_l1_v": 221,
+    "active_voltage_l2_v": 222,
+    "active_voltage_l3_v": 223,
+    "voltage_sag_l1_count": 1,
+    "voltage_sag_l2_count": 2,
+    "voltage_sag_l3_count": 3,
+    "voltage_swell_l1_count": 4,
+    "voltage_swell_l2_count": 5,
+    "voltage_swell_l3_count": 6,
+    "any_power_fail_count": 7,
+    "long_power_fail_count": 2,
+    "total_gas_m3": 2569.646,
+    "gas_timestamp": 210606140010,
+    "gas_unique_id": "FFEEDDCCBBAA99887766554433221100",
+    "active_power_average_w": 123.080,
+    "montly_power_peak_w": 1111.000,
+    "montly_power_peak_timestamp": 230101080010,
+    "external": [
+       {
+          "unique_id": "FFEEDDCCBBAA99887766554433221100",
+          "type": "gas_meter",
+          "timestamp": 210606140010,
+          "value": 2569.646,
+          "unit": "m3"
+       },
+       {
+          "unique_id": "ABCDEF0123456789ABCDEF0123456789",
+          "type": "water_meter",
+          "timestamp": 210606140015,
+          "value": 123.456,
+          "unit": "m3"
+       }
+    ]
+ }
\ No newline at end of file