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

[veSync] 131 and Vital Purifiers base support #15296

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
504a0d4
[veSync] Device support enhancements
dag81 Jul 23, 2023
0eb55a1
[veSync] Rebase
dag81 Oct 6, 2024
6d1034a
[veSync] SCA Adjustments
dag81 Oct 6, 2024
5bdb786
[veSync] SCA Adjustments Fix
dag81 Oct 6, 2024
6073de3
[veSync] PR corrections
dag81 Oct 6, 2024
c823115
[veSync] Magic number removal
dag81 Oct 6, 2024
d9fd054
[veSync] Http calls cleanup
dag81 Oct 6, 2024
41d429d
[veSync] HttpClientFactory cleanup
dag81 Oct 6, 2024
e497a9c
[veSync] Bridge i18n support improvements
dag81 Oct 6, 2024
ea5c6f9
[veSync] i18n updates
dag81 Oct 6, 2024
184aded
[veSync] unithints corrected for correct display from devices
dag81 Nov 23, 2024
7284cbb
[veSync] httpClient clean-up
dag81 Nov 23, 2024
4d9acc2
[veSync] PR Feedback
dag81 Nov 23, 2024
99b43ae
[veSync] english cleanup
dag81 Nov 23, 2024
64dc88c
[veSync] cleanups
dag81 Nov 23, 2024
83194c2
[veSync] Humidifier units tuning
dag81 Nov 23, 2024
c94058e
[veSync] protocol device id update
dag81 Nov 24, 2024
9e4276a
[veSync] protocol alignment to redundant data against pyvesync
dag81 Nov 24, 2024
25610d4
[veSync] cleanup casting uses
dag81 Nov 24, 2024
c181915
[veSync] air purifier update instructions pass 1
dag81 Nov 24, 2024
89e4976
[veSync] air purifier options attempt 1
dag81 Nov 24, 2024
d1b0a77
[veSync] air humidifier update template base
dag81 Nov 24, 2024
615fd7c
[veSync] readme updates
dag81 Nov 24, 2024
536f1a8
[veSync] update script updates
dag81 Nov 24, 2024
f5e0b4f
[veSync] update script updates
dag81 Nov 25, 2024
f7aac5a
[veSync] Air purifier update modifications
dag81 Nov 26, 2024
2816d0f
[veSync] Air humidifier update modifications
dag81 Nov 26, 2024
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
204 changes: 159 additions & 45 deletions bundles/org.openhab.binding.vesync/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class VeSyncConstants {

public static final Gson GSON = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setPrettyPrinting()
.disableHtmlEscaping().serializeNulls().create();
.disableHtmlEscaping().create();

private static final String BINDING_ID = "vesync";

Expand Down Expand Up @@ -65,6 +65,8 @@ public class VeSyncConstants {
public static final String DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE = "configAutoRoomSize";
public static final String DEVICE_CHANNEL_AF_SCHEDULES_COUNT = "schedulesCount";
public static final String DEVICE_CHANNEL_AF_NIGHT_LIGHT = "nightLightMode";
public static final String DEVICE_CHANNEL_AF_LIGHT_DETECTION = "lightDetection";
public static final String DEVICE_CHANNEL_AF_LIGHT_DETECTED = "lightDetected";

// Humidity related channels
public static final String DEVICE_CHANNEL_WATER_LACKS = "waterLacking";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.vesync.internal.api.IHttpClientProvider;
import org.openhab.binding.vesync.internal.handlers.VeSyncBridgeHandler;
import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirHumidifierHandler;
import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirPurifierHandler;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

Expand All @@ -41,12 +42,23 @@
*/
@NonNullByDefault
@Component(configurationPid = "binding.vesync", service = ThingHandlerFactory.class)
public class VeSyncHandlerFactory extends BaseThingHandlerFactory implements IHttpClientProvider {
public class VeSyncHandlerFactory extends BaseThingHandlerFactory {

private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE,
THING_TYPE_AIR_PURIFIER, THING_TYPE_AIR_HUMIDIFIER);

private @Nullable HttpClient httpClientRef = null;
private final HttpClientFactory httpClientFactory;
private final TranslationProvider translationProvider;
private final LocaleProvider localeProvider;

@Activate
public VeSyncHandlerFactory(@Reference HttpClientFactory httpClientFactory,
@Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) {
super();
this.httpClientFactory = httpClientFactory;
this.translationProvider = translationProvider;
this.localeProvider = localeProvider;
}

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
Expand All @@ -58,23 +70,13 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
final ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (VeSyncDeviceAirPurifierHandler.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new VeSyncDeviceAirPurifierHandler(thing);
return new VeSyncDeviceAirPurifierHandler(thing, translationProvider, localeProvider);
} else if (VeSyncDeviceAirHumidifierHandler.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new VeSyncDeviceAirHumidifierHandler(thing);
return new VeSyncDeviceAirHumidifierHandler(thing, translationProvider, localeProvider);
} else if (THING_TYPE_BRIDGE.equals(thingTypeUID)) {
return new VeSyncBridgeHandler((Bridge) thing, this);
return new VeSyncBridgeHandler((Bridge) thing, httpClientFactory, translationProvider, localeProvider);
}

return null;
}

@Reference
protected void setHttpClientFactory(HttpClientFactory httpClientFactory) {
httpClientRef = httpClientFactory.getCommonHttpClient();
}

@Override
public @Nullable HttpClient getHttpClient() {
return httpClientRef;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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.openhab.binding.vesync.internal.VeSyncConstants;
import org.openhab.binding.vesync.internal.dto.requests.VeSyncAuthenticatedRequest;
import org.openhab.binding.vesync.internal.dto.requests.VeSyncLoginCredentials;
Expand All @@ -58,10 +59,12 @@ public class VeSyncV2ApiHelper {

private final Logger logger = LoggerFactory.getLogger(VeSyncV2ApiHelper.class);

private @NonNullByDefault({}) HttpClient httpClient;
private static final int RESPONSE_TIMEOUT_SEC = 5;

private volatile @Nullable VeSyncUserSession loggedInSession;

private @NonNullByDefault({}) HttpClient httpClient;

private Map<String, @NotNull VeSyncManagedDeviceBase> macLookup;

public VeSyncV2ApiHelper() {
Expand Down Expand Up @@ -154,6 +157,7 @@ public String reqV2Authorized(final String url, final String macId, final VeSync
}
veSyncRequestManagedDeviceBypassV2.cid = deviceData.cid;
veSyncRequestManagedDeviceBypassV2.configModule = deviceData.configModule;
veSyncRequestManagedDeviceBypassV2.configModel = deviceData.configModule;
veSyncRequestManagedDeviceBypassV2.deviceRegion = deviceData.deviceRegion;
}
return reqV1Authorized(url, requestData);
Expand All @@ -167,16 +171,18 @@ public String reqV1Authorized(final String url, final VeSyncAuthenticatedRequest
private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData)
throws AuthenticationException {
try {
Request request = httpClient.POST(url);
Request request = httpClient.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC,
TimeUnit.SECONDS);

// No headers for login
request.content(new StringContentProvider(VeSyncConstants.GSON.toJson(requestData)));

logger.debug("POST @ {} with content\r\n{}", url, VeSyncConstants.GSON.toJson(requestData));
logger.debug("{} @ {} with content\r\n{}", requestData.httpMethod, url,
VeSyncConstants.GSON.toJson(requestData));

request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8");

ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send();
ContentResponse response = request.send();
if (response.getStatus() == HttpURLConnection.HTTP_OK) {
VeSyncResponse commResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(),
VeSyncResponse.class);
Expand Down Expand Up @@ -220,15 +226,16 @@ public void updateBridgeData(final VeSyncBridgeHandler bridge) {
private VeSyncLoginResponse processLogin(String username, String password, String timezone)
throws AuthenticationException {
try {
Request request = httpClient.POST(V1_LOGIN_ENDPOINT);
Request request = httpClient.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST)
.timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS);

// No headers for login
request.content(new StringContentProvider(
VeSyncConstants.GSON.toJson(new VeSyncLoginCredentials(username, password))));

request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8");

ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send();
ContentResponse response = request.send();
if (response.getStatus() == HttpURLConnection.HTTP_OK) {
VeSyncLoginResponse loginResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(),
VeSyncLoginResponse.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public interface VeSyncProtocolConstants {
String MODE_AUTO = "auto";
String MODE_MANUAL = "manual";
String MODE_SLEEP = "sleep";
String MODE_PET = "pet";
String MODE_AUTO_HUMIDITY = "humidity";

String MODE_ON = "on";
String MODE_DIM = "dim";
Expand All @@ -42,19 +44,24 @@ public interface VeSyncProtocolConstants {
String DEVICE_GET_HUMIDIFIER_STATUS = "getHumidifierStatus";

String DEVICE_LEVEL_TYPE_MIST = "mist";
String DEVICE_LEVEL_TYPE_WARM_MIST = "warm";

// Air Purifier Commands
String DEVICE_SET_PURIFIER_MODE = "setPurifierMode";
String DEVICE_SET_CHILD_LOCK = "setChildLock";
String DEVICE_SET_NIGHT_LIGHT = "setNightLight";
String DEVICE_GET_PURIFIER_STATUS = "getPurifierStatus";
String DEVICE_LEVEL_TYPE_WIND = "wind";
String DEVICE_SET_LIGHT_DETECTION = "setLightDetection";

/**
* Base URL for AUTHENTICATION REQUESTS
*/
String PROTOCOL = "https";
String HOST_ENDPOINT = PROTOCOL + "://smartapi.vesync.com/cloud";
String SERVER_ADDRESS = "smartapi.vesync.com";
String SERVER_ENDPOINT = PROTOCOL + "://" + SERVER_ADDRESS;

String HOST_ENDPOINT = SERVER_ENDPOINT + "/cloud";
String V1_LOGIN_ENDPOINT = HOST_ENDPOINT + "/v1/user/login";
String V1_MANAGED_DEVICES_ENDPOINT = HOST_ENDPOINT + "/v1/deviceManaged/devices";
String V2_BYPASS_ENDPOINT = HOST_ENDPOINT + "/v2/deviceManaged/bypassV2";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package org.openhab.binding.vesync.internal.dto.requests;

import org.eclipse.jetty.http.HttpMethod;

import com.google.gson.annotations.SerializedName;

/**
Expand All @@ -21,6 +23,8 @@
*/
public class VeSyncRequest {

public transient HttpMethod httpMethod;

@SerializedName("timeZone")
public String timeZone = "America/New_York";

Expand All @@ -42,7 +46,11 @@ public class VeSyncRequest {
@SerializedName("method")
public String method;

@SerializedName("deviceId")
public String deviceId;

public VeSyncRequest() {
traceId = String.valueOf(System.currentTimeMillis());
httpMethod = HttpMethod.POST;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public class VeSyncRequestManagedDeviceBypassV2 extends VeSyncAuthenticatedReque
@SerializedName("configModule")
public String configModule = "";

@SerializedName("configModel")
public String configModel = "";

@SerializedName("payload")
public VesyncManagedDeviceBase payload = new VesyncManagedDeviceBase();

Expand All @@ -55,6 +58,75 @@ public class VesyncManagedDeviceBase {
public static class EmptyPayload {
}

public static class SetLightDetectionPayload extends EmptyPayload {

public SetLightDetectionPayload(final boolean enabled) {
lightDetectionSwitch = enabled ? 1 : 0;
}

@SerializedName("lightDetectionSwitch")
public int lightDetectionSwitch = -1;
}

public static class SetPowerPayload extends EmptyPayload {

public SetPowerPayload(final boolean enabled, final int switchIdx) {
this.powerSwitch = enabled ? 1 : 0;
this.switchIdx = switchIdx;
}

@SerializedName("switchIdx")
public int switchIdx = -1;

@SerializedName("powerSwitch")
public int powerSwitch = -1;
}

public static class SetChildLockPayload extends EmptyPayload {

public SetChildLockPayload(final boolean enabled) {
this.childLockSwitch = enabled ? 1 : 0;
}

@SerializedName("childLockSwitch")
public int childLockSwitch = -1;
}

public static class SetScreenSwitchPayload extends EmptyPayload {

public SetScreenSwitchPayload(final boolean enabled) {
this.screenSwitch = enabled ? 1 : 0;
}

@SerializedName("screenSwitch")
public int screenSwitch = -1;
}

public static class SetManualSpeedLevelPayload extends EmptyPayload {

public SetManualSpeedLevelPayload(final int manualSpeedLevel) {
this.manualSpeedLevel = manualSpeedLevel;
}

@SerializedName("levelIdx")
public int levelIdx = 0;

@SerializedName("levelType")
public String levelType = "wind";

@SerializedName("manualSpeedLevel")
public int manualSpeedLevel = -1;
}

public static class SetWorkModePayload extends EmptyPayload {
public SetWorkModePayload(final String workMode) {
this.workMode = workMode;
}

@SerializedName("workMode")
public String workMode = "";
}

public static class SetSwitchPayload extends EmptyPayload {

public SetSwitchPayload(final boolean enabled, final int id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2024 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.vesync.internal.dto.requests;

import org.eclipse.jetty.http.HttpMethod;

import com.google.gson.annotations.SerializedName;

/**
* The {@link VeSyncRequestV1Command} is the Java class as a DTO to define the base implementation of a V1 command for
* the Vesync API.
*
* @author David Goodyear - Initial contribution
*/
public class VeSyncRequestV1Command extends VeSyncAuthenticatedRequest {

@SerializedName("uuid")
public String uuid = null;

public VeSyncRequestV1Command(final String deviceUuid) {
// Exclude fields that shouldn't be there by setting to null
super.phoneOS = null;
super.phoneBrand = null;
super.method = null;
super.appVersion = null;
super.httpMethod = HttpMethod.PUT;
// Set the required payload parameters
uuid = deviceUuid;
}

public String getUuid() {
return uuid;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import com.google.gson.annotations.SerializedName;

/**
* The {@link VeSyncRequestV1ManagedDeviceDetails} is the Java class as a DTO to hold login credentials for the Vesync
* API.
* The {@link VeSyncRequestV1ManagedDeviceDetails} is the Java class as a DTO to request the managed device details for
* the Vesync API.
*
* @author David Goodyear - Initial contribution
*/
Expand Down
Loading