Skip to content

Commit fe624dd

Browse files
authored
[mybmw] Fix hcaptchatoken issue (openhab#17862) (openhab#17896)
* [mybmw] add stop charging command * [mybmw] fix hcaptcha issue (openhab#17862) Signed-off-by: Martin Grassl <[email protected]>
1 parent 7fc3d3b commit fe624dd

22 files changed

+677
-124
lines changed

bundles/org.openhab.binding.mybmw/README.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ Properties will be attached to predefined vehicles if the VIN is matching.
7878

7979
### Bridge Configuration
8080

81-
| Parameter | Type | Description |
82-
|-----------------|---------|--------------------------------------------------------------------|
83-
| userName | text | MyBMW Username |
84-
| password | text | MyBMW Password |
85-
| region | text | Select region in order to connect to the appropriate BMW server. |
81+
| Parameter | Type | Description |
82+
|-----------------|---------|--------------------------------------------------------------------------------------------------------|
83+
| userName | text | MyBMW Username |
84+
| password | text | MyBMW Password |
85+
| hcaptchatoken | text | HCaptcha-Token for initial login (see https://bimmer-connected.readthedocs.io/en/latest/captcha.html) |
86+
| region | text | Select region in order to connect to the appropriate BMW server. |
8687

8788
The region Configuration has 3 different options
8889

@@ -849,4 +850,4 @@ sitemap BMW label="BMW" {
849850

850851
## Credits
851852

852-
This work is based on the project of [Bimmer Connected](https://github.com/bimmerconnected/bimmer_connected).
853+
This work is based on the great work of the project of [Bimmer Connected](https://github.com/bimmerconnected/bimmer_connected).

bundles/org.openhab.binding.mybmw/src/main/java/org/openhab/binding/mybmw/internal/MyBMWBridgeConfiguration.java

+56-5
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,79 @@
1919
* The {@link MyBMWBridgeConfiguration} class contains fields mapping thing configuration parameters.
2020
*
2121
* @author Bernd Weymann - Initial contribution
22-
* @author Martin Grassl - renamed
22+
* @author Martin Grassl - renamed and added hcaptchastring
2323
*/
2424
@NonNullByDefault
2525
public class MyBMWBridgeConfiguration {
2626

2727
/**
2828
* Depending on the location the correct server needs to be called
2929
*/
30-
public String region = Constants.EMPTY;
30+
private String region = Constants.EMPTY;
3131

3232
/**
3333
* MyBMW App Username
3434
*/
35-
public String userName = Constants.EMPTY;
35+
private String userName = Constants.EMPTY;
3636

3737
/**
3838
* MyBMW App Password
3939
*/
40-
public String password = Constants.EMPTY;
40+
private String password = Constants.EMPTY;
4141

4242
/**
4343
* Preferred Locale language
4444
*/
45-
public String language = Constants.LANGUAGE_AUTODETECT;
45+
private String language = Constants.LANGUAGE_AUTODETECT;
46+
47+
/**
48+
* the hCaptcha string
49+
*/
50+
private String hcaptchatoken = Constants.EMPTY;
51+
52+
public String getRegion() {
53+
return region;
54+
}
55+
56+
public void setRegion(String region) {
57+
this.region = region;
58+
}
59+
60+
public String getUserName() {
61+
return userName;
62+
}
63+
64+
public void setUserName(String userName) {
65+
this.userName = userName;
66+
}
67+
68+
public String getPassword() {
69+
return password;
70+
}
71+
72+
public void setPassword(String password) {
73+
this.password = password;
74+
}
75+
76+
public String getLanguage() {
77+
return language;
78+
}
79+
80+
public void setLanguage(String language) {
81+
this.language = language;
82+
}
83+
84+
public String getHcaptchatoken() {
85+
return hcaptchatoken;
86+
}
87+
88+
public void setHcaptchatoken(String hcaptchatoken) {
89+
this.hcaptchatoken = hcaptchatoken;
90+
}
91+
92+
@Override
93+
public String toString() {
94+
return "MyBMWBridgeConfiguration [region=" + region + ", userName=" + userName + ", password=" + password
95+
+ ", language=" + language + ", hcaptchatoken=" + hcaptchatoken + "]";
96+
}
4697
}

bundles/org.openhab.binding.mybmw/src/main/java/org/openhab/binding/mybmw/internal/console/MyBMWCommandExtension.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import java.util.zip.ZipEntry;
3838
import java.util.zip.ZipOutputStream;
3939

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

@@ -314,7 +313,8 @@ public boolean complete(String[] args, int cursorArgumentIndex, int cursorPositi
314313
.filter(t -> THING_TYPE_CONNECTED_DRIVE_ACCOUNT.equals(t.getThingTypeUID())
315314
&& args[1].equals(t.getConfiguration().get("userName")))
316315
.map(t -> t.getHandler()).findAny().get();
317-
List<VehicleBase> vehicles = handler.getMyBmwProxy().get().requestVehiclesBase();
316+
List<VehicleBase> vehicles = handler != null ? handler.getMyBmwProxy().get().requestVehiclesBase()
317+
: List.of();
318318
return new StringsCompleter(
319319
vehicles.stream().map(v -> v.getVin()).filter(Objects::nonNull).collect(Collectors.toList()),
320320
false).complete(args, cursorArgumentIndex, cursorPosition, candidates);

bundles/org.openhab.binding.mybmw/src/main/java/org/openhab/binding/mybmw/internal/discovery/VehicleDiscovery.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import java.util.Map;
1818
import java.util.Optional;
1919

20-
import org.eclipse.jdt.annotation.NonNull;
2120
import org.eclipse.jdt.annotation.NonNullByDefault;
2221
import org.openhab.binding.mybmw.internal.MyBMWConstants;
2322
import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
@@ -80,16 +79,21 @@ public void discoverVehicles() {
8079
myBMWProxy = thingHandler.getMyBmwProxy();
8180

8281
try {
83-
Optional<List<@NonNull Vehicle>> vehicleList = myBMWProxy.map(prox -> {
82+
Optional<List<Vehicle>> vehicleList = myBMWProxy.map(prox -> {
8483
try {
8584
return prox.requestVehicles();
8685
} catch (NetworkException e) {
8786
throw new IllegalStateException("vehicles could not be discovered: " + e.getMessage(), e);
8887
}
8988
});
9089
vehicleList.ifPresentOrElse(vehicles -> {
91-
thingHandler.vehicleDiscoverySuccess();
92-
processVehicles(vehicles);
90+
if (vehicles.size() > 0) {
91+
thingHandler.vehicleDiscoverySuccess();
92+
processVehicles(vehicles);
93+
} else {
94+
logger.warn("no vehicle found, maybe because of network error");
95+
thingHandler.vehicleDiscoveryError();
96+
}
9397
}, () -> thingHandler.vehicleDiscoveryError());
9498
} catch (IllegalStateException ex) {
9599
thingHandler.vehicleDiscoveryError();

bundles/org.openhab.binding.mybmw/src/main/java/org/openhab/binding/mybmw/internal/dto/auth/AuthResponse.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,27 @@
2020
* The {@link AuthResponse} Data Transfer Object
2121
*
2222
* @author Bernd Weymann - Initial contribution
23+
* @author Martin Grassl - extracted from myBmwProxy
2324
*/
2425
public class AuthResponse {
2526
@SerializedName("access_token")
2627
public String accessToken = Constants.EMPTY;
28+
29+
@SerializedName("refresh_token")
30+
public String refreshToken = Constants.EMPTY;
31+
2732
@SerializedName("token_type")
2833
public String tokenType = Constants.EMPTY;
34+
35+
@SerializedName("gcid")
36+
public String gcid = Constants.EMPTY;
37+
2938
@SerializedName("expires_in")
3039
public int expiresIn = -1;
3140

3241
@Override
3342
public String toString() {
34-
return "Token " + accessToken + " type " + tokenType + " expires in " + expiresIn;
43+
return "AuthResponse [accessToken=" + accessToken + ", refreshToken=" + refreshToken + ", tokenType="
44+
+ tokenType + ", gcid=" + gcid + ", expiresIn=" + expiresIn + "]";
3545
}
3646
}

bundles/org.openhab.binding.mybmw/src/main/java/org/openhab/binding/mybmw/internal/handler/MyBMWBridgeHandler.java

+42-12
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class MyBMWBridgeHandler extends BaseBridgeHandler {
6060
private Optional<ScheduledFuture<?>> initializerJob = Optional.empty();
6161
private Optional<VehicleDiscovery> vehicleDiscovery = Optional.empty();
6262
private LocaleProvider localeProvider;
63+
private Optional<MyBMWBridgeConfiguration> bmwBridgeConfiguration = Optional.empty();
6364

6465
public MyBMWBridgeHandler(Bridge bridge, HttpClientFactory hcf, LocaleProvider localeProvider) {
6566
super(bridge);
@@ -82,11 +83,23 @@ public void handleCommand(ChannelUID channelUID, Command command) {
8283
public void initialize() {
8384
logger.trace("MyBMWBridgeHandler.initialize");
8485
updateStatus(ThingStatus.UNKNOWN);
85-
MyBMWBridgeConfiguration config = getConfigAs(MyBMWBridgeConfiguration.class);
86-
if (config.language.equals(Constants.LANGUAGE_AUTODETECT)) {
87-
config.language = localeProvider.getLocale().getLanguage().toLowerCase();
86+
87+
this.bmwBridgeConfiguration = Optional.of(getConfigAs(MyBMWBridgeConfiguration.class));
88+
89+
MyBMWBridgeConfiguration localBridgeConfiguration;
90+
91+
if (bmwBridgeConfiguration.isPresent()) {
92+
localBridgeConfiguration = bmwBridgeConfiguration.get();
93+
} else {
94+
logger.warn("the bridge configuration could not be retrieved");
95+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
96+
return;
8897
}
89-
if (!MyBMWConfigurationChecker.checkConfiguration(config)) {
98+
99+
if (localBridgeConfiguration.getLanguage().equals(Constants.LANGUAGE_AUTODETECT)) {
100+
localBridgeConfiguration.setLanguage(localeProvider.getLocale().getLanguage().toLowerCase());
101+
}
102+
if (!MyBMWConfigurationChecker.checkInitialConfiguration(localBridgeConfiguration)) {
90103
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
91104
} else {
92105
// there is no risk in this functionality as several steps have to happen to get the file proxy working:
@@ -100,19 +113,26 @@ public void initialize() {
100113
environment = "";
101114
}
102115

103-
createMyBmwProxy(config, environment);
116+
// this access has to be synchronized as the vehicleHandler as well as the bridge itself request the
117+
// instance
118+
Optional<MyBMWProxy> localProxy = getMyBmwProxy();
119+
localProxy.ifPresent(proxy -> proxy.setBridgeConfiguration(localBridgeConfiguration));
120+
104121
initializerJob = Optional.of(scheduler.schedule(this::discoverVehicles, 2, TimeUnit.SECONDS));
105122
}
106123
}
107124

108125
private synchronized void createMyBmwProxy(MyBMWBridgeConfiguration config, String environment) {
109126
if (!myBmwProxy.isPresent()) {
110-
if (!(TEST.equals(environment) && TESTUSER.equals(config.userName))) {
127+
if (!(TEST.equals(environment) && TESTUSER.equals(config.getUserName()))) {
111128
myBmwProxy = Optional.of(new MyBMWHttpProxy(httpClientFactory, config));
112129
} else {
113130
myBmwProxy = Optional.of(new MyBMWFileProxy(httpClientFactory, config));
114131
}
115132
logger.trace("MyBMWBridgeHandler proxy set");
133+
} else {
134+
myBmwProxy.get().setBridgeConfiguration(config);
135+
logger.trace("MyBMWBridgeHandler update proxy with bridge configuration");
116136
}
117137
}
118138

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

138-
MyBMWBridgeConfiguration config = getConfigAs(MyBMWBridgeConfiguration.class);
139-
140-
myBmwProxy.ifPresent(proxy -> proxy.setBridgeConfiguration(config));
141-
142158
vehicleDiscovery.ifPresent(discovery -> discovery.discoverVehicles());
143159
}
144160

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

151-
public Optional<MyBMWProxy> getMyBmwProxy() {
167+
public synchronized Optional<MyBMWProxy> getMyBmwProxy() {
152168
logger.trace("MyBMWBridgeHandler.getProxy");
153-
createMyBmwProxy(getConfigAs(MyBMWBridgeConfiguration.class), ENVIRONMENT);
169+
170+
MyBMWBridgeConfiguration localBridgeConfiguration = null;
171+
172+
if (bmwBridgeConfiguration.isPresent()) {
173+
localBridgeConfiguration = bmwBridgeConfiguration.get();
174+
} else {
175+
logger.warn("the bridge configuration could not be retrieved");
176+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
177+
throw new IllegalStateException("bridge handler - configuration is not available");
178+
}
179+
180+
if (!myBmwProxy.isPresent()) {
181+
createMyBmwProxy(localBridgeConfiguration, ENVIRONMENT);
182+
}
183+
154184
return myBmwProxy;
155185
}
156186
}

bundles/org.openhab.binding.mybmw/src/main/java/org/openhab/binding/mybmw/internal/handler/RemoteServiceExecutor.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ private void getState() {
8383
serviceExecuting.ifPresentOrElse(service -> {
8484
if (counter >= GIVEUP_COUNTER) {
8585
logger.warn("Giving up updating state for {} after {} times", service, GIVEUP_COUNTER);
86-
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
86+
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
8787
ExecutionState.TIMEOUT.name().toLowerCase());
8888
reset();
8989
// immediately refresh data
@@ -107,7 +107,7 @@ private void getState() {
107107

108108
private void handleRemoteServiceException(NetworkException e) {
109109
synchronized (this) {
110-
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
110+
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
111111
ExecutionState.ERROR.name().toLowerCase() + Constants.SPACE + Integer.toString(e.getStatus()));
112112
reset();
113113
}
@@ -117,12 +117,12 @@ private void handleRemoteExecution(ExecutionStatusContainer executionStatusConta
117117
if (!executionStatusContainer.getEventId().isEmpty()) {
118118
// service initiated - store event id for further MyBMW updates
119119
executingEventId = Optional.of(executionStatusContainer.getEventId());
120-
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
120+
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
121121
ExecutionState.INITIATED.name().toLowerCase());
122122
} else if (!executionStatusContainer.getEventStatus().isEmpty()) {
123123
// service status updated
124124
synchronized (this) {
125-
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
125+
handler.updateRemoteExecutionStatus(serviceExecuting.orElse(Constants.EMPTY),
126126
executionStatusContainer.getEventStatus().toLowerCase());
127127
if (ExecutionState.EXECUTED.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())
128128
|| ExecutionState.ERROR.name().equalsIgnoreCase(executionStatusContainer.getEventStatus())) {

0 commit comments

Comments
 (0)