Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 44 additions & 46 deletions bundles/org.openhab.binding.homewizard/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ 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_WATER = "water";
public static final String CHANNEL_GROUP_SKT_CONTROL = "control";

Expand Down Expand Up @@ -104,6 +105,12 @@ public class HomeWizardBindingConstants {
public static final String CHANNEL_GAS_TIMESTAMP = "gas_timestamp";
public static final String CHANNEL_GAS_TOTAL = "total_gas";

public static final String CHANNEL_BATTERIES_MODE = "batteries_mode";
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";

// Energy Socket And kWh Meter Channels
public static final String CHANNEL_REACTIVE_POWER = "reactive_power";
public static final String CHANNEL_APPARENT_POWER = "apparent_power";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
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.HomeWizardConfiguration;
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.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -116,7 +122,7 @@ public void initialize() {

if (configure() && processDeviceInformation()) {
updateStatus(ThingStatus.UNKNOWN);
pollingJob = executorService.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay,
pollingJob = executorService.scheduleWithFixedDelay(this::retrieveData, 0, config.refreshDelay,
TimeUnit.SECONDS);
}
}
Expand Down Expand Up @@ -155,6 +161,20 @@ private boolean configure() {
return true;
}

/**
* Not listening to any commands.
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}

/**
* The actual polling loop
*/
protected void retrieveData() {
retrieveMeasurementData();
}

private boolean processDeviceInformation() {
String deviceInformation = "";

Expand Down Expand Up @@ -226,19 +246,29 @@ protected void updateState(String groupID, String channelID, State state) {
}

/**
* Device specific handling of the returned data.
* Device specific handling of the returned measurement data.
*
* @param payload The data obtained form the API call
*/
protected abstract void handleDataPayload(String data);
protected abstract void handleMeasurementData(String data);

protected ContentResponse putDataTo(String url, String data)
throws InterruptedException, TimeoutException, ExecutionException {
var request = httpClient.newRequest(url).method(HttpMethod.PUT).content(new StringContentProvider(data));

return sendRequest(request);
}

protected ContentResponse getResponseFrom(String url)
throws InterruptedException, TimeoutException, ExecutionException {
var request = httpClient.newRequest(url);
return sendRequest(httpClient.newRequest(url));
}

private ContentResponse sendRequest(Request request)
throws InterruptedException, TimeoutException, ExecutionException {
if (config.apiVersion > 1) {
request = request.header(HttpHeader.AUTHORIZATION, BEARER + " " + config.bearerToken);
request = request.header(API_VERSION_HEADER, "" + config.apiVersion);
request.header(HttpHeader.AUTHORIZATION, BEARER + " " + config.bearerToken);
request.header(API_VERSION_HEADER, "" + config.apiVersion);
}
return request.timeout(20, TimeUnit.SECONDS).send();
}
Expand All @@ -250,7 +280,7 @@ protected ContentResponse getResponseFrom(String url)
public String getDeviceInformationData()
throws InterruptedException, TimeoutException, ExecutionException, SecurityException {
var response = getResponseFrom(apiURL);
if (response.getStatus() == 401) {
if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
throw new SecurityException("Bearer token is invalid.");
}
return response.getContentAsString();
Expand All @@ -267,29 +297,19 @@ public String getMeasurementData() throws InterruptedException, TimeoutException
} else {
url += "measurement";
}

return getResponseFrom(url).getContentAsString();
}

protected void pollData() {
protected void retrieveMeasurementData() {
final String measurementData;

try {
measurementData = getMeasurementData();
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Device is offline or doesn't support the API version"));
return;
}

updateStatus(ThingStatus.ONLINE);
handleDataPayload(measurementData);
}

/**
* The actual polling loop
*/
protected void pollingCode() {
pollData();
handleMeasurementData(measurementData);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
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.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;

/**
* The {@link HomeWizardEnergyMeterHandler} implements functionality generic to several energy meters.
Expand All @@ -38,20 +36,13 @@ public HomeWizardEnergyMeterHandler(Thing thing) {
super(thing);
}

/**
* Not listening to any commands.
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}

/**
* Device specific handling of the returned data.
*
* @param data The data obtained form the API call
*/
@Override
protected void handleDataPayload(String data) {
protected void handleMeasurementData(String data) {
var payload = gson.fromJson(data, HomeWizardEnergyMeterMeasurementPayload.class);
if (payload != null) {
if (!thing.getThingTypeUID().equals(HomeWizardBindingConstants.THING_TYPE_P1_METER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ public void handleCommand(ChannelUID channelUID, Command command) {
* @param data The data obtained from the API call
*/
@Override
protected void handleDataPayload(String data) {
super.handleDataPayload(data);
protected void handleMeasurementData(String data) {
super.handleMeasurementData(data);

var payload = gson.fromJson(data, HomeWizardEnergySocketMeasurementPayload.class);
if (payload != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,13 @@
*/
package org.openhab.binding.homewizard.internal.devices.energy_socket;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.homewizard.internal.devices.HomeWizardEnergyMeterHandler;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;

import com.google.gson.JsonSyntaxException;

/**
* The {@link HomeWizardEnergySocketStateHandler} extends the base class
* to provide support for devices that also have a 'state' interface.
Expand All @@ -38,6 +31,8 @@
@NonNullByDefault
public abstract class HomeWizardEnergySocketStateHandler extends HomeWizardEnergyMeterHandler {

private final String STATE_URL = "v1/state";

/**
* Constructor
*
Expand All @@ -57,10 +52,10 @@ public HomeWizardEnergySocketStateHandler(Thing thing) {

/**
* @return json response from the state api
* @throws IOException
* @throws Exception
*/
public String getStateData() throws Exception {
return getResponseFrom(apiURL + "v1/state").getContentAsString();
return getResponseFrom(apiURL + STATE_URL).getContentAsString();
}

protected void pollState() {
Expand Down Expand Up @@ -96,21 +91,22 @@ protected void pollState() {
* @param command The command to send.
*/
protected @Nullable HomeWizardEnergySocketStatePayload sendStateCommand(String command) {
try (InputStream is = new ByteArrayInputStream(command.getBytes())) {
String updatedState = HttpUtil.executeUrl("PUT", apiURL + "v1/state", is, "application/json", 30000);
return gson.fromJson(updatedState, HomeWizardEnergySocketStatePayload.class);
} catch (IOException | JsonSyntaxException e) {
logger.warn("Failed to send command {} to {}", command, apiURL + "state");
return null;
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 pollingCode() {
pollData();
protected void retrieveData() {
retrieveMeasurementData();
pollState();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public HomeWizardKwhMeterHandler(Thing thing) {
* @param payload The data obtained form the API call
*/
@Override
protected void handleDataPayload(String data) {
super.handleDataPayload(data);
protected void handleMeasurementData(String data) {
super.handleMeasurementData(data);

var payload = gson.fromJson(data, HomeWizardEnergySocketMeasurementPayload.class);
if (payload != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2010-2025 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.p1_meter;

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 HomeWizardP1MeterBatteriesPayload {

private String mode = "";

@SerializedName("power_w")
private Double power = 0.0;

@SerializedName("target_power_w")
private Double targetPower = 0.0;

@SerializedName("max_consumption_w")
private Double maxConsumption = 0.0;

@SerializedName("max_production_w")
private Double maxProduction = 0.0;

/**
* Getter for the mode
*
* @return mode
*/
public String getMode() {
return mode;
}

/**
* Getter for the power in watt
*
* @return power
*/
public int getPower() {
return power.intValue();
}

/**
* Getter for the target power in watt
*
* @return target power
*/
public int getTargetPower() {
return targetPower.intValue();
}

/**
* Getter for the max consumption in watt
*
* @return max consumption
*/
public int getMaxConsumption() {
return maxConsumption.intValue();
}

/**
* Getter for the max production in watt
*
* @return max production
*/
public int getMaxProduction() {
return maxProduction.intValue();
}

@Override
public String toString() {
return String.format("""
Data [mode: %s power: %f targetPower: %f
maxConsumption: %f maxProduction: %f

""", mode, power, targetPower, maxConsumption, maxProduction);
}
}
Loading