Skip to content

Commit 36bde00

Browse files
authored
[myuplink] Initial contribution (openhab#17451)
* myuplink skeleton Signed-off-by: Alexander Friese <af944580@googlemail.com>
1 parent b00a44a commit 36bde00

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4369
-0
lines changed

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
/bundles/org.openhab.binding.mybmw/ @ntruchsess @mherwege @martingrassl
247247
/bundles/org.openhab.binding.mynice/ @clinique
248248
/bundles/org.openhab.binding.mystrom/ @pail23
249+
/bundles/org.openhab.binding.myuplink/ @alexf2015
249250
/bundles/org.openhab.binding.nanoleaf/ @stefan-hoehn
250251
/bundles/org.openhab.binding.neato/ @jjlauterbach
251252
/bundles/org.openhab.binding.neeo/ @morph166955

bom/openhab-addons/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,11 @@
12361236
<artifactId>org.openhab.binding.mystrom</artifactId>
12371237
<version>${project.version}</version>
12381238
</dependency>
1239+
<dependency>
1240+
<groupId>org.openhab.addons.bundles</groupId>
1241+
<artifactId>org.openhab.binding.myuplink</artifactId>
1242+
<version>${project.version}</version>
1243+
</dependency>
12391244
<dependency>
12401245
<groupId>org.openhab.addons.bundles</groupId>
12411246
<artifactId>org.openhab.binding.nanoleaf</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,78 @@
1+
# myUplink Binding
2+
3+
The myUplink binding is used to get "live data" from from Nibe heat pumps without plugging any custom devices into your heat pump.
4+
This avoids the risk of losing your warranty.
5+
Instead data is retrieved from myUplink.
6+
The myUplink API is the successor of the Nibe Uplink API.
7+
This binding should in general be compatible with all heat pump models that support myUplink.
8+
Read or write access is supported by all channels as exposed by the API.
9+
Write access might only be available with a paid subscription for myUplink.
10+
You will need to create credentials at <https://dev.myuplink.com/apps> in order to use this binding.
11+
12+
## Supported Things
13+
14+
This binding provides two thing types:
15+
16+
| Thing/Bridge | Thing Type | Description |
17+
|---------------------|---------------------|-------------------------------------------------------------------|
18+
| bridge | account | cloud connection to a myUplink user account |
19+
| thing | generic-device | the physical heatpump which is connected to myUplink |
20+
## Discovery
21+
22+
When the `account` bridge is setup, the binding will discover all heatpumps within that account and also detect the specific channels supported by the model.
23+
24+
## Bridge Configuration
25+
26+
The following configuration parameters are available for the bridge:
27+
28+
| Configuration Parameter | Required | Description |
29+
|-------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
30+
| clientId | yes | The clientId to login at myUplink cloud service. This is some kind of UUID. Visit <https://dev.myuplink.com/apps> to generate login credentials. |
31+
| clientSecret | yes | The secret which belongs to the clientId. |
32+
| dataPollingInterval | no | Interval (seconds) in which live data values are retrieved from the Easee Cloud API. (default = 60) |
33+
34+
## Thing Configuration
35+
36+
It is recommended to use auto discovery which does not require further configuration.
37+
If manual configuration is preferred you need to specify configuration as below.
38+
39+
| Configuration Parameter | Required | Description |
40+
|-------------------------|----------|------------------------------------------------------------------------------------------------------------------------|
41+
| deviceId | yes | The id of the heatpump that will be represented by this thing. Can be retrieved via API call or autodiscovery. |
42+
| systemId | no | The systemId of the heatpump. Only needed for "SmartHomeMode". Can be retrieved via API call or autodiscovery. |
43+
44+
## Channels
45+
46+
The binding only supports channels which are explicitely exposed by the myUplink API.
47+
48+
Depending on your model and additional hardware the channels might be different.
49+
Thus no list is provided here.
50+
51+
## Full Example
52+
53+
The configuration below is an example which could easily be adopted to your actual model.
54+
Thing configuration (account and generic-device) is the same for all models.
55+
Item configuration depends on your specific model and thus channels will have different IDs and/or channels might not exist for all models.
56+
57+
### `demo.things` Example
58+
59+
```java
60+
Bridge myuplink:account:myAccount "myUplink" [
61+
clientId="c7c2f9a4-b960-448f-b00d-b8f30aff3324",
62+
clientSecret="471147114711ABCDEF133713371337AB",
63+
dataPollingInterval=55
64+
] {
65+
Thing generic-device vvm320 "VVM320" [ deviceId="id taken from automatic discovery", systemId="id taken from automatic discovery" ]
66+
}
67+
```
68+
69+
### `demo.items` Example
70+
71+
```java
72+
Number NIBE_ADD_STATUS "Status ZH [%s]" { channel="myuplink:generic-device:myAccount:vvm320:49993" }
73+
Number NIBE_COMP_STATUS "Status Compr. [%s]" { channel="myuplink:generic-device:myAccount:vvm320:44064" }
74+
Number:Temperature NIBE_SUPPLY "Supply line" { unit="°C", channel="myuplink:generic-device:myAccount:vvm320:40008" }
75+
Number:Temperature NIBE_RETURN "Return line" { unit="°C", channel="myuplink:generic-device:myAccount:vvm320:40012" }
76+
Number:Energy NIBE_HM_HEAT "HM heating" { unit="kWh", channel="myuplink:generic-device:myAccount:vvm320:44308" }
77+
Number:Energy NIBE_HM_HW "HM hot water" { unit="kWh", channel="myuplink:generic-device:myAccount:vvm320:44306" }
78+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.openhab.addons.bundles</groupId>
9+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
10+
<version>4.3.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>org.openhab.binding.myuplink</artifactId>
14+
15+
<name>openHAB Add-ons :: Bundles :: myUplink Binding</name>
16+
17+
</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.myuplink-${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-myuplink" description="myUplink Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.myuplink/${project.version}</bundle>
8+
</feature>
9+
</features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright (c) 2010-2024 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.myuplink.internal;
14+
15+
import java.util.concurrent.Future;
16+
import java.util.concurrent.atomic.AtomicReference;
17+
18+
import org.eclipse.jdt.annotation.NonNullByDefault;
19+
import org.eclipse.jdt.annotation.Nullable;
20+
21+
/**
22+
* trait class which contains useful helper methods. Thus, the interface can be implemented and methods are available
23+
* within the class.
24+
*
25+
* @author Alexander Friese - initial contribution
26+
*/
27+
@NonNullByDefault
28+
public interface AtomicReferenceTrait {
29+
30+
/**
31+
* this should usually not called directly. use updateJobReference or cancelJobReference instead
32+
*
33+
* @param job job to cancel.
34+
*/
35+
default void cancelJob(@Nullable Future<?> job) {
36+
if (job != null) {
37+
job.cancel(true);
38+
}
39+
}
40+
41+
/**
42+
* updates a job reference with a new job. the old job will be cancelled if there is one.
43+
*
44+
* @param jobReference reference to be updated
45+
* @param newJob job to be assigned
46+
*/
47+
default void updateJobReference(AtomicReference<@Nullable Future<?>> jobReference, Future<?> newJob) {
48+
cancelJob(jobReference.getAndSet(newJob));
49+
}
50+
51+
/**
52+
* updates a job reference to null and cancels any existing job which might be assigned to the reference.
53+
*
54+
* @param jobReference to be updated to null.
55+
*/
56+
default void cancelJobReference(AtomicReference<@Nullable Future<?>> jobReference) {
57+
cancelJob(jobReference.getAndSet(null));
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/**
2+
* Copyright (c) 2010-2024 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.myuplink.internal;
14+
15+
import java.time.Instant;
16+
import java.util.Set;
17+
18+
import org.eclipse.jdt.annotation.NonNullByDefault;
19+
import org.openhab.core.thing.ThingTypeUID;
20+
21+
/**
22+
* The {@link MyUplinkBindingConstants} class defines common constants, which are
23+
* used across the whole binding.
24+
*
25+
* @author Alexander Friese - Initial contribution
26+
*/
27+
@NonNullByDefault
28+
public class MyUplinkBindingConstants {
29+
30+
public static final String BINDING_ID = "myuplink";
31+
32+
// List of main device types
33+
public static final String DEVICE_ACCOUNT = "account";
34+
public static final String DEVICE_GENERIC_DEVICE = "generic-device";
35+
36+
// List of all Thing Type UIDs
37+
public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, DEVICE_ACCOUNT);
38+
public static final ThingTypeUID THING_TYPE_GENERIC_DEVICE = new ThingTypeUID(BINDING_ID, DEVICE_GENERIC_DEVICE);
39+
40+
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT,
41+
THING_TYPE_GENERIC_DEVICE);
42+
43+
// Channel types
44+
public static final String CHANNEL_TYPE_UNIT_NONE = "NO_UNIT";
45+
public static final String CHANNEL_TYPE_PREFIX_RW = "rw";
46+
public static final String CHANNEL_TYPE_ENUM_PRFIX = "type-enum-";
47+
public static final String CHANNEL_TYPE_NUMERIC_PRFIX = "type-numeric-";
48+
public static final String CHANNEL_TYPE_DEFAULT_DATATYPE = "Number";
49+
50+
public static final String CHANNEL_TYPE_ENERGY = "type-energy";
51+
public static final String CHANNEL_TYPE_ENERGY_UNIT = "kWh";
52+
public static final String CHANNEL_TYPE_PRESSURE = "type-pressure";
53+
public static final String CHANNEL_TYPE_PRESSURE_UNIT = "bar";
54+
public static final String CHANNEL_TYPE_PERCENT = "type-percent";
55+
public static final String CHANNEL_TYPE_PERCENT_UNIT = "%";
56+
public static final String CHANNEL_TYPE_TEMPERATURE = "type-temperature";
57+
public static final String CHANNEL_TYPE_TEMPERATURE_UNIT = "°C";
58+
public static final String CHANNEL_TYPE_FREQUENCY = "type-frequency";
59+
public static final String CHANNEL_TYPE_FREQUENCY_UNIT = "Hz";
60+
public static final String CHANNEL_TYPE_FLOW = "type-flow";
61+
public static final String CHANNEL_TYPE_FLOW_UNIT = "l/m";
62+
public static final String CHANNEL_TYPE_ELECTRIC_CURRENT = "type-electric-current";
63+
public static final String CHANNEL_TYPE_ELECTRIC_CURRENT_UNIT = "A";
64+
public static final String CHANNEL_TYPE_TIME = "type-time";
65+
public static final String CHANNEL_TYPE_TIME_UNIT = "h";
66+
public static final String CHANNEL_TYPE_INTEGER = "type-number-integer";
67+
public static final String CHANNEL_TYPE_DOUBLE = "type-number-double";
68+
public static final String CHANNEL_TYPE_ON_OFF = "type-on-off";
69+
public static final String CHANNEL_TYPE_RW_SWITCH = "rwtype-switch";
70+
public static final String CHANNEL_TYPE_RW_COMMAND = "rwtype-command";
71+
public static final String CHANNEL_TYPE_RW_MODE = "rwtype-mode";
72+
73+
public static final String CHANNEL_ID_COMMAND = "command";
74+
public static final String CHANNEL_ID_SMART_HOME_MODE = "smart-home-mode";
75+
76+
// JSON Keys
77+
public static final String JSON_KEY_ROOT_DATA = "data";
78+
public static final String JSON_KEY_CHANNEL_STR_VAL = "strVal";
79+
public static final String JSON_KEY_CHANNEL_VALUE = "value";
80+
public static final String JSON_KEY_CHANNEL_WRITABLE = "writable";
81+
public static final String JSON_KEY_CHANNEL_ENUM_VALUES = "enumValues";
82+
public static final String JSON_KEY_CHANNEL_ID = "parameterId";
83+
public static final String JSON_KEY_CHANNEL_LABEL = "parameterName";
84+
public static final String JSON_KEY_CHANNEL_UNIT = "parameterUnit";
85+
public static final String JSON_KEY_CHANNEL_SCALE = "scaleValue";
86+
public static final String JSON_KEY_CHANNEL_MIN = "minValue";
87+
public static final String JSON_KEY_CHANNEL_MAX = "maxValue";
88+
public static final String JSON_KEY_CHANNEL_STEP = "stepValue";
89+
public static final String JSON_KEY_SYSTEMS = "systems";
90+
public static final String JSON_KEY_SYSTEM_ID = "systemId";
91+
public static final String JSON_KEY_DEVICES = "devices";
92+
public static final String JSON_KEY_GENERIC_ID = "id";
93+
public static final String JSON_KEY_PRODUCT = "product";
94+
public static final String JSON_KEY_SERIAL = "serialNumber";
95+
public static final String JSON_KEY_NAME = "name";
96+
public static final String JSON_KEY_CURRENT_FW_VERSION = "currentFwVersion";
97+
public static final String JSON_KEY_CONNECTION_STATE = "connectionState";
98+
public static final String JSON_KEY_ERROR = "error";
99+
public static final String JSON_KEY_SMART_HOME_MODE = "smartHomeMode";
100+
101+
public static final String JSON_KEY_AUTH_ACCESS_TOKEN = "access_token";
102+
public static final String JSON_KEY_AUTH_EXPIRES_IN = "expires_in";
103+
104+
public static final String JSON_ENUM_KEY_TEXT = "text";
105+
public static final String JSON_ENUM_ORD_0 = "0";
106+
public static final String JSON_ENUM_ORD_1 = "1";
107+
public static final String JSON_ENUM_ORD_4 = "4";
108+
public static final String JSON_ENUM_ORD_6 = "6";
109+
public static final String JSON_ENUM_ORD_10 = "10";
110+
public static final String JSON_ENUM_ORD_20 = "20";
111+
public static final String JSON_ENUM_ORD_30 = "30";
112+
public static final String JSON_ENUM_ORD_40 = "40";
113+
public static final String JSON_ENUM_ORD_60 = "60";
114+
public static final String JSON_ENUM_ORD_100 = "100";
115+
public static final String JSON_ENUM_VAL_OFF = "off";
116+
public static final String JSON_ENUM_VAL_ON = "on";
117+
public static final String JSON_ENUM_VAL_HOT_WATER = "hot water";
118+
public static final String JSON_ENUM_VAL_HEATING = "heating";
119+
public static final String JSON_ENUM_VAL_POOL = "pool";
120+
public static final String JSON_ENUM_VAL_STARTS = "starts";
121+
public static final String JSON_ENUM_VAL_RUNS = "runs";
122+
public static final String JSON_ENUM_VAL_ALARM = "alarm";
123+
public static final String JSON_ENUM_VAL_BLOCKED = "blocked";
124+
public static final String JSON_ENUM_VAL_ACTIVE = "active";
125+
126+
public static final String JSON_VAL_CONNECTION_CONNECTED = "Connected";
127+
public static final String JSON_VAL_DECIMAL_SEPARATOR = ".";
128+
129+
// web request constants
130+
public static final long WEB_REQUEST_INITIAL_DELAY = 10;
131+
public static final long WEB_REQUEST_INTERVAL = 5;
132+
public static final int WEB_REQUEST_QUEUE_MAX_SIZE = 20;
133+
public static final int WEB_REQUEST_TOKEN_EXPIRY_BUFFER_MINUTES = 5;
134+
public static final int WEB_REQUEST_TOKEN_MAX_AGE_MINUTES = 45;
135+
public static final String WEB_REQUEST_PARAM_PAGE_KEY = "page";
136+
public static final String WEB_REQUEST_PARAM_PAGE_SIZE_KEY = "itemsPerPage";
137+
public static final String WEB_REQUEST_PATCH_CONTENT_TYPE = "application/json-patch+json";
138+
public static final int WEB_REQUEST_PARAM_PAGE_SIZE_VALUE = 100;
139+
public static final String WEB_REQUEST_BEARER_TOKEN_PREFIX = "Bearer ";
140+
public static final String LOGIN_BASIC_AUTH_PREFIX = "Basic ";
141+
public static final String LOGIN_FIELD_SCOPE_KEY = "scope";
142+
public static final String LOGIN_FIELD_SCOPE_VALUE = "READSYSTEM WRITESYSTEM";
143+
public static final String LOGIN_FIELD_GRANT_TYPE_KEY = "grant_type";
144+
public static final String LOGIN_FIELD_GRANT_TYPE_VALUE = "client_credentials";
145+
146+
// URLs
147+
private static final String API_BASE_URL = "https://api.myuplink.com";
148+
public static final String LOGIN_URL = API_BASE_URL + "/oauth/token";
149+
public static final String GET_SYSTEMS_URL = API_BASE_URL + "/v2/systems/me";
150+
public static final String GET_SMART_HOME_MODE_URL = API_BASE_URL + "/v2/systems/{systemId}/smart-home-mode";
151+
public static final String SET_SMART_HOME_MODE_URL = GET_SMART_HOME_MODE_URL;
152+
public static final String GET_DEVICE_POINTS = API_BASE_URL + "/v2/devices/{deviceId}/points";
153+
public static final String SET_DEVICE_POINTS = GET_DEVICE_POINTS;
154+
155+
// Status Keys
156+
public static final String STATUS_TOKEN_VALIDATED = "@text/status.token.validated";
157+
public static final String STATUS_WAITING_FOR_BRIDGE = "@text/status.waiting.for.bridge";
158+
public static final String STATUS_WAITING_FOR_LOGIN = "@text/status.waiting.for.login";
159+
public static final String STATUS_NO_VALID_DATA = "@text/status.no.valid.data";
160+
public static final String STATUS_NO_CONNECTION = "@text/status.no.connection";
161+
public static final String STATUS_DEVICE_NOT_FOUND = "@text/status.device.not.found";
162+
public static final String STATUS_CONFIG_ERROR_NO_CLIENT_ID = "@text/status.config.error.no.client.id";
163+
public static final String STATUS_CONFIG_ERROR_NO_CLIENT_SECRET = "@text/status.config.error.no.client.secret";
164+
165+
// other
166+
public static final long POLLING_INITIAL_DELAY = 5;
167+
168+
public static final String GENERIC_NO_VAL = "---";
169+
public static final String EMPTY = "";
170+
171+
public static final String THING_CONFIG_ID = "deviceId";
172+
public static final String THING_CONFIG_SYSTEM_ID = "systemId";
173+
public static final String THING_CONFIG_SERIAL = "serial";
174+
public static final String THING_CONFIG_CURRENT_FW_VERSION = "currentFwVersion";
175+
176+
public static final Instant OUTDATED_DATE = Instant.EPOCH;
177+
178+
public static final String PARAMETER_NAME_WRITE_COMMAND = "writeCommand";
179+
public static final String PARAMETER_NAME_VALIDATION_REGEXP = "validationExpression";
180+
public static final String DEFAULT_VALIDATION_EXPRESSION = "[0-9]+";
181+
}

0 commit comments

Comments
 (0)