[mybmw] Fix charging statistics URL (#18153)

* [mybmw] add stop charging command

Signed-off-by: Martin Grassl <martin.grassl@digital-filestore.de>
This commit is contained in:
Martin Grassl 2025-01-22 21:01:37 +01:00 committed by Leo Siepel
parent 824fdc1527
commit eadf63fa17
4 changed files with 49 additions and 26 deletions

View File

@ -25,8 +25,6 @@ import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.OAUTH_END
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.OCP_APIM_KEYS;
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.REFRESH_TOKEN;
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.REGION_CHINA;
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.REGION_NORTH_AMERICA;
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.REGION_ROW;
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.USER_AGENT;
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.X_USER_AGENT;
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.AUTHORIZATION;
@ -110,7 +108,7 @@ public class MyBMWTokenController {
this.httpClient = httpClient;
}
public void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration) {
public synchronized void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration) {
this.bridgeConfiguration = bridgeConfiguration;
}
@ -122,9 +120,22 @@ public class MyBMWTokenController {
*
* @return token
*/
public Token getToken() {
if (!bridgeConfiguration.getHcaptchatoken().isBlank()) {
public synchronized Token getToken() {
logger.trace("getToken, current token {}", token.toString());
if (REGION_CHINA.equals(bridgeConfiguration.getRegion()) && !token.isValid()) {
// in China no hcaptchatoken is required
boolean tokenUpdateSuccess = false;
tokenUpdateSuccess = getAndUpdateTokenChina();
if (!tokenUpdateSuccess) {
logger.warn("Authorization failed!");
}
} else if (!bridgeConfiguration.getHcaptchatoken().isBlank()) {
// if the hcaptchastring is available, then a new login is triggered
logger.trace("initial login, using captchatoken {}", bridgeConfiguration.getHcaptchatoken());
boolean tokenCreationSuccess = getInitialToken();
if (!tokenCreationSuccess) {
@ -138,22 +149,15 @@ public class MyBMWTokenController {
} else if (!token.isValid() && !Constants.EMPTY.equals(token.getRefreshToken())) {
// if the token is invalid, try to refresh the token
boolean tokenUpdateSuccess = false;
switch (bridgeConfiguration.getRegion()) {
case REGION_CHINA:
tokenUpdateSuccess = getAndUpdateTokenChina();
break;
case REGION_NORTH_AMERICA:
case REGION_ROW:
tokenUpdateSuccess = getUpdatedToken();
break;
default:
logger.warn("Region {} not supported", bridgeConfiguration.getRegion());
break;
}
tokenUpdateSuccess = getUpdatedToken();
if (!tokenUpdateSuccess) {
logger.warn("Authorization failed!");
}
}
logger.trace("getToken, new token {}", token.toString());
return token;
}
@ -166,6 +170,9 @@ public class MyBMWTokenController {
* @return true if the token was successfully updated
*/
private synchronized boolean getInitialToken() {
logger.trace("get initial token");
try {
/*
* Step 1) Get basic values for further queries
@ -254,6 +261,8 @@ public class MyBMWTokenController {
AuthResponse ar = JsonStringDeserializer.deserializeString(codeResponse.getContentAsString(),
AuthResponse.class);
logger.trace("Login response: {}", ar.toString());
token.setType(ar.tokenType);
token.setToken(ar.accessToken);
token.setExpiration(ar.expiresIn);
@ -273,6 +282,9 @@ public class MyBMWTokenController {
* @return true if token has successfully been refreshed
*/
private synchronized boolean getUpdatedToken() {
logger.trace("getUpdatedToken");
try {
/*
* Step 1) Get basic values for further queries
@ -303,6 +315,8 @@ public class MyBMWTokenController {
AuthResponse ar = JsonStringDeserializer.deserializeString(codeResponse.getContentAsString(),
AuthResponse.class);
logger.trace("Refresh response: {}", ar.toString());
token.setToken(ar.accessToken);
token.setExpiration(ar.expiresIn);
token.setRefreshToken(ar.refreshToken);
@ -310,7 +324,7 @@ public class MyBMWTokenController {
return true;
} catch (Exception e) {
logger.warn("Refresh Exception: {}", e.getMessage());
logger.warn("Refresh Exception: ", e);
}
return false;
}

View File

@ -67,13 +67,20 @@ public class Token {
this.gcid = gcid;
}
/**
* check if the token is valid - for enough buffer it is not valid if it expires in <10s
*
* @return
*/
public boolean isValid() {
return (!token.equals(Constants.EMPTY) && !tokenType.equals(Constants.EMPTY)
&& !refreshToken.equals(Constants.EMPTY) && (this.expiration - System.currentTimeMillis() / 1000) > 1);
&& !refreshToken.equals(Constants.EMPTY) && (this.expiration - System.currentTimeMillis() / 1000) > 10);
}
@Override
public String toString() {
return tokenType + Constants.COLON + token + Constants.COLON + isValid();
return "Token [token=" + token + ", tokenType=" + tokenType + ", refreshToken=" + refreshToken + ", gcid="
+ gcid + ", expiration=" + expiration + "] - is valid " + isValid() + ", will expire in s "
+ (this.expiration - System.currentTimeMillis() / 1000);
}
}

View File

@ -12,8 +12,6 @@
*/
package org.openhab.binding.mybmw.internal.handler.backend;
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.APP_VERSIONS;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@ -178,7 +176,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
*/
public byte[] requestImage(String vin, String brand, ImageProperties props) throws NetworkException {
final String localImageUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
+ "/eadrax-ics/v3/presentation/vehicles/" + vin + "/images?carView=" + props.viewport;
+ BimmerConstants.IMAGE_URL.replace(BimmerConstants.PARAM_VIN, vin) + props.viewport;
return get(localImageUrl, brand, vin, HTTPConstants.CONTENT_TYPE_IMAGE);
}
@ -232,7 +230,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
chargeStatisticsParams.put("currentDate", Converter.getCurrentISOTime());
String params = UrlEncoded.encode(chargeStatisticsParams, StandardCharsets.UTF_8, false);
String chargeStatisticsUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
+ "/eadrax-chs/v1/charging-statistics?" + params;
+ BimmerConstants.CHARGING_STATISTICS + params;
byte[] chargeStatisticsResponse = get(chargeStatisticsUrl, brand, vin, HTTPConstants.CONTENT_TYPE_JSON);
String chargeStatisticsResponseString = new String(chargeStatisticsResponse);
return chargeStatisticsResponseString;
@ -264,7 +262,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
chargeSessionsParams.put("include_date_picker", "true");
String params = UrlEncoded.encode(chargeSessionsParams, StandardCharsets.UTF_8, false);
String chargeSessionsUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
+ "/eadrax-chs/v1/charging-sessions?" + params;
+ BimmerConstants.CHARGING_SESSIONS + params;
byte[] chargeSessionsResponse = get(chargeSessionsUrl, brand, vin, HTTPConstants.CONTENT_TYPE_JSON);
String chargeSessionsResponseString = new String(chargeSessionsResponse);
return chargeSessionsResponseString;
@ -369,7 +367,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
req.header(HttpHeader.AUTHORIZATION, myBMWTokenController.getToken().getBearerToken());
req.header(HTTPConstants.HEADER_X_USER_AGENT, String.format(BimmerConstants.X_USER_AGENT, brand.toLowerCase(),
APP_VERSIONS.get(bridgeConfiguration.getRegion()), bridgeConfiguration.getRegion()));
BimmerConstants.APP_VERSIONS.get(bridgeConfiguration.getRegion()), bridgeConfiguration.getRegion()));
req.header(HttpHeader.ACCEPT_LANGUAGE, bridgeConfiguration.getLanguage());
req.header(HttpHeader.ACCEPT, contentType);
req.header(HTTPConstants.HEADER_BMW_VIN, vin);

View File

@ -82,4 +82,8 @@ public interface BimmerConstants {
static final String API_VEHICLES = "/eadrax-vcs/v4/vehicles";
static final String API_REMOTE_SERVICE_BASE_URL = "/eadrax-vrccs/v3/presentation/remote-commands/"; // '/{vin}/{service_type}'
static final String API_POI = "/eadrax-dcs/v1/send-to-car/send-to-car";
static final String CHARGING_STATISTICS = "/eadrax-chs/v2/charging-statistics?";
static final String CHARGING_SESSIONS = "/eadrax-chs/v2/charging-sessions?";
static final String PARAM_VIN = "$vin$";
static final String IMAGE_URL = "/eadrax-ics/v3/presentation/vehicles/" + PARAM_VIN + "/images?carView=";
}