Skip to content

Commit

Permalink
[mybmw] Fix hcaptchatoken issue (openhab#17862) (openhab#17896)
Browse files Browse the repository at this point in the history
* [mybmw] add stop charging command
* [mybmw] fix hcaptcha issue (openhab#17862)

Signed-off-by: Martin Grassl <martin.grassl@digital-filestore.de>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
  • Loading branch information
martingrassl authored and Ciprian Pascu committed Jan 2, 2025
1 parent dcf2a1a commit 1fd1ac5
Show file tree
Hide file tree
Showing 22 changed files with 677 additions and 124 deletions.
13 changes: 7 additions & 6 deletions bundles/org.openhab.binding.mybmw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ Properties will be attached to predefined vehicles if the VIN is matching.

### Bridge Configuration

| Parameter | Type | Description |
|-----------------|---------|--------------------------------------------------------------------|
| userName | text | MyBMW Username |
| password | text | MyBMW Password |
| region | text | Select region in order to connect to the appropriate BMW server. |
| Parameter | Type | Description |
|-----------------|---------|--------------------------------------------------------------------------------------------------------|
| userName | text | MyBMW Username |
| password | text | MyBMW Password |
| 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

Expand Down Expand Up @@ -849,4 +850,4 @@ sitemap BMW label="BMW" {

## 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).
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,79 @@
* The {@link MyBMWBridgeConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Bernd Weymann - Initial contribution
* @author Martin Grassl - renamed
* @author Martin Grassl - renamed and added hcaptchastring
*/
@NonNullByDefault
public class MyBMWBridgeConfiguration {

/**
* Depending on the location the correct server needs to be called
*/
public String region = Constants.EMPTY;
private String region = Constants.EMPTY;

/**
* MyBMW App Username
*/
public String userName = Constants.EMPTY;
private String userName = Constants.EMPTY;

/**
* MyBMW App Password
*/
public String password = Constants.EMPTY;
private String password = Constants.EMPTY;

/**
* 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 + "]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleBase;
Expand Down Expand Up @@ -140,7 +139,7 @@ public void execute(String[] args, Console console) {
String accountPath = path + File.separator + "Account-" + String.valueOf(accountNdx);
handler.getMyBmwProxy().ifPresentOrElse(prox -> {
// get list of vehicles
List<@NonNull VehicleBase> vehicles = null;
List<VehicleBase> vehicles = null;
try {
vehicles = prox.requestVehiclesBase();

Expand Down Expand Up @@ -314,7 +313,8 @@ public boolean complete(String[] args, int cursorArgumentIndex, int cursorPositi
.filter(t -> THING_TYPE_CONNECTED_DRIVE_ACCOUNT.equals(t.getThingTypeUID())
&& args[1].equals(t.getConfiguration().get("userName")))
.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(
vehicles.stream().map(v -> v.getVin()).filter(Objects::nonNull).collect(Collectors.toList()),
false).complete(args, cursorArgumentIndex, cursorPosition, candidates);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.Map;
import java.util.Optional;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mybmw.internal.MyBMWConstants;
import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
Expand Down Expand Up @@ -80,16 +79,21 @@ public void discoverVehicles() {
myBMWProxy = thingHandler.getMyBmwProxy();

try {
Optional<List<@NonNull Vehicle>> vehicleList = myBMWProxy.map(prox -> {
Optional<List<Vehicle>> vehicleList = myBMWProxy.map(prox -> {
try {
return prox.requestVehicles();
} catch (NetworkException e) {
throw new IllegalStateException("vehicles could not be discovered: " + e.getMessage(), e);
}
});
vehicleList.ifPresentOrElse(vehicles -> {
thingHandler.vehicleDiscoverySuccess();
processVehicles(vehicles);
if (vehicles.size() > 0) {
thingHandler.vehicleDiscoverySuccess();
processVehicles(vehicles);
} else {
logger.warn("no vehicle found, maybe because of network error");
thingHandler.vehicleDiscoveryError();
}
}, () -> thingHandler.vehicleDiscoveryError());
} catch (IllegalStateException ex) {
thingHandler.vehicleDiscoveryError();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,27 @@
* The {@link AuthResponse} Data Transfer Object
*
* @author Bernd Weymann - Initial contribution
* @author Martin Grassl - extracted from myBmwProxy
*/
public class AuthResponse {
@SerializedName("access_token")
public String accessToken = Constants.EMPTY;

@SerializedName("refresh_token")
public String refreshToken = Constants.EMPTY;

@SerializedName("token_type")
public String tokenType = Constants.EMPTY;

@SerializedName("gcid")
public String gcid = Constants.EMPTY;

@SerializedName("expires_in")
public int expiresIn = -1;

@Override
public String toString() {
return "Token " + accessToken + " type " + tokenType + " expires in " + expiresIn;
return "AuthResponse [accessToken=" + accessToken + ", refreshToken=" + refreshToken + ", tokenType="
+ tokenType + ", gcid=" + gcid + ", expiresIn=" + expiresIn + "]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class MyBMWBridgeHandler extends BaseBridgeHandler {
private Optional<ScheduledFuture<?>> initializerJob = Optional.empty();
private Optional<VehicleDiscovery> vehicleDiscovery = Optional.empty();
private LocaleProvider localeProvider;
private Optional<MyBMWBridgeConfiguration> bmwBridgeConfiguration = Optional.empty();

public MyBMWBridgeHandler(Bridge bridge, HttpClientFactory hcf, LocaleProvider localeProvider) {
super(bridge);
Expand All @@ -82,11 +83,23 @@ public void handleCommand(ChannelUID channelUID, Command command) {
public void initialize() {
logger.trace("MyBMWBridgeHandler.initialize");
updateStatus(ThingStatus.UNKNOWN);
MyBMWBridgeConfiguration config = getConfigAs(MyBMWBridgeConfiguration.class);
if (config.language.equals(Constants.LANGUAGE_AUTODETECT)) {
config.language = localeProvider.getLocale().getLanguage().toLowerCase();

this.bmwBridgeConfiguration = Optional.of(getConfigAs(MyBMWBridgeConfiguration.class));

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);
} else {
// there is no risk in this functionality as several steps have to happen to get the file proxy working:
Expand All @@ -100,19 +113,26 @@ public void initialize() {
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));
}
}

private synchronized void createMyBmwProxy(MyBMWBridgeConfiguration config, String environment) {
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));
} else {
myBmwProxy = Optional.of(new MyBMWFileProxy(httpClientFactory, config));
}
logger.trace("MyBMWBridgeHandler proxy set");
} else {
myBmwProxy.get().setBridgeConfiguration(config);
logger.trace("MyBMWBridgeHandler update proxy with bridge configuration");
}
}

Expand All @@ -135,10 +155,6 @@ public void vehicleDiscoverySuccess() {
private void discoverVehicles() {
logger.trace("MyBMWBridgeHandler.requestVehicles");

MyBMWBridgeConfiguration config = getConfigAs(MyBMWBridgeConfiguration.class);

myBmwProxy.ifPresent(proxy -> proxy.setBridgeConfiguration(config));

vehicleDiscovery.ifPresent(discovery -> discovery.discoverVehicles());
}

Expand All @@ -148,9 +164,23 @@ public Collection<Class<? extends ThingHandlerService>> getServices() {
return List.of(VehicleDiscovery.class);
}

public Optional<MyBMWProxy> getMyBmwProxy() {
public synchronized Optional<MyBMWProxy> getMyBmwProxy() {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private void getState() {
serviceExecuting.ifPresentOrElse(service -> {
if (counter >= 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());
reset();
// immediately refresh data
Expand All @@ -107,7 +107,7 @@ private void getState() {

private void handleRemoteServiceException(NetworkException e) {
synchronized (this) {
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
ExecutionState.ERROR.name().toLowerCase() + Constants.SPACE + Integer.toString(e.getStatus()));
reset();
}
Expand All @@ -117,12 +117,12 @@ private void handleRemoteExecution(ExecutionStatusContainer executionStatusConta
if (!executionStatusContainer.getEventId().isEmpty()) {
// service initiated - store event id for further MyBMW updates
executingEventId = Optional.of(executionStatusContainer.getEventId());
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
ExecutionState.INITIATED.name().toLowerCase());
} else if (!executionStatusContainer.getEventStatus().isEmpty()) {
// service status updated
synchronized (this) {
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
executionStatusContainer.getEventStatus().toLowerCase());
if (ExecutionState.EXECUTED.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())
|| ExecutionState.ERROR.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())) {
Expand Down
Loading

0 comments on commit 1fd1ac5

Please sign in to comment.