2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.hdpowerview.internal;
15 import java.time.Instant;
16 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeoutException;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.eclipse.jetty.client.HttpClient;
23 import org.eclipse.jetty.client.api.ContentResponse;
24 import org.eclipse.jetty.client.api.Request;
25 import org.eclipse.jetty.client.util.StringContentProvider;
26 import org.eclipse.jetty.http.HttpHeader;
27 import org.eclipse.jetty.http.HttpMethod;
28 import org.eclipse.jetty.http.HttpStatus;
29 import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
30 import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking;
31 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate;
32 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog;
33 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
34 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
35 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion;
36 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersions;
37 import org.openhab.binding.hdpowerview.internal.api.responses.Repeater;
38 import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
39 import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters;
40 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
41 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
42 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
43 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
44 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents;
45 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent;
46 import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
47 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
48 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
49 import org.openhab.binding.hdpowerview.internal.api.responses.Survey;
50 import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
51 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
52 import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
53 import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 import com.google.gson.Gson;
58 import com.google.gson.JsonElement;
59 import com.google.gson.JsonObject;
60 import com.google.gson.JsonParseException;
61 import com.google.gson.JsonParser;
64 * JAX-RS targets for communicating with an HD PowerView hub
66 * @author Andy Lintner - Initial contribution
67 * @author Andrew Fiddian-Green - Added support for secondary rail positions
68 * @author Jacob Laursen - Added support for scene groups and automations
71 public class HDPowerViewWebTargets {
73 private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class);
76 * the hub returns a 423 error (resource locked) daily just after midnight;
77 * which means it is temporarily undergoing maintenance; so we use "soft"
78 * exception handling during the five minute maintenance period after a 423
81 private final int maintenancePeriod = 300;
82 private Instant maintenanceScheduledEnd = Instant.now().minusSeconds(2 * maintenancePeriod);
84 private final String base;
85 private final String firmwareVersion;
86 private final String shades;
87 private final String sceneActivate;
88 private final String scenes;
89 private final String sceneCollectionActivate;
90 private final String sceneCollections;
91 private final String scheduledEvents;
92 private final String repeaters;
94 private final Gson gson = new Gson();
95 private final HttpClient httpClient;
98 * private helper class for passing http url query parameters
100 private static class Query {
101 private final String key;
102 private final String value;
104 private Query(String key, String value) {
109 public static Query of(String key, String value) {
110 return new Query(key, value);
113 public String getKey() {
117 public String getValue() {
122 public String toString() {
123 return String.format("?%s=%s", key, value);
128 * Initialize the web targets
130 * @param httpClient the HTTP client (the binding)
131 * @param ipAddress the IP address of the server (the hub)
133 public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) {
134 base = "http://" + ipAddress + "/api/";
135 shades = base + "shades/";
136 firmwareVersion = base + "fwversion/";
137 sceneActivate = base + "scenes";
138 scenes = base + "scenes/";
140 // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections".
141 sceneCollectionActivate = base + "scenecollections";
142 sceneCollections = base + "scenecollections/";
144 scheduledEvents = base + "scheduledevents";
146 repeaters = base + "repeaters/";
148 this.httpClient = httpClient;
152 * Fetches a JSON package with firmware information for the hub.
154 * @return FirmwareVersions class instance
155 * @throws HubInvalidResponseException if response is invalid
156 * @throws HubProcessingException if there is any processing error
157 * @throws HubMaintenanceException if the hub is down for maintenance
159 public FirmwareVersions getFirmwareVersions()
160 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
161 String json = invoke(HttpMethod.GET, firmwareVersion, null, null);
163 FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class);
164 if (firmwareVersion == null) {
165 throw new HubInvalidResponseException("Missing firmware response");
167 FirmwareVersions firmwareVersions = firmwareVersion.firmware;
168 if (firmwareVersions == null) {
169 throw new HubInvalidResponseException("Missing 'firmware' element");
171 return firmwareVersions;
172 } catch (JsonParseException e) {
173 throw new HubInvalidResponseException("Error parsing firmware response", e);
178 * Fetches a JSON package that describes all shades in the hub, and wraps it in
179 * a Shades class instance
181 * @return Shades class instance
182 * @throws HubInvalidResponseException if response is invalid
183 * @throws HubProcessingException if there is any processing error
184 * @throws HubMaintenanceException if the hub is down for maintenance
186 public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
187 String json = invoke(HttpMethod.GET, shades, null, null);
189 Shades shades = gson.fromJson(json, Shades.class);
190 if (shades == null) {
191 throw new HubInvalidResponseException("Missing shades response");
193 List<ShadeData> shadeData = shades.shadeData;
194 if (shadeData == null) {
195 throw new HubInvalidResponseException("Missing 'shades.shadeData' element");
198 } catch (JsonParseException e) {
199 throw new HubInvalidResponseException("Error parsing shades response", e);
204 * Instructs the hub to move a specific shade
206 * @param shadeId id of the shade to be moved
207 * @param position instance of ShadePosition containing the new position
208 * @return ShadeData class instance (with new position)
209 * @throws HubInvalidResponseException if response is invalid
210 * @throws HubProcessingException if there is any processing error
211 * @throws HubMaintenanceException if the hub is down for maintenance
212 * @throws HubShadeTimeoutException if the shade did not respond to a request
214 public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException,
215 HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
216 String jsonRequest = gson.toJson(new ShadeMove(position));
217 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
218 return shadeDataFromJson(jsonResponse);
221 private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException {
223 Shade shade = gson.fromJson(json, Shade.class);
225 throw new HubInvalidResponseException("Missing shade response");
227 ShadeData shadeData = shade.shade;
228 if (shadeData == null) {
229 throw new HubInvalidResponseException("Missing 'shade.shade' element");
231 if (Boolean.TRUE.equals(shadeData.timedOut)) {
232 throw new HubShadeTimeoutException("Timeout when sending request to the shade");
235 } catch (JsonParseException e) {
236 throw new HubInvalidResponseException("Error parsing shade response", e);
241 * Instructs the hub to stop movement of a specific shade
243 * @param shadeId id of the shade to be stopped
244 * @return ShadeData class instance (new position cannot be relied upon)
245 * @throws HubInvalidResponseException if response is invalid
246 * @throws HubProcessingException if there is any processing error
247 * @throws HubMaintenanceException if the hub is down for maintenance
248 * @throws HubShadeTimeoutException if the shade did not respond to a request
250 public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
251 HubMaintenanceException, HubShadeTimeoutException {
252 String jsonRequest = gson.toJson(new ShadeStop());
253 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
254 return shadeDataFromJson(jsonResponse);
258 * Instructs the hub to jog a specific shade
260 * @param shadeId id of the shade to be jogged
261 * @return ShadeData class instance
262 * @throws HubInvalidResponseException if response is invalid
263 * @throws HubProcessingException if there is any processing error
264 * @throws HubMaintenanceException if the hub is down for maintenance
265 * @throws HubShadeTimeoutException if the shade did not respond to a request
267 public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
268 HubMaintenanceException, HubShadeTimeoutException {
269 String jsonRequest = gson.toJson(new ShadeJog());
270 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
271 return shadeDataFromJson(jsonResponse);
275 * Instructs the hub to calibrate a specific shade
277 * @param shadeId id of the shade to be calibrated
278 * @return ShadeData class instance
279 * @throws HubInvalidResponseException if response is invalid
280 * @throws HubProcessingException if there is any processing error
281 * @throws HubMaintenanceException if the hub is down for maintenance
282 * @throws HubShadeTimeoutException if the shade did not respond to a request
284 public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
285 HubMaintenanceException, HubShadeTimeoutException {
286 String jsonRequest = gson.toJson(new ShadeCalibrate());
287 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
288 return shadeDataFromJson(jsonResponse);
292 * Fetches a JSON package that describes all scenes in the hub, and wraps it in
293 * a Scenes class instance
295 * @return Scenes class instance
296 * @throws HubInvalidResponseException if response is invalid
297 * @throws HubProcessingException if there is any processing error
298 * @throws HubMaintenanceException if the hub is down for maintenance
300 public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
301 String json = invoke(HttpMethod.GET, scenes, null, null);
303 Scenes scenes = gson.fromJson(json, Scenes.class);
304 if (scenes == null) {
305 throw new HubInvalidResponseException("Missing scenes response");
307 List<Scene> sceneData = scenes.sceneData;
308 if (sceneData == null) {
309 throw new HubInvalidResponseException("Missing 'scenes.sceneData' element");
312 } catch (JsonParseException e) {
313 throw new HubInvalidResponseException("Error parsing scenes response", e);
318 * Instructs the hub to execute a specific scene
320 * @param sceneId id of the scene to be executed
321 * @throws HubProcessingException if there is any processing error
322 * @throws HubMaintenanceException if the hub is down for maintenance
324 public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException {
325 invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null);
329 * Fetches a JSON package that describes all scene collections in the hub, and wraps it in
330 * a SceneCollections class instance
332 * @return SceneCollections class instance
333 * @throws HubInvalidResponseException if response is invalid
334 * @throws HubProcessingException if there is any processing error
335 * @throws HubMaintenanceException if the hub is down for maintenance
337 public SceneCollections getSceneCollections()
338 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
339 String json = invoke(HttpMethod.GET, sceneCollections, null, null);
341 SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
342 if (sceneCollections == null) {
343 throw new HubInvalidResponseException("Missing sceneCollections response");
345 List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
346 if (sceneCollectionData == null) {
347 throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element");
349 return sceneCollections;
350 } catch (JsonParseException e) {
351 throw new HubInvalidResponseException("Error parsing sceneCollections response", e);
356 * Instructs the hub to execute a specific scene collection
358 * @param sceneCollectionId id of the scene collection to be executed
359 * @throws HubProcessingException if there is any processing error
360 * @throws HubMaintenanceException if the hub is down for maintenance
362 public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException {
363 invoke(HttpMethod.GET, sceneCollectionActivate,
364 Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null);
368 * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in
369 * a ScheduledEvents class instance
371 * @return ScheduledEvents class instance
372 * @throws HubInvalidResponseException if response is invalid
373 * @throws HubProcessingException if there is any processing error
374 * @throws HubMaintenanceException if the hub is down for maintenance
376 public ScheduledEvents getScheduledEvents()
377 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
378 String json = invoke(HttpMethod.GET, scheduledEvents, null, null);
380 ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class);
381 if (scheduledEvents == null) {
382 throw new HubInvalidResponseException("Missing scheduledEvents response");
384 List<ScheduledEvent> scheduledEventData = scheduledEvents.scheduledEventData;
385 if (scheduledEventData == null) {
386 throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element");
388 return scheduledEvents;
389 } catch (JsonParseException e) {
390 throw new HubInvalidResponseException("Error parsing scheduledEvents response", e);
395 * Enables or disables a scheduled event in the hub.
397 * @param scheduledEventId id of the scheduled event to be enabled or disabled
398 * @param enable true to enable scheduled event, false to disable
399 * @throws HubInvalidResponseException if response is invalid
400 * @throws HubProcessingException if there is any processing error
401 * @throws HubMaintenanceException if the hub is down for maintenance
403 public void enableScheduledEvent(int scheduledEventId, boolean enable)
404 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
405 String uri = scheduledEvents + "/" + scheduledEventId;
406 String jsonResponse = invoke(HttpMethod.GET, uri, null, null);
408 JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject();
409 JsonElement scheduledEventElement = jsonObject.get("scheduledEvent");
410 if (scheduledEventElement == null) {
411 throw new HubInvalidResponseException("Missing 'scheduledEvent' element");
413 JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject();
414 scheduledEventObject.addProperty("enabled", enable);
415 invoke(HttpMethod.PUT, uri, null, jsonObject.toString());
416 } catch (JsonParseException | IllegalStateException e) {
417 throw new HubInvalidResponseException("Error parsing scheduledEvent response", e);
422 * Fetches a JSON package that describes all repeaters in the hub, and wraps it in
423 * a Repeaters class instance
425 * @return Repeaters class instance
426 * @throws HubInvalidResponseException if response is invalid
427 * @throws HubProcessingException if there is any processing error
428 * @throws HubMaintenanceException if the hub is down for maintenance
430 public Repeaters getRepeaters()
431 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
432 String json = invoke(HttpMethod.GET, repeaters, null, null);
434 Repeaters repeaters = gson.fromJson(json, Repeaters.class);
435 if (repeaters == null) {
436 throw new HubInvalidResponseException("Missing repeaters response");
438 List<RepeaterData> repeaterData = repeaters.repeaterData;
439 if (repeaterData == null) {
440 throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element");
443 } catch (JsonParseException e) {
444 throw new HubInvalidResponseException("Error parsing repeaters response", e);
449 * Fetches a JSON package that describes a specific repeater in the hub, and wraps it
450 * in a RepeaterData class instance
452 * @param repeaterId id of the repeater to be fetched
453 * @return RepeaterData class instance
454 * @throws HubInvalidResponseException if response is invalid
455 * @throws HubProcessingException if there is any processing error
456 * @throws HubMaintenanceException if the hub is down for maintenance
458 public RepeaterData getRepeater(int repeaterId)
459 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
460 String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null);
461 return repeaterDataFromJson(jsonResponse);
464 private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException {
466 Repeater repeater = gson.fromJson(json, Repeater.class);
467 if (repeater == null) {
468 throw new HubInvalidResponseException("Missing repeater response");
470 RepeaterData repeaterData = repeater.repeater;
471 if (repeaterData == null) {
472 throw new HubInvalidResponseException("Missing 'repeater.repeater' element");
475 } catch (JsonParseException e) {
476 throw new HubInvalidResponseException("Error parsing repeater response", e);
481 * Instructs the hub to identify a specific repeater by blinking
483 * @param repeaterId id of the repeater to be identified
484 * @return RepeaterData class instance
485 * @throws HubInvalidResponseException if response is invalid
486 * @throws HubProcessingException if there is any processing error
487 * @throws HubMaintenanceException if the hub is down for maintenance
489 public RepeaterData identifyRepeater(int repeaterId)
490 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
491 String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId,
492 Query.of("identify", Boolean.toString(true)), null);
493 return repeaterDataFromJson(jsonResponse);
497 * Enables or disables blinking for a repeater
499 * @param repeaterId id of the repeater for which to be enable or disable blinking
500 * @param enable true to enable blinking, false to disable
501 * @return RepeaterData class instance
502 * @throws HubInvalidResponseException if response is invalid
503 * @throws HubProcessingException if there is any processing error
504 * @throws HubMaintenanceException if the hub is down for maintenance
506 public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable)
507 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
508 String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable));
509 String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
510 return repeaterDataFromJson(jsonResponse);
514 * Invoke a call on the hub server to retrieve information or send a command
516 * @param method GET or PUT
517 * @param url the host url to be called
518 * @param query the http query parameter
519 * @param jsonCommand the request command content (as a json string)
520 * @return the response content (as a json string)
521 * @throws HubMaintenanceException
522 * @throws HubProcessingException
524 private synchronized String invoke(HttpMethod method, String url, @Nullable Query query,
525 @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException {
526 if (logger.isTraceEnabled()) {
528 logger.trace("API command {} {}{}", method, url, query);
530 logger.trace("API command {} {}", method, url);
532 if (jsonCommand != null) {
533 logger.trace("JSON command = {}", jsonCommand);
536 Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*");
538 request.param(query.getKey(), query.getValue());
540 if (jsonCommand != null) {
541 request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand));
543 ContentResponse response;
545 response = request.send();
546 } catch (InterruptedException | TimeoutException | ExecutionException e) {
547 if (Instant.now().isBefore(maintenanceScheduledEnd)) {
548 // throw "softer" exception during maintenance window
549 logger.debug("Hub still undergoing maintenance");
550 throw new HubMaintenanceException("Hub still undergoing maintenance");
552 throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage()));
554 int statusCode = response.getStatus();
555 if (statusCode == HttpStatus.LOCKED_423) {
556 // set end of maintenance window, and throw a "softer" exception
557 maintenanceScheduledEnd = Instant.now().plusSeconds(maintenancePeriod);
558 logger.debug("Hub undergoing maintenance");
559 throw new HubMaintenanceException("Hub undergoing maintenance");
561 if (statusCode != HttpStatus.OK_200) {
562 logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason());
563 throw new HubProcessingException(String.format("HTTP %d error", statusCode));
565 String jsonResponse = response.getContentAsString();
566 if ("".equals(jsonResponse)) {
567 logger.warn("Hub returned no content");
568 throw new HubProcessingException("Missing response entity");
570 if (logger.isTraceEnabled()) {
571 logger.trace("JSON response = {}", jsonResponse);
577 * Fetches a JSON package that describes a specific shade in the hub, and wraps it
578 * in a Shade class instance
580 * @param shadeId id of the shade to be fetched
581 * @return ShadeData class instance
582 * @throws HubInvalidResponseException if response is invalid
583 * @throws HubProcessingException if there is any processing error
584 * @throws HubMaintenanceException if the hub is down for maintenance
585 * @throws HubShadeTimeoutException if the shade did not respond to a request
587 public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
588 HubMaintenanceException, HubShadeTimeoutException {
589 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null);
590 return shadeDataFromJson(jsonResponse);
594 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
595 * a specific shade's position; fetches a JSON package that describes that shade,
596 * and wraps it in a Shade class instance
598 * @param shadeId id of the shade to be refreshed
599 * @return ShadeData class instance
600 * @throws HubInvalidResponseException if response is invalid
601 * @throws HubProcessingException if there is any processing error
602 * @throws HubMaintenanceException if the hub is down for maintenance
603 * @throws HubShadeTimeoutException if the shade did not respond to a request
605 public ShadeData refreshShadePosition(int shadeId)
606 throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
607 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
608 Query.of("refresh", Boolean.toString(true)), null);
609 return shadeDataFromJson(jsonResponse);
613 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
614 * a specific shade's survey data, which will also refresh signal strength;
615 * fetches a JSON package that describes that survey, and wraps it in a Survey
618 * @param shadeId id of the shade to be surveyed
619 * @return Survey class instance
620 * @throws HubInvalidResponseException if response is invalid
621 * @throws HubProcessingException if there is any processing error
622 * @throws HubMaintenanceException if the hub is down for maintenance
624 public Survey getShadeSurvey(int shadeId)
625 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
626 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
627 Query.of("survey", Boolean.toString(true)), null);
629 Survey survey = gson.fromJson(jsonResponse, Survey.class);
630 if (survey == null) {
631 throw new HubInvalidResponseException("Missing survey response");
634 } catch (JsonParseException e) {
635 throw new HubInvalidResponseException("Error parsing survey response", e);
640 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
641 * a specific shade's battery level; fetches a JSON package that describes that shade,
642 * and wraps it in a Shade class instance
644 * @param shadeId id of the shade to be refreshed
645 * @return ShadeData class instance
646 * @throws HubInvalidResponseException if response is invalid
647 * @throws HubProcessingException if there is any processing error
648 * @throws HubMaintenanceException if the hub is down for maintenance
649 * @throws HubShadeTimeoutException if the shade did not respond to a request
651 public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException,
652 HubMaintenanceException, HubShadeTimeoutException {
653 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
654 Query.of("updateBatteryLevel", Boolean.toString(true)), null);
655 return shadeDataFromJson(jsonResponse);