Skip to content

Commit 687aa9c

Browse files
committed
Updated to tackle latest API blocking from Panasonic.
1 parent 5b58213 commit 687aa9c

File tree

2 files changed

+39
-23
lines changed
  • src
    • main/java/no/seime/openhab/binding/panasoniccomfortcloud/internal
    • test/java/no/seime/openhab/binding/panasoniccomfortcloud/internal

2 files changed

+39
-23
lines changed

src/main/java/no/seime/openhab/binding/panasoniccomfortcloud/internal/ApiBridge.java

+39-15
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public class ApiBridge {
6363
private static final String BASE_PATH_AUTH = "https://authglb.digital.panasonic.com";
6464
private static final String BASE_PATH_ACC = "https://accsmart.panasonic.com";
6565
private static final String APPBRAIN_URL = "https://www.appbrain.com/app/panasonic-comfort-cloud/com.panasonic.ACCsmart";
66-
private static final String DEFAULT_APP_VERSION = "1.21.1";
66+
private static final String DEFAULT_APP_VERSION = "1.22.0";
6767

6868
private static final String ACCESS_TOKEN_KEY = "accessToken";
6969
private static final String REFRESH_TOKEN_KEY = "refreshToken";
@@ -74,6 +74,7 @@ public class ApiBridge {
7474
private static final int ERROR_CODE_UPDATE_VERSION = 4106;
7575

7676
private final Logger logger = LoggerFactory.getLogger(ApiBridge.class);
77+
private MessageDigest digest = null;
7778

7879
private String clientId;
7980
private String username;
@@ -82,11 +83,19 @@ public class ApiBridge {
8283
private Gson gson;
8384
private OkHttpClient client;
8485
private Storage<String> storage;
86+
private DateTimeFormatter dateTimeFormatter;
8587

8688
public ApiBridge(Storage<String> storage) {
8789
this.storage = storage;
8890
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> logger.debug(message));
8991
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
92+
dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("UTC"));
93+
94+
try {
95+
digest = MessageDigest.getInstance("SHA-256");
96+
} catch (NoSuchAlgorithmException e) {
97+
throw new RuntimeException(e);
98+
}
9099

91100
CookieJar cookieJar = new CookieJar() {
92101

@@ -122,13 +131,29 @@ public List<Cookie> loadForRequest(HttpUrl httpUrl) {
122131
return cookies;
123132
}
124133

125-
public static String generateRandomStringHex(int bitLength) {
134+
public String generateAPIKey(Instant timestamp, String accessToken) {
126135
StringBuilder b = new StringBuilder();
127-
for (int i = 0; i < bitLength; i++) {
128-
b.append(Integer.toHexString((int) (Math.random() * 16)));
129-
}
136+
b.append("Comfort Cloud");
137+
b.append("521325fb2dd486bf4831b47644317fca");
138+
b.append(timestamp.getEpochSecond() * 1000);
139+
b.append("Bearer ");
140+
b.append(accessToken);
141+
142+
byte[] hash = digest.digest(b.toString().getBytes(StandardCharsets.UTF_8));
143+
String hex = bytesToHex(hash);
144+
return hex.substring(0, 9) + "cfc" + hex.substring(9);
145+
}
130146

131-
return b.toString();
147+
private static String bytesToHex(byte[] hash) {
148+
StringBuilder hexString = new StringBuilder(2 * hash.length);
149+
for (int i = 0; i < hash.length; i++) {
150+
String hex = Integer.toHexString(0xff & hash[i]);
151+
if (hex.length() == 1) {
152+
hexString.append('0');
153+
}
154+
hexString.append(hex);
155+
}
156+
return hexString.toString();
132157
}
133158

134159
public static String generateHash(String codeVerifier) throws NoSuchAlgorithmException {
@@ -175,14 +200,14 @@ private Request buildRequest(Token token, final AbstractRequest req) {
175200
request = request.post(RequestBody.create(JSON, reqJson));
176201
}
177202

203+
Instant timestamp = Instant.now();
204+
178205
request.addHeader("Accept-Encoding", "gzip, deflate").addHeader("Accept", "*/*")
179206
.addHeader("User-Agent", "G-RAC").addHeader("Content-Type", "application/json;charset=utf-8")
180207
.addHeader("x-app-name", "Comfort Cloud")
181-
.addHeader("x-app-timestamp",
182-
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault())
183-
.format(Instant.now()))
184-
.addHeader("x-app-type", "1").addHeader("x-app-version", appVersion)
185-
.addHeader("x-cfc-api-key", generateRandomStringHex(128))
208+
.addHeader("x-app-timestamp", dateTimeFormatter.format(timestamp)).addHeader("x-app-type", "1")
209+
.addHeader("x-app-version", appVersion)
210+
.addHeader("x-cfc-api-key", generateAPIKey(timestamp, token.getAccessToken()))
186211
.addHeader("x-user-authorization-v2", "Bearer " + token.getAccessToken())
187212
.addHeader("x-client-id", token.getClientId()).build();
188213

@@ -375,14 +400,13 @@ private Token doV2AuthorizationFlow() throws IOException, NoSuchAlgorithmExcepti
375400

376401
RequestBody body = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),
377402
gson.toJson(new GetAccClientIdDTO()));
403+
378404
Request getAccClientIdRequest = new Request.Builder().post(body).url(BASE_PATH_ACC + "/auth/v2/login")
379405
.addHeader("Accept-Encoding", "gzip, deflate").addHeader("Accept", "*/*")
380406
.addHeader("User-Agent", "G-RAC").addHeader("Content-Type", "application/json;charset=utf-8")
381-
.addHeader("x-app-name", "Comfort Cloud")
382-
.addHeader("x-app-timestamp",
383-
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()).format(now))
407+
.addHeader("x-app-name", "Comfort Cloud").addHeader("x-app-timestamp", dateTimeFormatter.format(now))
384408
.addHeader("x-app-type", "1").addHeader("x-app-version", appVersion)
385-
.addHeader("x-cfc-api-key", generateRandomStringHex(128))
409+
.addHeader("x-cfc-api-key", generateAPIKey(now, accessToken))
386410
.addHeader("x-user-authorization-v2", "Bearer " + accessToken).build();
387411

388412
Response getAccClientResponse = client.newCall(getAccClientIdRequest).execute();

src/test/java/no/seime/openhab/binding/panasoniccomfortcloud/internal/APIClientTest.java

-8
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,6 @@ public void testGenerateRandom() {
4747
assertEquals(20, random.length());
4848
}
4949

50-
@Test
51-
public void testGenerateRandomHexString() {
52-
int length = "B7d80fb2bc3faE769b89Bf4EC9C4729eCfe690C1CbBa7c42a40A062dc2f4f3671daAaFF1Cf6777cdC23dfcfFfa6DabdDec825c8b0BfB2EFDe04FCE17Bb5e086e"
53-
.length();
54-
String random = ApiBridge.generateRandomStringHex(length);
55-
assertEquals(length, random.length());
56-
}
57-
5850
@Test
5951
public void testParseAppBrain() throws IOException {
6052
InputStream is = getClass().getResourceAsStream("/appbrain_index.html");

0 commit comments

Comments
 (0)