-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
- Loading branch information
Showing
11 changed files
with
593 additions
and
317 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
291 changes: 26 additions & 265 deletions
291
....boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoController.java
Large diffs are not rendered by default.
Oops, something went wrong.
281 changes: 281 additions & 0 deletions
281
...indego/src/main/java/org/openhab/binding/boschindego/internal/IndegoDeviceController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
/** | ||
* 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 java.time.Duration; | ||
import java.time.Instant; | ||
|
||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
import org.eclipse.jdt.annotation.Nullable; | ||
import org.eclipse.jetty.client.HttpClient; | ||
import org.eclipse.jetty.http.HttpStatus; | ||
import org.openhab.binding.boschindego.internal.dto.DeviceCommand; | ||
import org.openhab.binding.boschindego.internal.dto.PredictiveAdjustment; | ||
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.DeviceStateResponse; | ||
import org.openhab.binding.boschindego.internal.dto.response.LocationWeatherResponse; | ||
import org.openhab.binding.boschindego.internal.dto.response.OperatingDataResponse; | ||
import org.openhab.binding.boschindego.internal.dto.response.PredictiveLastCuttingResponse; | ||
import org.openhab.binding.boschindego.internal.dto.response.PredictiveNextCuttingResponse; | ||
import org.openhab.binding.boschindego.internal.exceptions.IndegoAuthenticationException; | ||
import org.openhab.binding.boschindego.internal.exceptions.IndegoException; | ||
import org.openhab.binding.boschindego.internal.exceptions.IndegoInvalidCommandException; | ||
import org.openhab.binding.boschindego.internal.exceptions.IndegoInvalidResponseException; | ||
import org.openhab.binding.boschindego.internal.exceptions.IndegoTimeoutException; | ||
import org.openhab.core.auth.client.oauth2.OAuthClientService; | ||
import org.openhab.core.library.types.RawType; | ||
|
||
/** | ||
* Controller for communicating with a Bosch Indego device through Bosch services. | ||
* This class provides methods for retrieving state information as well as controlling | ||
* the device. | ||
* | ||
* The implementation is based on zazaz-de/iot-device-bosch-indego-controller, but | ||
* rewritten from scratch to use Jetty HTTP client for HTTP communication and GSON for | ||
* JSON parsing. Thanks to Oliver Schünemann for providing the original implementation. | ||
* | ||
* @author Jacob Laursen - Initial contribution | ||
*/ | ||
@NonNullByDefault | ||
public class IndegoDeviceController extends IndegoController { | ||
|
||
private String serialNumber = ""; | ||
|
||
/** | ||
* Initialize the controller instance. | ||
* | ||
* @param httpClient the HttpClient for communicating with the service | ||
* @param oAuthClientService the OAuthClientService for authorization | ||
* @param serialNumber the serial number of the device instance | ||
*/ | ||
public IndegoDeviceController(HttpClient httpClient, OAuthClientService oAuthClientService, String serialNumber) { | ||
super(httpClient, oAuthClientService); | ||
if (serialNumber.isBlank()) { | ||
throw new IllegalArgumentException("Serial number must be provided"); | ||
} | ||
this.serialNumber = serialNumber; | ||
} | ||
|
||
/** | ||
* Queries the device state from the server. | ||
* | ||
* @return the device state | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public DeviceStateResponse getState() throws IndegoAuthenticationException, IndegoException { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/state", DeviceStateResponse.class); | ||
} | ||
|
||
/** | ||
* Queries the device state from the server. This overload will return when the state | ||
* has changed, or the timeout has been reached. | ||
* | ||
* @param timeout Maximum time to wait for response | ||
* @return the device state | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public DeviceStateResponse getState(Duration timeout) throws IndegoAuthenticationException, IndegoException { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/state?longpoll=true&timeout=" + timeout.getSeconds(), | ||
DeviceStateResponse.class); | ||
} | ||
|
||
/** | ||
* Queries the device operating data from the server. | ||
* Server will request this directly from the device, so operation might be slow. | ||
* | ||
* @return the device state | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoTimeoutException if device cannot be reached (gateway timeout error) | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public OperatingDataResponse getOperatingData() | ||
throws IndegoAuthenticationException, IndegoTimeoutException, IndegoException { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/operatingData", OperatingDataResponse.class); | ||
} | ||
|
||
/** | ||
* Queries the map generated by the device from the server. | ||
* | ||
* @return the garden map | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public RawType getMap() throws IndegoAuthenticationException, IndegoException { | ||
return getRawRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/map"); | ||
} | ||
|
||
/** | ||
* Queries the calendar. | ||
* | ||
* @return the calendar | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public DeviceCalendarResponse getCalendar() throws IndegoAuthenticationException, IndegoException { | ||
DeviceCalendarResponse calendar = getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/calendar", | ||
DeviceCalendarResponse.class); | ||
return calendar; | ||
} | ||
|
||
/** | ||
* Sends a command to the Indego device. | ||
* | ||
* @param command the control command to send to the device | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoInvalidCommandException if the command was not processed correctly | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public void sendCommand(DeviceCommand command) | ||
throws IndegoAuthenticationException, IndegoInvalidCommandException, IndegoException { | ||
SetStateRequest request = new SetStateRequest(); | ||
request.state = command.getActionCode(); | ||
putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/state", request); | ||
} | ||
|
||
/** | ||
* Queries the predictive weather forecast. | ||
* | ||
* @return the weather forecast DTO | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public LocationWeatherResponse getWeather() throws IndegoAuthenticationException, IndegoException { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/weather", LocationWeatherResponse.class); | ||
} | ||
|
||
/** | ||
* Queries the predictive adjustment. | ||
* | ||
* @return the predictive adjustment | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public int getPredictiveAdjustment() throws IndegoAuthenticationException, IndegoException { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/useradjustment", | ||
PredictiveAdjustment.class).adjustment; | ||
} | ||
|
||
/** | ||
* Sets the predictive adjustment. | ||
* | ||
* @param adjust the predictive adjustment | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public void setPredictiveAdjustment(final int adjust) throws IndegoAuthenticationException, IndegoException { | ||
final PredictiveAdjustment adjustment = new PredictiveAdjustment(); | ||
adjustment.adjustment = adjust; | ||
putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/useradjustment", adjustment); | ||
} | ||
|
||
/** | ||
* Queries predictive moving. | ||
* | ||
* @return predictive moving | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public boolean getPredictiveMoving() throws IndegoAuthenticationException, IndegoException { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive", PredictiveStatus.class).enabled; | ||
} | ||
|
||
/** | ||
* Sets predictive moving. | ||
* | ||
* @param enable | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public void setPredictiveMoving(final boolean enable) throws IndegoAuthenticationException, IndegoException { | ||
final PredictiveStatus status = new PredictiveStatus(); | ||
status.enabled = enable; | ||
putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive", status); | ||
} | ||
|
||
/** | ||
* Queries predictive last cutting as {@link Instant}. | ||
* | ||
* @return predictive last cutting | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public @Nullable Instant getPredictiveLastCutting() throws IndegoAuthenticationException, IndegoException { | ||
try { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/lastcutting", | ||
PredictiveLastCuttingResponse.class).getLastCutting(); | ||
} catch (IndegoInvalidResponseException e) { | ||
if (e.getHttpStatusCode() == HttpStatus.NO_CONTENT_204) { | ||
return null; | ||
} | ||
throw e; | ||
} | ||
} | ||
|
||
/** | ||
* Queries predictive next cutting as {@link Instant}. | ||
* | ||
* @return predictive next cutting | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public @Nullable Instant getPredictiveNextCutting() throws IndegoAuthenticationException, IndegoException { | ||
try { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/nextcutting", | ||
PredictiveNextCuttingResponse.class).getNextCutting(); | ||
} catch (IndegoInvalidResponseException e) { | ||
if (e.getHttpStatusCode() == HttpStatus.NO_CONTENT_204) { | ||
return null; | ||
} | ||
throw e; | ||
} | ||
} | ||
|
||
/** | ||
* Queries predictive exclusion time. | ||
* | ||
* @return predictive exclusion time DTO | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public DeviceCalendarResponse getPredictiveExclusionTime() throws IndegoAuthenticationException, IndegoException { | ||
return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/calendar", DeviceCalendarResponse.class); | ||
} | ||
|
||
/** | ||
* Sets predictive exclusion time. | ||
* | ||
* @param calendar calendar DTO | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public void setPredictiveExclusionTime(final DeviceCalendarResponse calendar) | ||
throws IndegoAuthenticationException, IndegoException { | ||
putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/calendar", calendar); | ||
} | ||
|
||
/** | ||
* Request map position updates for the next ({@link count} * {@link interval}) number of seconds. | ||
* | ||
* @param count Number of updates | ||
* @param interval Number of seconds between updates | ||
* @throws IndegoAuthenticationException if request was rejected as unauthorized | ||
* @throws IndegoException if any communication or parsing error occurred | ||
*/ | ||
public void requestPosition(int count, int interval) throws IndegoAuthenticationException, IndegoException { | ||
postRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/requestPosition?count=" + count + "&interval=" + interval); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.