[homewizard] Add support for the system API (#19967)

* Added support for the system API. This API is available for all devices; only a subset
of the channels is available for the v1 devices.
In addition, the code was refactored to make is more simple and consistent.

Signed-off-by: Gearrel Welvaart <gearrel.welvaart@gmail.com>
This commit is contained in:
GearrelW
2026-03-09 22:12:31 +01:00
committed by GitHub
parent 8f94091066
commit 20b10d7a4f
40 changed files with 1064 additions and 420 deletions
@@ -26,7 +26,7 @@ The binding offers Things, providing access to all the supported HomeWizard devi
| hwe-p1 | P1 Meter | Reads total and current energy usage and total gas usage (v1 and v2). |
| hwe-skt | Energy Socket | Reads total and current energy usage. Controls power switch, lock, and ring brightness (v1). |
| hwe-wtr | Watermeter | Reads total and current water usage (v1). |
| hwe-kwh | kWh Meter | Reads total and current energy usage (v1). |
| hwe-kwh | kWh Meter | Reads total and current energy usage (v1 and v2). |
| hwe-bat | Plug-In Battery | Reads total and current energy usage and the current charge (v2). |
| p1_wifi_meter | Wi-Fi P1 Meter | [Deprecated] Reads total and current energy usage and total gas usage. |
| energy_socket | Wi-Fi Energy Socket | [Deprecated] Reads total and current energy usage. Controls power switch, lock, and ring brightness.|
@@ -87,6 +87,11 @@ For DSMR5 meters this is generally once per second, for older versions the frequ
| batteries_target_power | Number:Power | The target power consumption/production of the controlled Plug-In Batteries. | v1, 2.1.0 |
| batteries_max_consumption | Number:Power | The maximum allowed consumption power of the controlled Plug-In Batteries. | v1, 2.1.0 |
| batteries_max_production | Number:Power | The maximum allowed production power of the controlled Plug-In Batteries. | v1, 2.1.0 |
| wifi_ssid | String | The SSID of the Wi-Fi network. | v1, 2.0.0 |
| wifi_rssi | Number:Dimensionless | The signal strength of the Wi-Fi network. | v1, 2.0.0 |
| uptime | Number:Time | The uptime of the device in seconds. | v1, 2.0.0 |
| cloud_enabled | Switch | The state of the cloud communication. | v1, 2.0.0 |
| status_led_brightness | Number:Dimensionless | The brightness of the status LED in percent. | v1, 2.0.0 |
### HWE-KWH
@@ -115,6 +120,11 @@ For DSMR5 meters this is generally once per second, for older versions the frequ
| batteries_target_power | Number:Power | The target power consumption/production of the controlled Plug-In Batteries. | v1, 2.1.0 |
| batteries_max_consumption | Number:Power | The maximum allowed consumption power of the controlled Plug-In Batteries. | v1, 2.1.0 |
| batteries_max_production | Number:Power | The maximum allowed production power of the controlled Plug-In Batteries. | v1, 2.1.0 |
| wifi_ssid | String | The SSID of the Wi-Fi network. | v1, 2.0.0 |
| wifi_rssi | Number:Dimensionless | The signal strength of the Wi-Fi network. | v1, 2.0.0 |
| uptime | Number:Time | The uptime of the device in seconds. | v1, 2.0.0 |
| cloud_enabled | Switch | The state of the cloud communication. | v1, 2.0.0 |
| status_led_brightness | Number:Dimensionless | The brightness of the status LED in percent. | v1, 2.0.0 |
### HWE-SKT
@@ -132,6 +142,9 @@ For DSMR5 meters this is generally once per second, for older versions the frequ
| power_switch | Switch | Access to the power switch of the Energy Socket. | v1 |
| power_lock | Switch | Access to the power lock of the Energy Socket. | v1 |
| ring_brightness | Number:Dimensionless | Access to the brightness of the ring of the Energy Socket. | v1 |
| wifi_ssid | String | The SSID of the Wi-Fi network. | v1 |
| wifi_rssi | Number:Dimensionless | The signal strength of the Wi-Fi network. | v1 |
| cloud_enabled | Switch | The state of the cloud communication. | v1 |
### HWE-BAT
@@ -145,6 +158,11 @@ For DSMR5 meters this is generally once per second, for older versions the frequ
| frequency | Number:Frequency | The active frequency in Hertz. | 2.0.0 |
| state_of_charge | Number:Dimensionless | Access to the current state of charge in percent. | 2.0.0 |
| cycles | Number:Dimensionless | Access to the number of battery cycles. | 2.0.0 |
| wifi_ssid | String | The SSID of the Wi-Fi network. | 2.0.0 |
| wifi_rssi | Number:Dimensionless | The signal strength of the Wi-Fi network. | 2.0.0 |
| uptime | Number:Time | The uptime of the device in seconds. | 2.0.0 |
| cloud_enabled | Switch | The state of the cloud communication. | 2.0.0 |
| status_led_brightness | Number:Dimensionless | The brightness of the status LED in percent. | 2.0.0 |
### HWE-WTR
@@ -152,6 +170,9 @@ For DSMR5 meters this is generally once per second, for older versions the frequ
|---------------------------|---------------------------|----------------------------------------------------------------------------------------|--------------|
| total_liter | Number:Volume | Total water usage in cubic meters. | v1 |
| active_liter | Number:VolumetricFlowRate | The active water usage in liters per minute. | v1 |
| wifi_ssid | String | The SSID of the Wi-Fi network. | v1 |
| wifi_rssi | Number:Dimensionless | The signal strength of the Wi-Fi network. | v1 |
| cloud_enabled | Switch | The state of the cloud communication. | v1 |
## Full Example
@@ -52,7 +52,8 @@ public class HomeWizardBindingConstants {
// Channel Groups
public static final String CHANNEL_GROUP_ENERGY = "energy";
public static final String CHANNEL_GROUP_P1_BATTERIES = "batteries";
public static final String CHANNEL_GROUP_BATTERIES = "batteries";
public static final String CHANNEL_GROUP_SYSTEM = "system";
public static final String CHANNEL_GROUP_WATER = "water";
public static final String CHANNEL_GROUP_SKT_CONTROL = "control";
@@ -107,14 +108,21 @@ public class HomeWizardBindingConstants {
public static final String CHANNEL_GAS_TIMESTAMP = "gas_timestamp";
public static final String CHANNEL_GAS_TOTAL = "total_gas";
// System
public static final String CHANNEL_SYSTEM_WIFI_SSID = "wifi_ssid";
public static final String CHANNEL_SYSTEM_WIFI_RSSI = "wifi_rssi";
public static final String CHANNEL_SYSTEM_CLOUD_ENABLED = "cloud_enabled";
public static final String CHANNEL_SYSTEM_UPTIME = "uptime";
public static final String CHANNEL_SYSTEM_STATUS_LED_BRIGHTNESS = "status_led_brightness";
// Batteries
public static final String CHANNEL_BATTERIES_MODE = "batteries_mode";
public static final String CHANNEL_BATTERIES_COUNT = "batteries_count";
public static final String CHANNEL_BATTERIES_COUNT = "batteries_battery_count";
public static final String CHANNEL_BATTERIES_POWER = "batteries_power";
public static final String CHANNEL_BATTERIES_TARGET_POWER = "batteries_target_power";
public static final String CHANNEL_BATTERIES_MAX_CONSUMPTION = "batteries_max_consumption";
public static final String CHANNEL_BATTERIES_MAX_PRODUCTION = "batteries_max_production";
// Batteries
public static final String BATTERIES_MODE_STANDBY = "standby";
public static final String BATTERIES_MODE_ZERO = "zero";
public static final String BATTERIES_MODE_TO_FULL = "to_full";
@@ -12,11 +12,7 @@
*/
package org.openhab.binding.homewizard.internal.devices;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
@@ -39,24 +35,11 @@ import com.google.gson.JsonSyntaxException;
@NonNullByDefault
public class HomeWizardBatteriesSubHandler {
private final String BATTERIES_URL = "batteries";
protected final static Logger logger = LoggerFactory.getLogger(HomeWizardBatteriesSubHandler.class);
private final static Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
protected final Logger logger = LoggerFactory.getLogger(HomeWizardBatteriesSubHandler.class);
private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
private HomeWizardDeviceHandler handler;
/**
* Constructor
*
* @param handler The device handler used to communicate with the HomeWizard device
*/
public HomeWizardBatteriesSubHandler(HomeWizardDeviceHandler handler) {
this.handler = handler;
}
public void handleCommand(Command command) {
public static void handleCommand(Command command, HomeWizardDeviceHandler handler) {
var mode = "";
var permissions = "";
@@ -96,30 +79,19 @@ public class HomeWizardBatteriesSubHandler {
}
var cmd = String.format("{%s\"mode\": \"%s\"}", permissions, mode);
try {
var response = handler.putDataTo(handler.apiURL + BATTERIES_URL, cmd);
if (response.getStatus() == HttpStatus.OK_200) {
handleBatteriesData(response.getContentAsString());
} else {
logger.warn("Failed to send command {} to {}", command, handler.apiURL + BATTERIES_URL);
}
} catch (Exception ex) {
logger.warn("Failed to send command {} to {}", command, handler.apiURL + BATTERIES_URL);
}
handler.sendBatteriesCommand(cmd);
}
/**
* Device specific handling of the returned batteries data.
*
* @param data The data obtained from the API call
* @throws JsonSyntaxException when the data cannot be parsed to the expected format
*/
public void handleBatteriesData(String data) {
public static void handleBatteriesData(String data, HomeWizardDeviceHandler handler) throws JsonSyntaxException {
HomeWizardBatteriesPayload payload = null;
try {
payload = gson.fromJson(data, HomeWizardBatteriesPayload.class);
} catch (JsonSyntaxException ex) {
logger.warn("No Batteries data available");
}
payload = gson.fromJson(data, HomeWizardBatteriesPayload.class);
if (payload != null) {
var mode = payload.getMode();
if (mode.equals(HomeWizardBindingConstants.BATTERIES_MODE_ZERO)) {
@@ -133,43 +105,22 @@ public class HomeWizardBatteriesSubHandler {
mode = HomeWizardBindingConstants.BATTERIES_MODE_STANDBY;
}
}
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MODE, new StringType(mode));
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_COUNT, new DecimalType(payload.getBatteryCount()));
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_POWER,
new QuantityType<>(payload.getPower(), Units.WATT));
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_TARGET_POWER,
new QuantityType<>(payload.getTargetPower(), Units.WATT));
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MAX_CONSUMPTION,
new QuantityType<>(payload.getMaxConsumption(), Units.WATT));
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MAX_PRODUCTION,
new QuantityType<>(payload.getMaxProduction(), Units.WATT));
}
}
public void retrieveBatteriesData() throws InterruptedException, TimeoutException, ExecutionException {
final String batteriesData;
batteriesData = getBatteriesData();
handleBatteriesData(batteriesData);
}
/**
* @return json response from the batteries api
* @throws InterruptedException, TimeoutException, ExecutionException
*/
public String getBatteriesData() throws InterruptedException, TimeoutException, ExecutionException {
var response = handler.getResponseFrom(handler.apiURL + BATTERIES_URL);
if (response.getStatus() == HttpStatus.OK_200) {
return response.getContentAsString();
} else {
logger.warn("No Batteries data available");
return "";
}
}
}
@@ -12,7 +12,11 @@
*/
package org.openhab.binding.homewizard.internal.devices;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.Arrays;
@@ -34,13 +38,21 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.binding.homewizard.internal.HomeWizardConfiguration;
import org.openhab.binding.homewizard.internal.devices.water_meter.HomeWizardWaterMeterMeasurementPayload;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,6 +60,7 @@ import org.slf4j.LoggerFactory;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
/**
* The {@link HomeWizardDeviceHandler} is a base class for all
@@ -61,6 +74,9 @@ import com.google.gson.GsonBuilder;
@NonNullByDefault
public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
private final String SYSTEM_URL = "system";
private final String BATTERIES_URL = "batteries";
private static final String BEARER = "Bearer";
private static final String API_VERSION_HEADER = "X-Api-Version";
private static final String CERTIFICATE_ALIAS = "caCert";
@@ -84,7 +100,7 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
protected List<String> supportedTypes = new ArrayList<String>();
protected List<Integer> supportedApiVersions = Arrays.asList(API_V1);
public String apiURL = "";
private String apiURL = "";
/**
* Constructor
@@ -114,7 +130,7 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
keyStore.load(null, null);
keyStore.setCertificateEntry(CERTIFICATE_ALIAS, CertificateFactory.getInstance(CERTIFICATE_TYPE)
.generateCertificate(classloader.getResourceAsStream(caCertPath)));
} catch (Exception ex) {
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException ex) {
}
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
@@ -163,7 +179,7 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
try {
httpClient.setConnectTimeout(30000);
httpClient.start();
} catch (Exception ex) {
} catch (Exception ex) { // No specific exception is thrown by the start method
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
logger.debug("Unable to reach device", ex);
@@ -174,17 +190,57 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
}
/**
* Not listening to any commands.
* Listening to commands for the system api.
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
return;
}
var cmd = "";
switch (channelUID.getIdWithoutGroup()) {
case HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED: {
boolean onOff = command.equals(OnOffType.ON);
cmd = String.format("{\"cloud_enabled\": %b}", onOff);
break;
}
case HomeWizardBindingConstants.CHANNEL_SYSTEM_STATUS_LED_BRIGHTNESS: {
cmd = String.format("{\"status_led_brightness_pct\": %s}", command.toFullString());
break;
}
default: {
logger.warn("Unhandled command for channel: {} command: {}", channelUID.getIdWithoutGroup(), command);
return;
}
}
sendSystemCommand(cmd);
}
/**
* The actual polling loop
*/
protected void retrieveData() {
retrieveMeasurementData();
try {
handleSystemData(getSystemData());
handleMeasurementData(getMeasurementData());
updateStatus(ThingStatus.ONLINE);
} catch (JsonSyntaxException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
logger.debug("Unable to get data from the API", ex);
return;
}
}
protected String getApiUrl() {
if (config.isUsingApiVersion2()) {
return apiURL;
} else {
return apiURL + "v1/";
}
}
private boolean processDeviceInformation() {
@@ -192,7 +248,7 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
try {
deviceInformation = getDeviceInformationData();
} catch (Exception ex) {
} catch (SecurityException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
logger.debug("Unable to get device information", ex);
@@ -243,7 +299,7 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
pollingJob = null;
try {
httpClient.stop();
} catch (Exception ex) {
} catch (Exception ex) { // No specific exception is thrown by the stop method
logger.debug("Error stopping the http client: {}", ex.getMessage());
}
}
@@ -256,76 +312,182 @@ public abstract class HomeWizardDeviceHandler extends BaseThingHandler {
* @param channelID id of the channel, which was updated
* @param state new state
*/
protected void updateState(String groupID, String channelID, State state) {
updateState(groupID + "#" + channelID, state);
public void updateState(String groupID, String channelID, State state) {
if (!groupID.isEmpty()) {
updateState(groupID + "#" + channelID, state);
} else {
updateState(channelID, state);
}
}
/**
* Device specific handling of the returned measurement data.
*
* @param payload The data obtained form the API call
* @param payload The data obtained from the API call
* @throws JsonSyntaxException when the returned data cannot be parsed
*/
protected abstract void handleMeasurementData(String data);
protected void handleMeasurementData(String data) throws JsonSyntaxException {
if (!config.isUsingApiVersion2()) {
// We're only interested in the Wi-Fi data and the water meter payload processes these data.
HomeWizardWaterMeterMeasurementPayload payload = null;
payload = gson.fromJson(data, HomeWizardWaterMeterMeasurementPayload.class);
protected ContentResponse putDataTo(String url, String data)
throws InterruptedException, TimeoutException, ExecutionException {
if (payload != null) {
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID, new StringType(payload.getWifiSsid()));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_RSSI, new DecimalType(payload.getWifiRssi()));
}
}
}
/**
* Device specific handling of the returned batteries data.
*
* @param data The data obtained from the API call
*/
protected void handleBatteriesData(String data) {
}
/**
* Device specific handling of the returned system data.
*
* @param data The data obtained from the API call
* @throws JsonSyntaxException when the returned data cannot be parsed
*/
protected void handleSystemData(String data) throws JsonSyntaxException {
HomeWizardSystemPayload payload = null;
payload = gson.fromJson(data, HomeWizardSystemPayload.class);
if (payload != null) {
if (config.isUsingApiVersion2()) {
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID, new StringType(payload.getWifiSsid()));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_RSSI, new DecimalType(payload.getWifiRssi()));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_UPTIME,
new QuantityType<>(payload.getUptime(), Units.SECOND));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_STATUS_LED_BRIGHTNESS,
new DecimalType(payload.getStatusLedBrightness()));
}
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED, OnOffType.from(payload.isCloudEnabled()));
}
}
protected @Nullable ContentResponse putDataTo(String url, String data) {
var request = httpClient.newRequest(url).method(HttpMethod.PUT).content(new StringContentProvider(data));
return sendRequest(request);
}
protected ContentResponse getResponseFrom(String url)
throws InterruptedException, TimeoutException, ExecutionException {
public @Nullable ContentResponse getResponseFrom(String url) {
return sendRequest(httpClient.newRequest(url));
}
private ContentResponse sendRequest(Request request)
throws InterruptedException, TimeoutException, ExecutionException {
private @Nullable ContentResponse sendRequest(Request request) {
if (config.isUsingApiVersion2()) {
request.header(HttpHeader.AUTHORIZATION, BEARER + " " + config.bearerToken);
request.header(API_VERSION_HEADER, "2");
}
return request.timeout(20, TimeUnit.SECONDS).send();
try {
return request.timeout(20, TimeUnit.SECONDS).send();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt(); // restore interrupt status
return null;
} catch (TimeoutException | ExecutionException ex) {
logger.debug("Error sending request", ex);
return null;
}
}
/**
* @return json response from the device information api
* @throws InterruptedException, TimeoutException, ExecutionException, SecurityException
* @return json response from the device information api or an empty string if no data is available
* @throws SecurityException
*/
public String getDeviceInformationData()
throws InterruptedException, TimeoutException, ExecutionException, SecurityException {
public String getDeviceInformationData() throws SecurityException {
var response = getResponseFrom(apiURL);
if (response == null) {
logger.warn("No Device Information data available");
return "";
}
if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
throw new SecurityException("Bearer token is invalid.");
}
return response.getContentAsString();
if (response.getStatus() == HttpStatus.OK_200) {
return response.getContentAsString();
} else {
logger.warn("No Device Information data available");
return "";
}
}
/**
* @return json response from the measurement api
* @throws InterruptedException, TimeoutException, ExecutionException
* @return json response from the system api or an empty string if no data is available
*
*/
public String getMeasurementData() throws InterruptedException, TimeoutException, ExecutionException {
var url = apiURL;
public String getSystemData() {
var response = getResponseFrom(getApiUrl() + SYSTEM_URL);
if (response != null && response.getStatus() == HttpStatus.OK_200) {
return response.getContentAsString();
} else {
logger.warn("No System data available");
return "";
}
}
public void sendSystemCommand(String command) {
var url = getApiUrl() + SYSTEM_URL;
var response = putDataTo(url, command);
if (response != null && response.getStatus() == HttpStatus.OK_200) {
handleSystemData(response.getContentAsString());
} else {
logger.warn("Failed to send command {} to {}", command, url);
}
}
/**
* @return json response from the measurement api or an empty string if no data is available
*
*/
public String getMeasurementData() {
var url = getApiUrl();
if (config.isUsingApiVersion2()) {
url += "measurement";
} else {
url += "v1/data";
url += "data";
}
var response = getResponseFrom(url);
if (response != null && response.getStatus() == HttpStatus.OK_200) {
return response.getContentAsString();
} else {
logger.warn("No Measurements data available");
return "";
}
return getResponseFrom(url).getContentAsString();
}
protected void retrieveMeasurementData() {
final String measurementData;
try {
measurementData = getMeasurementData();
} catch (Exception ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
logger.debug("Unable to get measurement data", ex);
return;
/**
* @return json response from the batteries api or an empty string if no data is available
*
*/
public String getBatteriesData() {
var response = getResponseFrom(getApiUrl() + BATTERIES_URL);
if (response != null && response.getStatus() == HttpStatus.OK_200) {
return response.getContentAsString();
} else {
logger.warn("No Batteries data available");
return "";
}
}
protected void sendBatteriesCommand(String command) {
var url = getApiUrl() + BATTERIES_URL;
var response = putDataTo(url, command);
if (response != null && response.getStatus() == HttpStatus.OK_200) {
handleBatteriesData(response.getContentAsString());
} else {
logger.warn("Failed to send command {} to {}", command, url);
}
updateStatus(ThingStatus.ONLINE);
handleMeasurementData(measurementData);
}
}
@@ -16,131 +16,131 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Thing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* The {@link HomeWizardEnergyMeterHandler} implements functionality generic to several energy meters.
* The {@link HomeWizardEnergyMeterSubHandler} implements functionality generic to several energy meters.
*
* @author Gearrel Welvaart - Initial contribution
*/
@NonNullByDefault
public class HomeWizardEnergyMeterHandler extends HomeWizardDeviceHandler {
public class HomeWizardEnergyMeterSubHandler {
/**
* Constructor
*
* @param thing The thing to handle
* @param timeZoneProvider The TimeZoneProvider
*/
public HomeWizardEnergyMeterHandler(Thing thing) {
super(thing);
}
protected final Logger logger = LoggerFactory.getLogger(HomeWizardEnergyMeterSubHandler.class);
private final static Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
/**
* Device specific handling of the returned data.
*
* @param data The data obtained form the API call
* @param data The data obtained from the API call
*/
@Override
protected void handleMeasurementData(String data) {
public static void handleMeasurementData(String data, HomeWizardDeviceHandler handler) {
var payload = gson.fromJson(data, HomeWizardEnergyMeterMeasurementPayload.class);
if (payload != null) {
if (!thing.getThingTypeUID().equals(HomeWizardBindingConstants.THING_TYPE_P1_METER)
&& !thing.getThingTypeUID().equals(HomeWizardBindingConstants.THING_TYPE_ENERGY_SOCKET)) {
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
if (!handler.getThing().getThingTypeUID().equals(HomeWizardBindingConstants.THING_TYPE_P1_METER) && !handler
.getThing().getThingTypeUID().equals(HomeWizardBindingConstants.THING_TYPE_ENERGY_SOCKET)) {
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT,
new QuantityType<>(payload.getEnergyImport(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
new QuantityType<>(payload.getEnergyImportT1(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2,
new QuantityType<>(payload.getEnergyImportT2(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT,
new QuantityType<>(payload.getEnergyExport(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
new QuantityType<>(payload.getEnergyExportT1(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2,
new QuantityType<>(payload.getEnergyExportT2(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY, HomeWizardBindingConstants.CHANNEL_POWER,
new QuantityType<>(payload.getPower(), Units.WATT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_POWER, new QuantityType<>(payload.getPower(), Units.WATT));
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_POWER_L1,
new QuantityType<>(payload.getPowerL1(), Units.WATT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_POWER_L2,
new QuantityType<>(payload.getPowerL2(), Units.WATT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_POWER_L3,
new QuantityType<>(payload.getPowerL3(), Units.WATT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY, HomeWizardBindingConstants.CHANNEL_CURRENT,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_CURRENT,
new QuantityType<>(payload.getCurrent(), Units.AMPERE));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_CURRENT_L1,
new QuantityType<>(payload.getCurrentL1(), Units.AMPERE));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_CURRENT_L2,
new QuantityType<>(payload.getCurrentL2(), Units.AMPERE));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_CURRENT_L3,
new QuantityType<>(payload.getCurrentL3(), Units.AMPERE));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY, HomeWizardBindingConstants.CHANNEL_VOLTAGE,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_VOLTAGE,
new QuantityType<>(payload.getVoltage(), Units.VOLT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_VOLTAGE_L1,
new QuantityType<>(payload.getVoltageL1(), Units.VOLT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_VOLTAGE_L2,
new QuantityType<>(payload.getVoltageL2(), Units.VOLT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_VOLTAGE_L3,
new QuantityType<>(payload.getVoltageL3(), Units.VOLT));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
handler.updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_FREQUENCY,
new QuantityType<>(payload.getFrequency(), Units.HERTZ));
} else {
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_IMPORT_T1,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_IMPORT_T1,
new QuantityType<>(payload.getEnergyImportT1(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_IMPORT_T2,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_IMPORT_T2,
new QuantityType<>(payload.getEnergyImportT2(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_EXPORT_T1,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_EXPORT_T1,
new QuantityType<>(payload.getEnergyExportT1(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_EXPORT_T2,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_EXPORT_T2,
new QuantityType<>(payload.getEnergyExportT2(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_POWER,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_POWER,
new QuantityType<>(payload.getPower(), Units.WATT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_POWER_L1,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_POWER_L1,
new QuantityType<>(payload.getPowerL1(), Units.WATT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_POWER_L2,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_POWER_L2,
new QuantityType<>(payload.getPowerL2(), Units.WATT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_POWER_L3,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_POWER_L3,
new QuantityType<>(payload.getPowerL3(), Units.WATT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE,
new QuantityType<>(payload.getVoltage(), Units.VOLT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE_L1,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE_L1,
new QuantityType<>(payload.getVoltageL1(), Units.VOLT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE_L2,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE_L2,
new QuantityType<>(payload.getVoltageL2(), Units.VOLT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE_L3,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_VOLTAGE_L3,
new QuantityType<>(payload.getVoltageL3(), Units.VOLT));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT,
new QuantityType<>(payload.getCurrent(), Units.AMPERE));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT_L1,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT_L1,
new QuantityType<>(payload.getCurrentL1(), Units.AMPERE));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT_L2,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT_L2,
new QuantityType<>(payload.getCurrentL2(), Units.AMPERE));
updateState(HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT_L3,
handler.updateState("", HomeWizardBindingConstants.LEGACY_CHANNEL_CURRENT_L3,
new QuantityType<>(payload.getCurrentL3(), Units.AMPERE));
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2010-2026 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.devices;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* Class that provides storage for the json objects obtained from HomeWizard devices.
*
* @author Gearrel Welvaart - Initial contribution
*
*
*/
@NonNullByDefault
public class HomeWizardSystemPayload {
@SerializedName("wifi_ssid")
private String wifiSsid = "";
@SerializedName("wifi_rssi_db")
private int wifiRssi = 0;
@SerializedName("cloud_enabled")
private Boolean cloudEnabled = false;
@SerializedName("uptime_s")
private int uptime = 0;
@SerializedName("status_led_brightness_pct")
private int ledBrightness = 0;
/**
* Getter for the wifi ssid
*
* @return wifiSsid
*/
public String getWifiSsid() {
return wifiSsid;
}
/**
* Getter for the wifi rssi
*
* @return wifiRssi
*/
public int getWifiRssi() {
return wifiRssi;
}
/**
* Getter for cloud enabled
*
* @return cloudEnabled
*/
public Boolean isCloudEnabled() {
return cloudEnabled;
}
/**
* Getter for the uptime
*
* @return uptime
*/
public int getUptime() {
return uptime;
}
/**
* Getter for the status led brightness
*
* @return ledBrightness
*/
public int getStatusLedBrightness() {
return ledBrightness;
}
@Override
public String toString() {
return String.format("""
Data [wifiSsid: %s wifiRssi: %d cloudEnable: %B uptime: %d ledBrightness: %d]
""", wifiSsid, wifiRssi, cloudEnabled, uptime, ledBrightness);
}
}
@@ -20,10 +20,11 @@ import com.google.gson.annotations.SerializedName;
* Class that provides storage for the json object obtained from the HomeWizard Energy Socket State API
*
* @author Daniël van Os - Initial contribution
* @author Gearrel Welvaart - Renamed class
*
*/
@NonNullByDefault
public class HomeWizardEnergySocketStatePayload {
public class HomeWizardDeviceStatePayload {
@SerializedName("power_on")
private boolean powerOn;
@SerializedName("switch_lock")
@@ -13,16 +13,24 @@
package org.openhab.binding.homewizard.internal.devices.energy_socket;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.binding.homewizard.internal.devices.HomeWizardDeviceHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterSubHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import com.google.gson.JsonSyntaxException;
/**
* The {@link HomeWizardEnergySocketHandler} implements functionality to handle a HomeWizard EnergySocket.
*
@@ -31,7 +39,8 @@ import org.openhab.core.types.RefreshType;
*
*/
@NonNullByDefault
public class HomeWizardEnergySocketHandler extends HomeWizardEnergySocketStateHandler {
public class HomeWizardEnergySocketHandler extends HomeWizardDeviceHandler {
private final String STATE_URL = "state";
/**
* Constructor
@@ -44,28 +53,16 @@ public class HomeWizardEnergySocketHandler extends HomeWizardEnergySocketStateHa
supportedTypes.add(HomeWizardBindingConstants.HWE_SKT);
}
/**
* Converts a brightness value (0..255) to a percentage.
*
* @param brightness The brightness to convert.
* @return brightness percentage
*/
private double brightnessToPercentage(int brightness) {
return (100.0 * (brightness / 255.0));
}
@Override
protected void retrieveData() {
super.retrieveData();
/**
* Converts a percentage to a brightness value (0..255)
*
* @param percentage The percentage to convert.
* @return brightness value
*/
private int percentageToBrightness(String percentage) {
try {
return (int) (Double.valueOf(percentage.replaceAll("[^\\d.]", "")) * 255.0 / 100.0);
} catch (NumberFormatException ex) {
logger.warn("Recevied invalid brightness percentage from socket");
return 0;
handleStateData(getStateData());
} catch (JsonSyntaxException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
return;
}
}
@@ -77,17 +74,10 @@ public class HomeWizardEnergySocketHandler extends HomeWizardEnergySocketStateHa
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
// For now I prefer not updating immediately above firing a full update request for each channel
return;
}
HomeWizardEnergySocketStatePayload result = null;
/*
* The returned payloads below only contain the modified value, so each has it's own
* call to updateState instead of just calling handleStatePayload() with the returned
* payload.
*/
HomeWizardDeviceStatePayload result = null;
switch (channelUID.getIdWithoutGroup()) {
case HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS: {
@@ -119,7 +109,7 @@ public class HomeWizardEnergySocketHandler extends HomeWizardEnergySocketStateHa
break;
}
default:
logger.warn("Unhandled command for channel: {} command: {}", channelUID.getIdWithoutGroup(), command);
super.handleCommand(channelUID, command);
break;
}
}
@@ -131,7 +121,7 @@ public class HomeWizardEnergySocketHandler extends HomeWizardEnergySocketStateHa
*/
@Override
protected void handleMeasurementData(String data) {
super.handleMeasurementData(data);
HomeWizardEnergyMeterSubHandler.handleMeasurementData(data, this);
var payload = gson.fromJson(data, HomeWizardEnergySocketMeasurementPayload.class);
if (payload != null) {
@@ -144,23 +134,95 @@ public class HomeWizardEnergySocketHandler extends HomeWizardEnergySocketStateHa
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_POWER_FACTOR, new DecimalType(payload.getPowerFactor()));
}
super.handleMeasurementData(data);
}
@Override
protected void handleStatePayload(HomeWizardEnergySocketStatePayload payload) {
if (!thing.getThingTypeUID().equals(HomeWizardBindingConstants.THING_TYPE_ENERGY_SOCKET)) {
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SKT_CONTROL,
HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(payload.getPowerOn()));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SKT_CONTROL,
HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(payload.getSwitchLock()));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SKT_CONTROL,
HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
new DecimalType(brightnessToPercentage(payload.getBrightness())));
/**
* Device specific handling of the returned state data.
*
* @param data The data obtained from the API call
*/
public void handleStateData(String data) {
HomeWizardDeviceStatePayload payload = null;
try {
payload = gson.fromJson(data, HomeWizardDeviceStatePayload.class);
} catch (JsonSyntaxException ex) {
logger.warn("No State data available");
}
if (payload != null) {
if (!thing.getThingTypeUID().equals(HomeWizardBindingConstants.THING_TYPE_ENERGY_SOCKET)) {
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SKT_CONTROL,
HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(payload.getPowerOn()));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SKT_CONTROL,
HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(payload.getSwitchLock()));
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_SKT_CONTROL,
HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
new DecimalType(brightnessToPercentage(payload.getBrightness())));
} else {
updateState("", HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(payload.getPowerOn()));
updateState("", HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(payload.getSwitchLock()));
updateState("", HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
new DecimalType(brightnessToPercentage(payload.getBrightness())));
}
}
}
/**
* @return json response from the state api
*
*/
public String getStateData() {
var response = getResponseFrom(getApiUrl() + STATE_URL);
if (response != null && response.getStatus() == HttpStatus.OK_200) {
return response.getContentAsString();
} else {
updateState(HomeWizardBindingConstants.CHANNEL_POWER_SWITCH, OnOffType.from(payload.getPowerOn()));
updateState(HomeWizardBindingConstants.CHANNEL_POWER_LOCK, OnOffType.from(payload.getSwitchLock()));
updateState(HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS,
new DecimalType(brightnessToPercentage(payload.getBrightness())));
logger.warn("No State data available");
return "";
}
}
/**
* Sends a command to the state interface of the device.
*
* @param command The command to send.
*/
private @Nullable HomeWizardDeviceStatePayload sendStateCommand(String command) {
var url = getApiUrl() + STATE_URL;
var response = putDataTo(url, command);
if (response == null) {
logger.warn("Failed to send command {} to {}", command, url);
return null;
}
try {
return gson.fromJson(response.getContentAsString(), HomeWizardDeviceStatePayload.class);
} catch (JsonSyntaxException ex) {
logger.warn("Failed to parse response for command {} to {}", command, url);
return null;
}
}
/**
* Converts a brightness value (0..255) to a percentage.
*
* @param brightness The brightness to convert.
* @return brightness percentage
*/
private static double brightnessToPercentage(int brightness) {
return (100.0 * (brightness / 255.0));
}
/**
* Converts a percentage to a brightness value (0..255)
*
* @param percentage The percentage to convert.
* @return brightness value
*/
private int percentageToBrightness(String percentage) {
try {
return (int) (Double.valueOf(percentage.replaceAll("[^\\d.]", "")) * 255.0 / 100.0);
} catch (NumberFormatException ex) {
logger.warn("Received invalid brightness percentage from socket");
return 0;
}
}
}
@@ -1,112 +0,0 @@
/*
* Copyright (c) 2010-2026 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.devices.energy_socket;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterHandler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
/**
* The {@link HomeWizardEnergySocketStateHandler} extends the base class
* to provide support for devices that also have a 'state' interface.
* This interface can be used to query and control the state of a device.
*
* @author Daniël van Os - Initial contribution
* @author Gearrel Welvaart - Several minor tweaks
*
*/
@NonNullByDefault
public abstract class HomeWizardEnergySocketStateHandler extends HomeWizardEnergyMeterHandler {
private final String STATE_URL = "v1/state";
/**
* Constructor
*
* @param thing The thing to handle
*
*/
public HomeWizardEnergySocketStateHandler(Thing thing) {
super(thing);
}
/**
* Device specific handling of the returned state payload.
*
* @param payload The data parsed from the state Json file
*/
protected abstract void handleStatePayload(HomeWizardEnergySocketStatePayload payload);
/**
* @return json response from the state api
* @throws Exception
*/
public String getStateData() throws Exception {
return getResponseFrom(apiURL + STATE_URL).getContentAsString();
}
protected void pollState() {
final String stateResult;
try {
stateResult = getStateData();
} catch (Exception ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Unable to query device state: %s", ex.getMessage()));
return;
}
if (stateResult.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device returned empty state");
return;
}
HomeWizardEnergySocketStatePayload statePayload = gson.fromJson(stateResult,
HomeWizardEnergySocketStatePayload.class);
if (statePayload == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Unable to parse state response from device");
return;
}
handleStatePayload(statePayload);
}
/**
* Sends a command to the state interface of the device.
*
* @param command The command to send.
*/
protected @Nullable HomeWizardEnergySocketStatePayload sendStateCommand(String command) {
String updatedState = "";
try {
updatedState = putDataTo(apiURL + STATE_URL, command).getContentAsString();
} catch (Exception ex) {
logger.warn("Failed to send command {} to {}", command, apiURL + STATE_URL);
}
return gson.fromJson(updatedState, HomeWizardEnergySocketStatePayload.class);
}
/*
* This overrides the original polling loop by including a request for the current state..
*/
@Override
protected void retrieveData() {
retrieveMeasurementData();
pollState();
}
}
@@ -17,7 +17,8 @@ import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.binding.homewizard.internal.devices.HomeWizardBatteriesSubHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardDeviceHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterSubHandler;
import org.openhab.binding.homewizard.internal.devices.energy_socket.HomeWizardEnergySocketMeasurementPayload;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
@@ -29,6 +30,8 @@ import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import com.google.gson.JsonSyntaxException;
/**
* The {@link HomeWizardKwhMeterHandler} implements functionality to handle a HomeWizard kWh Meter.
*
@@ -36,9 +39,7 @@ import org.openhab.core.types.RefreshType;
*
*/
@NonNullByDefault
public class HomeWizardKwhMeterHandler extends HomeWizardEnergyMeterHandler {
protected HomeWizardBatteriesSubHandler batteriesHandler;
public class HomeWizardKwhMeterHandler extends HomeWizardDeviceHandler {
/**
* Constructor
@@ -53,8 +54,22 @@ public class HomeWizardKwhMeterHandler extends HomeWizardEnergyMeterHandler {
supportedTypes.add(HomeWizardBindingConstants.HWE_KWH3);
supportedTypes.add(HomeWizardBindingConstants.SDM630_WIFI);
supportedApiVersions = Arrays.asList(API_V1, API_V2);
}
batteriesHandler = new HomeWizardBatteriesSubHandler(this);
@Override
protected void retrieveData() {
super.retrieveData();
try {
if (config.isUsingApiVersion2()) {
handleBatteriesData(getBatteriesData());
}
} catch (JsonSyntaxException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
logger.debug("Unable to get data from the API", ex);
return;
}
}
@Override
@@ -65,23 +80,9 @@ public class HomeWizardKwhMeterHandler extends HomeWizardEnergyMeterHandler {
}
if (channelUID.getIdWithoutGroup().equals(HomeWizardBindingConstants.CHANNEL_BATTERIES_MODE)) {
batteriesHandler.handleCommand(command);
HomeWizardBatteriesSubHandler.handleCommand(command, this);
} else {
logger.warn("Unhandled command for channel: {} command: {}", channelUID.getIdWithoutGroup(), command);
}
}
@Override
protected void retrieveData() {
super.retrieveData();
if (config.isUsingApiVersion2()) {
try {
batteriesHandler.retrieveBatteriesData();
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
return;
}
super.handleCommand(channelUID, command);
}
}
@@ -92,7 +93,7 @@ public class HomeWizardKwhMeterHandler extends HomeWizardEnergyMeterHandler {
*/
@Override
protected void handleMeasurementData(String data) {
super.handleMeasurementData(data);
HomeWizardEnergyMeterSubHandler.handleMeasurementData(data, this);
var payload = gson.fromJson(data, HomeWizardEnergySocketMeasurementPayload.class);
if (payload != null) {
@@ -105,5 +106,17 @@ public class HomeWizardKwhMeterHandler extends HomeWizardEnergyMeterHandler {
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_POWER_FACTOR, new DecimalType(payload.getPowerFactor()));
}
super.handleMeasurementData(data);
}
/**
* Device specific handling of the returned batteries data.
*
* @param data The data obtained from the API call
* @throws JsonSyntaxException when the data cannot be parsed to the expected format
*/
@Override
protected void handleBatteriesData(String data) throws JsonSyntaxException {
HomeWizardBatteriesSubHandler.handleBatteriesData(data, this);
}
}
@@ -19,7 +19,8 @@ import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.binding.homewizard.internal.devices.HomeWizardBatteriesSubHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardDeviceHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterSubHandler;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
@@ -32,6 +33,8 @@ import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import com.google.gson.JsonSyntaxException;
/**
* The {@link HomeWizardP1MeterHandler} implements functionality to handle a HomeWizard P1 Meter.
*
@@ -39,9 +42,7 @@ import org.openhab.core.types.RefreshType;
* @author Gearrel Welvaart - Adapted to new structure
*/
@NonNullByDefault
public class HomeWizardP1MeterHandler extends HomeWizardEnergyMeterHandler {
protected HomeWizardBatteriesSubHandler batteriesHandler;
public class HomeWizardP1MeterHandler extends HomeWizardDeviceHandler {
private String meterModel = "";
private int meterVersion = 0;
private TimeZoneProvider timeZoneProvider;
@@ -57,21 +58,21 @@ public class HomeWizardP1MeterHandler extends HomeWizardEnergyMeterHandler {
this.timeZoneProvider = timeZoneProvider;
supportedTypes.add(HomeWizardBindingConstants.HWE_P1);
supportedApiVersions = Arrays.asList(API_V1, API_V2);
batteriesHandler = new HomeWizardBatteriesSubHandler(this);
}
@Override
protected void retrieveData() {
super.retrieveData();
if (config.isUsingApiVersion2()) {
try {
batteriesHandler.retrieveBatteriesData();
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
return;
try {
if (config.isUsingApiVersion2()) {
handleBatteriesData(getBatteriesData());
}
} catch (JsonSyntaxException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-device-offline");
logger.debug("Unable to get data from the API", ex);
return;
}
}
@@ -83,9 +84,9 @@ public class HomeWizardP1MeterHandler extends HomeWizardEnergyMeterHandler {
}
if (channelUID.getIdWithoutGroup().equals(HomeWizardBindingConstants.CHANNEL_BATTERIES_MODE)) {
batteriesHandler.handleCommand(command);
HomeWizardBatteriesSubHandler.handleCommand(command, this);
} else {
logger.warn("Unhandled command for channel: {} command: {}", channelUID.getIdWithoutGroup(), command);
super.handleCommand(channelUID, command);
}
}
@@ -96,7 +97,7 @@ public class HomeWizardP1MeterHandler extends HomeWizardEnergyMeterHandler {
*/
@Override
protected void handleMeasurementData(String data) {
super.handleMeasurementData(data);
HomeWizardEnergyMeterSubHandler.handleMeasurementData(data, this);
var payload = gson.fromJson(data, HomeWizardP1MeterMeasurementPayload.class);
if (payload != null) {
@@ -167,5 +168,16 @@ public class HomeWizardP1MeterHandler extends HomeWizardEnergyMeterHandler {
}
}
}
super.handleMeasurementData(data);
}
/**
* Device specific handling of the returned batteries data.
*
* @param data The data obtained from the API call
*/
@Override
protected void handleBatteriesData(String data) {
HomeWizardBatteriesSubHandler.handleBatteriesData(data, this);
}
}
@@ -17,7 +17,9 @@ import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -110,7 +112,7 @@ public class HomeWizardP1MeterMeasurementPayload extends HomeWizardEnergyMeterMe
var gas = external.stream().filter(e -> e.getType().equals(EXTERNAL_GAS_METER)).findFirst();
try {
return Double.parseDouble(gas.get().getValue());
} catch (Exception e) {
} catch (NumberFormatException | NoSuchElementException ex) {
return 0.0;
}
}
@@ -153,15 +155,15 @@ public class HomeWizardP1MeterMeasurementPayload extends HomeWizardEnergyMeterMe
try {
zonedDateTime = ZonedDateTime.of(year, month, day, hours, minutes, seconds, 0, timeZoneId);
} catch (Exception ex) {
} catch (DateTimeException ex) {
return null;
}
} catch (Exception e) {
} catch (NumberFormatException | NoSuchElementException ex) {
try {
zonedDateTime = ZonedDateTime.of(
LocalDateTime.parse(gas.get().getTimestamp(), DateTimeFormatter.ISO_LOCAL_DATE_TIME),
timeZoneId);
} catch (Exception ex) {
} catch (DateTimeParseException | NoSuchElementException e) {
return null;
}
}
@@ -16,7 +16,8 @@ import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardDeviceHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterSubHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.Thing;
@@ -27,8 +28,7 @@ import org.openhab.core.thing.Thing;
*
*/
@NonNullByDefault
public class HomeWizardPlugInBatteryHandler extends HomeWizardEnergyMeterHandler {
public class HomeWizardPlugInBatteryHandler extends HomeWizardDeviceHandler {
/**
* Constructor
*
@@ -48,7 +48,7 @@ public class HomeWizardPlugInBatteryHandler extends HomeWizardEnergyMeterHandler
*/
@Override
protected void handleMeasurementData(String data) {
super.handleMeasurementData(data);
HomeWizardEnergyMeterSubHandler.handleMeasurementData(data, this);
var payload = gson.fromJson(data, HomeWizardPlugInBatteryMeasurementPayload.class);
if (payload != null) {
@@ -57,5 +57,6 @@ public class HomeWizardPlugInBatteryHandler extends HomeWizardEnergyMeterHandler
updateState(HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY, HomeWizardBindingConstants.CHANNEL_CYCLES,
new DecimalType(payload.getCycles()));
}
super.handleMeasurementData(data);
}
}
@@ -64,5 +64,6 @@ public class HomeWizardWaterMeterHandler extends HomeWizardDeviceHandler {
new QuantityType<>(payload.getTotalLiter(), SIUnits.CUBIC_METRE));
}
}
super.handleMeasurementData(data);
}
}
@@ -26,12 +26,36 @@ import com.google.gson.annotations.SerializedName;
@NonNullByDefault
public class HomeWizardWaterMeterMeasurementPayload {
@SerializedName("wifi_ssid")
private String wifiSsid = "";
@SerializedName("wifi_strength")
private int wifiRssi = 0;
@SerializedName("active_liter_lpm")
private double activeLiter;
@SerializedName("total_liter_m3")
private double totalLiter;
/**
* Getter for the wifi ssid
*
* @return wifiSsid
*/
public String getWifiSsid() {
return wifiSsid;
}
/**
* Getter for the wifi rssi. The value is converted from percentage to dB.
*
* @return wifiRssi
*/
public int getWifiRssi() {
return (wifiRssi / 2) - 100;
}
/**
* Getter for the active liter per minute
*
@@ -53,7 +77,7 @@ public class HomeWizardWaterMeterMeasurementPayload {
@Override
public String toString() {
return String.format("""
Water Meter Data [activeLiter: %f totalLiter: %f"]
""", activeLiter, totalLiter);
Water Meter Data [wifiSsid: %s wifiRssi: %d activeLiter: %f totalLiter: %f"]
""", wifiSsid, wifiRssi, activeLiter, totalLiter);
}
}
@@ -5,8 +5,8 @@
<type>binding</type>
<name>HomeWizard Binding</name>
<description>This binding provides access to the data provided by the API of HomeWizard devices. API version 1 is
supported, API version 2 (still in beta) is not yet supported.</description>
<description>This binding provides access to the data provided by the API of HomeWizard devices. API versions 1 and 2
are supported.</description>
<connection>local</connection>
</addon:addon>
@@ -1,7 +1,7 @@
# add-on
addon.homewizard.name = HomeWizard Binding
addon.homewizard.description = This binding provides access to the data provided by the API of HomeWizard devices. API version 1 is supported, API version 2 (still in beta) is not yet supported.
addon.homewizard.description = This binding provides access to the data provided by the API of HomeWizard devices. API versions 1 and 2 are supported.
# thing types
@@ -33,15 +33,6 @@ thing-type.homewizard.p1_wifi_meter.channel.long_power_failures.description = Th
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.
# thing status descriptions
offline.config-error-missing-host = Missing ipAddress/host configuration
offline.config-error-invalid-api-version = The selected API version is not supported
offline.config-error-missing-bearer-token = Missing bearer token
offline.comm-error-no-data = The device returned empty data
offline.comm-error-device-offline = The device is offline or doesn't support the API version
offline.comm-error-device-not-compatible = The device is not compatible with this thing type
# thing types config
thing-type.config.homewizard.device.apiVersion.label = API Version
@@ -80,6 +71,8 @@ channel-group-type.homewizard.hw-batteries-group.channel.batteries_power.label =
channel-group-type.homewizard.hw-batteries-group.channel.batteries_power.description = This channel provides the current combined power consumption/production of the controlled Plug-In Batteries.
channel-group-type.homewizard.hw-batteries-group.channel.batteries_target_power.label = Target Power
channel-group-type.homewizard.hw-batteries-group.channel.batteries_target_power.description = This channel provides the target power consumption/production of the controlled Plug-In Batteries.
channel-group-type.homewizard.hw-system-group.label = System
channel-group-type.homewizard.hw-v1-system-group.label = System
channel-group-type.homewizard.kwh-energy-group.label = Recent Measurements
channel-group-type.homewizard.kwh-energy-group.channel.current_l1.label = Active Current L1
channel-group-type.homewizard.kwh-energy-group.channel.current_l1.description = This channel provides the active current for phase 1 in Ampere.
@@ -166,6 +159,8 @@ channel-type.homewizard.hw-batteries-mode.command.option.zero_charge_only = Main
channel-type.homewizard.hw-batteries-mode.command.option.zero_discharge_only = Maintain net-zero balance, discharge only
channel-type.homewizard.hw-batteries-mode.command.option.to_full = Charge to 100%
channel-type.homewizard.hw-batteries-mode.command.option.standby = Stand-by mode
channel-type.homewizard.hw-cloud-enabled.label = Cloud Enabled
channel-type.homewizard.hw-cloud-enabled.description = This channel provides the state of the cloud communication.
channel-type.homewizard.hw-current-advanced.label = Active Current
channel-type.homewizard.hw-current-advanced.description = This channel provides the active current in Ampere.
channel-type.homewizard.hw-current.label = Active Current
@@ -188,10 +183,18 @@ channel-type.homewizard.hw-power.label = Active Power
channel-type.homewizard.hw-power.description = This channel provides the active usage in Watt.
channel-type.homewizard.hw-reactive-power.label = Reactive Power
channel-type.homewizard.hw-reactive-power.description = This channel provides the active reactive power in Volt-Ampere reactive.
channel-type.homewizard.hw-status-led-brightness.label = Status LED Brightness
channel-type.homewizard.hw-status-led-brightness.description = This channel provides the brightness of the status LED in percent.
channel-type.homewizard.hw-uptime.label = Uptime
channel-type.homewizard.hw-uptime.description = This channel provides the uptime of the device in seconds.
channel-type.homewizard.hw-voltage-advanced.label = Active Voltage
channel-type.homewizard.hw-voltage-advanced.description = This channel provides the active voltage in Volt.
channel-type.homewizard.hw-voltage.label = Active Voltage
channel-type.homewizard.hw-voltage.description = This channel provides the active voltage in Volt.
channel-type.homewizard.hw-wifi-rssi.label = Wi-Fi RSSI
channel-type.homewizard.hw-wifi-rssi.description = This channel provides the signal strength of the Wi-Fi network.
channel-type.homewizard.hw-wifi-ssid.label = Wi-Fi SSID
channel-type.homewizard.hw-wifi-ssid.description = This channel provides the SSID of the Wi-Fi network.
channel-type.homewizard.p1-gas-timestamp.label = Gas Update Time Stamp
channel-type.homewizard.p1-gas-timestamp.description = This channel provides the time stamp of the total_gas measurement.
channel-type.homewizard.p1-power-failures.label = Power Failures
@@ -226,3 +229,12 @@ channel-type.homewizard.total_gas.label = Total Imported Gas
channel-type.homewizard.total_gas.description = This channel provides the most recently reported total imported gas in m^3. It does not get updated as frequently as the data in the other channels, the gas_timestamp channel provides the time stamp of the most recent update.
channel-type.homewizard.total_water.label = Total Imported Water
channel-type.homewizard.total_water.description = This channel provides the most recently reported total imported water in m^3.
# thing status descriptions
offline.config-error-missing-host = Missing ipAddress/host configuration
offline.config-error-invalid-api-version = The selected API version is not supported
offline.config-error-missing-bearer-token = Missing bearer token
offline.comm-error-no-data = The device returned empty data
offline.comm-error-device-offline = The device is offline or doesn't support the API version
offline.comm-error-device-not-compatible = The device is not compatible with this thing type
@@ -4,6 +4,26 @@
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">
<channel-group-type id="hw-v1-system-group">
<label>System</label>
<channels>
<channel id="wifi_ssid" typeId="hw-wifi-ssid"/>
<channel id="wifi_rssi" typeId="hw-wifi-rssi"/>
<channel id="cloud_enabled" typeId="hw-cloud-enabled"/>
</channels>
</channel-group-type>
<channel-group-type id="hw-system-group">
<label>System</label>
<channels>
<channel id="wifi_ssid" typeId="hw-wifi-ssid"/>
<channel id="wifi_rssi" typeId="hw-wifi-rssi"/>
<channel id="uptime" typeId="hw-uptime"/>
<channel id="cloud_enabled" typeId="hw-cloud-enabled"/>
<channel id="status_led_brightness" typeId="hw-status-led-brightness"/>
</channels>
</channel-group-type>
<channel-group-type id="hw-batteries-group">
<label>Batteries</label>
<channels>
@@ -39,6 +59,64 @@
</channels>
</channel-group-type>
<channel-type id="hw-wifi-ssid">
<item-type>String</item-type>
<label>Wi-Fi SSID</label>
<description>
This channel provides the SSID of the Wi-Fi network.
</description>
</channel-type>
<channel-type id="hw-wifi-rssi" advanced="true">
<item-type unitHint="dB">Number:Dimensionless</item-type>
<label>Wi-Fi RSSI</label>
<description>
This channel provides the signal strength of the Wi-Fi network.
</description>
<category>QualityOfService</category>
<tags>
<tag>Measurement</tag>
<tag>SignalStrength</tag>
</tags>
</channel-type>
<channel-type id="hw-uptime" advanced="true">
<item-type>Number:Time</item-type>
<label>Uptime</label>
<description>
This channel provides the uptime of the device in seconds.
</description>
<category>Duration</category>
<tags>
<tag>Calculation</tag>
<tag>Duration</tag>
</tags>
</channel-type>
<channel-type id="hw-cloud-enabled">
<item-type>Switch</item-type>
<label>Cloud Enabled</label>
<description>
This channel provides the state of the cloud communication.
</description>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
</channel-type>
<channel-type id="hw-status-led-brightness" advanced="true">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Status LED Brightness</label>
<description>
This channel provides the brightness of the status LED in percent.
</description>
<tags>
<tag>Control</tag>
<tag>Brightness</tag>
</tags>
</channel-type>
<channel-type id="hw-power">
<item-type>Number:Power</item-type>
<label>Active Power</label>
@@ -11,9 +11,10 @@
<semantic-equipment-tag>Battery</semantic-equipment-tag>
<channel-groups>
<channel-group id="energy" typeId="bat-energy-group"/>
<channel-group id="system" typeId="hw-system-group"/>
</channel-groups>
<properties>
<property name="thingTypeVersion">2</property>
<property name="thingTypeVersion">3</property>
</properties>
<config-description-ref uri="thing-type:homewizard:device"/>
@@ -12,9 +12,10 @@
<channel-groups>
<channel-group id="energy" typeId="kwh-energy-group"/>
<channel-group id="batteries" typeId="hw-batteries-group"/>
<channel-group id="system" typeId="hw-system-group"/>
</channel-groups>
<properties>
<property name="thingTypeVersion">2</property>
<property name="thingTypeVersion">3</property>
</properties>
<config-description-ref uri="thing-type:homewizard:device"/>
@@ -12,9 +12,10 @@
<channel-groups>
<channel-group id="energy" typeId="p1-energy-group"/>
<channel-group id="batteries" typeId="hw-batteries-group"/>
<channel-group id="system" typeId="hw-system-group"/>
</channel-groups>
<properties>
<property name="thingTypeVersion">4</property>
<property name="thingTypeVersion">5</property>
</properties>
<config-description-ref uri="thing-type:homewizard:device"/>
@@ -12,9 +12,10 @@
<channel-groups>
<channel-group id="energy" typeId="skt-energy-group"/>
<channel-group id="control" typeId="skt-control-group"/>
<channel-group id="system" typeId="hw-v1-system-group"/>
</channel-groups>
<properties>
<property name="thingTypeVersion">1</property>
<property name="thingTypeVersion">2</property>
</properties>
<config-description-ref uri="thing-type:homewizard:device"/>
@@ -11,9 +11,10 @@
<semantic-equipment-tag>WaterMeter</semantic-equipment-tag>
<channel-groups>
<channel-group id="water" typeId="es-water-group"/>
<channel-group id="system" typeId="hw-v1-system-group"/>
</channel-groups>
<properties>
<property name="thingTypeVersion">1</property>
<property name="thingTypeVersion">2</property>
</properties>
<config-description-ref uri="thing-type:homewizard:device"/>
@@ -14,6 +14,24 @@
</add-channel>
</instruction-set>
<instruction-set targetVersion="3">
<add-channel id="wifi_ssid" groupIds="system">
<type>homewizard:hw-wifi-ssid</type>
</add-channel>
<add-channel id="wifi_rssi" groupIds="system">
<type>homewizard:hw-wifi-rssi</type>
</add-channel>
<add-channel id="uptime" groupIds="system">
<type>homewizard:hw-uptime</type>
</add-channel>
<add-channel id="cloud_enabled" groupIds="system">
<type>homewizard:hw-cloud-enabled</type>
</add-channel>
<add-channel id="status_led_brightness" groupIds="system">
<type>homewizard:hw-status-led-brightness</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>
@@ -44,6 +44,24 @@
</instruction-set>
<instruction-set targetVersion="3">
<add-channel id="wifi_ssid" groupIds="system">
<type>homewizard:hw-wifi-ssid</type>
</add-channel>
<add-channel id="wifi_rssi" groupIds="system">
<type>homewizard:hw-wifi-rssi</type>
</add-channel>
<add-channel id="uptime" groupIds="system">
<type>homewizard:hw-uptime</type>
</add-channel>
<add-channel id="cloud_enabled" groupIds="system">
<type>homewizard:hw-cloud-enabled</type>
</add-channel>
<add-channel id="status_led_brightness" groupIds="system">
<type>homewizard:hw-status-led-brightness</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>
@@ -56,6 +56,24 @@
</add-channel>
</instruction-set>
<instruction-set targetVersion="5">
<add-channel id="wifi_ssid" groupIds="system">
<type>homewizard:hw-wifi-ssid</type>
</add-channel>
<add-channel id="wifi_rssi" groupIds="system">
<type>homewizard:hw-wifi-rssi</type>
</add-channel>
<add-channel id="uptime" groupIds="system">
<type>homewizard:hw-uptime</type>
</add-channel>
<add-channel id="cloud_enabled" groupIds="system">
<type>homewizard:hw-cloud-enabled</type>
</add-channel>
<add-channel id="status_led_brightness" groupIds="system">
<type>homewizard:hw-status-led-brightness</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>
@@ -0,0 +1,22 @@
<?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:hwe-skt">
<instruction-set targetVersion="2">
<add-channel id="wifi_ssid" groupIds="system">
<type>homewizard:hw-wifi-ssid</type>
</add-channel>
<add-channel id="wifi_rssi" groupIds="system">
<type>homewizard:hw-wifi-rssi</type>
</add-channel>
<add-channel id="cloud_enabled" groupIds="system">
<type>homewizard:hw-cloud-enabled</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>
@@ -0,0 +1,22 @@
<?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:hwe-wtr">
<instruction-set targetVersion="2">
<add-channel id="wifi_ssid" groupIds="system">
<type>homewizard:hw-wifi-ssid</type>
</add-channel>
<add-channel id="wifi_rssi" groupIds="system">
<type>homewizard:hw-wifi-rssi</type>
</add-channel>
<add-channel id="cloud_enabled" groupIds="system">
<type>homewizard:hw-cloud-enabled</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>
@@ -91,6 +91,10 @@ public class HomeWizardHandlerTest {
}
protected static ChannelUID getBatteriesChannelUid(Thing thing, String channelId) {
return new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES + "#" + channelId);
return new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES + "#" + channelId);
}
protected static ChannelUID getSystemChannelUid(Thing thing, String channelId) {
return new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM + "#" + channelId);
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2010-2026 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.devices;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.homewizard.internal.dto.DataUtil;
/**
* Tests deserialization of HomeWizard API responses from JSON.
*
* @author Gearrel Welvaart - Initial contribution
*
*
*/
@NonNullByDefault
public class HomeWizardSystemPayloadTest {
private static final DataUtil DATA_UTIL = new DataUtil();
@Test
public void deserializeResponse() throws IOException {
HomeWizardSystemPayload key = DATA_UTIL.fromJson("response-system.json", HomeWizardSystemPayload.class);
assertThat(key, is(notNullValue()));
assertThat(key.getWifiSsid(), is("My Wi-Fi"));
assertThat(key.getWifiRssi(), is(-77));
assertThat(key.isCloudEnabled(), is(true));
assertThat(key.getUptime(), is(356));
assertThat(key.getStatusLedBrightness(), is(100));
}
@Test
public void deserializeResponseEmpty() throws IOException {
HomeWizardSystemPayload key = DATA_UTIL.fromJson("response-empty.json", HomeWizardSystemPayload.class);
assertThat(key, is(notNullValue()));
assertThat(key.getWifiSsid(), is(""));
assertThat(key.getWifiRssi(), is(0));
assertThat(key.isCloudEnabled(), is(false));
assertThat(key.getUptime(), is(0));
assertThat(key.getStatusLedBrightness(), is(0));
}
}
@@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -81,6 +82,11 @@ public class HomeWizardEnergySocketHandlerTest extends HomeWizardHandlerTest {
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS),
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID),
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_SYSTEM,
HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED),
mockChannel(thing.getUID(), HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_IMPORT_T1),
mockChannel(thing.getUID(), HomeWizardBindingConstants.LEGACY_CHANNEL_ENERGY_EXPORT_T1),
mockChannel(thing.getUID(), HomeWizardBindingConstants.LEGACY_CHANNEL_POWER),
@@ -100,7 +106,8 @@ public class HomeWizardEnergySocketHandlerTest extends HomeWizardHandlerTest {
.getDeviceInformationData();
doReturn(DataUtil.fromFile("response-measurement-energy-socket.json")).when(handler).getMeasurementData();
doReturn(DataUtil.fromFile("response-state-energy-socket.json")).when(handler).getStateData();
} catch (Exception e) {
doReturn(DataUtil.fromFile("response-system.json")).when(handler).getSystemData();
} catch (IOException ex) {
assertFalse(true);
}
@@ -146,6 +153,14 @@ public class HomeWizardEnergySocketHandlerTest extends HomeWizardHandlerTest {
verify(callback)
.stateUpdated(new ChannelUID(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_SKT_CONTROL
+ "#" + HomeWizardBindingConstants.CHANNEL_RING_BRIGHTNESS), getState(100.0));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID),
getState("My Wi-Fi"));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_RSSI), getState(-50));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED),
getState(true));
} finally {
handler.dispose();
@@ -34,8 +34,8 @@ public class HomeWizardEnergySocketStatePayloadTest {
@Test
public void deserializeResponse() throws IOException {
HomeWizardEnergySocketStatePayload key = DATA_UTIL.fromJson("response-state-energy-socket.json",
HomeWizardEnergySocketStatePayload.class);
HomeWizardDeviceStatePayload key = DATA_UTIL.fromJson("response-state-energy-socket.json",
HomeWizardDeviceStatePayload.class);
assertThat(key, is(notNullValue()));
assertThat(key.getPowerOn(), is(true));
@@ -45,8 +45,8 @@ public class HomeWizardEnergySocketStatePayloadTest {
@Test
public void deserializeResponseEmpty() throws IOException {
HomeWizardEnergySocketStatePayload key = DATA_UTIL.fromJson("response-empty.json",
HomeWizardEnergySocketStatePayload.class);
HomeWizardDeviceStatePayload key = DATA_UTIL.fromJson("response-empty.json",
HomeWizardDeviceStatePayload.class);
assertThat(key, is(notNullValue()));
assertThat(key.getPowerOn(), is(false));
@@ -16,13 +16,13 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.binding.homewizard.internal.devices.HomeWizardBatteriesSubHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardHandlerTest;
import org.openhab.binding.homewizard.internal.dto.DataUtil;
import org.openhab.core.library.unit.Units;
@@ -85,17 +85,17 @@ public class HomeWizardKwhMeterHandlerTest extends HomeWizardHandlerTest {
HomeWizardBindingConstants.CHANNEL_APPARENT_POWER), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_FREQUENCY),
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MODE), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_COUNT), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_POWER), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_TARGET_POWER), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MAX_CONSUMPTION), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MAX_PRODUCTION)
);
@@ -107,14 +107,14 @@ public class HomeWizardKwhMeterHandlerTest extends HomeWizardHandlerTest {
private static HomeWizardKwhMeterHandlerMock createAndInitHandler(final ThingHandlerCallback callback,
final Thing thing) {
final HomeWizardKwhMeterHandlerMock handler = spy(new HomeWizardKwhMeterHandlerMock(thing));
handler.batteriesHandler = spy(new HomeWizardBatteriesSubHandler(handler));
try {
doReturn(DataUtil.fromFile("response-device-information-kwh3-meter.json")).when(handler)
.getDeviceInformationData();
doReturn(DataUtil.fromFile("response-measurement-kwh-meter.json")).when(handler).getMeasurementData();
doReturn(DataUtil.fromFile("response-batteries.json")).when(handler.batteriesHandler).getBatteriesData();
} catch (Exception e) {
doReturn(DataUtil.fromFile("response-batteries.json")).when(handler).getBatteriesData();
doReturn(DataUtil.fromFile("response-system.json")).when(handler).getSystemData();
} catch (IOException ex) {
assertFalse(true);
}
@@ -16,6 +16,7 @@ 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;
@@ -24,7 +25,6 @@ import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.homewizard.internal.HomeWizardBindingConstants;
import org.openhab.binding.homewizard.internal.devices.HomeWizardBatteriesSubHandler;
import org.openhab.binding.homewizard.internal.devices.HomeWizardHandlerTest;
import org.openhab.binding.homewizard.internal.dto.DataUtil;
import org.openhab.core.i18n.TimeZoneProvider;
@@ -105,17 +105,17 @@ public class HomeWizardP1MeterHandlerTest extends HomeWizardHandlerTest {
HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_ENERGY,
HomeWizardBindingConstants.CHANNEL_GAS_TOTAL),
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MODE), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_COUNT), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_POWER), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_TARGET_POWER), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MAX_CONSUMPTION), //
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_P1_BATTERIES,
mockChannel(thing.getUID(), HomeWizardBindingConstants.CHANNEL_GROUP_BATTERIES,
HomeWizardBindingConstants.CHANNEL_BATTERIES_MAX_PRODUCTION));
when(thing.getChannels()).thenReturn(channelList);
@@ -123,18 +123,22 @@ public class HomeWizardP1MeterHandlerTest extends HomeWizardHandlerTest {
}
private static HomeWizardP1MeterHandlerMock createAndInitHandler(final ThingHandlerCallback callback,
final Thing thing) {
final Thing thing, int apiVersion) {
final TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
doReturn(ZoneId.systemDefault()).when(timeZoneProvider).getTimeZone();
final HomeWizardP1MeterHandlerMock handler = spy(new HomeWizardP1MeterHandlerMock(thing, timeZoneProvider));
handler.batteriesHandler = spy(new HomeWizardBatteriesSubHandler(handler));
try {
doReturn(DataUtil.fromFile("response-device-information-p1-meter.json")).when(handler)
.getDeviceInformationData();
doReturn(DataUtil.fromFile("response-measurement-p1-meter.json")).when(handler).getMeasurementData();
doReturn(DataUtil.fromFile("response-batteries.json")).when(handler.batteriesHandler).getBatteriesData();
} catch (Exception e) {
if (apiVersion == 1) {
doReturn(DataUtil.fromFile("v1-response-measurement-p1-meter.json")).when(handler).getMeasurementData();
} else {
doReturn(DataUtil.fromFile("response-measurement-p1-meter.json")).when(handler).getMeasurementData();
}
doReturn(DataUtil.fromFile("response-batteries.json")).when(handler).getBatteriesData();
doReturn(DataUtil.fromFile("response-system.json")).when(handler).getSystemData();
} catch (IOException ex) {
assertFalse(true);
}
@@ -144,10 +148,10 @@ public class HomeWizardP1MeterHandlerTest extends HomeWizardHandlerTest {
}
@Test
public void testUpdateChannels() {
public void testUpdateChannelsForApiV1() {
final Thing thing = mockThing(1, false);
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing);
final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing, 1);
try {
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
@@ -199,6 +203,90 @@ public class HomeWizardP1MeterHandlerTest extends HomeWizardHandlerTest {
new DateTimeType(ZonedDateTime.of(2021, 6, 06, 14, 0, 10, 0, ZoneId.systemDefault())));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_GAS_TOTAL),
getState(2569.646, SIUnits.CUBIC_METRE));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID),
getState("My Wi-Fi"));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_RSSI), getState(-58));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED),
getState(true));
} finally {
handler.dispose();
}
}
@Test
public void testUpdateChannelsForApiV2() {
final Thing thing = mockThing(2, false);
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing, 2);
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(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_CURRENT_L1),
getState(-4.0, Units.AMPERE));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_CURRENT_L2),
getState(2.0, Units.AMPERE));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_CURRENT_L3),
getState(333.0, Units.AMPERE));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_POWER),
getState(-543, Units.WATT));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_TARIFF),
getState(2));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_POWER_L1),
getState(-676, Units.WATT));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_POWER_L2),
getState(133, Units.WATT));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_POWER_L3),
getState(18, Units.WATT));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_VOLTAGE_L1),
getState(221, Units.VOLT));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_VOLTAGE_L2),
getState(222, Units.VOLT));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_VOLTAGE_L3),
getState(223, Units.VOLT));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT),
getState(8877.0, Units.KILOWATT_HOUR));
verify(callback).stateUpdated(
getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1),
getState(8874, Units.KILOWATT_HOUR));
verify(callback).stateUpdated(
getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2),
getState(7788, Units.KILOWATT_HOUR));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT),
getState(13779.338, Units.KILOWATT_HOUR));
verify(callback).stateUpdated(
getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1),
getState(10830.511, Units.KILOWATT_HOUR));
verify(callback).stateUpdated(
getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2),
getState(2948.827, Units.KILOWATT_HOUR));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_POWER_FAILURES),
getState(7));
verify(callback).stateUpdated(
getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_LONG_POWER_FAILURES), getState(2));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP),
new DateTimeType(ZonedDateTime.of(2021, 6, 06, 14, 0, 10, 0, ZoneId.systemDefault())));
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_GAS_TOTAL),
getState(2569.646, SIUnits.CUBIC_METRE));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID),
getState("My Wi-Fi"));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_RSSI), getState(-77));
verify(callback).stateUpdated(getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_UPTIME),
getState(356, Units.SECOND));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED),
getState(true));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_STATUS_LED_BRIGHTNESS),
getState(100));
} finally {
handler.dispose();
}
@@ -208,7 +296,7 @@ public class HomeWizardP1MeterHandlerTest extends HomeWizardHandlerTest {
public void testUpdateBatteriesChannels() {
final Thing thing = mockThing(2, false);
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing);
final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing, 2);
try {
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
@@ -241,7 +329,7 @@ public class HomeWizardP1MeterHandlerTest extends HomeWizardHandlerTest {
public void testUpdateLegacyChannels() {
final Thing thing = mockThing(1, true);
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing);
final HomeWizardP1MeterHandlerMock handler = createAndInitHandler(callback, thing, 1);
try {
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
@@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -76,7 +77,8 @@ public class HomeWizardPlugInBatteryMeterHandlerTest extends HomeWizardHandlerTe
doReturn(DataUtil.fromFile("response-device-information-plug-in-battery.json")).when(handler)
.getDeviceInformationData();
doReturn(DataUtil.fromFile("response-measurement-plug-in-battery.json")).when(handler).getMeasurementData();
} catch (Exception e) {
doReturn(DataUtil.fromFile("response-system.json")).when(handler).getSystemData();
} catch (IOException ex) {
assertFalse(true);
}
@@ -109,6 +111,20 @@ public class HomeWizardPlugInBatteryMeterHandlerTest extends HomeWizardHandlerTe
verify(callback).stateUpdated(getEnergyChannelUid(thing, HomeWizardBindingConstants.CHANNEL_FREQUENCY),
getState(50.0, Units.HERTZ));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID),
getState("My Wi-Fi"));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_RSSI), getState(-77));
verify(callback).stateUpdated(getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_UPTIME),
getState(356, Units.SECOND));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED),
getState(true));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_STATUS_LED_BRIGHTNESS),
getState(100));
} finally {
handler.dispose();
}
@@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -81,7 +82,8 @@ public class HomeWizardWaterMeterHandlerTest extends HomeWizardHandlerTest {
doReturn(DataUtil.fromFile("response-device-information-water-meter.json")).when(handler)
.getDeviceInformationData();
doReturn(DataUtil.fromFile("response-measurement-water-meter.json")).when(handler).getMeasurementData();
} catch (Exception e) {
doReturn(DataUtil.fromFile("response-system.json")).when(handler).getSystemData();
} catch (IOException ex) {
assertFalse(true);
}
@@ -111,6 +113,15 @@ public class HomeWizardWaterMeterHandlerTest extends HomeWizardHandlerTest {
+ HomeWizardBindingConstants.CHANNEL_TOTAL_LITER),
getState(123.456, SIUnits.CUBIC_METRE));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_SSID),
getState("My Wi-Fi"));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_WIFI_RSSI), getState(-58));
verify(callback).stateUpdated(
getSystemChannelUid(thing, HomeWizardBindingConstants.CHANNEL_SYSTEM_CLOUD_ENABLED),
getState(true));
} finally {
handler.dispose();
}
@@ -38,6 +38,9 @@ public class HomeWizardWaterMeterMeasurementPayloadTest {
HomeWizardWaterMeterMeasurementPayload.class);
assertThat(key, is(notNullValue()));
assertThat(key.getWifiSsid(), is("My Wi-Fi"));
assertThat(key.getWifiRssi(), is(-58));
assertThat(key.getActiveLiter(), is(7.2));
assertThat(key.getTotalLiter(), is(123.456));
}
@@ -48,6 +51,9 @@ public class HomeWizardWaterMeterMeasurementPayloadTest {
HomeWizardWaterMeterMeasurementPayload.class);
assertThat(key, is(notNullValue()));
assertThat(key.getWifiSsid(), is(""));
assertThat(key.getWifiRssi(), is(-100));
assertThat(key.getActiveLiter(), is(0.0));
assertThat(key.getTotalLiter(), is(0.0));
}
@@ -1,6 +1,6 @@
{
"wifi_ssid": "My Wi-Fi",
"wifi_strength": 100,
"wifi_strength": 85,
"total_liter_m3": 123.456,
"active_liter_lpm": 7.2,
"total_liter_offset_m3": 0
@@ -0,0 +1,9 @@
{
"wifi_ssid": "My Wi-Fi",
"wifi_rssi_db": -77,
"cloud_enabled": true,
"uptime_s": 356,
"status_led_brightness_pct": 100,
"api_v1_enabled": true
}
@@ -1,6 +1,6 @@
{
"wifi_ssid": "My Wi-Fi",
"wifi_strength": 100,
"wifi_strength": 85,
"smr_version": 50,
"meter_model": "ISKRA 2M550T-101",
"unique_id": "00112233445566778899AABBCCDDEEFF",