Skip to content

Commit

Permalink
Changed to okhttp3 due to changes in panasonic api
Browse files Browse the repository at this point in the history
  • Loading branch information
seime committed Jul 14, 2022
1 parent 76f49ca commit 6e7af8e
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 88 deletions.
39 changes: 38 additions & 1 deletion bundles/org.openhab.binding.panasoniccomfortcloud/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,51 @@
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.binding.panasoniccomfortcloud</artifactId>

<name>openHAB Add-ons :: Bundles :: Panasonic Comfort Cloud Binding</name>

<properties>
<bnd.importpackage>
android.*;resolution:=optional,com.android.org.*;resolution:=optional,dalvik.*;resolution:=optional,javax.annotation.meta.*;resolution:=optional,org.apache.harmony.*;resolution:=optional,org.conscrypt.*;resolution:=optional,sun.security.*;resolution:=optional,org.apache.http.*;resolution:=optional
</bnd.importpackage>
</properties>

<dependencies>
<dependency>
<groupId>org.openhab.osgiify</groupId>
<artifactId>io.socket.engine.io-client</artifactId>
<version>1.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.okhttp</artifactId>
<version>3.8.1_1</version>
<scope>compile</scope>
</dependency>
<!-- <dependency>
<groupId>org.openhab.osgiify</groupId>
<artifactId>io.socket.socket.io-client</artifactId>
<version>1.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.okio</artifactId>
<version>1.13.0_1</version>
<scope>compile</scope>
</dependency>
-->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-standalone</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

<feature name="openhab-binding-panasoniccomfortcloud" description="Panasonic Comfort Cloud Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1</bundle>
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1</bundle>
<!-- <bundle dependency="true">mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.1</bundle>
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.1</bundle> -->
<bundle dependency="true">mvn:org.json/json/20180813</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.panasoniccomfortcloud/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,33 @@
*/
package org.openhab.binding.panasoniccomfortcloud.internal;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

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.BytesContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.panasoniccomfortcloud.internal.dto.AbstractRequest;
import org.openhab.binding.panasoniccomfortcloud.internal.dto.LoginRequest;
import org.openhab.binding.panasoniccomfortcloud.internal.dto.LoginResponse;
import org.openhab.binding.panasoniccomfortcloud.internal.logging.RequestLogger;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;

/**
* The {@link ApiBridge} is responsible for API login and communication
*
Expand All @@ -45,7 +47,8 @@
public class ApiBridge {
private static final String API_ENDPOINT = "https://accsmart.panasonic.com";

private HttpClient httpClient;
private final Logger logger = LoggerFactory.getLogger(ApiBridge.class);

private String username;
private String password;
private int refreshInterval;
Expand All @@ -54,42 +57,45 @@ public class ApiBridge {
private String accessToken = null;

private Gson gson;
@Nullable
private RequestLogger requestLogger = null;

public ApiBridge(HttpClient httpClient) {
this.httpClient = httpClient;
OkHttpClient client;

public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

public ApiBridge() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> logger.debug(message));
logging.setLevel(HttpLoggingInterceptor.Level.BODY);

client = new OkHttpClient.Builder().readTimeout(1, TimeUnit.MINUTES).addInterceptor(logging).build();
gson = new GsonBuilder().setLenient().setPrettyPrinting().create();
}

public void init(ThingUID bridgeUid, String username, String password, int refreshInterval) {
this.username = username;
this.password = password;
this.refreshInterval = refreshInterval;
this.requestLogger = new RequestLogger(bridgeUid.getId(), gson);
}

private Request buildRequest(final AbstractRequest req) {
Request request = httpClient.newRequest(API_ENDPOINT + req.getRequestUrl()).method(req.getMethod());

request.getHeaders().remove(HttpHeader.USER_AGENT);
request.getHeaders().remove(HttpHeader.ACCEPT);
request.header(HttpHeader.USER_AGENT, "G-RAC");
request.header(HttpHeader.ACCEPT, "application/json");
request.header(HttpHeader.CONTENT_TYPE, "application/json");
request.header("X-APP-TYPE", "1");
request.header("X-APP-VERSION", "1.20.0");
request.header("X-User-Authorization", accessToken);
Request.Builder request = new Request.Builder().url(API_ENDPOINT + req.getRequestUrl());

if (!req.getMethod().contentEquals(HttpMethod.GET.asString())) { // POST, PATCH
if (req.getMethod().equals("POST")) {
final String reqJson = gson.toJson(req);
request = request.content(new BytesContentProvider(reqJson.getBytes(StandardCharsets.UTF_8)),
"application/json");
request = request.post(RequestBody.create(JSON, reqJson));
}

requestLogger.listenTo(request, new String[] { password });

return request;
request.removeHeader("User-Agent");
request.removeHeader("Accept");
request.header("User-Agent", "G-RAC");
request.header("Accept", "application/json; charset=utf-8");
request.header("Content-Type", "application/json; charset=utf-8");
request.header("X-APP-TYPE", "1");
request.header("X-APP-VERSION", "1.15.0");
if (accessToken != null) {
request.header("X-User-Authorization", accessToken);
}
return request.build();
}

public <T> T sendRequest(final AbstractRequest req, final Type responseType) throws PanasonicComfortCloudException {
Expand All @@ -114,49 +120,30 @@ public <T> T sendRequest(final AbstractRequest req, final Type responseType) thr
public <T> T sendRequestInternal(final Request request, final AbstractRequest req, final Type responseType)
throws PanasonicComfortCloudException, ExecutionException, InterruptedException, TimeoutException {

final ContentResponse contentResponse = request.send();
final String responseJson = contentResponse.getContentAsString();
if (contentResponse.getStatus() == HttpStatus.OK_200) {
final JsonObject o = JsonParser.parseString(responseJson).getAsJsonObject();
if (o.has("message")) {
throw new CommunicationException(req, o.get("message").getAsString());
} else {
return gson.fromJson(o, responseType);
}
} else if (contentResponse.getStatus() == HttpStatus.UNAUTHORIZED_401) {
if (accessToken == null) {
throw new CommunicationException("Could not renew token");
try (Response response = client.newCall(request).execute()) {
if (response.code() == 200) {

final JsonObject o = JsonParser.parseString(response.body().string()).getAsJsonObject();
if (o.has("message")) {
throw new CommunicationException(req, o.get("message").getAsString());
} else {
return gson.fromJson(o, responseType);
}

} else if (response.code() == 401) {
if (accessToken == null) {
throw new CommunicationException("Could not renew token");
} else {
accessToken = null; // expired
return sendRequest(req, responseType); // Retry login + request

}
} else {
accessToken = null; // expired
return sendRequest(req, responseType); // Retry login + request
throw new CommunicationException("Error sending request to server. Server responded with "
+ response.code() + " and payload " + response.body().string());
}

} else if (contentResponse.getStatus() == HttpStatus.FORBIDDEN_403) {
throw new ConfigurationException("Invalid credentials");
} else {
throw new CommunicationException("Error sending request to server. Server responded with "
+ contentResponse.getStatus() + " and payload " + responseJson);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* else {
* try {
* if(response.statusCode == 500 || response.statusCode == 503) {
* if(body.code == 5005) {this.log("Warning - Device has lost connectivity to Comfort Cloud. Check your Wi-Fi
* connectivity or restart the Heat Pump.", body.code, body.message);}
* else {this.log("Error - 500 Internal Server Error. Comfort Cloud server is experiencing issues.", err);}
* }
* else if(response.statusCode == 403) {this.log("Error - 403 Forbidden. Check the configured email and password,
* then restart Homebridge.", body.code, body.message, err);}
* else if(response.statusCode == 401) {this.log("Warning - 401 Unauthorized. Login token has expired.", body.code,
* body.message, err);}
* else {this.log("Unknown error. An update to the plug-in may be required. Check for the latest version.",
* response.statusCode, body.code, body.message);}
* }
* catch(err) {this.log("Unknown error. An update to the plug-in may be required. Check for the latest version.",
* err);}
* }
*
*
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.panasoniccomfortcloud.internal.ApiBridge;
import org.openhab.binding.panasoniccomfortcloud.internal.ConfigurationException;
import org.openhab.binding.panasoniccomfortcloud.internal.PanasonicComfortCloudException;
Expand Down Expand Up @@ -57,9 +56,9 @@ public class PanasonicComfortCloudAccountHandler extends BaseBridgeHandler {
AccountConfiguration config;
private ApiBridge apiBridge;

public PanasonicComfortCloudAccountHandler(final Bridge bridge, HttpClient httpClient) {
public PanasonicComfortCloudAccountHandler(final Bridge bridge) {
super(bridge);
this.apiBridge = new ApiBridge(httpClient);
this.apiBridge = new ApiBridge();
this.model = new GroupModel(0);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.panasoniccomfortcloud.internal.BindingConstants;
import org.openhab.binding.panasoniccomfortcloud.internal.discovery.PanasonicComfortCloudDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryService;
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;
Expand All @@ -35,9 +33,7 @@
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
* The {@link PanasonicComfortCloudHandlerFactory} is responsible for creating things and thing
Expand All @@ -51,23 +47,15 @@ public class PanasonicComfortCloudHandlerFactory extends BaseThingHandlerFactory
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(BindingConstants.THING_TYPE_ACCOUNT, BindingConstants.THING_TYPE_AIRCONDITION)
.collect(Collectors.toSet()));
@NonNullByDefault({})
private final HttpClient httpClient;
private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();

@Activate
public PanasonicComfortCloudHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
}

@Override
protected @Nullable ThingHandler createHandler(final Thing thing) {
final ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (BindingConstants.THING_TYPE_AIRCONDITION.equals(thingTypeUID)) {
return new PanasonicComfortCloudAirconditionHandler(thing);
} else if (BindingConstants.THING_TYPE_ACCOUNT.equals(thingTypeUID)) {
PanasonicComfortCloudAccountHandler handler = new PanasonicComfortCloudAccountHandler((Bridge) thing,
httpClient);
PanasonicComfortCloudAccountHandler handler = new PanasonicComfortCloudAccountHandler((Bridge) thing);
registerDeviceDiscoveryService(handler);
return handler;
}
Expand Down

0 comments on commit 6e7af8e

Please sign in to comment.