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.Duration;
16 import java.time.Instant;
17 import java.util.List;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.TimeoutException;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.eclipse.jetty.client.HttpClient;
24 import org.eclipse.jetty.client.api.ContentResponse;
25 import org.eclipse.jetty.client.api.Request;
26 import org.eclipse.jetty.client.util.StringContentProvider;
27 import org.eclipse.jetty.http.HttpHeader;
28 import org.eclipse.jetty.http.HttpMethod;
29 import org.eclipse.jetty.http.HttpStatus;
30 import org.openhab.binding.hdpowerview.internal.api.Color;
31 import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
32 import org.openhab.binding.hdpowerview.internal.api.SurveyData;
33 import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking;
34 import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor;
35 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate;
36 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog;
37 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
38 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
39 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion;
40 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersions;
41 import org.openhab.binding.hdpowerview.internal.api.responses.Repeater;
42 import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
43 import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters;
44 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
45 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
46 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
47 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
48 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents;
49 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent;
50 import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
51 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
52 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
53 import org.openhab.binding.hdpowerview.internal.api.responses.Survey;
54 import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
55 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
56 import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
57 import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
61 import com.google.gson.Gson;
62 import com.google.gson.JsonElement;
63 import com.google.gson.JsonObject;
64 import com.google.gson.JsonParseException;
65 import com.google.gson.JsonParser;
68 * JAX-RS targets for communicating with an HD PowerView hub
70 * @author Andy Lintner - Initial contribution
71 * @author Andrew Fiddian-Green - Added support for secondary rail positions
72 * @author Jacob Laursen - Added support for scene groups and automations
75 public class HDPowerViewWebTargets {
77 private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class);
80 * the hub returns a 423 error (resource locked) daily just after midnight;
81 * which means it is temporarily undergoing maintenance; so we use "soft"
82 * exception handling during the five minute maintenance period after a 423
85 private final Duration maintenancePeriod = Duration.ofMinutes(5);
86 private Instant maintenanceScheduledEnd = Instant.MIN;
88 private final String base;
89 private final String firmwareVersion;
90 private final String shades;
91 private final String sceneActivate;
92 private final String scenes;
93 private final String sceneCollectionActivate;
94 private final String sceneCollections;
95 private final String scheduledEvents;
96 private final String repeaters;
98 private final Gson gson = new Gson();
99 private final HttpClient httpClient;
102 * private helper class for passing http url query parameters
104 private static class Query {
105 private final String key;
106 private final String value;
108 private Query(String key, String value) {
113 public static Query of(String key, String value) {
114 return new Query(key, value);
117 public String getKey() {
121 public String getValue() {
126 public String toString() {
127 return String.format("?%s=%s", key, value);
132 * Initialize the web targets
134 * @param httpClient the HTTP client (the binding)
135 * @param ipAddress the IP address of the server (the hub)
137 public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) {
138 base = "http://" + ipAddress + "/api/";
139 shades = base + "shades/";
140 firmwareVersion = base + "fwversion/";
141 sceneActivate = base + "scenes";
142 scenes = base + "scenes/";
144 // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections".
145 sceneCollectionActivate = base + "scenecollections";
146 sceneCollections = base + "scenecollections/";
148 scheduledEvents = base + "scheduledevents";
150 repeaters = base + "repeaters/";
152 this.httpClient = httpClient;
156 * Fetches a JSON package with firmware information for the hub.
158 * @return FirmwareVersions class instance
159 * @throws HubInvalidResponseException if response is invalid
160 * @throws HubProcessingException if there is any processing error
161 * @throws HubMaintenanceException if the hub is down for maintenance
163 public FirmwareVersions getFirmwareVersions()
164 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
165 String json = invoke(HttpMethod.GET, firmwareVersion, null, null);
167 FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class);
168 if (firmwareVersion == null) {
169 throw new HubInvalidResponseException("Missing firmware response");
171 FirmwareVersions firmwareVersions = firmwareVersion.firmware;
172 if (firmwareVersions == null) {
173 throw new HubInvalidResponseException("Missing 'firmware' element");
175 return firmwareVersions;
176 } catch (JsonParseException e) {
177 throw new HubInvalidResponseException("Error parsing firmware response", e);
182 * Fetches a JSON package that describes all shades in the hub, and wraps it in
183 * a Shades class instance
185 * @return Shades class instance
186 * @throws HubInvalidResponseException if response is invalid
187 * @throws HubProcessingException if there is any processing error
188 * @throws HubMaintenanceException if the hub is down for maintenance
190 public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
191 String json = invoke(HttpMethod.GET, shades, null, null);
193 Shades shades = gson.fromJson(json, Shades.class);
194 if (shades == null) {
195 throw new HubInvalidResponseException("Missing shades response");
197 List<ShadeData> shadeData = shades.shadeData;
198 if (shadeData == null) {
199 throw new HubInvalidResponseException("Missing 'shades.shadeData' element");
202 } catch (JsonParseException e) {
203 throw new HubInvalidResponseException("Error parsing shades response", e);
208 * Instructs the hub to move a specific shade
210 * @param shadeId id of the shade to be moved
211 * @param position instance of ShadePosition containing the new position
212 * @return ShadeData class instance (with new position)
213 * @throws HubInvalidResponseException if response is invalid
214 * @throws HubProcessingException if there is any processing error
215 * @throws HubMaintenanceException if the hub is down for maintenance
216 * @throws HubShadeTimeoutException if the shade did not respond to a request
218 public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException,
219 HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
220 String jsonRequest = gson.toJson(new ShadeMove(position));
221 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
222 return shadeDataFromJson(jsonResponse);
225 private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException {
227 Shade shade = gson.fromJson(json, Shade.class);
229 throw new HubInvalidResponseException("Missing shade response");
231 ShadeData shadeData = shade.shade;
232 if (shadeData == null) {
233 throw new HubInvalidResponseException("Missing 'shade.shade' element");
235 if (Boolean.TRUE.equals(shadeData.timedOut)) {
236 throw new HubShadeTimeoutException("Timeout when sending request to the shade");
239 } catch (JsonParseException e) {
240 throw new HubInvalidResponseException("Error parsing shade response", e);
245 * Instructs the hub to stop movement of a specific shade
247 * @param shadeId id of the shade to be stopped
248 * @return ShadeData class instance (new position cannot be relied upon)
249 * @throws HubInvalidResponseException if response is invalid
250 * @throws HubProcessingException if there is any processing error
251 * @throws HubMaintenanceException if the hub is down for maintenance
252 * @throws HubShadeTimeoutException if the shade did not respond to a request
254 public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
255 HubMaintenanceException, HubShadeTimeoutException {
256 String jsonRequest = gson.toJson(new ShadeStop());
257 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
258 return shadeDataFromJson(jsonResponse);
262 * Instructs the hub to jog a specific shade
264 * @param shadeId id of the shade to be jogged
265 * @return ShadeData class instance
266 * @throws HubInvalidResponseException if response is invalid
267 * @throws HubProcessingException if there is any processing error
268 * @throws HubMaintenanceException if the hub is down for maintenance
269 * @throws HubShadeTimeoutException if the shade did not respond to a request
271 public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
272 HubMaintenanceException, HubShadeTimeoutException {
273 String jsonRequest = gson.toJson(new ShadeJog());
274 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
275 return shadeDataFromJson(jsonResponse);
279 * Instructs the hub to calibrate a specific shade
281 * @param shadeId id of the shade to be calibrated
282 * @return ShadeData class instance
283 * @throws HubInvalidResponseException if response is invalid
284 * @throws HubProcessingException if there is any processing error
285 * @throws HubMaintenanceException if the hub is down for maintenance
286 * @throws HubShadeTimeoutException if the shade did not respond to a request
288 public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
289 HubMaintenanceException, HubShadeTimeoutException {
290 String jsonRequest = gson.toJson(new ShadeCalibrate());
291 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
292 return shadeDataFromJson(jsonResponse);
296 * Fetches a JSON package that describes all scenes in the hub, and wraps it in
297 * a Scenes class instance
299 * @return Scenes class instance
300 * @throws HubInvalidResponseException if response is invalid
301 * @throws HubProcessingException if there is any processing error
302 * @throws HubMaintenanceException if the hub is down for maintenance
304 public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
305 String json = invoke(HttpMethod.GET, scenes, null, null);
307 Scenes scenes = gson.fromJson(json, Scenes.class);
308 if (scenes == null) {
309 throw new HubInvalidResponseException("Missing scenes response");
311 List<Scene> sceneData = scenes.sceneData;
312 if (sceneData == null) {
313 throw new HubInvalidResponseException("Missing 'scenes.sceneData' element");
316 } catch (JsonParseException e) {
317 throw new HubInvalidResponseException("Error parsing scenes response", e);
322 * Instructs the hub to execute a specific scene
324 * @param sceneId id of the scene to be executed
325 * @throws HubProcessingException if there is any processing error
326 * @throws HubMaintenanceException if the hub is down for maintenance
328 public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException {
329 invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null);
333 * Fetches a JSON package that describes all scene collections in the hub, and wraps it in
334 * a SceneCollections class instance
336 * @return SceneCollections class instance
337 * @throws HubInvalidResponseException if response is invalid
338 * @throws HubProcessingException if there is any processing error
339 * @throws HubMaintenanceException if the hub is down for maintenance
341 public SceneCollections getSceneCollections()
342 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
343 String json = invoke(HttpMethod.GET, sceneCollections, null, null);
345 SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
346 if (sceneCollections == null) {
347 throw new HubInvalidResponseException("Missing sceneCollections response");
349 List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
350 if (sceneCollectionData == null) {
351 throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element");
353 return sceneCollections;
354 } catch (JsonParseException e) {
355 throw new HubInvalidResponseException("Error parsing sceneCollections response", e);
360 * Instructs the hub to execute a specific scene collection
362 * @param sceneCollectionId id of the scene collection to be executed
363 * @throws HubProcessingException if there is any processing error
364 * @throws HubMaintenanceException if the hub is down for maintenance
366 public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException {
367 invoke(HttpMethod.GET, sceneCollectionActivate,
368 Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null);
372 * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in
373 * a ScheduledEvents class instance
375 * @return ScheduledEvents class instance
376 * @throws HubInvalidResponseException if response is invalid
377 * @throws HubProcessingException if there is any processing error
378 * @throws HubMaintenanceException if the hub is down for maintenance
380 public ScheduledEvents getScheduledEvents()
381 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
382 String json = invoke(HttpMethod.GET, scheduledEvents, null, null);
384 ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class);
385 if (scheduledEvents == null) {
386 throw new HubInvalidResponseException("Missing scheduledEvents response");
388 List<ScheduledEvent> scheduledEventData = scheduledEvents.scheduledEventData;
389 if (scheduledEventData == null) {
390 throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element");
392 return scheduledEvents;
393 } catch (JsonParseException e) {
394 throw new HubInvalidResponseException("Error parsing scheduledEvents response", e);
399 * Enables or disables a scheduled event in the hub.
401 * @param scheduledEventId id of the scheduled event to be enabled or disabled
402 * @param enable true to enable scheduled event, false to disable
403 * @throws HubInvalidResponseException if response is invalid
404 * @throws HubProcessingException if there is any processing error
405 * @throws HubMaintenanceException if the hub is down for maintenance
407 public void enableScheduledEvent(int scheduledEventId, boolean enable)
408 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
409 String uri = scheduledEvents + "/" + scheduledEventId;
410 String jsonResponse = invoke(HttpMethod.GET, uri, null, null);
412 JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject();
413 JsonElement scheduledEventElement = jsonObject.get("scheduledEvent");
414 if (scheduledEventElement == null) {
415 throw new HubInvalidResponseException("Missing 'scheduledEvent' element");
417 JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject();
418 scheduledEventObject.addProperty("enabled", enable);
419 invoke(HttpMethod.PUT, uri, null, jsonObject.toString());
420 } catch (JsonParseException | IllegalStateException e) {
421 throw new HubInvalidResponseException("Error parsing scheduledEvent response", e);
426 * Fetches a JSON package that describes all repeaters in the hub, and wraps it in
427 * a Repeaters class instance
429 * @return Repeaters class instance
430 * @throws HubInvalidResponseException if response is invalid
431 * @throws HubProcessingException if there is any processing error
432 * @throws HubMaintenanceException if the hub is down for maintenance
434 public Repeaters getRepeaters()
435 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
436 String json = invoke(HttpMethod.GET, repeaters, null, null);
438 Repeaters repeaters = gson.fromJson(json, Repeaters.class);
439 if (repeaters == null) {
440 throw new HubInvalidResponseException("Missing repeaters response");
442 List<RepeaterData> repeaterData = repeaters.repeaterData;
443 if (repeaterData == null) {
444 throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element");
447 } catch (JsonParseException e) {
448 throw new HubInvalidResponseException("Error parsing repeaters response", e);
453 * Fetches a JSON package that describes a specific repeater in the hub, and wraps it
454 * in a RepeaterData class instance
456 * @param repeaterId id of the repeater to be fetched
457 * @return RepeaterData class instance
458 * @throws HubInvalidResponseException if response is invalid
459 * @throws HubProcessingException if there is any processing error
460 * @throws HubMaintenanceException if the hub is down for maintenance
462 public RepeaterData getRepeater(int repeaterId)
463 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
464 String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null);
465 return repeaterDataFromJson(jsonResponse);
468 private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException {
470 Repeater repeater = gson.fromJson(json, Repeater.class);
471 if (repeater == null) {
472 throw new HubInvalidResponseException("Missing repeater response");
474 RepeaterData repeaterData = repeater.repeater;
475 if (repeaterData == null) {
476 throw new HubInvalidResponseException("Missing 'repeater.repeater' element");
479 } catch (JsonParseException e) {
480 throw new HubInvalidResponseException("Error parsing repeater response", e);
485 * Instructs the hub to identify a specific repeater by blinking
487 * @param repeaterId id of the repeater to be identified
488 * @return RepeaterData class instance
489 * @throws HubInvalidResponseException if response is invalid
490 * @throws HubProcessingException if there is any processing error
491 * @throws HubMaintenanceException if the hub is down for maintenance
493 public RepeaterData identifyRepeater(int repeaterId)
494 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
495 String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId,
496 Query.of("identify", Boolean.toString(true)), null);
497 return repeaterDataFromJson(jsonResponse);
501 * Enables or disables blinking for a repeater
503 * @param repeaterId id of the repeater for which to be enable or disable blinking
504 * @param enable true to enable blinking, false to disable
505 * @return RepeaterData class instance
506 * @throws HubInvalidResponseException if response is invalid
507 * @throws HubProcessingException if there is any processing error
508 * @throws HubMaintenanceException if the hub is down for maintenance
510 public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable)
511 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
512 String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable));
513 String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
514 return repeaterDataFromJson(jsonResponse);
518 * Sets color and brightness for a repeater
520 * @param repeaterId id of the repeater for which to set color and brightness
521 * @return RepeaterData class instance
522 * @throws HubInvalidResponseException if response is invalid
523 * @throws HubProcessingException if there is any processing error
524 * @throws HubMaintenanceException if the hub is down for maintenance
526 public RepeaterData setRepeaterColor(int repeaterId, Color color)
527 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
528 String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color));
529 String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
530 return repeaterDataFromJson(jsonResponse);
534 * Invoke a call on the hub server to retrieve information or send a command
536 * @param method GET or PUT
537 * @param url the host url to be called
538 * @param query the http query parameter
539 * @param jsonCommand the request command content (as a json string)
540 * @return the response content (as a json string)
541 * @throws HubMaintenanceException
542 * @throws HubProcessingException
544 private synchronized String invoke(HttpMethod method, String url, @Nullable Query query,
545 @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException {
546 if (logger.isTraceEnabled()) {
548 logger.trace("API command {} {}{}", method, url, query);
550 logger.trace("API command {} {}", method, url);
552 if (jsonCommand != null) {
553 logger.trace("JSON command = {}", jsonCommand);
556 Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*");
558 request.param(query.getKey(), query.getValue());
560 if (jsonCommand != null) {
561 request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand));
563 ContentResponse response;
565 response = request.send();
566 } catch (InterruptedException e) {
567 Thread.currentThread().interrupt();
568 throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage()));
569 } catch (TimeoutException | ExecutionException e) {
570 if (Instant.now().isBefore(maintenanceScheduledEnd)) {
571 // throw "softer" exception during maintenance window
572 logger.debug("Hub still undergoing maintenance");
573 throw new HubMaintenanceException("Hub still undergoing maintenance");
575 throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage()));
577 int statusCode = response.getStatus();
578 if (statusCode == HttpStatus.LOCKED_423) {
579 // set end of maintenance window, and throw a "softer" exception
580 maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod);
581 logger.debug("Hub undergoing maintenance");
582 throw new HubMaintenanceException("Hub undergoing maintenance");
584 if (statusCode != HttpStatus.OK_200) {
585 logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason());
586 throw new HubProcessingException(String.format("HTTP %d error", statusCode));
588 String jsonResponse = response.getContentAsString();
589 if (logger.isTraceEnabled()) {
590 logger.trace("JSON response = {}", jsonResponse);
592 if (jsonResponse == null || jsonResponse.isEmpty()) {
593 logger.warn("Hub returned no content");
594 throw new HubProcessingException("Missing response entity");
600 * Fetches a JSON package that describes a specific shade in the hub, and wraps it
601 * in a Shade class instance
603 * @param shadeId id of the shade to be fetched
604 * @return ShadeData class instance
605 * @throws HubInvalidResponseException if response is invalid
606 * @throws HubProcessingException if there is any processing error
607 * @throws HubMaintenanceException if the hub is down for maintenance
608 * @throws HubShadeTimeoutException if the shade did not respond to a request
610 public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
611 HubMaintenanceException, HubShadeTimeoutException {
612 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null);
613 return shadeDataFromJson(jsonResponse);
617 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
618 * a specific shade's position; fetches a JSON package that describes that shade,
619 * and wraps it in a Shade class instance
621 * @param shadeId id of the shade to be refreshed
622 * @return ShadeData class instance
623 * @throws HubInvalidResponseException if response is invalid
624 * @throws HubProcessingException if there is any processing error
625 * @throws HubMaintenanceException if the hub is down for maintenance
626 * @throws HubShadeTimeoutException if the shade did not respond to a request
628 public ShadeData refreshShadePosition(int shadeId)
629 throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
630 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
631 Query.of("refresh", Boolean.toString(true)), null);
632 return shadeDataFromJson(jsonResponse);
636 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
637 * a specific shade's survey data, which will also refresh signal strength;
638 * fetches a JSON package that describes that survey, and wraps it in a Survey
641 * @param shadeId id of the shade to be surveyed
642 * @return List of SurveyData class instances
643 * @throws HubInvalidResponseException if response is invalid
644 * @throws HubProcessingException if there is any processing error
645 * @throws HubMaintenanceException if the hub is down for maintenance
647 public List<SurveyData> getShadeSurvey(int shadeId)
648 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
649 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
650 Query.of("survey", Boolean.toString(true)), null);
652 Survey survey = gson.fromJson(jsonResponse, Survey.class);
653 if (survey == null) {
654 throw new HubInvalidResponseException("Missing survey response");
656 List<SurveyData> surveyData = survey.surveyData;
657 if (surveyData == null) {
658 throw new HubInvalidResponseException("Missing 'survey.surveyData' element");
661 } catch (JsonParseException e) {
662 throw new HubInvalidResponseException("Error parsing survey response", e);
667 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
668 * a specific shade's battery level; fetches a JSON package that describes that shade,
669 * and wraps it in a Shade class instance
671 * @param shadeId id of the shade to be refreshed
672 * @return ShadeData class instance
673 * @throws HubInvalidResponseException if response is invalid
674 * @throws HubProcessingException if there is any processing error
675 * @throws HubMaintenanceException if the hub is down for maintenance
676 * @throws HubShadeTimeoutException if the shade did not respond to a request
678 public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException,
679 HubMaintenanceException, HubShadeTimeoutException {
680 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
681 Query.of("updateBatteryLevel", Boolean.toString(true)), null);
682 return shadeDataFromJson(jsonResponse);