Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[boschindego] Implement OAuth2 authorization #14745

Merged
merged 7 commits into from
Apr 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 46 additions & 23 deletions bundles/org.openhab.binding.boschindego/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,37 @@ This is the Binding for Bosch Indego Connect lawn mowers.
Thank´s to zazaz-de who found out how the API works.
His [Java Library](https://github.com/zazaz-de/iot-device-bosch-indego-controller) made this Binding possible.

## Discovery

When the bridge is authorized, the binding can automatically discover Indego mowers connected to the SingleKey ID account.

## Thing Configuration

Currently the binding supports _**indego**_ mowers as a thing type with these configuration parameters:
### `account` Bridge Configuration

There are no parameters for the bridge.
However, the bridge is used for managing the [SingleKey ID](https://singlekey-id.com/) digital identity.

#### Authorization

To authorize, please follow these steps:

- In your browser, go to the [Bosch Indego login page](https://prodindego.b2clogin.com/prodindego.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/authorize?redirect_uri=com.bosch.indegoconnect://login&client_id=65bb8c9d-1070-4fb4-aa95-853618acc876&response_type=code&scope=openid%20offline_access%20https://prodindego.onmicrosoft.com/indego-mobile-api/Indego.Mower.User).
- Select "Bosch ID", enter your e-mail address and password and click "Log-in".
- In your browser, open Developer Tools.
- With developer tools showing on the right, go to [Bosch Indego login page](https://prodindego.b2clogin.com/prodindego.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/authorize?redirect_uri=com.bosch.indegoconnect://login&client_id=65bb8c9d-1070-4fb4-aa95-853618acc876&response_type=code&scope=openid%20offline_access%20https://prodindego.onmicrosoft.com/indego-mobile-api/Indego.Mower.User) again.
- "Please wait..." should now be displayed.
- Find the `authresp` and copy the code: `com.bosch.indegoconnect://login/?code=<copy this>`
- Use the openHAB console to authorize with this code: `openhab:boschindego authorize <paste code>`

### `indego` Thing Configuration

| Parameter | Description | Default |
|--------------------|-------------------------------------------------------------------|---------|
| username | Username for the Bosch Indego account | |
| password | Password for the Bosch Indego account | |
| refresh | The number of seconds between refreshing device state when idle | 180 |
| stateActiveRefresh | The number of seconds between refreshing device state when active | 30 |
| cuttingTimeRefresh | The number of minutes between refreshing last/next cutting time | 60 |
| Parameter | Description | Default | Required |
|--------------------|-------------------------------------------------------------------|---------|----------|
| serialNumber | The serial number of the connected Indego mower | | yes |
| refresh | The number of seconds between refreshing device state when idle | 180 | no |
| stateActiveRefresh | The number of seconds between refreshing device state when active | 30 | no |
| cuttingTimeRefresh | The number of minutes between refreshing last/next cutting time | 60 | no |

## Channels

Expand Down Expand Up @@ -80,26 +100,29 @@ Currently the binding supports _**indego**_ mowers as a thing type with these
### `indego.things` File

```java
boschindego:indego:lawnmower [username="mail@example.com", password="idontneedtocutthelawnagain", refresh=120]
Bridge boschindego:account:singlekey {
Things:
Thing indego lawnmower [serialNumber="1234567890", refresh=120]
}
```

### `indego.items` File

```java
Number Indego_State { channel="boschindego:indego:lawnmower:state" }
Number Indego_ErrorCode { channel="boschindego:indego:lawnmower:errorcode" }
Number Indego_StateCode { channel="boschindego:indego:lawnmower:statecode" }
String Indego_TextualState { channel="boschindego:indego:lawnmower:textualstate" }
Number Indego_Ready { channel="boschindego:indego:lawnmower:ready" }
Dimmer Indego_Mowed { channel="boschindego:indego:lawnmower:mowed" }
DateTime Indego_LastCutting { channel="boschindego:indego:lawnmower:lastCutting" }
DateTime Indego_NextCutting { channel="boschindego:indego:lawnmower:nextCutting" }
Number:ElectricPotential Indego_BatteryVoltage { channel="boschindego:indego:lawnmower:batteryVoltage" }
Number Indego_BatteryLevel { channel="boschindego:indego:lawnmower:batteryLevel" }
Switch Indego_LowBattery { channel="boschindego:indego:lawnmower:lowBattery" }
Number:Temperature Indego_BatteryTemperature { channel="boschindego:indego:lawnmower:batteryTemperature" }
Number:Area Indego_GardenSize { channel="boschindego:indego:lawnmower:gardenSize" }
Image Indego_GardenMap { channel="boschindego:indego:lawnmower:gardenMap" }
Number Indego_State { channel="boschindego:indego:singlekey:lawnmower:state" }
Number Indego_ErrorCode { channel="boschindego:indego:singlekey:lawnmower:errorcode" }
Number Indego_StateCode { channel="boschindego:indego:singlekey:lawnmower:statecode" }
String Indego_TextualState { channel="boschindego:indego:singlekey:lawnmower:textualstate" }
Number Indego_Ready { channel="boschindego:indego:singlekey:lawnmower:ready" }
Dimmer Indego_Mowed { channel="boschindego:indego:singlekey:lawnmower:mowed" }
DateTime Indego_LastCutting { channel="boschindego:indego:singlekey:lawnmower:lastCutting" }
DateTime Indego_NextCutting { channel="boschindego:indego:singlekey:lawnmower:nextCutting" }
Number:ElectricPotential Indego_BatteryVoltage { channel="boschindego:indego:singlekey:lawnmower:batteryVoltage" }
Number Indego_BatteryLevel { channel="boschindego:indego:singlekey:lawnmower:batteryLevel" }
Switch Indego_LowBattery { channel="boschindego:indego:singlekey:lawnmower:lowBattery" }
Number:Temperature Indego_BatteryTemperature { channel="boschindego:indego:singlekey:lawnmower:batteryTemperature" }
Number:Area Indego_GardenSize { channel="boschindego:indego:singlekey:lawnmower:gardenSize" }
Image Indego_GardenMap { channel="boschindego:indego:singlekey:lawnmower:gardenMap" }
```

### `indego.sitemap` File
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class BoschIndegoBindingConstants {
public static final String BINDING_ID = "boschindego";

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
public static final ThingTypeUID THING_TYPE_INDEGO = new ThingTypeUID(BINDING_ID, "indego");

// List of all Channel ids
Expand All @@ -47,5 +48,13 @@ public class BoschIndegoBindingConstants {
public static final String GARDEN_SIZE = "gardenSize";
public static final String GARDEN_MAP = "gardenMap";

public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_INDEGO);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT, THING_TYPE_INDEGO);

// Bosch SingleKey ID OAuth2
private static final String BSK_BASE_URI = "https://prodindego.b2clogin.com/prodindego.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/";
public static final String BSK_CLIENT_ID = "65bb8c9d-1070-4fb4-aa95-853618acc876";
public static final String BSK_AUTH_URI = BSK_BASE_URI + "authorize";
public static final String BSK_TOKEN_URI = BSK_BASE_URI + "token";
public static final String BSK_REDIRECT_URI = "com.bosch.indegoconnect://login";
public static final String BSK_SCOPE = "openid offline_access https://prodindego.onmicrosoft.com/indego-mobile-api/Indego.Mower.User";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
*/
package org.openhab.binding.boschindego.internal;

import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.THING_TYPE_INDEGO;
import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.*;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.boschindego.internal.handler.BoschAccountHandler;
import org.openhab.binding.boschindego.internal.handler.BoschIndegoHandler;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
Expand All @@ -37,21 +40,25 @@
* handlers.
*
* @author Jonas Fleck - Initial contribution
* @author Jacob Laursen - Replaced authorization by OAuth2
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.boschindego")
public class BoschIndegoHandlerFactory extends BaseThingHandlerFactory {

private final HttpClient httpClient;
private final OAuthFactory oAuthFactory;
private final BoschIndegoTranslationProvider translationProvider;
private final TimeZoneProvider timeZoneProvider;

@Activate
public BoschIndegoHandlerFactory(@Reference HttpClientFactory httpClientFactory,
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider,
final @Reference TimeZoneProvider timeZoneProvider, ComponentContext componentContext) {
final @Reference OAuthFactory oAuthFactory, final @Reference TranslationProvider i18nProvider,
final @Reference LocaleProvider localeProvider, final @Reference TimeZoneProvider timeZoneProvider,
ComponentContext componentContext) {
super.activate(componentContext);
this.httpClient = httpClientFactory.getCommonHttpClient();
this.oAuthFactory = oAuthFactory;
this.translationProvider = new BoschIndegoTranslationProvider(i18nProvider, localeProvider);
this.timeZoneProvider = timeZoneProvider;
}
Expand All @@ -65,7 +72,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (THING_TYPE_INDEGO.equals(thingTypeUID)) {
if (THING_TYPE_ACCOUNT.equals(thingTypeUID)) {
return new BoschAccountHandler((Bridge) thing, httpClient, oAuthFactory);
} else if (THING_TYPE_INDEGO.equals(thingTypeUID)) {
return new BoschIndegoHandler(thing, httpClient, translationProvider, timeZoneProvider);
}

Expand Down
Loading