Skip to content

Commit 95cdc3c

Browse files
authored
[Homewizard] Initial contribution (openhab#9831)
Signed-off-by: Daniël van Os <[email protected]>
1 parent 5ba6451 commit 95cdc3c

File tree

14 files changed

+883
-0
lines changed

14 files changed

+883
-0
lines changed

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
/bundles/org.openhab.binding.heliosventilation/ @ramack
102102
/bundles/org.openhab.binding.heos/ @Wire82
103103
/bundles/org.openhab.binding.homematic/ @FStolte @gerrieg @mdicke2s
104+
/bundles/org.openhab.binding.homewizard/ @Daniel-42
104105
/bundles/org.openhab.binding.hpprinter/ @cossey
105106
/bundles/org.openhab.binding.http/ @openhab/add-ons-maintainers
106107
/bundles/org.openhab.binding.hue/ @cweitkamp

bom/openhab-addons/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,11 @@
491491
<artifactId>org.openhab.binding.homematic</artifactId>
492492
<version>${project.version}</version>
493493
</dependency>
494+
<dependency>
495+
<groupId>org.openhab.addons.bundles</groupId>
496+
<artifactId>org.openhab.binding.homewizard</artifactId>
497+
<version>${project.version}</version>
498+
</dependency>
494499
<dependency>
495500
<groupId>org.openhab.addons.bundles</groupId>
496501
<artifactId>org.openhab.binding.hpprinter</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# HomeWizard Binding
2+
3+
The HomeWizard binding retrieves measurements from the HomeWizard Wi-Fi P1 meter.
4+
The meter itself is attached to a DSMR Smart Meter and reads out the telegrams, which it will forward to cloud storage.
5+
However, recently HomeWizard also added an interface that can be queried locally.
6+
7+
This binding uses that local interface to make the measurements available.
8+
9+
## Supported Things
10+
11+
The binding provides the P1 Meter thing.
12+
13+
## Discovery
14+
15+
Auto discovery is not available for this binding.
16+
17+
## Thing Configuration
18+
19+
The P1 Meter thing can be configured through the web interface.
20+
21+
| Parameter | Required | Default | Description |
22+
|--------------|----------|---------|---------------------------------------------------------------------------------------------------|
23+
| ipAddress | * | | This specifies the IP address (or host name) where the meter can be found. |
24+
| refreshDelay | | 5 | This specifies the interval in seconds used by the binding to read updated values from the meter. |
25+
26+
Note that update rate of the P1 Meter itself depends on the frequency of the telegrams it receives from the Smart Meter.
27+
For DSMR5 meters this is generally once per second, for older versions the frequency is much lower.
28+
29+
Example of configuration through a .thing file:
30+
31+
```
32+
Thing homewizard:p1_wifi_meter:my_meter [ ipAddress="192.178.1.67", refreshDelay=5 ]
33+
```
34+
35+
## Channels
36+
37+
| Channel ID | Item Type | Description |
38+
|------------------------|---------------|--------------------------------------------------------------------------------------------|
39+
| total_energy_import_t1 | Number:Energy | The most recently reported total imported energy in kWh by counter 1. |
40+
| total_energy_import_t2 | Number:Energy | The most recently reported total imported energy in kWh by counter 2. |
41+
| total_energy_export_t1 | Number:Energy | The most recently reported total exported energy in kWh by counter 1. |
42+
| total_energy_export_t2 | Number:Energy | The most recently reported total exported energy in kWh by counter 2. |
43+
| active_power | Number:Power | The current net total power in W. It will be below 0 if power is currently being exported. |
44+
| active_power_l1 | Number:Power | The current net total power in W for phase 1. |
45+
| active_power_l2 | Number:Power | The current net total power in W for phase 2. |
46+
| active_power_l3 | Number:Power | The current net total power in W for phase 3. |
47+
| total_gas | Number:Volume | The most recently reported total imported gas in m^3. |
48+
| gas_timestamp | DateTime | The time stamp of the total_gas measurement. |
49+
50+
51+
Example of configuration through a .items file:
52+
53+
```
54+
Number:Energy Energy_Import_T1 "Imported Energy T1 [%.0f kWh]" {channel="homewizard:p1_wifi_meter:my_meter:total_energy_import_t1" }
55+
Number:Power Active_Power_L1 "Active Power Phase 1 [%.1f W]" {channel="homewizard:p1_wifi_meter:my_meter:active_power_l1" }
56+
DateTime Gas_Update "Gas Update Time [%1$tH:%1$tM]" {channel="homewizard:p1_wifi_meter:my_meter:gas_timestamp" }
57+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.openhab.addons.bundles</groupId>
7+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
8+
<version>3.1.0-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>org.openhab.binding.homewizard</artifactId>
11+
<name>openHAB Add-ons :: Bundles :: HomeWizard Binding</name>
12+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<features name="org.openhab.binding.homewizard-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
3+
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
4+
5+
<feature name="openhab-binding-homewizard" description="HomeWizard Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.homewizard/${project.version}</bundle>
8+
</feature>
9+
</features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Copyright (c) 2010-2021 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.homewizard.internal;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.core.thing.ThingTypeUID;
17+
18+
/**
19+
* The {@link HomeWizardBindingConstants} class defines common constants, which are
20+
* used across the full binding.
21+
*
22+
* @author Daniël van Os - Initial contribution
23+
*/
24+
@NonNullByDefault
25+
public class HomeWizardBindingConstants {
26+
27+
private static final String BINDING_ID = "homewizard";
28+
29+
// List of all Thing Type UIDs
30+
public static final ThingTypeUID THING_TYPE_P1_WIFI_METER = new ThingTypeUID(BINDING_ID, "p1_wifi_meter");
31+
32+
// List of all Channel ids
33+
public static final String CHANNEL_ENERGY_IMPORT_T1 = "total_energy_import_t1";
34+
public static final String CHANNEL_ENERGY_IMPORT_T2 = "total_energy_import_t2";
35+
public static final String CHANNEL_ENERGY_EXPORT_T1 = "total_energy_export_t1";
36+
public static final String CHANNEL_ENERGY_EXPORT_T2 = "total_energy_export_t2";
37+
public static final String CHANNEL_ACTIVE_POWER = "active_power";
38+
public static final String CHANNEL_ACTIVE_POWER_L1 = "active_power_l1";
39+
public static final String CHANNEL_ACTIVE_POWER_L2 = "active_power_l2";
40+
public static final String CHANNEL_ACTIVE_POWER_L3 = "active_power_l3";
41+
public static final String CHANNEL_TOTAL_GAS = "total_gas";
42+
public static final String CHANNEL_GAS_TIMESTAMP = "gas_timestamp";
43+
44+
public static final String PROPERTY_METER_MODEL = "meterModel";
45+
public static final String PROPERTY_METER_VERSION = "meterVersion";
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Copyright (c) 2010-2021 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.homewizard.internal;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
17+
/**
18+
* The {@link HomeWizardConfiguration} class contains fields mapping thing configuration parameters.
19+
*
20+
* @author Daniël van Os - Initial contribution
21+
*/
22+
@NonNullByDefault
23+
public class HomeWizardConfiguration {
24+
25+
/**
26+
* IP Address or host for the P1 Meter
27+
*/
28+
public String ipAddress = "";
29+
30+
/**
31+
* Refresh delay in seconds
32+
*/
33+
public Integer refreshDelay = 5;
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/**
2+
* Copyright (c) 2010-2021 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.homewizard.internal;
14+
15+
import java.io.IOException;
16+
import java.util.concurrent.ScheduledFuture;
17+
import java.util.concurrent.TimeUnit;
18+
19+
import org.eclipse.jdt.annotation.NonNullByDefault;
20+
import org.eclipse.jdt.annotation.Nullable;
21+
import org.openhab.core.io.net.http.HttpUtil;
22+
import org.openhab.core.library.types.DateTimeType;
23+
import org.openhab.core.library.types.QuantityType;
24+
import org.openhab.core.library.unit.SIUnits;
25+
import org.openhab.core.library.unit.Units;
26+
import org.openhab.core.thing.ChannelUID;
27+
import org.openhab.core.thing.Thing;
28+
import org.openhab.core.thing.ThingStatus;
29+
import org.openhab.core.thing.ThingStatusDetail;
30+
import org.openhab.core.thing.binding.BaseThingHandler;
31+
import org.openhab.core.types.Command;
32+
33+
import com.google.gson.FieldNamingPolicy;
34+
import com.google.gson.Gson;
35+
import com.google.gson.GsonBuilder;
36+
37+
/**
38+
* The {@link HomeWizardHandler} is responsible for handling commands, which are
39+
* sent to one of the channels.
40+
*
41+
* @author Daniël van Os - Initial contribution
42+
*/
43+
@NonNullByDefault
44+
public class HomeWizardHandler extends BaseThingHandler {
45+
46+
private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
47+
.create();
48+
49+
private HomeWizardConfiguration config = new HomeWizardConfiguration();
50+
private @Nullable ScheduledFuture<?> pollingJob;
51+
52+
private String apiURL = "";
53+
private String meterModel = "";
54+
private int meterVersion = 0;
55+
56+
/**
57+
* Constructor
58+
*
59+
* @param thing The thing to handle
60+
*/
61+
public HomeWizardHandler(Thing thing) {
62+
super(thing);
63+
}
64+
65+
/**
66+
* Not listening to any commands.
67+
*/
68+
@Override
69+
public void handleCommand(ChannelUID channelUID, Command command) {
70+
}
71+
72+
/**
73+
* If a host has been specified start polling it
74+
*/
75+
@Override
76+
public void initialize() {
77+
config = getConfigAs(HomeWizardConfiguration.class);
78+
if (configure()) {
79+
pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, TimeUnit.SECONDS);
80+
}
81+
}
82+
83+
/**
84+
* Check the current configuration
85+
*
86+
* @return true if the configuration is ok to start polling, false otherwise
87+
*/
88+
private boolean configure() {
89+
if (config.ipAddress.trim().isEmpty()) {
90+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
91+
"Missing ipAddress/host configuration");
92+
return false;
93+
} else {
94+
updateStatus(ThingStatus.UNKNOWN);
95+
apiURL = String.format("http://%s/api/v1/data", config.ipAddress.trim());
96+
return true;
97+
}
98+
}
99+
100+
/**
101+
* dispose: stop the poller
102+
*/
103+
@Override
104+
public void dispose() {
105+
var job = pollingJob;
106+
if (job != null) {
107+
job.cancel(true);
108+
}
109+
pollingJob = null;
110+
}
111+
112+
/**
113+
* The actual polling loop
114+
*/
115+
private void pollingCode() {
116+
final String result;
117+
118+
try {
119+
result = HttpUtil.executeUrl("GET", apiURL, 30000);
120+
} catch (IOException e) {
121+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
122+
String.format("Unable to query P1 Meter: %s", e.getMessage()));
123+
return;
124+
}
125+
126+
if (result.trim().isEmpty()) {
127+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
128+
"P1 Meter API returned empty status");
129+
return;
130+
}
131+
132+
P1Payload payload = gson.fromJson(result, P1Payload.class);
133+
if (payload == null) {
134+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
135+
"Unable to parse response from P1 meter");
136+
return;
137+
}
138+
139+
if ("".equals(payload.getMeterModel())) {
140+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Results from API are empty");
141+
return;
142+
}
143+
144+
updateStatus(ThingStatus.ONLINE);
145+
146+
if (!meterModel.equals(payload.getMeterModel())) {
147+
meterModel = payload.getMeterModel();
148+
updateProperty(HomeWizardBindingConstants.PROPERTY_METER_MODEL, meterModel);
149+
}
150+
151+
if (meterVersion != payload.getSmrVersion()) {
152+
meterVersion = payload.getSmrVersion();
153+
updateProperty(HomeWizardBindingConstants.PROPERTY_METER_VERSION, String.format("%d", meterVersion));
154+
}
155+
156+
updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
157+
new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
158+
updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2,
159+
new QuantityType<>(payload.getTotalEnergyImportT2Kwh(), Units.KILOWATT_HOUR));
160+
updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
161+
new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
162+
updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2,
163+
new QuantityType<>(payload.getTotalEnergyExportT2Kwh(), Units.KILOWATT_HOUR));
164+
165+
updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
166+
new QuantityType<>(payload.getActivePowerW(), Units.WATT));
167+
updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1,
168+
new QuantityType<>(payload.getActivePowerL1W(), Units.WATT));
169+
updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2,
170+
new QuantityType<>(payload.getActivePowerL2W(), Units.WATT));
171+
updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3,
172+
new QuantityType<>(payload.getActivePowerL3W(), Units.WATT));
173+
174+
updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
175+
new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
176+
177+
// 210119164000
178+
long dtv = payload.getGasTimestamp();
179+
long seconds = dtv % 100;
180+
181+
dtv /= 100;
182+
long minutes = dtv % 100;
183+
184+
dtv /= 100;
185+
long hours = dtv % 100;
186+
187+
dtv /= 100;
188+
long day = dtv % 100;
189+
190+
dtv /= 100;
191+
long month = dtv % 100;
192+
193+
dtv /= 100;
194+
long year = dtv + 2000; // Where (When?) have I seen this before?
195+
196+
DateTimeType dtt = DateTimeType
197+
.valueOf(String.format("%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hours, minutes, seconds));
198+
updateState(HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP, dtt);
199+
}
200+
}

0 commit comments

Comments
 (0)