diff --git a/bundles/org.openhab.binding.homewizard/README.md b/bundles/org.openhab.binding.homewizard/README.md index 93e2d84813b22..f76ef561f4dbc 100644 --- a/bundles/org.openhab.binding.homewizard/README.md +++ b/bundles/org.openhab.binding.homewizard/README.md @@ -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/HomeWizardBindingConstants.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardBindingConstants.java index 3943f2bed36c4..8b28b204c890f 100644 --- a/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardBindingConstants.java +++ b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardBindingConstants.java @@ -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/DataPayload.java b/bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/DataPayload.java similarity index 57% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/DataPayload.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/DataPayload.java index c1bc9e6255d27..e6154bebfb64f 100644 --- 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/dto/DataPayload.java @@ -10,9 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +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; @@ -20,6 +25,7 @@ * 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 @@ -38,13 +44,37 @@ public class DataPayload { @SerializedName("total_power_export_t2_kwh") private double totalEnergyExportT2Kwh; - private double activePowerW; - private double activePowerL1W; - private double activePowerL2W; - private double activePowerL3W; + 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") @@ -59,15 +89,6 @@ 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 * @@ -77,15 +98,6 @@ 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 * @@ -95,15 +107,6 @@ 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 * @@ -113,15 +116,6 @@ 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 * @@ -131,15 +125,6 @@ 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 * @@ -149,15 +134,6 @@ 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 * @@ -167,15 +143,6 @@ 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 * @@ -186,156 +153,191 @@ public double getTotalEnergyExportT2Kwh() { } /** - * Setter for the total exported energy on counter 2 + * Getter for the count of any power failures * - * @param totalEnergyExportT2Kwh + * @return count of any power failures */ - public void setTotalEnergyExportT2Kwh(double totalEnergyExportT2Kwh) { - this.totalEnergyExportT2Kwh = totalEnergyExportT2Kwh; + public int getAnyPowerFailCount() { + return anyPowerFailCount; } /** - * Getter for the current active total power + * Getter for the count of long power failures * - * @return current active total power + * @return count of long power failures */ - public double getActivePowerW() { - return activePowerW; + public int getLongPowerFailCount() { + return longPowerFailCount; } /** - * Setter for the current active total power + * Getter for the active voltage * - * @param activePowerW + * @return current active voltage */ - public void setActivePowerW(double activePowerW) { - this.activePowerW = activePowerW; + public int getActiveVoltage() { + return activeVoltage; } /** - * Getter for the current active total power on phase 1 + * Getter for the active voltage on phase 1 * - * @return current active total power on phase 1 + * @return active voltage on phase 1 */ - public double getActivePowerL1W() { - return activePowerL1W; + public int getActiveVoltageL1() { + return activeVoltageL1; } /** - * Setter for the current active power on phase 1 + * Getter for the active voltage on phase 2 * - * @param activePowerL1W current active total power on phase 1 + * @return active voltage on phase 2 */ - public void setActivePowerL1W(double activePowerL1W) { - this.activePowerL1W = activePowerL1W; + public int getActiveVoltageL2() { + return activeVoltageL2; } /** - * Getter for the current active total power on phase 2 + * Getter for the active voltage on phase 3 * - * @return current active total power on phase 2 + * @return active voltage on phase 3 */ - public double getActivePowerL2W() { - return activePowerL2W; + public int getActiveVoltageL3() { + return activeVoltageL3; } /** - * Setter for the current active power on phase 2 + * Getter for the active current (sum of all phases) * - * @param activePowerL2W current active total power on phase 2 + * @return active current (all phases) */ - public void setActivePowerL2W(double activePowerL2W) { - this.activePowerL2W = activePowerL2W; + public double getActiveCurrent() { + return activeCurrent; } /** - * Getter for the current active total power on phase 3 + * Getter for the active current on phase 1 * - * @return current active total power on phase 3 + * @return active current on phase 1 */ - public double getActivePowerL3W() { - return activePowerL3W; + public double getActiveCurrentL1() { + return activeCurrentL1; } /** - * Setter for the current active power on phase 3 + * Getter for the active current on phase 2 * - * @param activePowerL3W current active total power on phase 3 + * @return active current on phase 2 */ - public void setActivePowerL3W(double activePowerL3W) { - this.activePowerL3W = activePowerL3W; + public double getActiveCurrentL2() { + return activeCurrentL2; } /** - * Getter for the total imported gas volume + * Getter for the active current on phase 3 * - * @return total imported gas volume + * @return active current on phase 3 */ - public double getTotalGasM3() { - return totalGasM3; + public double getActiveCurrentL3() { + return activeCurrentL3; } /** - * Setter for the total imported gas volume + * Getter for the current active total power * - * @param totalGasM3 total imported gas volume + * @return current active total power */ - public void setTotalGasM3(double totalGasM3) { - this.totalGasM3 = totalGasM3; + public int getActivePowerW() { + return activePowerW; } /** - * Getter for the time stamp of the last gas update + * Getter for the current active total power on phase 2 * - * @return time stamp of the last gas update + * @return current active total power on phase 2 */ - public long getGasTimestamp() { - return gasTimestamp; + public int getActivePowerL1W() { + return activePowerL1W; } /** - * Setter for the time stamp of the last gas update + * Getter for the current active total power on phase 2 * - * @param gasTimestamp time stamp of the last gas update + * @return current active total power on phase 2 */ - public void setGasTimestamp(long gasTimestamp) { - this.gasTimestamp = gasTimestamp; + public int getActivePowerL2W() { + return activePowerL2W; } /** - * Getter for the total imported water volume + * Getter for the current active total power on phase 3 * - * @return total imported water volume + * @return current active total power on phase 3 */ - public double getTotalWaterM3() { - return totalWaterM3; + public int getActivePowerL3W() { + return activePowerL3W; } /** - * Setter for the total imported water volume + * Getter for the total imported gas volume * - * @param totalWaterM3 total imported water 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 void setTotalWaterM3(double totalWaterM3) { - this.totalWaterM3 = totalWaterM3; + 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 current water flow + * Getter for the total imported water volume * - * @return current water flow + * @return total imported water volume */ - public double getCurrentWaterLPM() { - return currentWaterLPM; + public double getTotalWaterM3() { + return totalWaterM3; } /** - * Setter for the current water flow + * Getter for the current water flow * - * @param currentWaterLPM current water flow + * @return current water flow */ - public void setCurrentWaterLPM(double currentWaterLPM) { - this.currentWaterLPM = currentWaterLPM; + public double getCurrentWaterLPM() { + return currentWaterLPM; } @Override 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/dto/StatePayload.java similarity index 97% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/StatePayload.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/dto/StatePayload.java index 8235dd34ba26e..76d31026490a4 100644 --- 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/dto/StatePayload.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +package org.openhab.binding.homewizard.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; 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/handler/HomeWizardDeviceHandler.java similarity index 84% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardDeviceHandler.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardDeviceHandler.java index 9e9f74ff604e1..984ca853748e5 100644 --- 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/handler/HomeWizardDeviceHandler.java @@ -10,14 +10,17 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +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; @@ -44,6 +47,7 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler { 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; @@ -65,7 +69,8 @@ public HomeWizardDeviceHandler(Thing thing) { public void initialize() { config = getConfigAs(HomeWizardConfiguration.class); if (configure()) { - pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, TimeUnit.SECONDS); + pollingJob = executorService.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, + TimeUnit.SECONDS); } } @@ -75,7 +80,7 @@ public void initialize() { * @return true if the configuration is ok to start polling, false otherwise */ private boolean configure() { - if (config.ipAddress.trim().isEmpty()) { + if (config.ipAddress.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing ipAddress/host configuration"); return false; @@ -105,6 +110,14 @@ public void dispose() { */ 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); + } + /** * */ @@ -112,7 +125,7 @@ protected void pollData() { final String dataResult; try { - dataResult = HttpUtil.executeUrl("GET", apiURL + "data", 30000); + dataResult = getData(); } catch (IOException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, String.format("Unable to query device data: %s", e.getMessage())); 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/handler/HomeWizardEnergySocketHandler.java similarity index 91% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardEnergySocketHandler.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardEnergySocketHandler.java index 72c844fada39b..aaac22c0502e8 100644 --- 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/handler/HomeWizardEnergySocketHandler.java @@ -10,9 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +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; @@ -34,9 +38,10 @@ public class HomeWizardEnergySocketHandler extends HomeWizardStatefulDeviceHandl * Constructor * * @param thing The thing to handle + * @param timeZoneProvider The TimeZoneProvider */ - public HomeWizardEnergySocketHandler(Thing thing) { - super(thing); + public HomeWizardEnergySocketHandler(Thing thing, TimeZoneProvider timeZoneProvider) { + super(thing, timeZoneProvider); } /** 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/handler/HomeWizardHandlerFactory.java similarity index 74% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardHandlerFactory.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardHandlerFactory.java index 57e6b805fac00..2eb4a8863c49a 100644 --- 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/handler/HomeWizardHandlerFactory.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +package org.openhab.binding.homewizard.internal.handler; import static org.openhab.binding.homewizard.internal.HomeWizardBindingConstants.*; @@ -18,12 +18,15 @@ 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 @@ -38,6 +41,13 @@ public class HomeWizardHandlerFactory extends BaseThingHandlerFactory { private static final Set 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); @@ -48,15 +58,15 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (THING_TYPE_P1_METER.equals(thingTypeUID)) { - return new HomeWizardP1MeterHandler(thing); + return new HomeWizardP1MeterHandler(thing, timeZoneProvider); } if (THING_TYPE_ENERGY_SOCKET.equals(thingTypeUID)) { - return new HomeWizardEnergySocketHandler(thing); + return new HomeWizardEnergySocketHandler(thing, timeZoneProvider); } if (THING_TYPE_WATERMETER.equals(thingTypeUID)) { - return new HomeWizardWaterMeterHandler(thing); + return new HomeWizardWaterMeterHandler(thing, timeZoneProvider); } 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/handler/HomeWizardP1MeterHandler.java similarity index 57% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandler.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardP1MeterHandler.java index 3f8785ce611e4..7419ba4603d75 100644 --- 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/handler/HomeWizardP1MeterHandler.java @@ -10,14 +10,17 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +package org.openhab.binding.homewizard.internal.handler; import java.time.DateTimeException; -import java.time.ZoneId; 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; @@ -35,14 +38,17 @@ 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) { + public HomeWizardP1MeterHandler(Thing thing, TimeZoneProvider timeZoneProvider) { super(thing); + this.timeZoneProvider = timeZoneProvider; } /** @@ -87,40 +93,39 @@ protected void handleDataPayload(DataPayload payload) { 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, + 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)); - - // 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()); - } + updateState(HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP, new DateTimeType(gasTimestamp)); } } } 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/handler/HomeWizardStatefulDeviceHandler.java similarity index 89% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardStatefulDeviceHandler.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardStatefulDeviceHandler.java index 11197b7f9f76f..9afd528afb792 100644 --- 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/handler/HomeWizardStatefulDeviceHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +package org.openhab.binding.homewizard.internal.handler; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -18,6 +18,8 @@ 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; @@ -31,15 +33,16 @@ * @author Daniël van Os - Initial contribution */ @NonNullByDefault -public abstract class HomeWizardStatefulDeviceHandler extends HomeWizardDeviceHandler { +public abstract class HomeWizardStatefulDeviceHandler extends HomeWizardP1MeterHandler { /** * Constructor * * @param thing The thing to handle + * @param timeZoneProvider The TimeZoneProvider */ - public HomeWizardStatefulDeviceHandler(Thing thing) { - super(thing); + public HomeWizardStatefulDeviceHandler(Thing thing, TimeZoneProvider timeZoneProvider) { + super(thing, timeZoneProvider); } /** 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/handler/HomeWizardWaterMeterHandler.java similarity index 76% rename from bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/HomeWizardWaterMeterHandler.java rename to bundles/org.openhab.binding.homewizard/src/main/java/org/openhab/binding/homewizard/internal/handler/HomeWizardWaterMeterHandler.java index 5fcc2ac7a88b2..fd46bdd1aa4c2 100644 --- 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/handler/HomeWizardWaterMeterHandler.java @@ -10,9 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.homewizard.internal; +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; @@ -26,15 +29,16 @@ * @author Daniël van Os - Initial contribution */ @NonNullByDefault -public class HomeWizardWaterMeterHandler extends HomeWizardDeviceHandler { +public class HomeWizardWaterMeterHandler extends HomeWizardP1MeterHandler { /** * Constructor * * @param thing The thing to handle + * @param timeZoneProvider The TimeZoneProvider */ - public HomeWizardWaterMeterHandler(Thing thing) { - super(thing); + public HomeWizardWaterMeterHandler(Thing thing, TimeZoneProvider timeZoneProvider) { + super(thing, timeZoneProvider); } /** diff --git a/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/i18n/homewizard.properties b/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/i18n/homewizard.properties index 867755a84c4d3..2d38c10234dfe 100644 --- a/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/i18n/homewizard.properties +++ b/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/i18n/homewizard.properties @@ -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 diff --git a/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/thing/thing-types.xml index 14982a4185111..2b26551e2d73b 100644 --- a/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/thing/thing-types.xml @@ -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"> - This thing provides the measurement data that is available through the http interface of the HomeWizard Wi-Fi P1 Meter. - - - - + + + The sum of the current for all phases + + + + + + + + + + + + + + + + + + + + + + + + + + + This channel provides the count of long power failures. + + + + + + + Unknown + 1 @@ -98,6 +131,13 @@ + + + Number + + This channel provides the count of any type of power failure. + + Number:Energy 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 index 0000000000000..168fc512a0cad --- /dev/null +++ b/bundles/org.openhab.binding.homewizard/src/main/resources/OH-INF/update/instructions.xml @@ -0,0 +1,57 @@ + + + + + + + + system:electric-voltage + + + system:electric-voltage + + + system:electric-voltage + + + system:electric-voltage + + + system:electric-current + + + system:electric-current + + + system:electric-current + + + system:electric-current + + + homewizard:power_failures + + + homewizard:power_failures + + This channel provides the count of long power failures. + + + homewizard:total_energy_import_t1 + + + homewizard:total_energy_import_t2 + + + homewizard:total_energy_export_t1 + + + homewizard:total_energy_export_t2 + + + + + + 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 index 0000000000000..224a367f4239c --- /dev/null +++ b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerMock.java @@ -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 index 0000000000000..30fb17e30621b --- /dev/null +++ b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/HomeWizardP1MeterHandlerTest.java @@ -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 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 index 0000000000000..d0396e8e9d1dc --- /dev/null +++ b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/DataUtil.java @@ -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 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 index 0000000000000..cf7bdbb59bbbf --- /dev/null +++ b/bundles/org.openhab.binding.homewizard/src/test/java/org/openhab/binding/homewizard/internal/dto/P1PayloadTest.java @@ -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 index 0000000000000..7a73a41bfdf76 --- /dev/null +++ b/bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response-empty.json @@ -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 index 0000000000000..5844ea388b5f0 --- /dev/null +++ b/bundles/org.openhab.binding.homewizard/src/test/resources/org/openhab/binding/homewizard/internal/dto/response.json @@ -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