]> git.basschouten.com Git - openhab-addons.git/blob
18afdf0e401c17c3463d9612d31451c2a3d199fd
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.boschindego.internal;
14
15 import java.time.Duration;
16 import java.time.Instant;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.eclipse.jetty.client.HttpClient;
21 import org.eclipse.jetty.http.HttpStatus;
22 import org.openhab.binding.boschindego.internal.dto.DeviceCommand;
23 import org.openhab.binding.boschindego.internal.dto.PredictiveAdjustment;
24 import org.openhab.binding.boschindego.internal.dto.PredictiveStatus;
25 import org.openhab.binding.boschindego.internal.dto.request.SetStateRequest;
26 import org.openhab.binding.boschindego.internal.dto.response.DeviceCalendarResponse;
27 import org.openhab.binding.boschindego.internal.dto.response.DevicePropertiesResponse;
28 import org.openhab.binding.boschindego.internal.dto.response.DeviceStateResponse;
29 import org.openhab.binding.boschindego.internal.dto.response.LocationWeatherResponse;
30 import org.openhab.binding.boschindego.internal.dto.response.OperatingDataResponse;
31 import org.openhab.binding.boschindego.internal.dto.response.PredictiveLastCuttingResponse;
32 import org.openhab.binding.boschindego.internal.dto.response.PredictiveNextCuttingResponse;
33 import org.openhab.binding.boschindego.internal.exceptions.IndegoAuthenticationException;
34 import org.openhab.binding.boschindego.internal.exceptions.IndegoException;
35 import org.openhab.binding.boschindego.internal.exceptions.IndegoInvalidCommandException;
36 import org.openhab.binding.boschindego.internal.exceptions.IndegoInvalidResponseException;
37 import org.openhab.binding.boschindego.internal.exceptions.IndegoTimeoutException;
38 import org.openhab.core.library.types.RawType;
39
40 /**
41  * Controller for communicating with a Bosch Indego device through Bosch services.
42  * This class provides methods for retrieving state information as well as controlling
43  * the device.
44  * 
45  * The implementation is based on zazaz-de's iot-device-bosch-indego-controller, but
46  * rewritten from scratch to use Jetty HTTP client for HTTP communication and GSON for
47  * JSON parsing. Thanks to Oliver Schünemann for providing the original implementation.
48  * 
49  * @see <a href=
50  *      "https://github.com/zazaz-de/iot-device-bosch-indego-controller">zazaz-de/iot-device-bosch-indego-controller</a>
51  * 
52  * @author Jacob Laursen - Initial contribution
53  */
54 @NonNullByDefault
55 public class IndegoDeviceController extends IndegoController {
56
57     private String serialNumber;
58
59     /**
60      * Initialize the controller instance.
61      * 
62      * @param httpClient the HttpClient for communicating with the service
63      * @param authorizationProvider the AuthorizationProvider for authenticating with the service
64      * @param serialNumber the serial number of the device instance
65      */
66     public IndegoDeviceController(HttpClient httpClient, AuthorizationProvider authorizationProvider,
67             String serialNumber) {
68         super(httpClient, authorizationProvider);
69         if (serialNumber.isBlank()) {
70             throw new IllegalArgumentException("Serial number must be provided");
71         }
72         this.serialNumber = serialNumber;
73     }
74
75     /**
76      * Queries the serial number and device service properties from the server.
77      *
78      * @return the device serial number and properties
79      * @throws IndegoAuthenticationException if request was rejected as unauthorized
80      * @throws IndegoException if any communication or parsing error occurred
81      */
82     public DevicePropertiesResponse getDeviceProperties() throws IndegoAuthenticationException, IndegoException {
83         return super.getDeviceProperties(serialNumber);
84     }
85
86     /**
87      * Queries the device state from the server.
88      * 
89      * @return the device state
90      * @throws IndegoAuthenticationException if request was rejected as unauthorized
91      * @throws IndegoException if any communication or parsing error occurred
92      */
93     public DeviceStateResponse getState() throws IndegoAuthenticationException, IndegoException {
94         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/state", DeviceStateResponse.class);
95     }
96
97     /**
98      * Queries the device state from the server. This overload will return when the state
99      * has changed, or the timeout has been reached.
100      * 
101      * @param timeout maximum time to wait for response
102      * @return the device state
103      * @throws IndegoAuthenticationException if request was rejected as unauthorized
104      * @throws IndegoException if any communication or parsing error occurred
105      */
106     public DeviceStateResponse getState(Duration timeout) throws IndegoAuthenticationException, IndegoException {
107         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/state?longpoll=true&timeout=" + timeout.getSeconds(),
108                 DeviceStateResponse.class);
109     }
110
111     /**
112      * Queries the device operating data from the server.
113      * Server will request this directly from the device, so operation might be slow.
114      * 
115      * @return the device state
116      * @throws IndegoAuthenticationException if request was rejected as unauthorized
117      * @throws IndegoTimeoutException if device cannot be reached (gateway timeout error)
118      * @throws IndegoException if any communication or parsing error occurred
119      */
120     public OperatingDataResponse getOperatingData()
121             throws IndegoAuthenticationException, IndegoTimeoutException, IndegoException {
122         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/operatingData", OperatingDataResponse.class);
123     }
124
125     /**
126      * Queries the map generated by the device from the server.
127      * 
128      * @return the garden map
129      * @throws IndegoAuthenticationException if request was rejected as unauthorized
130      * @throws IndegoException if any communication or parsing error occurred
131      */
132     public RawType getMap() throws IndegoAuthenticationException, IndegoException {
133         return getRawRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/map");
134     }
135
136     /**
137      * Queries the calendar.
138      * 
139      * @return the calendar
140      * @throws IndegoAuthenticationException if request was rejected as unauthorized
141      * @throws IndegoException if any communication or parsing error occurred
142      */
143     public DeviceCalendarResponse getCalendar() throws IndegoAuthenticationException, IndegoException {
144         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/calendar", DeviceCalendarResponse.class);
145     }
146
147     /**
148      * Sends a command to the Indego device.
149      * 
150      * @param command the control command to send to the device
151      * @throws IndegoAuthenticationException if request was rejected as unauthorized
152      * @throws IndegoInvalidCommandException if the command was not processed correctly
153      * @throws IndegoException if any communication or parsing error occurred
154      */
155     public void sendCommand(DeviceCommand command)
156             throws IndegoAuthenticationException, IndegoInvalidCommandException, IndegoException {
157         SetStateRequest request = new SetStateRequest();
158         request.state = command.getActionCode();
159         putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/state", request);
160     }
161
162     /**
163      * Queries the predictive weather forecast.
164      * 
165      * @return the weather forecast DTO
166      * @throws IndegoAuthenticationException if request was rejected as unauthorized
167      * @throws IndegoException if any communication or parsing error occurred
168      */
169     public LocationWeatherResponse getWeather() throws IndegoAuthenticationException, IndegoException {
170         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/weather", LocationWeatherResponse.class);
171     }
172
173     /**
174      * Queries the predictive adjustment.
175      * 
176      * @return the predictive adjustment
177      * @throws IndegoAuthenticationException if request was rejected as unauthorized
178      * @throws IndegoException if any communication or parsing error occurred
179      */
180     public int getPredictiveAdjustment() throws IndegoAuthenticationException, IndegoException {
181         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/useradjustment",
182                 PredictiveAdjustment.class).adjustment;
183     }
184
185     /**
186      * Sets the predictive adjustment.
187      * 
188      * @param adjust the predictive adjustment
189      * @throws IndegoAuthenticationException if request was rejected as unauthorized
190      * @throws IndegoException if any communication or parsing error occurred
191      */
192     public void setPredictiveAdjustment(final int adjust) throws IndegoAuthenticationException, IndegoException {
193         final PredictiveAdjustment adjustment = new PredictiveAdjustment();
194         adjustment.adjustment = adjust;
195         putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/useradjustment", adjustment);
196     }
197
198     /**
199      * Queries predictive moving.
200      * 
201      * @return predictive moving
202      * @throws IndegoAuthenticationException if request was rejected as unauthorized
203      * @throws IndegoException if any communication or parsing error occurred
204      */
205     public boolean getPredictiveMoving() throws IndegoAuthenticationException, IndegoException {
206         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive", PredictiveStatus.class).enabled;
207     }
208
209     /**
210      * Sets predictive moving.
211      * 
212      * @param enable
213      * @throws IndegoAuthenticationException if request was rejected as unauthorized
214      * @throws IndegoException if any communication or parsing error occurred
215      */
216     public void setPredictiveMoving(final boolean enable) throws IndegoAuthenticationException, IndegoException {
217         final PredictiveStatus status = new PredictiveStatus();
218         status.enabled = enable;
219         putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive", status);
220     }
221
222     /**
223      * Queries predictive last cutting as {@link Instant}.
224      * 
225      * @return predictive last cutting
226      * @throws IndegoAuthenticationException if request was rejected as unauthorized
227      * @throws IndegoException if any communication or parsing error occurred
228      */
229     public @Nullable Instant getPredictiveLastCutting() throws IndegoAuthenticationException, IndegoException {
230         try {
231             return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/lastcutting",
232                     PredictiveLastCuttingResponse.class).getLastCutting();
233         } catch (IndegoInvalidResponseException e) {
234             if (e.getHttpStatusCode() == HttpStatus.NO_CONTENT_204) {
235                 return null;
236             }
237             throw e;
238         }
239     }
240
241     /**
242      * Queries predictive next cutting as {@link Instant}.
243      * 
244      * @return predictive next cutting
245      * @throws IndegoAuthenticationException if request was rejected as unauthorized
246      * @throws IndegoException if any communication or parsing error occurred
247      */
248     public @Nullable Instant getPredictiveNextCutting() throws IndegoAuthenticationException, IndegoException {
249         try {
250             return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/nextcutting",
251                     PredictiveNextCuttingResponse.class).getNextCutting();
252         } catch (IndegoInvalidResponseException e) {
253             if (e.getHttpStatusCode() == HttpStatus.NO_CONTENT_204) {
254                 return null;
255             }
256             throw e;
257         }
258     }
259
260     /**
261      * Queries predictive exclusion time.
262      * 
263      * @return predictive exclusion time DTO
264      * @throws IndegoAuthenticationException if request was rejected as unauthorized
265      * @throws IndegoException if any communication or parsing error occurred
266      */
267     public DeviceCalendarResponse getPredictiveExclusionTime() throws IndegoAuthenticationException, IndegoException {
268         return getRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/calendar", DeviceCalendarResponse.class);
269     }
270
271     /**
272      * Sets predictive exclusion time.
273      * 
274      * @param calendar calendar DTO
275      * @throws IndegoAuthenticationException if request was rejected as unauthorized
276      * @throws IndegoException if any communication or parsing error occurred
277      */
278     public void setPredictiveExclusionTime(final DeviceCalendarResponse calendar)
279             throws IndegoAuthenticationException, IndegoException {
280         putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + serialNumber + "/predictive/calendar", calendar);
281     }
282
283     /**
284      * Request map position updates for the next ({@link count} * {@link interval}) number of seconds.
285      * 
286      * @param count number of updates
287      * @param interval number of seconds between updates
288      * @throws IndegoAuthenticationException if request was rejected as unauthorized
289      * @throws IndegoException if any communication or parsing error occurred
290      */
291     public void requestPosition(int count, int interval) throws IndegoAuthenticationException, IndegoException {
292         postRequest(SERIAL_NUMBER_SUBPATH + serialNumber + "/requestPosition?count=" + count + "&interval=" + interval);
293     }
294 }