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.Color;
30 import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
31 import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking;
32 import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor;
33 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate;
34 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog;
35 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
36 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
37 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion;
38 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersions;
39 import org.openhab.binding.hdpowerview.internal.api.responses.Repeater;
40 import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
41 import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters;
42 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
43 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
44 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
45 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
46 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents;
47 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent;
48 import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
49 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
50 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
51 import org.openhab.binding.hdpowerview.internal.api.responses.Survey;
52 import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
53 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
54 import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
55 import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
59 import com.google.gson.Gson;
60 import com.google.gson.JsonElement;
61 import com.google.gson.JsonObject;
62 import com.google.gson.JsonParseException;
63 import com.google.gson.JsonParser;
66 * JAX-RS targets for communicating with an HD PowerView hub
68 * @author Andy Lintner - Initial contribution
69 * @author Andrew Fiddian-Green - Added support for secondary rail positions
70 * @author Jacob Laursen - Added support for scene groups and automations
73 public class HDPowerViewWebTargets {
75 private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class);
78 * the hub returns a 423 error (resource locked) daily just after midnight;
79 * which means it is temporarily undergoing maintenance; so we use "soft"
80 * exception handling during the five minute maintenance period after a 423
83 private final int maintenancePeriod = 300;
84 private Instant maintenanceScheduledEnd = Instant.now().minusSeconds(2 * maintenancePeriod);
86 private final String base;
87 private final String firmwareVersion;
88 private final String shades;
89 private final String sceneActivate;
90 private final String scenes;
91 private final String sceneCollectionActivate;
92 private final String sceneCollections;
93 private final String scheduledEvents;
94 private final String repeaters;
96 private final Gson gson = new Gson();
97 private final HttpClient httpClient;
100 * private helper class for passing http url query parameters
102 private static class Query {
103 private final String key;
104 private final String value;
106 private Query(String key, String value) {
111 public static Query of(String key, String value) {
112 return new Query(key, value);
115 public String getKey() {
119 public String getValue() {
124 public String toString() {
125 return String.format("?%s=%s", key, value);
130 * Initialize the web targets
132 * @param httpClient the HTTP client (the binding)
133 * @param ipAddress the IP address of the server (the hub)
135 public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) {
136 base = "http://" + ipAddress + "/api/";
137 shades = base + "shades/";
138 firmwareVersion = base + "fwversion/";
139 sceneActivate = base + "scenes";
140 scenes = base + "scenes/";
142 // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections".
143 sceneCollectionActivate = base + "scenecollections";
144 sceneCollections = base + "scenecollections/";
146 scheduledEvents = base + "scheduledevents";
148 repeaters = base + "repeaters/";
150 this.httpClient = httpClient;
154 * Fetches a JSON package with firmware information for the hub.
156 * @return FirmwareVersions class instance
157 * @throws HubInvalidResponseException if response is invalid
158 * @throws HubProcessingException if there is any processing error
159 * @throws HubMaintenanceException if the hub is down for maintenance
161 public FirmwareVersions getFirmwareVersions()
162 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
163 String json = invoke(HttpMethod.GET, firmwareVersion, null, null);
165 FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class);
166 if (firmwareVersion == null) {
167 throw new HubInvalidResponseException("Missing firmware response");
169 FirmwareVersions firmwareVersions = firmwareVersion.firmware;
170 if (firmwareVersions == null) {
171 throw new HubInvalidResponseException("Missing 'firmware' element");
173 return firmwareVersions;
174 } catch (JsonParseException e) {
175 throw new HubInvalidResponseException("Error parsing firmware response", e);
180 * Fetches a JSON package that describes all shades in the hub, and wraps it in
181 * a Shades class instance
183 * @return Shades class instance
184 * @throws HubInvalidResponseException if response is invalid
185 * @throws HubProcessingException if there is any processing error
186 * @throws HubMaintenanceException if the hub is down for maintenance
188 public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
189 String json = invoke(HttpMethod.GET, shades, null, null);
191 Shades shades = gson.fromJson(json, Shades.class);
192 if (shades == null) {
193 throw new HubInvalidResponseException("Missing shades response");
195 List<ShadeData> shadeData = shades.shadeData;
196 if (shadeData == null) {
197 throw new HubInvalidResponseException("Missing 'shades.shadeData' element");
200 } catch (JsonParseException e) {
201 throw new HubInvalidResponseException("Error parsing shades response", e);
206 * Instructs the hub to move a specific shade
208 * @param shadeId id of the shade to be moved
209 * @param position instance of ShadePosition containing the new position
210 * @return ShadeData class instance (with new position)
211 * @throws HubInvalidResponseException if response is invalid
212 * @throws HubProcessingException if there is any processing error
213 * @throws HubMaintenanceException if the hub is down for maintenance
214 * @throws HubShadeTimeoutException if the shade did not respond to a request
216 public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException,
217 HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
218 String jsonRequest = gson.toJson(new ShadeMove(position));
219 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
220 return shadeDataFromJson(jsonResponse);
223 private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException {
225 Shade shade = gson.fromJson(json, Shade.class);
227 throw new HubInvalidResponseException("Missing shade response");
229 ShadeData shadeData = shade.shade;
230 if (shadeData == null) {
231 throw new HubInvalidResponseException("Missing 'shade.shade' element");
233 if (Boolean.TRUE.equals(shadeData.timedOut)) {
234 throw new HubShadeTimeoutException("Timeout when sending request to the shade");
237 } catch (JsonParseException e) {
238 throw new HubInvalidResponseException("Error parsing shade response", e);
243 * Instructs the hub to stop movement of a specific shade
245 * @param shadeId id of the shade to be stopped
246 * @return ShadeData class instance (new position cannot be relied upon)
247 * @throws HubInvalidResponseException if response is invalid
248 * @throws HubProcessingException if there is any processing error
249 * @throws HubMaintenanceException if the hub is down for maintenance
250 * @throws HubShadeTimeoutException if the shade did not respond to a request
252 public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
253 HubMaintenanceException, HubShadeTimeoutException {
254 String jsonRequest = gson.toJson(new ShadeStop());
255 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
256 return shadeDataFromJson(jsonResponse);
260 * Instructs the hub to jog a specific shade
262 * @param shadeId id of the shade to be jogged
263 * @return ShadeData class instance
264 * @throws HubInvalidResponseException if response is invalid
265 * @throws HubProcessingException if there is any processing error
266 * @throws HubMaintenanceException if the hub is down for maintenance
267 * @throws HubShadeTimeoutException if the shade did not respond to a request
269 public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
270 HubMaintenanceException, HubShadeTimeoutException {
271 String jsonRequest = gson.toJson(new ShadeJog());
272 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
273 return shadeDataFromJson(jsonResponse);
277 * Instructs the hub to calibrate a specific shade
279 * @param shadeId id of the shade to be calibrated
280 * @return ShadeData class instance
281 * @throws HubInvalidResponseException if response is invalid
282 * @throws HubProcessingException if there is any processing error
283 * @throws HubMaintenanceException if the hub is down for maintenance
284 * @throws HubShadeTimeoutException if the shade did not respond to a request
286 public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
287 HubMaintenanceException, HubShadeTimeoutException {
288 String jsonRequest = gson.toJson(new ShadeCalibrate());
289 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
290 return shadeDataFromJson(jsonResponse);
294 * Fetches a JSON package that describes all scenes in the hub, and wraps it in
295 * a Scenes class instance
297 * @return Scenes class instance
298 * @throws HubInvalidResponseException if response is invalid
299 * @throws HubProcessingException if there is any processing error
300 * @throws HubMaintenanceException if the hub is down for maintenance
302 public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
303 String json = invoke(HttpMethod.GET, scenes, null, null);
305 Scenes scenes = gson.fromJson(json, Scenes.class);
306 if (scenes == null) {
307 throw new HubInvalidResponseException("Missing scenes response");
309 List<Scene> sceneData = scenes.sceneData;
310 if (sceneData == null) {
311 throw new HubInvalidResponseException("Missing 'scenes.sceneData' element");
314 } catch (JsonParseException e) {
315 throw new HubInvalidResponseException("Error parsing scenes response", e);
320 * Instructs the hub to execute a specific scene
322 * @param sceneId id of the scene to be executed
323 * @throws HubProcessingException if there is any processing error
324 * @throws HubMaintenanceException if the hub is down for maintenance
326 public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException {
327 invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null);
331 * Fetches a JSON package that describes all scene collections in the hub, and wraps it in
332 * a SceneCollections class instance
334 * @return SceneCollections class instance
335 * @throws HubInvalidResponseException if response is invalid
336 * @throws HubProcessingException if there is any processing error
337 * @throws HubMaintenanceException if the hub is down for maintenance
339 public SceneCollections getSceneCollections()
340 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
341 String json = invoke(HttpMethod.GET, sceneCollections, null, null);
343 SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
344 if (sceneCollections == null) {
345 throw new HubInvalidResponseException("Missing sceneCollections response");
347 List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
348 if (sceneCollectionData == null) {
349 throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element");
351 return sceneCollections;
352 } catch (JsonParseException e) {
353 throw new HubInvalidResponseException("Error parsing sceneCollections response", e);
358 * Instructs the hub to execute a specific scene collection
360 * @param sceneCollectionId id of the scene collection to be executed
361 * @throws HubProcessingException if there is any processing error
362 * @throws HubMaintenanceException if the hub is down for maintenance
364 public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException {
365 invoke(HttpMethod.GET, sceneCollectionActivate,
366 Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null);
370 * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in
371 * a ScheduledEvents class instance
373 * @return ScheduledEvents class instance
374 * @throws HubInvalidResponseException if response is invalid
375 * @throws HubProcessingException if there is any processing error
376 * @throws HubMaintenanceException if the hub is down for maintenance
378 public ScheduledEvents getScheduledEvents()
379 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
380 String json = invoke(HttpMethod.GET, scheduledEvents, null, null);
382 ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class);
383 if (scheduledEvents == null) {
384 throw new HubInvalidResponseException("Missing scheduledEvents response");
386 List<ScheduledEvent> scheduledEventData = scheduledEvents.scheduledEventData;
387 if (scheduledEventData == null) {
388 throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element");
390 return scheduledEvents;
391 } catch (JsonParseException e) {
392 throw new HubInvalidResponseException("Error parsing scheduledEvents response", e);
397 * Enables or disables a scheduled event in the hub.
399 * @param scheduledEventId id of the scheduled event to be enabled or disabled
400 * @param enable true to enable scheduled event, false to disable
401 * @throws HubInvalidResponseException if response is invalid
402 * @throws HubProcessingException if there is any processing error
403 * @throws HubMaintenanceException if the hub is down for maintenance
405 public void enableScheduledEvent(int scheduledEventId, boolean enable)
406 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
407 String uri = scheduledEvents + "/" + scheduledEventId;
408 String jsonResponse = invoke(HttpMethod.GET, uri, null, null);
410 JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject();
411 JsonElement scheduledEventElement = jsonObject.get("scheduledEvent");
412 if (scheduledEventElement == null) {
413 throw new HubInvalidResponseException("Missing 'scheduledEvent' element");
415 JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject();
416 scheduledEventObject.addProperty("enabled", enable);
417 invoke(HttpMethod.PUT, uri, null, jsonObject.toString());
418 } catch (JsonParseException | IllegalStateException e) {
419 throw new HubInvalidResponseException("Error parsing scheduledEvent response", e);
424 * Fetches a JSON package that describes all repeaters in the hub, and wraps it in
425 * a Repeaters class instance
427 * @return Repeaters class instance
428 * @throws HubInvalidResponseException if response is invalid
429 * @throws HubProcessingException if there is any processing error
430 * @throws HubMaintenanceException if the hub is down for maintenance
432 public Repeaters getRepeaters()
433 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
434 String json = invoke(HttpMethod.GET, repeaters, null, null);
436 Repeaters repeaters = gson.fromJson(json, Repeaters.class);
437 if (repeaters == null) {
438 throw new HubInvalidResponseException("Missing repeaters response");
440 List<RepeaterData> repeaterData = repeaters.repeaterData;
441 if (repeaterData == null) {
442 throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element");
445 } catch (JsonParseException e) {
446 throw new HubInvalidResponseException("Error parsing repeaters response", e);
451 * Fetches a JSON package that describes a specific repeater in the hub, and wraps it
452 * in a RepeaterData class instance
454 * @param repeaterId id of the repeater to be fetched
455 * @return RepeaterData class instance
456 * @throws HubInvalidResponseException if response is invalid
457 * @throws HubProcessingException if there is any processing error
458 * @throws HubMaintenanceException if the hub is down for maintenance
460 public RepeaterData getRepeater(int repeaterId)
461 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
462 String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null);
463 return repeaterDataFromJson(jsonResponse);
466 private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException {
468 Repeater repeater = gson.fromJson(json, Repeater.class);
469 if (repeater == null) {
470 throw new HubInvalidResponseException("Missing repeater response");
472 RepeaterData repeaterData = repeater.repeater;
473 if (repeaterData == null) {
474 throw new HubInvalidResponseException("Missing 'repeater.repeater' element");
477 } catch (JsonParseException e) {
478 throw new HubInvalidResponseException("Error parsing repeater response", e);
483 * Instructs the hub to identify a specific repeater by blinking
485 * @param repeaterId id of the repeater to be identified
486 * @return RepeaterData class instance
487 * @throws HubInvalidResponseException if response is invalid
488 * @throws HubProcessingException if there is any processing error
489 * @throws HubMaintenanceException if the hub is down for maintenance
491 public RepeaterData identifyRepeater(int repeaterId)
492 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
493 String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId,
494 Query.of("identify", Boolean.toString(true)), null);
495 return repeaterDataFromJson(jsonResponse);
499 * Enables or disables blinking for a repeater
501 * @param repeaterId id of the repeater for which to be enable or disable blinking
502 * @param enable true to enable blinking, false to disable
503 * @return RepeaterData class instance
504 * @throws HubInvalidResponseException if response is invalid
505 * @throws HubProcessingException if there is any processing error
506 * @throws HubMaintenanceException if the hub is down for maintenance
508 public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable)
509 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
510 String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable));
511 String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
512 return repeaterDataFromJson(jsonResponse);
516 * Sets color and brightness for a repeater
518 * @param repeaterId id of the repeater for which to set color and brightness
519 * @return RepeaterData class instance
520 * @throws HubInvalidResponseException if response is invalid
521 * @throws HubProcessingException if there is any processing error
522 * @throws HubMaintenanceException if the hub is down for maintenance
524 public RepeaterData setRepeaterColor(int repeaterId, Color color)
525 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
526 String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color));
527 String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
528 return repeaterDataFromJson(jsonResponse);
532 * Invoke a call on the hub server to retrieve information or send a command
534 * @param method GET or PUT
535 * @param url the host url to be called
536 * @param query the http query parameter
537 * @param jsonCommand the request command content (as a json string)
538 * @return the response content (as a json string)
539 * @throws HubMaintenanceException
540 * @throws HubProcessingException
542 private synchronized String invoke(HttpMethod method, String url, @Nullable Query query,
543 @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException {
544 if (logger.isTraceEnabled()) {
546 logger.trace("API command {} {}{}", method, url, query);
548 logger.trace("API command {} {}", method, url);
550 if (jsonCommand != null) {
551 logger.trace("JSON command = {}", jsonCommand);
554 Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*");
556 request.param(query.getKey(), query.getValue());
558 if (jsonCommand != null) {
559 request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand));
561 ContentResponse response;
563 response = request.send();
564 } catch (InterruptedException | TimeoutException | ExecutionException e) {
565 if (Instant.now().isBefore(maintenanceScheduledEnd)) {
566 // throw "softer" exception during maintenance window
567 logger.debug("Hub still undergoing maintenance");
568 throw new HubMaintenanceException("Hub still undergoing maintenance");
570 throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage()));
572 int statusCode = response.getStatus();
573 if (statusCode == HttpStatus.LOCKED_423) {
574 // set end of maintenance window, and throw a "softer" exception
575 maintenanceScheduledEnd = Instant.now().plusSeconds(maintenancePeriod);
576 logger.debug("Hub undergoing maintenance");
577 throw new HubMaintenanceException("Hub undergoing maintenance");
579 if (statusCode != HttpStatus.OK_200) {
580 logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason());
581 throw new HubProcessingException(String.format("HTTP %d error", statusCode));
583 String jsonResponse = response.getContentAsString();
584 if ("".equals(jsonResponse)) {
585 logger.warn("Hub returned no content");
586 throw new HubProcessingException("Missing response entity");
588 if (logger.isTraceEnabled()) {
589 logger.trace("JSON response = {}", jsonResponse);
595 * Fetches a JSON package that describes a specific shade in the hub, and wraps it
596 * in a Shade class instance
598 * @param shadeId id of the shade to be fetched
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 getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
606 HubMaintenanceException, HubShadeTimeoutException {
607 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null);
608 return shadeDataFromJson(jsonResponse);
612 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
613 * a specific shade's position; fetches a JSON package that describes that shade,
614 * and wraps it in a Shade class instance
616 * @param shadeId id of the shade to be refreshed
617 * @return ShadeData class instance
618 * @throws HubInvalidResponseException if response is invalid
619 * @throws HubProcessingException if there is any processing error
620 * @throws HubMaintenanceException if the hub is down for maintenance
621 * @throws HubShadeTimeoutException if the shade did not respond to a request
623 public ShadeData refreshShadePosition(int shadeId)
624 throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
625 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
626 Query.of("refresh", Boolean.toString(true)), null);
627 return shadeDataFromJson(jsonResponse);
631 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
632 * a specific shade's survey data, which will also refresh signal strength;
633 * fetches a JSON package that describes that survey, and wraps it in a Survey
636 * @param shadeId id of the shade to be surveyed
637 * @return Survey class instance
638 * @throws HubInvalidResponseException if response is invalid
639 * @throws HubProcessingException if there is any processing error
640 * @throws HubMaintenanceException if the hub is down for maintenance
642 public Survey getShadeSurvey(int shadeId)
643 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
644 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
645 Query.of("survey", Boolean.toString(true)), null);
647 Survey survey = gson.fromJson(jsonResponse, Survey.class);
648 if (survey == null) {
649 throw new HubInvalidResponseException("Missing survey response");
652 } catch (JsonParseException e) {
653 throw new HubInvalidResponseException("Error parsing survey response", e);
658 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
659 * a specific shade's battery level; fetches a JSON package that describes that shade,
660 * and wraps it in a Shade class instance
662 * @param shadeId id of the shade to be refreshed
663 * @return ShadeData class instance
664 * @throws HubInvalidResponseException if response is invalid
665 * @throws HubProcessingException if there is any processing error
666 * @throws HubMaintenanceException if the hub is down for maintenance
667 * @throws HubShadeTimeoutException if the shade did not respond to a request
669 public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException,
670 HubMaintenanceException, HubShadeTimeoutException {
671 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
672 Query.of("updateBatteryLevel", Boolean.toString(true)), null);
673 return shadeDataFromJson(jsonResponse);