[homewizard] Add current, voltage and failure channels (#16995)

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
lsiepel 2024-10-07 08:11:04 +02:00 committed by GitHub
parent 46d6884a5c
commit 6b411899cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 880 additions and 238 deletions

View 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

View 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";

View File

@ -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 @@ 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
@ -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 class DataPayload {
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 class DataPayload {
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 class DataPayload {
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 class DataPayload {
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 class DataPayload {
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 class DataPayload {
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 class DataPayload {
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,12 +153,93 @@ public class DataPayload {
}
/**
* 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 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;
}
/**
@ -199,53 +247,26 @@ public class DataPayload {
*
* @return current active total power
*/
public double getActivePowerW() {
public int 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;
public int getActivePowerL1W() {
return activePowerL1W;
}
/**
* Setter for the current active power on phase 2
* Getter for the current active total power on phase 2
*
* @param activePowerL2W current active total power on phase 2
* @return current active total power on phase 2
*/
public void setActivePowerL2W(double activePowerL2W) {
this.activePowerL2W = activePowerL2W;
public int getActivePowerL2W() {
return activePowerL2W;
}
/**
@ -253,19 +274,10 @@ public class DataPayload {
*
* @return current active total power on phase 3
*/
public double getActivePowerL3W() {
public int 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
*
@ -275,31 +287,39 @@ public class DataPayload {
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
* @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 long getGasTimestamp() {
return gasTimestamp;
}
public @Nullable ZonedDateTime getGasTimestamp(@Nullable ZoneId zoneId) throws DateTimeException {
ZoneId timeZoneId = zoneId == null ? ZoneId.systemDefault() : zoneId;
long dtv = gasTimestamp;
if (dtv < 1) {
return null;
}
/**
* 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;
// 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);
}
/**
@ -311,15 +331,6 @@ public class DataPayload {
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
*
@ -329,15 +340,6 @@ public class DataPayload {
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(

View File

@ -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;

View File

@ -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 abstract class HomeWizardDeviceHandler extends BaseThingHandler {
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 abstract class HomeWizardDeviceHandler extends BaseThingHandler {
* @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 abstract class HomeWizardDeviceHandler extends BaseThingHandler {
*/
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 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
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()));

View File

@ -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);
}
/**

View File

@ -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 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
@ -38,6 +41,13 @@ 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);
@ -48,15 +58,15 @@ public class HomeWizardHandlerFactory extends BaseThingHandlerFactory {
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;

View File

@ -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 @@ public class HomeWizardP1MeterHandler extends HomeWizardDeviceHandler {
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));
}
}
}

View File

@ -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 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;
@ -31,15 +33,16 @@ import org.openhab.core.thing.ThingStatusDetail;
* @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);
}
/**

View File

@ -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 @@ import org.openhab.core.types.Command;
* @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);
}
/**

View 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

View 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>
@ -98,6 +131,13 @@
</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>

View File

@ -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>

View File

@ -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));
}
}

View File

@ -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();
}
}
}

View File

@ -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"));
}
}
}

View File

@ -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));
}
}

View File

@ -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"
}
]
}