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] Add device properties #14829

Merged
merged 3 commits into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public class BoschIndegoBindingConstants {
public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
public static final ThingTypeUID THING_TYPE_INDEGO = new ThingTypeUID(BINDING_ID, "indego");

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

// List of all Channel ids
public static final String STATE = "state";
public static final String TEXTUAL_STATE = "textualstate";
Expand All @@ -48,7 +50,11 @@ 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_ACCOUNT, THING_TYPE_INDEGO);
// Device properties
public static final String PROPERTY_BARE_TOOL_NUMBER = "bareToolNumber";
public static final String PROPERTY_SERVICE_COUNTER = "serviceCounter";
public static final String PROPERTY_NEEDS_SERVICE = "needsService";
public static final String PROPERTY_RENEW_DATE = "renewDate";

// 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/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.*;

import java.io.IOException;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
Expand All @@ -31,8 +32,10 @@
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.boschindego.internal.dto.response.DevicePropertiesResponse;
import org.openhab.binding.boschindego.internal.dto.response.ErrorResponse;
import org.openhab.binding.boschindego.internal.dto.response.Mower;
import org.openhab.binding.boschindego.internal.dto.serialization.InstantDeserializer;
import org.openhab.binding.boschindego.internal.exceptions.IndegoAuthenticationException;
import org.openhab.binding.boschindego.internal.exceptions.IndegoException;
import org.openhab.binding.boschindego.internal.exceptions.IndegoInvalidCommandException;
Expand All @@ -48,6 +51,7 @@
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;

/**
Expand All @@ -62,11 +66,10 @@ public class IndegoController {

private static final String BASE_URL = "https://api.indego-cloud.iot.bosch-si.com/api/v1/";
private static final String CONTENT_TYPE_HEADER = "application/json";

private static final String BEARER = "Bearer ";

private final Logger logger = LoggerFactory.getLogger(IndegoController.class);
private final Gson gson = new Gson();
private final Gson gson = new GsonBuilder().registerTypeAdapter(Instant.class, new InstantDeserializer()).create();
private final HttpClient httpClient;
private final OAuthClientService oAuthClientService;
private final String userAgent;
Expand Down Expand Up @@ -96,6 +99,19 @@ public Collection<String> getSerialNumbers() throws IndegoAuthenticationExceptio
return Arrays.stream(mowers).map(m -> m.serialNumber).toList();
}

/**
* Queries the serial number and device service properties from the server.
*
* @param serialNumber the serial number of the device
* @return the device serial number and properties
* @throws IndegoAuthenticationException if request was rejected as unauthorized
* @throws IndegoException if any communication or parsing error occurred
*/
public DevicePropertiesResponse getDeviceProperties(String serialNumber)
throws IndegoAuthenticationException, IndegoException {
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/", DevicePropertiesResponse.class);
}

private String getAuthorizationUrl() {
try {
return oAuthClientService.getAuthorizationUrl(BSK_REDIRECT_URI, BSK_SCOPE, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.openhab.binding.boschindego.internal.dto.PredictiveStatus;
import org.openhab.binding.boschindego.internal.dto.request.SetStateRequest;
import org.openhab.binding.boschindego.internal.dto.response.DeviceCalendarResponse;
import org.openhab.binding.boschindego.internal.dto.response.DevicePropertiesResponse;
import org.openhab.binding.boschindego.internal.dto.response.DeviceStateResponse;
import org.openhab.binding.boschindego.internal.dto.response.LocationWeatherResponse;
import org.openhab.binding.boschindego.internal.dto.response.OperatingDataResponse;
Expand Down Expand Up @@ -71,6 +72,17 @@ public IndegoDeviceController(HttpClient httpClient, OAuthClientService oAuthCli
this.serialNumber = serialNumber;
}

/**
* Queries the serial number and device service properties from the server.
*
* @return the device serial number and properties
* @throws IndegoAuthenticationException if request was rejected as unauthorized
* @throws IndegoException if any communication or parsing error occurred
*/
public DevicePropertiesResponse getDeviceProperties() throws IndegoAuthenticationException, IndegoException {
return super.getDeviceProperties(serialNumber);
}

/**
* Queries the device state from the server.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2023 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.boschindego.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* Translates from tool number to model names.
*
* @author Jacob Laursen - Initial contribution
*/
@NonNullByDefault
public class IndegoTypeDatabase {

/**
* Return tool name from tool type number.
*
* @see https://www.boschtoolservice.com/gb/en/boschdiy/spareparts/search-results?q=Indego
*
* @param toolTypeNumber condensed tool type number, e.g. "3600HA2200" rather than "3 600 HA2 200".
* @return tool type name
*/
public static String nameFromTypeNumber(String toolTypeNumber) {
String name = switch (toolTypeNumber) {
case "3600HA2103" -> "800";
case "3600HA2104" -> "850";
case "3600HA2200", "3600HA2201" -> "1300";
case "3600HA2300" -> "1000 Connect";
case "3600HA2301" -> "1200 Connect";
case "3600HA2302" -> "1100 Connect";
case "3600HA2303" -> "13C";
case "3600HA2304" -> "10C";
case "3600HB0000" -> "350";
case "3600HB0001" -> "400";
case "3600HB0004" -> "XS 300";
case "3600HB0006" -> "350";
case "3600HB0007" -> "400";
case "3600HB0100" -> "350 Connect";
case "3600HB0101" -> "400 Connect";
case "3600HB0102" -> "S+ 350";
case "3600HB0103" -> "S+ 400";
case "3600HB0105" -> "S+ 350";
case "3600HB0106" -> "S+ 400";
case "3600HB0201" -> "M";
case "3600HB0202" -> "S 500";
case "3600HB0203" -> "M 700";
case "3600HB0301" -> "M+";
case "3600HB0302" -> "S+ 500";
case "3600HB0303" -> "M+ 700";
default -> "";
};

return (name.isEmpty() ? "Indego" : "Indego " + name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.boschindego.internal.IndegoTypeDatabase;
import org.openhab.binding.boschindego.internal.dto.response.DevicePropertiesResponse;
import org.openhab.binding.boschindego.internal.exceptions.IndegoException;
import org.openhab.binding.boschindego.internal.handler.BoschAccountHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
Expand Down Expand Up @@ -71,15 +73,15 @@ public Set<ThingTypeUID> getSupportedThingTypes() {
@Override
public void startScan() {
try {
Collection<String> serialNumbers = accountHandler.getSerialNumbers();
Collection<DevicePropertiesResponse> devices = accountHandler.getDevices();

ThingUID bridgeUID = accountHandler.getThing().getUID();
for (String serialNumber : serialNumbers) {
ThingUID thingUID = new ThingUID(THING_TYPE_INDEGO, bridgeUID, serialNumber);
for (DevicePropertiesResponse device : devices) {
ThingUID thingUID = new ThingUID(THING_TYPE_INDEGO, bridgeUID, device.serialNumber);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withProperty(Thing.PROPERTY_SERIAL_NUMBER, serialNumber).withBridge(bridgeUID)
.withProperty(Thing.PROPERTY_SERIAL_NUMBER, device.serialNumber).withBridge(bridgeUID)
.withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER)
.withLabel("Indego (" + serialNumber + ")").build();
.withLabel(IndegoTypeDatabase.nameFromTypeNumber(device.bareToolNumber)).build();

thingDiscovered(discoveryResult);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2023 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.boschindego.internal.dto.response;

import java.time.Instant;

import com.google.gson.annotations.SerializedName;

/**
* Response for serial number and other device service properties.
*
* @author Jacob Laursen - Initial contribution
*/
public class DevicePropertiesResponse {

@SerializedName("alm_sn")
public String serialNumber = "";

@SerializedName("service_counter")
public int serviceCounter;

@SerializedName("needs_service")
public boolean needsService;

/**
* Mode: manual, smart
*/
@SerializedName("alm_mode")
public String mode;

@SerializedName("bareToolnumber")
public String bareToolNumber;

@SerializedName("alm_firmware_version")
public String firmwareVersion;

@SerializedName("renew_date")
public Instant renewDate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2023 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.boschindego.internal.dto.serialization;

import java.lang.reflect.Type;
import java.time.Instant;
import java.time.format.DateTimeParseException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

/**
* The {@link InstantDeserializer} converts a formatted UTC string to {@link Instant}.
*
* @author Jacob Laursen - Initial contribution
*/
@NonNullByDefault
public class InstantDeserializer implements JsonDeserializer<Instant> {

@Override
public @Nullable Instant deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2)
throws JsonParseException {
try {
return Instant.parse(element.getAsString());
} catch (DateTimeParseException e) {
throw new JsonParseException("Could not parse as Instant: " + element.getAsString(), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.boschindego.internal.IndegoController;
import org.openhab.binding.boschindego.internal.discovery.IndegoDiscoveryService;
import org.openhab.binding.boschindego.internal.dto.response.DevicePropertiesResponse;
import org.openhab.binding.boschindego.internal.exceptions.IndegoAuthenticationException;
import org.openhab.binding.boschindego.internal.exceptions.IndegoException;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
Expand Down Expand Up @@ -119,7 +121,18 @@ public OAuthClientService getOAuthClientService() {
return oAuthClientService;
}

public Collection<String> getSerialNumbers() throws IndegoException {
return controller.getSerialNumbers();
public Collection<DevicePropertiesResponse> getDevices() throws IndegoException {
Collection<String> serialNumbers = controller.getSerialNumbers();
List<DevicePropertiesResponse> devices = new ArrayList<DevicePropertiesResponse>(serialNumbers.size());

for (String serialNumber : serialNumbers) {
DevicePropertiesResponse properties = controller.getDeviceProperties(serialNumber);
if (properties.serialNumber == null) {
properties.serialNumber = serialNumber;
}
devices.add(properties);
}

return devices;
}
}
Loading