Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Homewizard] Initial contribution #9831

Merged
merged 6 commits into from
Apr 10, 2021
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
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
/bundles/org.openhab.binding.heliosventilation/ @ramack
/bundles/org.openhab.binding.heos/ @Wire82
/bundles/org.openhab.binding.homematic/ @FStolte @gerrieg @mdicke2s
/bundles/org.openhab.binding.homewizard/ @Daniel-42
/bundles/org.openhab.binding.hpprinter/ @cossey
/bundles/org.openhab.binding.http/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.hue/ @cweitkamp
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,11 @@
<artifactId>org.openhab.binding.homematic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.homewizard</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.hpprinter</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.homewizard/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.

* Project home: https://www.openhab.org

== Declared Project Licenses

This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.

== Source Code

https://github.com/openhab/openhab-addons
57 changes: 57 additions & 0 deletions bundles/org.openhab.binding.homewizard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# HomeWizard Binding

The HomeWizard binding retrieves measurements from the HomeWizard Wi-Fi P1 meter.
The meter itself is attached to a DSMR Smart Meter and reads out the telegrams, which it will forward to cloud storage.
However, recently HomeWizard also added an interface that can be queried locally.

This binding uses that local interface to make the measurements available.

## Supported Things

The binding provides the P1 Meter thing.

## Discovery

Auto discovery is not available for this binding.

## Thing Configuration

The P1 Meter thing can be configured through the web interface.

| Parameter | Required | Default | Description |
|--------------|----------|---------|---------------------------------------------------------------------------------------------------|
| ipAddress | * | | This specifies the IP address (or host name) where the meter can be found. |
| refreshDelay | | 5 | This specifies the interval in seconds used by the binding to read updated values from the meter. |

Note that update rate of the P1 Meter itself depends on the frequency of the telegrams it receives from the Smart Meter.
For DSMR5 meters this is generally once per second, for older versions the frequency is much lower.

Example of configuration through a .thing file:

```
Thing homewizard:p1_wifi_meter:my_meter [ ipAddress="192.178.1.67", refreshDelay=5 ]
```

## Channels

| Channel ID | Item Type | Description |
|------------------------|---------------|--------------------------------------------------------------------------------------------|
| total_energy_import_t1 | Number:Energy | The most recently reported total imported energy in kWh by counter 1. |
| total_energy_import_t2 | Number:Energy | The most recently reported total imported energy in kWh by counter 2. |
| total_energy_export_t1 | Number:Energy | The most recently reported total exported energy in kWh by counter 1. |
| total_energy_export_t2 | Number:Energy | The most recently reported total exported energy in kWh by counter 2. |
| active_power | Number:Power | The current net total power in W. It will be below 0 if power is currently being exported. |
| active_power_l1 | Number:Power | The current net total power in W for phase 1. |
| active_power_l2 | Number:Power | The current net total power in W for phase 2. |
| active_power_l3 | Number:Power | The current net total power in W for phase 3. |
| total_gas | Number:Volume | The most recently reported total imported gas in m^3. |
| gas_timestamp | DateTime | The time stamp of the total_gas measurement. |


Example of configuration through a .items file:

```
Number:Energy Energy_Import_T1 "Imported Energy T1 [%.0f kWh]" {channel="homewizard:p1_wifi_meter:my_meter:total_energy_import_t1" }
Number:Power Active_Power_L1 "Active Power Phase 1 [%.1f W]" {channel="homewizard:p1_wifi_meter:my_meter:active_power_l1" }
DateTime Gas_Update "Gas Update Time [%1$tH:%1$tM]" {channel="homewizard:p1_wifi_meter:my_meter:gas_timestamp" }
```
12 changes: 12 additions & 0 deletions bundles/org.openhab.binding.homewizard/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.homewizard</artifactId>
<name>openHAB Add-ons :: Bundles :: HomeWizard Binding</name>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.homewizard-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>

<feature name="openhab-binding-homewizard" description="HomeWizard Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.homewizard/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.homewizard.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;

/**
* The {@link HomeWizardBindingConstants} class defines common constants, which are
* used across the full binding.
*
* @author Daniël van Os - Initial contribution
*/
@NonNullByDefault
public class HomeWizardBindingConstants {

private static final String BINDING_ID = "homewizard";

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_P1_WIFI_METER = new ThingTypeUID(BINDING_ID, "p1_wifi_meter");

// List of all Channel ids
public static final String CHANNEL_ENERGY_IMPORT_T1 = "total_energy_import_t1";
public static final String CHANNEL_ENERGY_IMPORT_T2 = "total_energy_import_t2";
public static final String CHANNEL_ENERGY_EXPORT_T1 = "total_energy_export_t1";
public static final String CHANNEL_ENERGY_EXPORT_T2 = "total_energy_export_t2";
public static final String CHANNEL_ACTIVE_POWER = "active_power";
public static final String CHANNEL_ACTIVE_POWER_L1 = "active_power_l1";
public static final String CHANNEL_ACTIVE_POWER_L2 = "active_power_l2";
public static final String CHANNEL_ACTIVE_POWER_L3 = "active_power_l3";
public static final String CHANNEL_TOTAL_GAS = "total_gas";
public static final String CHANNEL_GAS_TIMESTAMP = "gas_timestamp";

public static final String PROPERTY_METER_MODEL = "meterModel";
public static final String PROPERTY_METER_VERSION = "meterVersion";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.homewizard.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link HomeWizardConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Daniël van Os - Initial contribution
*/
@NonNullByDefault
public class HomeWizardConfiguration {

/**
* IP Address or host for the P1 Meter
*/
public String ipAddress = "";

/**
* Refresh delay in seconds
*/
public Integer refreshDelay = 5;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.homewizard.internal;

import java.io.IOException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;

import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
* The {@link HomeWizardHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Daniël van Os - Initial contribution
*/
@NonNullByDefault
public class HomeWizardHandler extends BaseThingHandler {

private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();

private HomeWizardConfiguration config = new HomeWizardConfiguration();
private @Nullable ScheduledFuture<?> pollingJob;

private String apiURL = "";
private String meterModel = "";
private int meterVersion = 0;

/**
* Constructor
*
* @param thing The thing to handle
*/
public HomeWizardHandler(Thing thing) {
super(thing);
}

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

/**
* If a host has been specified start polling it
*/
@Override
public void initialize() {
config = getConfigAs(HomeWizardConfiguration.class);
if (configure()) {
pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, TimeUnit.SECONDS);
}
}

/**
* Check the current configuration
*
* @return true if the configuration is ok to start polling, false otherwise
*/
private boolean configure() {
if (config.ipAddress.trim().isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Missing ipAddress/host configuration");
return false;
} else {
updateStatus(ThingStatus.UNKNOWN);
apiURL = String.format("http://%s/api/v1/data", config.ipAddress.trim());
return true;
}
}

/**
* dispose: stop the poller
*/
@Override
public void dispose() {
var job = pollingJob;
if (job != null) {
job.cancel(true);
}
pollingJob = null;
}

/**
* The actual polling loop
*/
private void pollingCode() {
final String result;

try {
result = HttpUtil.executeUrl("GET", apiURL, 30000);
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Unable to query P1 Meter: %s", e.getMessage()));
return;
}

if (result.trim().isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"P1 Meter API returned empty status");
return;
}

P1Payload payload = gson.fromJson(result, P1Payload.class);
if (payload == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Unable to parse response from P1 meter");
return;
}

if ("".equals(payload.getMeterModel())) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Results from API are empty");
return;
}

updateStatus(ThingStatus.ONLINE);

if (!meterModel.equals(payload.getMeterModel())) {
meterModel = payload.getMeterModel();
updateProperty(HomeWizardBindingConstants.PROPERTY_METER_MODEL, meterModel);
}

if (meterVersion != payload.getSmrVersion()) {
meterVersion = payload.getSmrVersion();
updateProperty(HomeWizardBindingConstants.PROPERTY_METER_VERSION, String.format("%d", meterVersion));
}

updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2,
new QuantityType<>(payload.getTotalEnergyImportT2Kwh(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2,
new QuantityType<>(payload.getTotalEnergyExportT2Kwh(), Units.KILOWATT_HOUR));

updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
new QuantityType<>(payload.getActivePowerW(), Units.WATT));
updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1,
new QuantityType<>(payload.getActivePowerL1W(), Units.WATT));
updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2,
new QuantityType<>(payload.getActivePowerL2W(), Units.WATT));
updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3,
new QuantityType<>(payload.getActivePowerL3W(), Units.WATT));

updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));

// 210119164000
long dtv = payload.getGasTimestamp();
long seconds = dtv % 100;

dtv /= 100;
long minutes = dtv % 100;

dtv /= 100;
long hours = dtv % 100;

dtv /= 100;
long day = dtv % 100;

dtv /= 100;
long month = dtv % 100;

dtv /= 100;
long year = dtv + 2000; // Where (When?) have I seen this before?

DateTimeType dtt = DateTimeType
.valueOf(String.format("%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hours, minutes, seconds));
updateState(HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP, dtt);
}
}
Loading