mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-25 14:55:55 +01:00
* [mybmw] add stop charging command * [mybmw] fix hcaptcha issue (#17862) Signed-off-by: Martin Grassl <martin.grassl@digital-filestore.de> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
dcf2a1aa6a
commit
1fd1ac5fd9
@ -78,11 +78,12 @@ Properties will be attached to predefined vehicles if the VIN is matching.
|
|||||||
|
|
||||||
### Bridge Configuration
|
### Bridge Configuration
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
|-----------------|---------|--------------------------------------------------------------------|
|
|-----------------|---------|--------------------------------------------------------------------------------------------------------|
|
||||||
| userName | text | MyBMW Username |
|
| userName | text | MyBMW Username |
|
||||||
| password | text | MyBMW Password |
|
| password | text | MyBMW Password |
|
||||||
| region | text | Select region in order to connect to the appropriate BMW server. |
|
| hcaptchatoken | text | HCaptcha-Token for initial login (see https://bimmer-connected.readthedocs.io/en/latest/captcha.html) |
|
||||||
|
| region | text | Select region in order to connect to the appropriate BMW server. |
|
||||||
|
|
||||||
The region Configuration has 3 different options
|
The region Configuration has 3 different options
|
||||||
|
|
||||||
@ -849,4 +850,4 @@ sitemap BMW label="BMW" {
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
This work is based on the project of [Bimmer Connected](https://github.com/bimmerconnected/bimmer_connected).
|
This work is based on the great work of the project of [Bimmer Connected](https://github.com/bimmerconnected/bimmer_connected).
|
||||||
|
@ -19,7 +19,7 @@ import org.openhab.binding.mybmw.internal.utils.Constants;
|
|||||||
* The {@link MyBMWBridgeConfiguration} class contains fields mapping thing configuration parameters.
|
* The {@link MyBMWBridgeConfiguration} class contains fields mapping thing configuration parameters.
|
||||||
*
|
*
|
||||||
* @author Bernd Weymann - Initial contribution
|
* @author Bernd Weymann - Initial contribution
|
||||||
* @author Martin Grassl - renamed
|
* @author Martin Grassl - renamed and added hcaptchastring
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class MyBMWBridgeConfiguration {
|
public class MyBMWBridgeConfiguration {
|
||||||
@ -27,20 +27,71 @@ public class MyBMWBridgeConfiguration {
|
|||||||
/**
|
/**
|
||||||
* Depending on the location the correct server needs to be called
|
* Depending on the location the correct server needs to be called
|
||||||
*/
|
*/
|
||||||
public String region = Constants.EMPTY;
|
private String region = Constants.EMPTY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MyBMW App Username
|
* MyBMW App Username
|
||||||
*/
|
*/
|
||||||
public String userName = Constants.EMPTY;
|
private String userName = Constants.EMPTY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MyBMW App Password
|
* MyBMW App Password
|
||||||
*/
|
*/
|
||||||
public String password = Constants.EMPTY;
|
private String password = Constants.EMPTY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preferred Locale language
|
* Preferred Locale language
|
||||||
*/
|
*/
|
||||||
public String language = Constants.LANGUAGE_AUTODETECT;
|
private String language = Constants.LANGUAGE_AUTODETECT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the hCaptcha string
|
||||||
|
*/
|
||||||
|
private String hcaptchatoken = Constants.EMPTY;
|
||||||
|
|
||||||
|
public String getRegion() {
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegion(String region) {
|
||||||
|
this.region = region;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguage(String language) {
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHcaptchatoken() {
|
||||||
|
return hcaptchatoken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHcaptchatoken(String hcaptchatoken) {
|
||||||
|
this.hcaptchatoken = hcaptchatoken;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MyBMWBridgeConfiguration [region=" + region + ", userName=" + userName + ", password=" + password
|
||||||
|
+ ", language=" + language + ", hcaptchatoken=" + hcaptchatoken + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleBase;
|
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleBase;
|
||||||
@ -140,7 +139,7 @@ public class MyBMWCommandExtension extends AbstractConsoleCommandExtension imple
|
|||||||
String accountPath = path + File.separator + "Account-" + String.valueOf(accountNdx);
|
String accountPath = path + File.separator + "Account-" + String.valueOf(accountNdx);
|
||||||
handler.getMyBmwProxy().ifPresentOrElse(prox -> {
|
handler.getMyBmwProxy().ifPresentOrElse(prox -> {
|
||||||
// get list of vehicles
|
// get list of vehicles
|
||||||
List<@NonNull VehicleBase> vehicles = null;
|
List<VehicleBase> vehicles = null;
|
||||||
try {
|
try {
|
||||||
vehicles = prox.requestVehiclesBase();
|
vehicles = prox.requestVehiclesBase();
|
||||||
|
|
||||||
@ -314,7 +313,8 @@ public class MyBMWCommandExtension extends AbstractConsoleCommandExtension imple
|
|||||||
.filter(t -> THING_TYPE_CONNECTED_DRIVE_ACCOUNT.equals(t.getThingTypeUID())
|
.filter(t -> THING_TYPE_CONNECTED_DRIVE_ACCOUNT.equals(t.getThingTypeUID())
|
||||||
&& args[1].equals(t.getConfiguration().get("userName")))
|
&& args[1].equals(t.getConfiguration().get("userName")))
|
||||||
.map(t -> t.getHandler()).findAny().get();
|
.map(t -> t.getHandler()).findAny().get();
|
||||||
List<VehicleBase> vehicles = handler.getMyBmwProxy().get().requestVehiclesBase();
|
List<VehicleBase> vehicles = handler != null ? handler.getMyBmwProxy().get().requestVehiclesBase()
|
||||||
|
: List.of();
|
||||||
return new StringsCompleter(
|
return new StringsCompleter(
|
||||||
vehicles.stream().map(v -> v.getVin()).filter(Objects::nonNull).collect(Collectors.toList()),
|
vehicles.stream().map(v -> v.getVin()).filter(Objects::nonNull).collect(Collectors.toList()),
|
||||||
false).complete(args, cursorArgumentIndex, cursorPosition, candidates);
|
false).complete(args, cursorArgumentIndex, cursorPosition, candidates);
|
||||||
|
@ -17,7 +17,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.binding.mybmw.internal.MyBMWConstants;
|
import org.openhab.binding.mybmw.internal.MyBMWConstants;
|
||||||
import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
|
import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
|
||||||
@ -80,7 +79,7 @@ public class VehicleDiscovery extends AbstractThingHandlerDiscoveryService<MyBMW
|
|||||||
myBMWProxy = thingHandler.getMyBmwProxy();
|
myBMWProxy = thingHandler.getMyBmwProxy();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Optional<List<@NonNull Vehicle>> vehicleList = myBMWProxy.map(prox -> {
|
Optional<List<Vehicle>> vehicleList = myBMWProxy.map(prox -> {
|
||||||
try {
|
try {
|
||||||
return prox.requestVehicles();
|
return prox.requestVehicles();
|
||||||
} catch (NetworkException e) {
|
} catch (NetworkException e) {
|
||||||
@ -88,8 +87,13 @@ public class VehicleDiscovery extends AbstractThingHandlerDiscoveryService<MyBMW
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
vehicleList.ifPresentOrElse(vehicles -> {
|
vehicleList.ifPresentOrElse(vehicles -> {
|
||||||
thingHandler.vehicleDiscoverySuccess();
|
if (vehicles.size() > 0) {
|
||||||
processVehicles(vehicles);
|
thingHandler.vehicleDiscoverySuccess();
|
||||||
|
processVehicles(vehicles);
|
||||||
|
} else {
|
||||||
|
logger.warn("no vehicle found, maybe because of network error");
|
||||||
|
thingHandler.vehicleDiscoveryError();
|
||||||
|
}
|
||||||
}, () -> thingHandler.vehicleDiscoveryError());
|
}, () -> thingHandler.vehicleDiscoveryError());
|
||||||
} catch (IllegalStateException ex) {
|
} catch (IllegalStateException ex) {
|
||||||
thingHandler.vehicleDiscoveryError();
|
thingHandler.vehicleDiscoveryError();
|
||||||
|
@ -20,17 +20,27 @@ import com.google.gson.annotations.SerializedName;
|
|||||||
* The {@link AuthResponse} Data Transfer Object
|
* The {@link AuthResponse} Data Transfer Object
|
||||||
*
|
*
|
||||||
* @author Bernd Weymann - Initial contribution
|
* @author Bernd Weymann - Initial contribution
|
||||||
|
* @author Martin Grassl - extracted from myBmwProxy
|
||||||
*/
|
*/
|
||||||
public class AuthResponse {
|
public class AuthResponse {
|
||||||
@SerializedName("access_token")
|
@SerializedName("access_token")
|
||||||
public String accessToken = Constants.EMPTY;
|
public String accessToken = Constants.EMPTY;
|
||||||
|
|
||||||
|
@SerializedName("refresh_token")
|
||||||
|
public String refreshToken = Constants.EMPTY;
|
||||||
|
|
||||||
@SerializedName("token_type")
|
@SerializedName("token_type")
|
||||||
public String tokenType = Constants.EMPTY;
|
public String tokenType = Constants.EMPTY;
|
||||||
|
|
||||||
|
@SerializedName("gcid")
|
||||||
|
public String gcid = Constants.EMPTY;
|
||||||
|
|
||||||
@SerializedName("expires_in")
|
@SerializedName("expires_in")
|
||||||
public int expiresIn = -1;
|
public int expiresIn = -1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Token " + accessToken + " type " + tokenType + " expires in " + expiresIn;
|
return "AuthResponse [accessToken=" + accessToken + ", refreshToken=" + refreshToken + ", tokenType="
|
||||||
|
+ tokenType + ", gcid=" + gcid + ", expiresIn=" + expiresIn + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ public class MyBMWBridgeHandler extends BaseBridgeHandler {
|
|||||||
private Optional<ScheduledFuture<?>> initializerJob = Optional.empty();
|
private Optional<ScheduledFuture<?>> initializerJob = Optional.empty();
|
||||||
private Optional<VehicleDiscovery> vehicleDiscovery = Optional.empty();
|
private Optional<VehicleDiscovery> vehicleDiscovery = Optional.empty();
|
||||||
private LocaleProvider localeProvider;
|
private LocaleProvider localeProvider;
|
||||||
|
private Optional<MyBMWBridgeConfiguration> bmwBridgeConfiguration = Optional.empty();
|
||||||
|
|
||||||
public MyBMWBridgeHandler(Bridge bridge, HttpClientFactory hcf, LocaleProvider localeProvider) {
|
public MyBMWBridgeHandler(Bridge bridge, HttpClientFactory hcf, LocaleProvider localeProvider) {
|
||||||
super(bridge);
|
super(bridge);
|
||||||
@ -82,11 +83,23 @@ public class MyBMWBridgeHandler extends BaseBridgeHandler {
|
|||||||
public void initialize() {
|
public void initialize() {
|
||||||
logger.trace("MyBMWBridgeHandler.initialize");
|
logger.trace("MyBMWBridgeHandler.initialize");
|
||||||
updateStatus(ThingStatus.UNKNOWN);
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
MyBMWBridgeConfiguration config = getConfigAs(MyBMWBridgeConfiguration.class);
|
|
||||||
if (config.language.equals(Constants.LANGUAGE_AUTODETECT)) {
|
this.bmwBridgeConfiguration = Optional.of(getConfigAs(MyBMWBridgeConfiguration.class));
|
||||||
config.language = localeProvider.getLocale().getLanguage().toLowerCase();
|
|
||||||
|
MyBMWBridgeConfiguration localBridgeConfiguration;
|
||||||
|
|
||||||
|
if (bmwBridgeConfiguration.isPresent()) {
|
||||||
|
localBridgeConfiguration = bmwBridgeConfiguration.get();
|
||||||
|
} else {
|
||||||
|
logger.warn("the bridge configuration could not be retrieved");
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!MyBMWConfigurationChecker.checkConfiguration(config)) {
|
|
||||||
|
if (localBridgeConfiguration.getLanguage().equals(Constants.LANGUAGE_AUTODETECT)) {
|
||||||
|
localBridgeConfiguration.setLanguage(localeProvider.getLocale().getLanguage().toLowerCase());
|
||||||
|
}
|
||||||
|
if (!MyBMWConfigurationChecker.checkInitialConfiguration(localBridgeConfiguration)) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
||||||
} else {
|
} else {
|
||||||
// there is no risk in this functionality as several steps have to happen to get the file proxy working:
|
// there is no risk in this functionality as several steps have to happen to get the file proxy working:
|
||||||
@ -100,19 +113,26 @@ public class MyBMWBridgeHandler extends BaseBridgeHandler {
|
|||||||
environment = "";
|
environment = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
createMyBmwProxy(config, environment);
|
// this access has to be synchronized as the vehicleHandler as well as the bridge itself request the
|
||||||
|
// instance
|
||||||
|
Optional<MyBMWProxy> localProxy = getMyBmwProxy();
|
||||||
|
localProxy.ifPresent(proxy -> proxy.setBridgeConfiguration(localBridgeConfiguration));
|
||||||
|
|
||||||
initializerJob = Optional.of(scheduler.schedule(this::discoverVehicles, 2, TimeUnit.SECONDS));
|
initializerJob = Optional.of(scheduler.schedule(this::discoverVehicles, 2, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void createMyBmwProxy(MyBMWBridgeConfiguration config, String environment) {
|
private synchronized void createMyBmwProxy(MyBMWBridgeConfiguration config, String environment) {
|
||||||
if (!myBmwProxy.isPresent()) {
|
if (!myBmwProxy.isPresent()) {
|
||||||
if (!(TEST.equals(environment) && TESTUSER.equals(config.userName))) {
|
if (!(TEST.equals(environment) && TESTUSER.equals(config.getUserName()))) {
|
||||||
myBmwProxy = Optional.of(new MyBMWHttpProxy(httpClientFactory, config));
|
myBmwProxy = Optional.of(new MyBMWHttpProxy(httpClientFactory, config));
|
||||||
} else {
|
} else {
|
||||||
myBmwProxy = Optional.of(new MyBMWFileProxy(httpClientFactory, config));
|
myBmwProxy = Optional.of(new MyBMWFileProxy(httpClientFactory, config));
|
||||||
}
|
}
|
||||||
logger.trace("MyBMWBridgeHandler proxy set");
|
logger.trace("MyBMWBridgeHandler proxy set");
|
||||||
|
} else {
|
||||||
|
myBmwProxy.get().setBridgeConfiguration(config);
|
||||||
|
logger.trace("MyBMWBridgeHandler update proxy with bridge configuration");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,10 +155,6 @@ public class MyBMWBridgeHandler extends BaseBridgeHandler {
|
|||||||
private void discoverVehicles() {
|
private void discoverVehicles() {
|
||||||
logger.trace("MyBMWBridgeHandler.requestVehicles");
|
logger.trace("MyBMWBridgeHandler.requestVehicles");
|
||||||
|
|
||||||
MyBMWBridgeConfiguration config = getConfigAs(MyBMWBridgeConfiguration.class);
|
|
||||||
|
|
||||||
myBmwProxy.ifPresent(proxy -> proxy.setBridgeConfiguration(config));
|
|
||||||
|
|
||||||
vehicleDiscovery.ifPresent(discovery -> discovery.discoverVehicles());
|
vehicleDiscovery.ifPresent(discovery -> discovery.discoverVehicles());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,9 +164,23 @@ public class MyBMWBridgeHandler extends BaseBridgeHandler {
|
|||||||
return List.of(VehicleDiscovery.class);
|
return List.of(VehicleDiscovery.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<MyBMWProxy> getMyBmwProxy() {
|
public synchronized Optional<MyBMWProxy> getMyBmwProxy() {
|
||||||
logger.trace("MyBMWBridgeHandler.getProxy");
|
logger.trace("MyBMWBridgeHandler.getProxy");
|
||||||
createMyBmwProxy(getConfigAs(MyBMWBridgeConfiguration.class), ENVIRONMENT);
|
|
||||||
|
MyBMWBridgeConfiguration localBridgeConfiguration = null;
|
||||||
|
|
||||||
|
if (bmwBridgeConfiguration.isPresent()) {
|
||||||
|
localBridgeConfiguration = bmwBridgeConfiguration.get();
|
||||||
|
} else {
|
||||||
|
logger.warn("the bridge configuration could not be retrieved");
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
||||||
|
throw new IllegalStateException("bridge handler - configuration is not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myBmwProxy.isPresent()) {
|
||||||
|
createMyBmwProxy(localBridgeConfiguration, ENVIRONMENT);
|
||||||
|
}
|
||||||
|
|
||||||
return myBmwProxy;
|
return myBmwProxy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ public class RemoteServiceExecutor {
|
|||||||
serviceExecuting.ifPresentOrElse(service -> {
|
serviceExecuting.ifPresentOrElse(service -> {
|
||||||
if (counter >= GIVEUP_COUNTER) {
|
if (counter >= GIVEUP_COUNTER) {
|
||||||
logger.warn("Giving up updating state for {} after {} times", service, GIVEUP_COUNTER);
|
logger.warn("Giving up updating state for {} after {} times", service, GIVEUP_COUNTER);
|
||||||
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
|
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
|
||||||
ExecutionState.TIMEOUT.name().toLowerCase());
|
ExecutionState.TIMEOUT.name().toLowerCase());
|
||||||
reset();
|
reset();
|
||||||
// immediately refresh data
|
// immediately refresh data
|
||||||
@ -107,7 +107,7 @@ public class RemoteServiceExecutor {
|
|||||||
|
|
||||||
private void handleRemoteServiceException(NetworkException e) {
|
private void handleRemoteServiceException(NetworkException e) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
|
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
|
||||||
ExecutionState.ERROR.name().toLowerCase() + Constants.SPACE + Integer.toString(e.getStatus()));
|
ExecutionState.ERROR.name().toLowerCase() + Constants.SPACE + Integer.toString(e.getStatus()));
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@ -117,12 +117,12 @@ public class RemoteServiceExecutor {
|
|||||||
if (!executionStatusContainer.getEventId().isEmpty()) {
|
if (!executionStatusContainer.getEventId().isEmpty()) {
|
||||||
// service initiated - store event id for further MyBMW updates
|
// service initiated - store event id for further MyBMW updates
|
||||||
executingEventId = Optional.of(executionStatusContainer.getEventId());
|
executingEventId = Optional.of(executionStatusContainer.getEventId());
|
||||||
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
|
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
|
||||||
ExecutionState.INITIATED.name().toLowerCase());
|
ExecutionState.INITIATED.name().toLowerCase());
|
||||||
} else if (!executionStatusContainer.getEventStatus().isEmpty()) {
|
} else if (!executionStatusContainer.getEventStatus().isEmpty()) {
|
||||||
// service status updated
|
// service status updated
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
|
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
|
||||||
executionStatusContainer.getEventStatus().toLowerCase());
|
executionStatusContainer.getEventStatus().toLowerCase());
|
||||||
if (ExecutionState.EXECUTED.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())
|
if (ExecutionState.EXECUTED.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())
|
||||||
|| ExecutionState.ERROR.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())) {
|
|| ExecutionState.ERROR.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())) {
|
||||||
|
@ -23,6 +23,7 @@ import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.EADRAX_SE
|
|||||||
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.LOGIN_NONCE;
|
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.LOGIN_NONCE;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.OAUTH_ENDPOINT;
|
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.OAUTH_ENDPOINT;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.OCP_APIM_KEYS;
|
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_CHINA;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.BimmerConstants.REGION_NORTH_AMERICA;
|
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.REGION_ROW;
|
||||||
@ -36,6 +37,7 @@ import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.CODE_CHALLE
|
|||||||
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.CODE_VERIFIER;
|
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.CODE_VERIFIER;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.CONTENT_TYPE_URL_ENCODED;
|
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.CONTENT_TYPE_URL_ENCODED;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.GRANT_TYPE;
|
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.GRANT_TYPE;
|
||||||
|
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.HCAPTCHA_TOKEN;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.HEADER_ACP_SUBSCRIPTION_KEY;
|
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.HEADER_ACP_SUBSCRIPTION_KEY;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.HEADER_BMW_CORRELATION_ID;
|
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.HEADER_BMW_CORRELATION_ID;
|
||||||
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.HEADER_X_CORRELATION_ID;
|
import static org.openhab.binding.mybmw.internal.utils.HTTPConstants.HEADER_X_CORRELATION_ID;
|
||||||
@ -57,6 +59,8 @@ import java.security.PublicKey;
|
|||||||
import java.security.spec.X509EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
@ -86,7 +90,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
*
|
*
|
||||||
* requests the tokens for MyBMW API authorization
|
* requests the tokens for MyBMW API authorization
|
||||||
*
|
*
|
||||||
* thanks to bimmer_connected https://github.com/bimmerconnected/bimmer_connected
|
* thanks to bimmer_connected
|
||||||
|
* https://github.com/bimmerconnected/bimmer_connected
|
||||||
*
|
*
|
||||||
* @author Bernd Weymann - Initial contribution
|
* @author Bernd Weymann - Initial contribution
|
||||||
* @author Martin Grassl - extracted from myBmwProxy
|
* @author Martin Grassl - extracted from myBmwProxy
|
||||||
@ -97,14 +102,18 @@ public class MyBMWTokenController {
|
|||||||
private final Logger logger = LoggerFactory.getLogger(MyBMWTokenController.class);
|
private final Logger logger = LoggerFactory.getLogger(MyBMWTokenController.class);
|
||||||
|
|
||||||
private Token token = new Token();
|
private Token token = new Token();
|
||||||
private MyBMWBridgeConfiguration configuration;
|
private MyBMWBridgeConfiguration bridgeConfiguration;
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
|
|
||||||
public MyBMWTokenController(MyBMWBridgeConfiguration configuration, HttpClient httpClient) {
|
public MyBMWTokenController(MyBMWBridgeConfiguration configuration, HttpClient httpClient) {
|
||||||
this.configuration = configuration;
|
this.bridgeConfiguration = configuration;
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration) {
|
||||||
|
this.bridgeConfiguration = bridgeConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets new token if old one is expired or invalid. In case of error the token
|
* Gets new token if old one is expired or invalid. In case of error the token
|
||||||
* remains.
|
* remains.
|
||||||
@ -114,18 +123,31 @@ public class MyBMWTokenController {
|
|||||||
* @return token
|
* @return token
|
||||||
*/
|
*/
|
||||||
public Token getToken() {
|
public Token getToken() {
|
||||||
if (!token.isValid()) {
|
if (!bridgeConfiguration.getHcaptchatoken().isBlank()) {
|
||||||
|
// if the hcaptchastring is available, then a new login is triggered
|
||||||
|
boolean tokenCreationSuccess = getInitialToken();
|
||||||
|
|
||||||
|
if (!tokenCreationSuccess) {
|
||||||
|
this.token = new Token();
|
||||||
|
logger.warn(
|
||||||
|
"initial Authentication failed, maybe request a new captcha token, see https://bimmer-connected.readthedocs.io/en/latest/captcha/rest_of_world.html!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the token as it times out
|
||||||
|
bridgeConfiguration.setHcaptchatoken(Constants.EMPTY);
|
||||||
|
} else if (!token.isValid() && !Constants.EMPTY.equals(token.getRefreshToken())) {
|
||||||
|
// if the token is invalid, try to refresh the token
|
||||||
boolean tokenUpdateSuccess = false;
|
boolean tokenUpdateSuccess = false;
|
||||||
switch (configuration.region) {
|
switch (bridgeConfiguration.getRegion()) {
|
||||||
case REGION_CHINA:
|
case REGION_CHINA:
|
||||||
tokenUpdateSuccess = updateTokenChina();
|
tokenUpdateSuccess = getAndUpdateTokenChina();
|
||||||
break;
|
break;
|
||||||
case REGION_NORTH_AMERICA:
|
case REGION_NORTH_AMERICA:
|
||||||
case REGION_ROW:
|
case REGION_ROW:
|
||||||
tokenUpdateSuccess = updateToken();
|
tokenUpdateSuccess = getUpdatedToken();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.warn("Region {} not supported", configuration.region);
|
logger.warn("Region {} not supported", bridgeConfiguration.getRegion());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!tokenUpdateSuccess) {
|
if (!tokenUpdateSuccess) {
|
||||||
@ -143,32 +165,12 @@ public class MyBMWTokenController {
|
|||||||
*
|
*
|
||||||
* @return true if the token was successfully updated
|
* @return true if the token was successfully updated
|
||||||
*/
|
*/
|
||||||
private synchronized boolean updateToken() {
|
private synchronized boolean getInitialToken() {
|
||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
* Step 1) Get basic values for further queries
|
* Step 1) Get basic values for further queries
|
||||||
*/
|
*/
|
||||||
String uuidString = UUID.randomUUID().toString();
|
AuthQueryResponse aqr = getBasicAuthenticationValues();
|
||||||
|
|
||||||
String authValuesUrl = "https://" + EADRAX_SERVER_MAP.get(configuration.region) + API_OAUTH_CONFIG;
|
|
||||||
Request authValuesRequest = httpClient.newRequest(authValuesUrl);
|
|
||||||
authValuesRequest.header(HEADER_ACP_SUBSCRIPTION_KEY, OCP_APIM_KEYS.get(configuration.region));
|
|
||||||
authValuesRequest.header(HEADER_X_USER_AGENT, String.format(X_USER_AGENT, BRAND_BMW,
|
|
||||||
APP_VERSIONS.get(configuration.region), configuration.region));
|
|
||||||
authValuesRequest.header(HEADER_X_IDENTITY_PROVIDER, AUTH_PROVIDER);
|
|
||||||
authValuesRequest.header(HEADER_X_CORRELATION_ID, uuidString);
|
|
||||||
authValuesRequest.header(HEADER_BMW_CORRELATION_ID, uuidString);
|
|
||||||
|
|
||||||
ContentResponse authValuesResponse = authValuesRequest.send();
|
|
||||||
if (authValuesResponse.getStatus() != 200) {
|
|
||||||
throw new HttpResponseException("URL: " + authValuesRequest.getURI() + ", Error: "
|
|
||||||
+ authValuesResponse.getStatus() + ", Message: " + authValuesResponse.getContentAsString(),
|
|
||||||
authValuesResponse);
|
|
||||||
}
|
|
||||||
AuthQueryResponse aqr = JsonStringDeserializer.deserializeString(authValuesResponse.getContentAsString(),
|
|
||||||
AuthQueryResponse.class);
|
|
||||||
|
|
||||||
logger.trace("authQueryResponse: {}", aqr);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Step 2) Calculate values for oauth base parameters
|
* Step 2) Calculate values for oauth base parameters
|
||||||
@ -188,17 +190,19 @@ public class MyBMWTokenController {
|
|||||||
baseParams.put(CODE_CHALLENGE_METHOD, "S256");
|
baseParams.put(CODE_CHALLENGE_METHOD, "S256");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Step 3) Authorization with username and password
|
* Step 3) Authentication with username and password
|
||||||
*/
|
*/
|
||||||
String loginUrl = aqr.gcdmBaseUrl + OAUTH_ENDPOINT;
|
String loginUrl = aqr.gcdmBaseUrl + OAUTH_ENDPOINT;
|
||||||
Request loginRequest = httpClient.POST(loginUrl);
|
Request loginRequest = httpClient.POST(loginUrl);
|
||||||
|
|
||||||
loginRequest.header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE_URL_ENCODED);
|
loginRequest.header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE_URL_ENCODED);
|
||||||
|
loginRequest.header(HCAPTCHA_TOKEN, bridgeConfiguration.getHcaptchatoken());
|
||||||
|
|
||||||
MultiMap<@Nullable String> loginParams = new MultiMap<>(baseParams);
|
MultiMap<@Nullable String> loginParams = new MultiMap<>(baseParams);
|
||||||
loginParams.put(GRANT_TYPE, AUTHORIZATION_CODE);
|
loginParams.put(GRANT_TYPE, AUTHORIZATION_CODE);
|
||||||
loginParams.put(USERNAME, configuration.userName);
|
loginParams.put(USERNAME, bridgeConfiguration.getUserName());
|
||||||
loginParams.put(PASSWORD, configuration.password);
|
loginParams.put(PASSWORD, bridgeConfiguration.getPassword());
|
||||||
|
logger.trace("loginParams {}", loginParams);
|
||||||
loginRequest.content(new StringContentProvider(CONTENT_TYPE_URL_ENCODED,
|
loginRequest.content(new StringContentProvider(CONTENT_TYPE_URL_ENCODED,
|
||||||
UrlEncoded.encode(loginParams, StandardCharsets.UTF_8, false), StandardCharsets.UTF_8));
|
UrlEncoded.encode(loginParams, StandardCharsets.UTF_8, false), StandardCharsets.UTF_8));
|
||||||
ContentResponse loginResponse = loginRequest.send();
|
ContentResponse loginResponse = loginRequest.send();
|
||||||
@ -207,10 +211,11 @@ public class MyBMWTokenController {
|
|||||||
+ loginResponse.getStatus() + ", Message: " + loginResponse.getContentAsString(),
|
+ loginResponse.getStatus() + ", Message: " + loginResponse.getContentAsString(),
|
||||||
loginResponse);
|
loginResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
String authCode = getAuthCode(loginResponse.getContentAsString());
|
String authCode = getAuthCode(loginResponse.getContentAsString());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Step 4) Authorize with code
|
* Step 4) Authenticate with code
|
||||||
*/
|
*/
|
||||||
Request authRequest = httpClient.POST(loginUrl).followRedirects(false);
|
Request authRequest = httpClient.POST(loginUrl).followRedirects(false);
|
||||||
MultiMap<@Nullable String> authParams = new MultiMap<>(baseParams);
|
MultiMap<@Nullable String> authParams = new MultiMap<>(baseParams);
|
||||||
@ -248,9 +253,13 @@ public class MyBMWTokenController {
|
|||||||
}
|
}
|
||||||
AuthResponse ar = JsonStringDeserializer.deserializeString(codeResponse.getContentAsString(),
|
AuthResponse ar = JsonStringDeserializer.deserializeString(codeResponse.getContentAsString(),
|
||||||
AuthResponse.class);
|
AuthResponse.class);
|
||||||
|
|
||||||
token.setType(ar.tokenType);
|
token.setType(ar.tokenType);
|
||||||
token.setToken(ar.accessToken);
|
token.setToken(ar.accessToken);
|
||||||
token.setExpiration(ar.expiresIn);
|
token.setExpiration(ar.expiresIn);
|
||||||
|
token.setRefreshToken(ar.refreshToken);
|
||||||
|
token.setGcid(ar.gcid);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Authorization Exception: {}", e.getMessage());
|
logger.warn("Authorization Exception: {}", e.getMessage());
|
||||||
@ -258,6 +267,80 @@ public class MyBMWTokenController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refresh the existing token
|
||||||
|
*
|
||||||
|
* @return true if token has successfully been refreshed
|
||||||
|
*/
|
||||||
|
private synchronized boolean getUpdatedToken() {
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* Step 1) Get basic values for further queries
|
||||||
|
*/
|
||||||
|
AuthQueryResponse aqr = getBasicAuthenticationValues();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 2) Request token
|
||||||
|
*/
|
||||||
|
Request codeRequest = httpClient.POST(aqr.tokenEndpoint);
|
||||||
|
String basicAuth = "Basic "
|
||||||
|
+ Base64.getUrlEncoder().encodeToString((aqr.clientId + ":" + aqr.clientSecret).getBytes());
|
||||||
|
codeRequest.header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE_URL_ENCODED);
|
||||||
|
codeRequest.header(AUTHORIZATION, basicAuth);
|
||||||
|
|
||||||
|
MultiMap<@Nullable String> codeParams = new MultiMap<>();
|
||||||
|
codeParams.put(SCOPE, String.join(Constants.SPACE, aqr.scopes));
|
||||||
|
codeParams.put(REDIRECT_URI, aqr.returnUrl);
|
||||||
|
codeParams.put(GRANT_TYPE, REFRESH_TOKEN);
|
||||||
|
codeParams.put(REFRESH_TOKEN, token.getRefreshToken());
|
||||||
|
codeRequest.content(new StringContentProvider(CONTENT_TYPE_URL_ENCODED,
|
||||||
|
UrlEncoded.encode(codeParams, StandardCharsets.UTF_8, false), StandardCharsets.UTF_8));
|
||||||
|
ContentResponse codeResponse = codeRequest.send();
|
||||||
|
if (codeResponse.getStatus() != 200) {
|
||||||
|
throw new HttpResponseException("URL: " + codeRequest.getURI() + ", Error: " + codeResponse.getStatus()
|
||||||
|
+ ", Message: " + codeResponse.getContentAsString(), codeResponse);
|
||||||
|
}
|
||||||
|
AuthResponse ar = JsonStringDeserializer.deserializeString(codeResponse.getContentAsString(),
|
||||||
|
AuthResponse.class);
|
||||||
|
|
||||||
|
token.setToken(ar.accessToken);
|
||||||
|
token.setExpiration(ar.expiresIn);
|
||||||
|
token.setRefreshToken(ar.refreshToken);
|
||||||
|
token.setGcid(ar.gcid);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Refresh Exception: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthQueryResponse getBasicAuthenticationValues()
|
||||||
|
throws InterruptedException, TimeoutException, ExecutionException {
|
||||||
|
String uuidString = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
String authValuesUrl = "https://" + EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion()) + API_OAUTH_CONFIG;
|
||||||
|
Request authValuesRequest = httpClient.newRequest(authValuesUrl);
|
||||||
|
authValuesRequest.header(HEADER_ACP_SUBSCRIPTION_KEY, OCP_APIM_KEYS.get(bridgeConfiguration.getRegion()));
|
||||||
|
authValuesRequest.header(HEADER_X_USER_AGENT, String.format(X_USER_AGENT, BRAND_BMW,
|
||||||
|
APP_VERSIONS.get(bridgeConfiguration.getRegion()), bridgeConfiguration.getRegion()));
|
||||||
|
authValuesRequest.header(HEADER_X_IDENTITY_PROVIDER, AUTH_PROVIDER);
|
||||||
|
authValuesRequest.header(HEADER_X_CORRELATION_ID, uuidString);
|
||||||
|
authValuesRequest.header(HEADER_BMW_CORRELATION_ID, uuidString);
|
||||||
|
|
||||||
|
ContentResponse authValuesResponse = authValuesRequest.send();
|
||||||
|
if (authValuesResponse.getStatus() != 200) {
|
||||||
|
throw new HttpResponseException("URL: " + authValuesRequest.getURI() + ", Error: "
|
||||||
|
+ authValuesResponse.getStatus() + ", Message: " + authValuesResponse.getContentAsString(),
|
||||||
|
authValuesResponse);
|
||||||
|
}
|
||||||
|
AuthQueryResponse aqr = JsonStringDeserializer.deserializeString(authValuesResponse.getContentAsString(),
|
||||||
|
AuthQueryResponse.class);
|
||||||
|
|
||||||
|
logger.trace("authQueryResponse: {}", aqr);
|
||||||
|
return aqr;
|
||||||
|
}
|
||||||
|
|
||||||
private String generateState() {
|
private String generateState() {
|
||||||
String stateBytes = StringUtils.getRandomAlphabetic(64).toLowerCase();
|
String stateBytes = StringUtils.getRandomAlphabetic(64).toLowerCase();
|
||||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(stateBytes.getBytes());
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(stateBytes.getBytes());
|
||||||
@ -301,7 +384,7 @@ public class MyBMWTokenController {
|
|||||||
return codeFound.toString();
|
return codeFound.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean updateTokenChina() {
|
private synchronized boolean getAndUpdateTokenChina() {
|
||||||
try {
|
try {
|
||||||
/**
|
/**
|
||||||
* Step 1) get public key
|
* Step 1) get public key
|
||||||
@ -310,7 +393,7 @@ public class MyBMWTokenController {
|
|||||||
Request oauthQueryRequest = httpClient.newRequest(publicKeyUrl);
|
Request oauthQueryRequest = httpClient.newRequest(publicKeyUrl);
|
||||||
oauthQueryRequest.header(HttpHeader.USER_AGENT, USER_AGENT);
|
oauthQueryRequest.header(HttpHeader.USER_AGENT, USER_AGENT);
|
||||||
oauthQueryRequest.header(HEADER_X_USER_AGENT, String.format(X_USER_AGENT, BRAND_BMW,
|
oauthQueryRequest.header(HEADER_X_USER_AGENT, String.format(X_USER_AGENT, BRAND_BMW,
|
||||||
APP_VERSIONS.get(configuration.region), configuration.region));
|
APP_VERSIONS.get(bridgeConfiguration.getRegion()), bridgeConfiguration.getRegion()));
|
||||||
ContentResponse publicKeyResponse = oauthQueryRequest.send();
|
ContentResponse publicKeyResponse = oauthQueryRequest.send();
|
||||||
if (publicKeyResponse.getStatus() != 200) {
|
if (publicKeyResponse.getStatus() != 200) {
|
||||||
throw new HttpResponseException("URL: " + oauthQueryRequest.getURI() + ", Error: "
|
throw new HttpResponseException("URL: " + oauthQueryRequest.getURI() + ", Error: "
|
||||||
@ -335,7 +418,7 @@ public class MyBMWTokenController {
|
|||||||
// https://www.thexcoders.net/java-ciphers-rsa/
|
// https://www.thexcoders.net/java-ciphers-rsa/
|
||||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
byte[] encryptedBytes = cipher.doFinal(configuration.password.getBytes());
|
byte[] encryptedBytes = cipher.doFinal(bridgeConfiguration.getPassword().getBytes());
|
||||||
String encodedPassword = Base64.getEncoder().encodeToString(encryptedBytes);
|
String encodedPassword = Base64.getEncoder().encodeToString(encryptedBytes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -344,9 +427,9 @@ public class MyBMWTokenController {
|
|||||||
String tokenUrl = "https://" + EADRAX_SERVER_MAP.get(REGION_CHINA) + CHINA_LOGIN;
|
String tokenUrl = "https://" + EADRAX_SERVER_MAP.get(REGION_CHINA) + CHINA_LOGIN;
|
||||||
Request loginRequest = httpClient.POST(tokenUrl);
|
Request loginRequest = httpClient.POST(tokenUrl);
|
||||||
loginRequest.header(HEADER_X_USER_AGENT, String.format(X_USER_AGENT, BRAND_BMW,
|
loginRequest.header(HEADER_X_USER_AGENT, String.format(X_USER_AGENT, BRAND_BMW,
|
||||||
APP_VERSIONS.get(configuration.region), configuration.region));
|
APP_VERSIONS.get(bridgeConfiguration.getRegion()), bridgeConfiguration.getRegion()));
|
||||||
String jsonContent = "{ \"mobile\":\"" + configuration.userName + "\", \"password\":\"" + encodedPassword
|
String jsonContent = "{ \"mobile\":\"" + bridgeConfiguration.getUserName() + "\", \"password\":\""
|
||||||
+ "\"}";
|
+ encodedPassword + "\"}";
|
||||||
loginRequest.content(new StringContentProvider(jsonContent));
|
loginRequest.content(new StringContentProvider(jsonContent));
|
||||||
ContentResponse tokenResponse = loginRequest.send();
|
ContentResponse tokenResponse = loginRequest.send();
|
||||||
if (tokenResponse.getStatus() != 200) {
|
if (tokenResponse.getStatus() != 200) {
|
||||||
|
@ -19,15 +19,20 @@ import org.openhab.binding.mybmw.internal.utils.Constants;
|
|||||||
* The {@link Token} MyBMW Token storage
|
* The {@link Token} MyBMW Token storage
|
||||||
*
|
*
|
||||||
* @author Bernd Weymann - Initial contribution
|
* @author Bernd Weymann - Initial contribution
|
||||||
|
* @author Martin Grassl - extracted to own class
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class Token {
|
public class Token {
|
||||||
private String token = Constants.EMPTY;
|
private String token = Constants.EMPTY;
|
||||||
private String tokenType = Constants.EMPTY;
|
private String tokenType = Constants.EMPTY;
|
||||||
|
private String refreshToken = Constants.EMPTY;
|
||||||
|
private String gcid = Constants.EMPTY;
|
||||||
|
|
||||||
private long expiration = 0;
|
private long expiration = 0;
|
||||||
|
|
||||||
public String getBearerToken() {
|
public String getBearerToken() {
|
||||||
return new StringBuilder(tokenType).append(Constants.SPACE).append(token).toString();
|
return token.equals(Constants.EMPTY) ? Constants.EMPTY
|
||||||
|
: new StringBuilder(tokenType).append(Constants.SPACE).append(token).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setToken(String token) {
|
public void setToken(String token) {
|
||||||
@ -46,9 +51,25 @@ public class Token {
|
|||||||
tokenType = type;
|
tokenType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRefreshToken() {
|
||||||
|
return refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRefreshToken(String refreshToken) {
|
||||||
|
this.refreshToken = refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGcid() {
|
||||||
|
return gcid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGcid(String gcid) {
|
||||||
|
this.gcid = gcid;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
return (!token.equals(Constants.EMPTY) && !tokenType.equals(Constants.EMPTY)
|
return (!token.equals(Constants.EMPTY) && !tokenType.equals(Constants.EMPTY)
|
||||||
&& (this.expiration - System.currentTimeMillis() / 1000) > 1);
|
&& !refreshToken.equals(Constants.EMPTY) && (this.expiration - System.currentTimeMillis() / 1000) > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,7 +19,6 @@ import java.io.InputStreamReader;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.binding.mybmw.internal.MyBMWBridgeConfiguration;
|
import org.openhab.binding.mybmw.internal.MyBMWBridgeConfiguration;
|
||||||
import org.openhab.binding.mybmw.internal.dto.charge.ChargingSessionsContainer;
|
import org.openhab.binding.mybmw.internal.dto.charge.ChargingSessionsContainer;
|
||||||
@ -60,17 +59,17 @@ public class MyBMWFileProxy implements MyBMWProxy {
|
|||||||
|
|
||||||
public MyBMWFileProxy(HttpClientFactory httpClientFactory, MyBMWBridgeConfiguration bridgeConfiguration) {
|
public MyBMWFileProxy(HttpClientFactory httpClientFactory, MyBMWBridgeConfiguration bridgeConfiguration) {
|
||||||
logger.trace("MyBMWFileProxy - initialize");
|
logger.trace("MyBMWFileProxy - initialize");
|
||||||
vehicleToBeTested = bridgeConfiguration.password;
|
vehicleToBeTested = bridgeConfiguration.getPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration) {
|
public void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration) {
|
||||||
logger.trace("MyBMWFileProxy - update bridge");
|
logger.trace("MyBMWFileProxy - update bridge");
|
||||||
vehicleToBeTested = bridgeConfiguration.password;
|
vehicleToBeTested = bridgeConfiguration.getPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<@NonNull Vehicle> requestVehicles() throws NetworkException {
|
public List<Vehicle> requestVehicles() throws NetworkException {
|
||||||
List<@NonNull Vehicle> vehicles = new ArrayList<>();
|
List<Vehicle> vehicles = new ArrayList<>();
|
||||||
List<@NonNull VehicleBase> vehiclesBase = requestVehiclesBase();
|
List<VehicleBase> vehiclesBase = requestVehiclesBase();
|
||||||
|
|
||||||
for (VehicleBase vehicleBase : vehiclesBase) {
|
for (VehicleBase vehicleBase : vehiclesBase) {
|
||||||
VehicleStateContainer vehicleState = requestVehicleState(vehicleBase.getVin(),
|
VehicleStateContainer vehicleState = requestVehicleState(vehicleBase.getVin(),
|
||||||
|
@ -22,7 +22,6 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
@ -67,7 +66,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
private final Logger logger = LoggerFactory.getLogger(MyBMWHttpProxy.class);
|
private final Logger logger = LoggerFactory.getLogger(MyBMWHttpProxy.class);
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
private MyBMWBridgeConfiguration bridgeConfiguration;
|
private MyBMWBridgeConfiguration bridgeConfiguration;
|
||||||
private final MyBMWTokenController myBMWTokenHandler;
|
MyBMWTokenController myBMWTokenController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URLs taken from
|
* URLs taken from
|
||||||
@ -82,16 +81,16 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
logger.trace("MyBMWHttpProxy - initialize");
|
logger.trace("MyBMWHttpProxy - initialize");
|
||||||
httpClient = httpClientFactory.getCommonHttpClient();
|
httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
|
||||||
myBMWTokenHandler = new MyBMWTokenController(bridgeConfiguration, httpClient);
|
myBMWTokenController = new MyBMWTokenController(bridgeConfiguration, httpClient);
|
||||||
|
|
||||||
this.bridgeConfiguration = bridgeConfiguration;
|
this.bridgeConfiguration = bridgeConfiguration;
|
||||||
|
|
||||||
vehicleUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.region)
|
vehicleUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
|
||||||
+ BimmerConstants.API_VEHICLES;
|
+ BimmerConstants.API_VEHICLES;
|
||||||
|
|
||||||
vehicleStateUrl = vehicleUrl + "/state";
|
vehicleStateUrl = vehicleUrl + "/state";
|
||||||
|
|
||||||
remoteCommandUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.region)
|
remoteCommandUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
|
||||||
+ BimmerConstants.API_REMOTE_SERVICE_BASE_URL;
|
+ BimmerConstants.API_REMOTE_SERVICE_BASE_URL;
|
||||||
remoteStatusUrl = remoteCommandUrl + "eventStatus";
|
remoteStatusUrl = remoteCommandUrl + "eventStatus";
|
||||||
logger.trace("MyBMWHttpProxy - ready");
|
logger.trace("MyBMWHttpProxy - ready");
|
||||||
@ -100,6 +99,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
@Override
|
@Override
|
||||||
public void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration) {
|
public void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration) {
|
||||||
this.bridgeConfiguration = bridgeConfiguration;
|
this.bridgeConfiguration = bridgeConfiguration;
|
||||||
|
myBMWTokenController.setBridgeConfiguration(bridgeConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,9 +107,9 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
*
|
*
|
||||||
* @return list of vehicles
|
* @return list of vehicles
|
||||||
*/
|
*/
|
||||||
public List<@NonNull Vehicle> requestVehicles() throws NetworkException {
|
public List<Vehicle> requestVehicles() throws NetworkException {
|
||||||
List<@NonNull Vehicle> vehicles = new ArrayList<>();
|
List<Vehicle> vehicles = new ArrayList<>();
|
||||||
List<@NonNull VehicleBase> vehiclesBase = requestVehiclesBase();
|
List<VehicleBase> vehiclesBase = requestVehiclesBase();
|
||||||
|
|
||||||
for (VehicleBase vehicleBase : vehiclesBase) {
|
for (VehicleBase vehicleBase : vehiclesBase) {
|
||||||
VehicleStateContainer vehicleState = requestVehicleState(vehicleBase.getVin(),
|
VehicleStateContainer vehicleState = requestVehicleState(vehicleBase.getVin(),
|
||||||
@ -177,7 +177,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
* @return the image as a byte array
|
* @return the image as a byte array
|
||||||
*/
|
*/
|
||||||
public byte[] requestImage(String vin, String brand, ImageProperties props) throws NetworkException {
|
public byte[] requestImage(String vin, String brand, ImageProperties props) throws NetworkException {
|
||||||
final String localImageUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.region)
|
final String localImageUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
|
||||||
+ "/eadrax-ics/v3/presentation/vehicles/" + vin + "/images?carView=" + props.viewport;
|
+ "/eadrax-ics/v3/presentation/vehicles/" + vin + "/images?carView=" + props.viewport;
|
||||||
return get(localImageUrl, brand, vin, HTTPConstants.CONTENT_TYPE_IMAGE);
|
return get(localImageUrl, brand, vin, HTTPConstants.CONTENT_TYPE_IMAGE);
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
chargeStatisticsParams.put("vin", vin);
|
chargeStatisticsParams.put("vin", vin);
|
||||||
chargeStatisticsParams.put("currentDate", Converter.getCurrentISOTime());
|
chargeStatisticsParams.put("currentDate", Converter.getCurrentISOTime());
|
||||||
String params = UrlEncoded.encode(chargeStatisticsParams, StandardCharsets.UTF_8, false);
|
String params = UrlEncoded.encode(chargeStatisticsParams, StandardCharsets.UTF_8, false);
|
||||||
String chargeStatisticsUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.region)
|
String chargeStatisticsUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
|
||||||
+ "/eadrax-chs/v1/charging-statistics?" + params;
|
+ "/eadrax-chs/v1/charging-statistics?" + params;
|
||||||
byte[] chargeStatisticsResponse = get(chargeStatisticsUrl, brand, vin, HTTPConstants.CONTENT_TYPE_JSON);
|
byte[] chargeStatisticsResponse = get(chargeStatisticsUrl, brand, vin, HTTPConstants.CONTENT_TYPE_JSON);
|
||||||
String chargeStatisticsResponseString = new String(chargeStatisticsResponse);
|
String chargeStatisticsResponseString = new String(chargeStatisticsResponse);
|
||||||
@ -263,7 +263,7 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
chargeSessionsParams.put("maxResults", "40");
|
chargeSessionsParams.put("maxResults", "40");
|
||||||
chargeSessionsParams.put("include_date_picker", "true");
|
chargeSessionsParams.put("include_date_picker", "true");
|
||||||
String params = UrlEncoded.encode(chargeSessionsParams, StandardCharsets.UTF_8, false);
|
String params = UrlEncoded.encode(chargeSessionsParams, StandardCharsets.UTF_8, false);
|
||||||
String chargeSessionsUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.region)
|
String chargeSessionsUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(bridgeConfiguration.getRegion())
|
||||||
+ "/eadrax-chs/v1/charging-sessions?" + params;
|
+ "/eadrax-chs/v1/charging-sessions?" + params;
|
||||||
byte[] chargeSessionsResponse = get(chargeSessionsUrl, brand, vin, HTTPConstants.CONTENT_TYPE_JSON);
|
byte[] chargeSessionsResponse = get(chargeSessionsUrl, brand, vin, HTTPConstants.CONTENT_TYPE_JSON);
|
||||||
String chargeSessionsResponseString = new String(chargeSessionsResponse);
|
String chargeSessionsResponseString = new String(chargeSessionsResponse);
|
||||||
@ -353,6 +353,12 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
throw new NetworkException("Unknown Brand " + brand);
|
throw new NetworkException("Unknown Brand " + brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if no token is available, no request can be triggered
|
||||||
|
if (Constants.EMPTY.equals(myBMWTokenController.getToken().getBearerToken())) {
|
||||||
|
logger.warn("The login failed, no token is available");
|
||||||
|
throw new NetworkException("The login failed, no token is available");
|
||||||
|
}
|
||||||
|
|
||||||
final Request req;
|
final Request req;
|
||||||
|
|
||||||
if (post) {
|
if (post) {
|
||||||
@ -361,10 +367,10 @@ public class MyBMWHttpProxy implements MyBMWProxy {
|
|||||||
req = httpClient.newRequest(url);
|
req = httpClient.newRequest(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
req.header(HttpHeader.AUTHORIZATION, myBMWTokenHandler.getToken().getBearerToken());
|
req.header(HttpHeader.AUTHORIZATION, myBMWTokenController.getToken().getBearerToken());
|
||||||
req.header(HTTPConstants.HEADER_X_USER_AGENT, String.format(BimmerConstants.X_USER_AGENT, brand.toLowerCase(),
|
req.header(HTTPConstants.HEADER_X_USER_AGENT, String.format(BimmerConstants.X_USER_AGENT, brand.toLowerCase(),
|
||||||
APP_VERSIONS.get(bridgeConfiguration.region), bridgeConfiguration.region));
|
APP_VERSIONS.get(bridgeConfiguration.getRegion()), bridgeConfiguration.getRegion()));
|
||||||
req.header(HttpHeader.ACCEPT_LANGUAGE, bridgeConfiguration.language);
|
req.header(HttpHeader.ACCEPT_LANGUAGE, bridgeConfiguration.getLanguage());
|
||||||
req.header(HttpHeader.ACCEPT, contentType);
|
req.header(HttpHeader.ACCEPT, contentType);
|
||||||
req.header(HTTPConstants.HEADER_BMW_VIN, vin);
|
req.header(HTTPConstants.HEADER_BMW_VIN, vin);
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ package org.openhab.binding.mybmw.internal.handler.backend;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.binding.mybmw.internal.MyBMWBridgeConfiguration;
|
import org.openhab.binding.mybmw.internal.MyBMWBridgeConfiguration;
|
||||||
import org.openhab.binding.mybmw.internal.dto.charge.ChargingSessionsContainer;
|
import org.openhab.binding.mybmw.internal.dto.charge.ChargingSessionsContainer;
|
||||||
@ -36,7 +35,7 @@ public interface MyBMWProxy {
|
|||||||
|
|
||||||
void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration);
|
void setBridgeConfiguration(MyBMWBridgeConfiguration bridgeConfiguration);
|
||||||
|
|
||||||
List<@NonNull Vehicle> requestVehicles() throws NetworkException;
|
List<Vehicle> requestVehicles() throws NetworkException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* request all vehicles for one specific brand and their state
|
* request all vehicles for one specific brand and their state
|
||||||
|
@ -59,17 +59,18 @@ public interface BimmerConstants {
|
|||||||
static final String CHINA_LOGIN = "/eadrax-coas/v2/login/pwd";
|
static final String CHINA_LOGIN = "/eadrax-coas/v2/login/pwd";
|
||||||
|
|
||||||
// Http variables
|
// Http variables
|
||||||
static final String APP_VERSION_NORTH_AMERICA = "2.12.0(19883)";
|
static final String APP_VERSION_NORTH_AMERICA = "4.9.2(36892)";
|
||||||
static final String APP_VERSION_ROW = "2.12.0(19883)";
|
static final String APP_VERSION_ROW = "4.9.2(36892)";
|
||||||
static final String APP_VERSION_CHINA = "2.3.0(13603)";
|
static final String APP_VERSION_CHINA = "4.9.2(36892)";
|
||||||
static final Map<String, String> APP_VERSIONS = Map.of(REGION_NORTH_AMERICA, APP_VERSION_NORTH_AMERICA, REGION_ROW,
|
static final Map<String, String> APP_VERSIONS = Map.of(REGION_NORTH_AMERICA, APP_VERSION_NORTH_AMERICA, REGION_ROW,
|
||||||
APP_VERSION_ROW, REGION_CHINA, APP_VERSION_CHINA);
|
APP_VERSION_ROW, REGION_CHINA, APP_VERSION_CHINA);
|
||||||
static final String USER_AGENT = "Dart/2.16 (dart:io)";
|
static final String USER_AGENT = "Dart/2.16 (dart:io)";
|
||||||
// see const.py of bimmer_constants: user-agent; brand; app_version; region
|
// see const.py of bimmer_constants: user-agent; brand; app_version; region
|
||||||
static final String X_USER_AGENT = "android(SP1A.210812.016.C1);%s;%s;%s";
|
static final String X_USER_AGENT = "android(AP2A.240605.024);%s;%s;%s";
|
||||||
|
|
||||||
static final String LOGIN_NONCE = "login_nonce";
|
static final String LOGIN_NONCE = "login_nonce";
|
||||||
static final String AUTHORIZATION_CODE = "authorization_code";
|
static final String AUTHORIZATION_CODE = "authorization_code";
|
||||||
|
static final String REFRESH_TOKEN = "refresh_token";
|
||||||
|
|
||||||
// Parameters for API Requests
|
// Parameters for API Requests
|
||||||
static final String TIRE_GUARD_MODE = "tireGuardMode";
|
static final String TIRE_GUARD_MODE = "tireGuardMode";
|
||||||
|
@ -42,6 +42,7 @@ public interface HTTPConstants {
|
|||||||
static final String CREDENTIALS = "Credentials";
|
static final String CREDENTIALS = "Credentials";
|
||||||
static final String USERNAME = "username";
|
static final String USERNAME = "username";
|
||||||
static final String PASSWORD = "password";
|
static final String PASSWORD = "password";
|
||||||
|
static final String HCAPTCHA_TOKEN = "hcaptchatoken";
|
||||||
static final String CONTENT_LENGTH = "Content-Length";
|
static final String CONTENT_LENGTH = "Content-Length";
|
||||||
static final String CODE_CHALLENGE = "code_challenge";
|
static final String CODE_CHALLENGE = "code_challenge";
|
||||||
static final String CODE_CHALLENGE_METHOD = "code_challenge_method";
|
static final String CODE_CHALLENGE_METHOD = "code_challenge_method";
|
||||||
|
@ -24,11 +24,11 @@ import org.openhab.binding.mybmw.internal.MyBMWBridgeConfiguration;
|
|||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public final class MyBMWConfigurationChecker {
|
public final class MyBMWConfigurationChecker {
|
||||||
public static boolean checkConfiguration(MyBMWBridgeConfiguration config) {
|
public static boolean checkInitialConfiguration(MyBMWBridgeConfiguration config) {
|
||||||
if (config.userName.isBlank() || config.password.isBlank()) {
|
if (config.getUserName().isBlank() || config.getPassword().isBlank() || config.getHcaptchatoken().isBlank()) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return BimmerConstants.EADRAX_SERVER_MAP.containsKey(config.region);
|
return BimmerConstants.EADRAX_SERVER_MAP.containsKey(config.getRegion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
<description>MyBMW Password</description>
|
<description>MyBMW Password</description>
|
||||||
<context>password</context>
|
<context>password</context>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
<parameter name="hcaptchatoken" type="text" required="true">
|
||||||
|
<label>Captcha-Token</label>
|
||||||
|
<description>Captcha-Token for login (see https://bimmer-connected.readthedocs.io/en/stable/captcha.html)</description>
|
||||||
|
<context>hcaptchatoken</context>
|
||||||
|
</parameter>
|
||||||
<parameter name="region" type="text" required="true">
|
<parameter name="region" type="text" required="true">
|
||||||
<label>Region</label>
|
<label>Region</label>
|
||||||
<description>Select Region in order to connect to the appropriate BMW Server</description>
|
<description>Select Region in order to connect to the appropriate BMW Server</description>
|
||||||
|
@ -8,6 +8,8 @@ thing-type.config.mybmw.bridge.language.description = Channel data can be return
|
|||||||
thing-type.config.mybmw.bridge.language.label = Language Settings
|
thing-type.config.mybmw.bridge.language.label = Language Settings
|
||||||
thing-type.config.mybmw.bridge.password.description = MyBMW Password
|
thing-type.config.mybmw.bridge.password.description = MyBMW Password
|
||||||
thing-type.config.mybmw.bridge.password.label = Password
|
thing-type.config.mybmw.bridge.password.label = Password
|
||||||
|
thing-type.config.mybmw.bridge.hcaptchatoken.description = Captcha-Token for the Login (see https://bimmer-connected.readthedocs.io/en/stable/captcha.html)
|
||||||
|
thing-type.config.mybmw.bridge.hcaptchatoken.label = Captcha-Token
|
||||||
thing-type.config.mybmw.bridge.region.description = Select Region in order to connect to the appropriate BMW Server
|
thing-type.config.mybmw.bridge.region.description = Select Region in order to connect to the appropriate BMW Server
|
||||||
thing-type.config.mybmw.bridge.region.label = Region
|
thing-type.config.mybmw.bridge.region.label = Region
|
||||||
thing-type.config.mybmw.bridge.region.option.CHINA = China
|
thing-type.config.mybmw.bridge.region.option.CHINA = China
|
||||||
|
@ -22,6 +22,8 @@ thing-type.config.mybmw.bridge.language.label = Sprachauswahl
|
|||||||
thing-type.config.mybmw.bridge.language.description = Daten werden für die gewünschte Sprache angefordert (en, de, fr ...)
|
thing-type.config.mybmw.bridge.language.description = Daten werden für die gewünschte Sprache angefordert (en, de, fr ...)
|
||||||
thing-type.config.mybmw.bridge.password.label = Passwort
|
thing-type.config.mybmw.bridge.password.label = Passwort
|
||||||
thing-type.config.mybmw.bridge.password.description = Passwort für die MyBMW App
|
thing-type.config.mybmw.bridge.password.description = Passwort für die MyBMW App
|
||||||
|
thing-type.config.mybmw.bridge.hcaptchatoken.label = Captcha-Token
|
||||||
|
thing-type.config.mybmw.bridge.hcaptchatoken.description = Captcha-Token für den Login (siehe https://bimmer-connected.readthedocs.io/en/stable/captcha.html)
|
||||||
thing-type.config.mybmw.bridge.region.label = Region
|
thing-type.config.mybmw.bridge.region.label = Region
|
||||||
thing-type.config.mybmw.bridge.region.description = Auswahl Ihrer Region
|
thing-type.config.mybmw.bridge.region.description = Auswahl Ihrer Region
|
||||||
thing-type.config.mybmw.bridge.region.option.NORTH_AMERICA = Nordamerika
|
thing-type.config.mybmw.bridge.region.option.NORTH_AMERICA = Nordamerika
|
||||||
|
@ -317,9 +317,9 @@ class AuthTest {
|
|||||||
HttpClientFactory mockHCF = mock(HttpClientFactory.class);
|
HttpClientFactory mockHCF = mock(HttpClientFactory.class);
|
||||||
when(mockHCF.getCommonHttpClient()).thenReturn(authHttpClient);
|
when(mockHCF.getCommonHttpClient()).thenReturn(authHttpClient);
|
||||||
MyBMWBridgeConfiguration config = new MyBMWBridgeConfiguration();
|
MyBMWBridgeConfiguration config = new MyBMWBridgeConfiguration();
|
||||||
config.region = BimmerConstants.REGION_CHINA;
|
config.setRegion(BimmerConstants.REGION_CHINA);
|
||||||
config.userName = "Hello User";
|
config.setUserName("Hello User");
|
||||||
config.password = "Hello Password";
|
config.setPassword("Hello Password");
|
||||||
MyBMWTokenController tokenHandler = new MyBMWTokenController(config, authHttpClient);
|
MyBMWTokenController tokenHandler = new MyBMWTokenController(config, authHttpClient);
|
||||||
Token token = tokenHandler.getToken();
|
Token token = tokenHandler.getToken();
|
||||||
assertNotNull(token);
|
assertNotNull(token);
|
||||||
|
@ -36,6 +36,8 @@ import org.openhab.binding.mybmw.internal.dto.remote.ExecutionStatusContainer;
|
|||||||
import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
|
import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
|
||||||
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleBase;
|
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleBase;
|
||||||
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleStateContainer;
|
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleStateContainer;
|
||||||
|
import org.openhab.binding.mybmw.internal.handler.auth.MyBMWTokenController;
|
||||||
|
import org.openhab.binding.mybmw.internal.handler.auth.Token;
|
||||||
import org.openhab.binding.mybmw.internal.handler.enums.RemoteService;
|
import org.openhab.binding.mybmw.internal.handler.enums.RemoteService;
|
||||||
import org.openhab.binding.mybmw.internal.util.FileReader;
|
import org.openhab.binding.mybmw.internal.util.FileReader;
|
||||||
import org.openhab.binding.mybmw.internal.utils.BimmerConstants;
|
import org.openhab.binding.mybmw.internal.utils.BimmerConstants;
|
||||||
@ -196,8 +198,13 @@ public class MyBMWHttpProxyTest {
|
|||||||
HttpClientFactory httpClientFactoryMock = Mockito.mock(HttpClientFactory.class);
|
HttpClientFactory httpClientFactoryMock = Mockito.mock(HttpClientFactory.class);
|
||||||
HttpClient httpClientMock = Mockito.mock(HttpClient.class);
|
HttpClient httpClientMock = Mockito.mock(HttpClient.class);
|
||||||
Request requestMock = Mockito.mock(Request.class);
|
Request requestMock = Mockito.mock(Request.class);
|
||||||
|
MyBMWTokenController bmwTokenControllerMock = Mockito.mock(MyBMWTokenController.class);
|
||||||
Mockito.when(httpClientMock.newRequest(Mockito.anyString())).thenReturn(requestMock);
|
Mockito.when(httpClientMock.newRequest(Mockito.anyString())).thenReturn(requestMock);
|
||||||
Mockito.when(httpClientMock.POST(Mockito.anyString())).thenReturn(requestMock);
|
Mockito.when(httpClientMock.POST(Mockito.anyString())).thenReturn(requestMock);
|
||||||
|
|
||||||
|
Token token = Mockito.mock(Token.class);
|
||||||
|
Mockito.when(token.getBearerToken()).thenReturn("blah");
|
||||||
|
Mockito.when(bmwTokenControllerMock.getToken()).thenReturn(token);
|
||||||
MyBMWBridgeConfiguration myBMWBridgeConfiguration = new MyBMWBridgeConfiguration();
|
MyBMWBridgeConfiguration myBMWBridgeConfiguration = new MyBMWBridgeConfiguration();
|
||||||
Mockito.when(httpClientFactoryMock.getCommonHttpClient()).thenReturn(httpClientMock);
|
Mockito.when(httpClientFactoryMock.getCommonHttpClient()).thenReturn(httpClientMock);
|
||||||
|
|
||||||
@ -216,6 +223,8 @@ public class MyBMWHttpProxyTest {
|
|||||||
logger.error(e1.getMessage(), e1);
|
logger.error(e1.getMessage(), e1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MyBMWHttpProxy(httpClientFactoryMock, myBMWBridgeConfiguration);
|
MyBMWHttpProxy proxy = new MyBMWHttpProxy(httpClientFactoryMock, myBMWBridgeConfiguration);
|
||||||
|
proxy.myBMWTokenController = bmwTokenControllerMock;
|
||||||
|
return proxy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,327 @@
|
|||||||
|
/**
|
||||||
|
* 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.mybmw.internal.handler.backend;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.openhab.binding.mybmw.internal.MyBMWBridgeConfiguration;
|
||||||
|
import org.openhab.binding.mybmw.internal.dto.charge.ChargingSessionsContainer;
|
||||||
|
import org.openhab.binding.mybmw.internal.dto.charge.ChargingStatisticsContainer;
|
||||||
|
import org.openhab.binding.mybmw.internal.dto.remote.ExecutionStatusContainer;
|
||||||
|
import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
|
||||||
|
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleBase;
|
||||||
|
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleStateContainer;
|
||||||
|
import org.openhab.binding.mybmw.internal.handler.enums.ExecutionState;
|
||||||
|
import org.openhab.binding.mybmw.internal.handler.enums.RemoteService;
|
||||||
|
import org.openhab.binding.mybmw.internal.utils.BimmerConstants;
|
||||||
|
import org.openhab.binding.mybmw.internal.utils.ImageProperties;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this integration test runs only if the connected account is set via environment variables
|
||||||
|
* CONNECTED_USER
|
||||||
|
* CONNECTED_PASSWORD
|
||||||
|
* HCAPTCHA_TOKEN
|
||||||
|
*
|
||||||
|
* if you want to execute the tests, please set the env variables and remove the disabled annotation
|
||||||
|
*
|
||||||
|
* @author Martin Grassl - initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class MyBMWProxyBackendIT {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(MyBMWProxyBackendIT.class);
|
||||||
|
|
||||||
|
public MyBMWProxy initializeProxy() {
|
||||||
|
String connectedUser = System.getenv("CONNECTED_USER");
|
||||||
|
String connectedPassword = System.getenv("CONNECTED_PASSWORD");
|
||||||
|
String hCaptchaString = System.getenv("HCAPTCHA_TOKEN");
|
||||||
|
assertNotNull(connectedUser);
|
||||||
|
assertNotNull(connectedPassword);
|
||||||
|
assertNotNull(hCaptchaString);
|
||||||
|
|
||||||
|
MyBMWBridgeConfiguration configuration = new MyBMWBridgeConfiguration();
|
||||||
|
configuration.setLanguage("de-DE");
|
||||||
|
configuration.setRegion(BimmerConstants.REGION_ROW);
|
||||||
|
configuration.setUserName(connectedUser);
|
||||||
|
configuration.setPassword(connectedPassword);
|
||||||
|
configuration.setHcaptchatoken(hCaptchaString);
|
||||||
|
|
||||||
|
return new MyBMWHttpProxy(new MyHttpClientFactory(), configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setupLogger() {
|
||||||
|
Logger root = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
|
||||||
|
|
||||||
|
((ch.qos.logback.classic.Logger) root).setLevel(Level.TRACE);
|
||||||
|
|
||||||
|
logger.trace("tracing enabled");
|
||||||
|
logger.debug("debugging enabled");
|
||||||
|
logger.info("info enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSequence() {
|
||||||
|
MyBMWProxy myBMWProxy = initializeProxy();
|
||||||
|
|
||||||
|
// get list of vehicles
|
||||||
|
List<VehicleBase> vehicles = null;
|
||||||
|
try {
|
||||||
|
vehicles = myBMWProxy.requestVehiclesBase();
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
fail(e.getReason(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNotNull(vehicles);
|
||||||
|
assertEquals(2, vehicles.size());
|
||||||
|
|
||||||
|
for (VehicleBase vehicleBase : vehicles) {
|
||||||
|
assertNotNull(vehicleBase.getVin());
|
||||||
|
assertNotNull(vehicleBase.getAttributes().getBrand());
|
||||||
|
|
||||||
|
// get image
|
||||||
|
try {
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage(vehicleBase.getVin(), vehicleBase.getAttributes().getBrand(),
|
||||||
|
new ImageProperties());
|
||||||
|
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
fail(e.getReason(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get state
|
||||||
|
VehicleStateContainer vehicleState = null;
|
||||||
|
try {
|
||||||
|
vehicleState = myBMWProxy.requestVehicleState(vehicleBase.getVin(),
|
||||||
|
vehicleBase.getAttributes().getBrand());
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
fail(e.getReason(), e);
|
||||||
|
}
|
||||||
|
assertNotNull(vehicleState);
|
||||||
|
|
||||||
|
// get charge statistics -> only successful for electric vehicles
|
||||||
|
ChargingStatisticsContainer chargeStatisticsContainer = null;
|
||||||
|
try {
|
||||||
|
chargeStatisticsContainer = myBMWProxy.requestChargeStatistics(vehicleBase.getVin(),
|
||||||
|
vehicleBase.getAttributes().getBrand());
|
||||||
|
assertNotNull(chargeStatisticsContainer);
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
logger.trace("error: {}", e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ChargingSessionsContainer chargeSessionsContainer = null;
|
||||||
|
try {
|
||||||
|
chargeSessionsContainer = myBMWProxy.requestChargeSessions(vehicleBase.getVin(),
|
||||||
|
vehicleBase.getAttributes().getBrand());
|
||||||
|
assertNotNull(chargeSessionsContainer);
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
logger.trace("error: {}", e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutionStatusContainer remoteExecutionResponse = null;
|
||||||
|
try {
|
||||||
|
remoteExecutionResponse = myBMWProxy.executeRemoteServiceCall(vehicleBase.getVin(),
|
||||||
|
vehicleBase.getAttributes().getBrand(), RemoteService.LIGHT_FLASH);
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
fail(e.getReason(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNotNull(remoteExecutionResponse);
|
||||||
|
logger.warn("{}", remoteExecutionResponse.toString());
|
||||||
|
|
||||||
|
ExecutionStatusContainer remoteExecutionStatusResponse = null;
|
||||||
|
try {
|
||||||
|
remoteExecutionStatusResponse = myBMWProxy.executeRemoteServiceStatusCall(
|
||||||
|
vehicleBase.getAttributes().getBrand(), remoteExecutionResponse.getEventId());
|
||||||
|
|
||||||
|
assertNotNull(remoteExecutionStatusResponse);
|
||||||
|
logger.warn("{}", remoteExecutionStatusResponse.toString());
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
while (!ExecutionState.EXECUTED.toString().equals(remoteExecutionStatusResponse.getEventStatus())
|
||||||
|
&& counter++ < 10) {
|
||||||
|
remoteExecutionStatusResponse = myBMWProxy.executeRemoteServiceStatusCall(
|
||||||
|
vehicleBase.getAttributes().getBrand(), remoteExecutionResponse.getEventId());
|
||||||
|
logger.warn("{}", remoteExecutionStatusResponse.toString());
|
||||||
|
|
||||||
|
Thread.sleep(5000);
|
||||||
|
}
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
fail(e.getReason(), e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetImages() {
|
||||||
|
MyBMWProxy myBMWProxy = initializeProxy();
|
||||||
|
|
||||||
|
ImageProperties imageProperties = new ImageProperties();
|
||||||
|
|
||||||
|
try {
|
||||||
|
imageProperties.viewport = "VehicleStatus";
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage("please_set_here_your_vin", "bmw", imageProperties);
|
||||||
|
Files.write(new File("./" + imageProperties.viewport + ".jpg").toPath(), bmwImage);
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException | IOException e) {
|
||||||
|
logger.error("error retrieving image", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
imageProperties.viewport = "SideViewLeft";
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage("please_set_here_your_vin", "bmw", imageProperties);
|
||||||
|
Files.write(new File("./" + imageProperties.viewport + ".jpg").toPath(), bmwImage);
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException | IOException e) {
|
||||||
|
logger.error("error retrieving image", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
imageProperties.viewport = "AngleSideViewForty";
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage("please_set_here_your_vin", "bmw", imageProperties);
|
||||||
|
Files.write(new File("./" + imageProperties.viewport + ".jpg").toPath(), bmwImage);
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException | IOException e) {
|
||||||
|
logger.error("error retrieving image", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
imageProperties.viewport = "FrontView";
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage("please_set_here_your_vin", "bmw", imageProperties);
|
||||||
|
Files.write(new File("./" + imageProperties.viewport + ".jpg").toPath(), bmwImage);
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException | IOException e) {
|
||||||
|
logger.error("error retrieving image", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
imageProperties.viewport = "FrontLeft";
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage("please_set_here_your_vin", "bmw", imageProperties);
|
||||||
|
Files.write(new File("./" + imageProperties.viewport + ".jpg").toPath(), bmwImage);
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException | IOException e) {
|
||||||
|
logger.error("error retrieving image", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
imageProperties.viewport = "FrontRight";
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage("please_set_here_your_vin", "bmw", imageProperties);
|
||||||
|
Files.write(new File("./" + imageProperties.viewport + ".jpg").toPath(), bmwImage);
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException | IOException e) {
|
||||||
|
logger.error("error retrieving image", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
imageProperties.viewport = "RearView";
|
||||||
|
byte[] bmwImage = myBMWProxy.requestImage("please_set_here_your_vin", "bmw", imageProperties);
|
||||||
|
Files.write(new File("./" + imageProperties.viewport + ".jpg").toPath(), bmwImage);
|
||||||
|
assertNotNull(bmwImage);
|
||||||
|
} catch (NetworkException | IOException e) {
|
||||||
|
logger.error("error retrieving image", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
public void testGetVehicles() {
|
||||||
|
MyBMWProxy myBMWProxy = initializeProxy();
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<Vehicle> vehicles = myBMWProxy.requestVehicles();
|
||||||
|
|
||||||
|
logger.warn(ResponseContentAnonymizer.anonymizeResponseContent(new Gson().toJson(vehicles)));
|
||||||
|
assertNotNull(vehicles);
|
||||||
|
assertEquals(2, vehicles.size());
|
||||||
|
} catch (NetworkException e) {
|
||||||
|
fail(e.getReason(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Martin Grassl - initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
class MyHttpClientFactory implements HttpClientFactory {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(MyHttpClientFactory.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpClient createHttpClient(String consumerName) {
|
||||||
|
// Instantiate and configure the SslContextFactory
|
||||||
|
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
|
||||||
|
|
||||||
|
// Instantiate HttpClient with the SslContextFactory
|
||||||
|
HttpClient httpClient = new HttpClient(sslContextFactory);
|
||||||
|
|
||||||
|
// Configure HttpClient, for example:
|
||||||
|
httpClient.setFollowRedirects(false);
|
||||||
|
|
||||||
|
// Start HttpClient
|
||||||
|
try {
|
||||||
|
httpClient.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpClient getCommonHttpClient() {
|
||||||
|
return createHttpClient("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HTTP2Client createHttp2Client(String arg0) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'createHttp2Client'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HTTP2Client createHttp2Client(String arg0, @Nullable SslContextFactory arg1) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'createHttp2Client'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpClient createHttpClient(String arg0, @Nullable SslContextFactory arg1) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'createHttpClient'");
|
||||||
|
}
|
||||||
|
}
|
@ -30,19 +30,21 @@ import org.openhab.binding.mybmw.internal.MyBMWBridgeConfiguration;
|
|||||||
public class MyBMWConfigurationCheckerTest {
|
public class MyBMWConfigurationCheckerTest {
|
||||||
@Test
|
@Test
|
||||||
void testCheckConfiguration() {
|
void testCheckConfiguration() {
|
||||||
MyBMWBridgeConfiguration cdc = new MyBMWBridgeConfiguration();
|
MyBMWBridgeConfiguration myBMWBridgeConfiguration = new MyBMWBridgeConfiguration();
|
||||||
assertFalse(MyBMWConfigurationChecker.checkConfiguration(cdc));
|
assertFalse(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
cdc.userName = "a";
|
myBMWBridgeConfiguration.setUserName("a");
|
||||||
assertFalse(MyBMWConfigurationChecker.checkConfiguration(cdc));
|
assertFalse(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
cdc.password = "b";
|
myBMWBridgeConfiguration.setPassword("b");
|
||||||
assertFalse(MyBMWConfigurationChecker.checkConfiguration(cdc));
|
assertFalse(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
cdc.region = "c";
|
myBMWBridgeConfiguration.setHcaptchatoken("d");
|
||||||
assertFalse(MyBMWConfigurationChecker.checkConfiguration(cdc));
|
assertFalse(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
cdc.region = BimmerConstants.REGION_NORTH_AMERICA;
|
myBMWBridgeConfiguration.setRegion("c");
|
||||||
assertTrue(MyBMWConfigurationChecker.checkConfiguration(cdc));
|
assertFalse(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
cdc.region = BimmerConstants.REGION_ROW;
|
myBMWBridgeConfiguration.setRegion(BimmerConstants.REGION_NORTH_AMERICA);
|
||||||
assertTrue(MyBMWConfigurationChecker.checkConfiguration(cdc));
|
assertTrue(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
cdc.region = BimmerConstants.REGION_CHINA;
|
myBMWBridgeConfiguration.setRegion(BimmerConstants.REGION_ROW);
|
||||||
assertTrue(MyBMWConfigurationChecker.checkConfiguration(cdc));
|
assertTrue(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
|
myBMWBridgeConfiguration.setRegion(BimmerConstants.REGION_CHINA);
|
||||||
|
assertTrue(MyBMWConfigurationChecker.checkInitialConfiguration(myBMWBridgeConfiguration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user