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.SurveyData;
32 import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking;
33 import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor;
34 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate;
35 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog;
36 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
37 import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
38 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion;
39 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersions;
40 import org.openhab.binding.hdpowerview.internal.api.responses.Repeater;
41 import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
42 import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters;
43 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
44 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
45 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
46 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
47 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents;
48 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent;
49 import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
50 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
51 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
52 import org.openhab.binding.hdpowerview.internal.api.responses.Survey;
53 import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
54 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
55 import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
56 import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 import com.google.gson.Gson;
61 import com.google.gson.JsonElement;
62 import com.google.gson.JsonObject;
63 import com.google.gson.JsonParseException;
64 import com.google.gson.JsonParser;
67 * JAX-RS targets for communicating with an HD PowerView hub
69 * @author Andy Lintner - Initial contribution
70 * @author Andrew Fiddian-Green - Added support for secondary rail positions
71 * @author Jacob Laursen - Added support for scene groups and automations
74 public class HDPowerViewWebTargets {
76 private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class);
79 * the hub returns a 423 error (resource locked) daily just after midnight;
80 * which means it is temporarily undergoing maintenance; so we use "soft"
81 * exception handling during the five minute maintenance period after a 423
84 private final int maintenancePeriod = 300;
85 private Instant maintenanceScheduledEnd = Instant.now().minusSeconds(2 * maintenancePeriod);
87 private final String base;
88 private final String firmwareVersion;
89 private final String shades;
90 private final String sceneActivate;
91 private final String scenes;
92 private final String sceneCollectionActivate;
93 private final String sceneCollections;
94 private final String scheduledEvents;
95 private final String repeaters;
97 private final Gson gson = new Gson();
98 private final HttpClient httpClient;
101 * private helper class for passing http url query parameters
103 private static class Query {
104 private final String key;
105 private final String value;
107 private Query(String key, String value) {
112 public static Query of(String key, String value) {
113 return new Query(key, value);
116 public String getKey() {
120 public String getValue() {
125 public String toString() {
126 return String.format("?%s=%s", key, value);
131 * Initialize the web targets
133 * @param httpClient the HTTP client (the binding)
134 * @param ipAddress the IP address of the server (the hub)
136 public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) {
137 base = "http://" + ipAddress + "/api/";
138 shades = base + "shades/";
139 firmwareVersion = base + "fwversion/";
140 sceneActivate = base + "scenes";
141 scenes = base + "scenes/";
143 // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections".
144 sceneCollectionActivate = base + "scenecollections";
145 sceneCollections = base + "scenecollections/";
147 scheduledEvents = base + "scheduledevents";
149 repeaters = base + "repeaters/";
151 this.httpClient = httpClient;
155 * Fetches a JSON package with firmware information for the hub.
157 * @return FirmwareVersions class instance
158 * @throws HubInvalidResponseException if response is invalid
159 * @throws HubProcessingException if there is any processing error
160 * @throws HubMaintenanceException if the hub is down for maintenance
162 public FirmwareVersions getFirmwareVersions()
163 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
164 String json = invoke(HttpMethod.GET, firmwareVersion, null, null);
166 FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class);
167 if (firmwareVersion == null) {
168 throw new HubInvalidResponseException("Missing firmware response");
170 FirmwareVersions firmwareVersions = firmwareVersion.firmware;
171 if (firmwareVersions == null) {
172 throw new HubInvalidResponseException("Missing 'firmware' element");
174 return firmwareVersions;
175 } catch (JsonParseException e) {
176 throw new HubInvalidResponseException("Error parsing firmware response", e);
181 * Fetches a JSON package that describes all shades in the hub, and wraps it in
182 * a Shades class instance
184 * @return Shades class instance
185 * @throws HubInvalidResponseException if response is invalid
186 * @throws HubProcessingException if there is any processing error
187 * @throws HubMaintenanceException if the hub is down for maintenance
189 public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
190 String json = invoke(HttpMethod.GET, shades, null, null);
192 Shades shades = gson.fromJson(json, Shades.class);
193 if (shades == null) {
194 throw new HubInvalidResponseException("Missing shades response");
196 List<ShadeData> shadeData = shades.shadeData;
197 if (shadeData == null) {
198 throw new HubInvalidResponseException("Missing 'shades.shadeData' element");
201 } catch (JsonParseException e) {
202 throw new HubInvalidResponseException("Error parsing shades response", e);
207 * Instructs the hub to move a specific shade
209 * @param shadeId id of the shade to be moved
210 * @param position instance of ShadePosition containing the new position
211 * @return ShadeData class instance (with new position)
212 * @throws HubInvalidResponseException if response is invalid
213 * @throws HubProcessingException if there is any processing error
214 * @throws HubMaintenanceException if the hub is down for maintenance
215 * @throws HubShadeTimeoutException if the shade did not respond to a request
217 public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException,
218 HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
219 String jsonRequest = gson.toJson(new ShadeMove(position));
220 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
221 return shadeDataFromJson(jsonResponse);
224 private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException {
226 Shade shade = gson.fromJson(json, Shade.class);
228 throw new HubInvalidResponseException("Missing shade response");
230 ShadeData shadeData = shade.shade;
231 if (shadeData == null) {
232 throw new HubInvalidResponseException("Missing 'shade.shade' element");
234 if (Boolean.TRUE.equals(shadeData.timedOut)) {
235 throw new HubShadeTimeoutException("Timeout when sending request to the shade");
238 } catch (JsonParseException e) {
239 throw new HubInvalidResponseException("Error parsing shade response", e);
244 * Instructs the hub to stop movement of a specific shade
246 * @param shadeId id of the shade to be stopped
247 * @return ShadeData class instance (new position cannot be relied upon)
248 * @throws HubInvalidResponseException if response is invalid
249 * @throws HubProcessingException if there is any processing error
250 * @throws HubMaintenanceException if the hub is down for maintenance
251 * @throws HubShadeTimeoutException if the shade did not respond to a request
253 public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
254 HubMaintenanceException, HubShadeTimeoutException {
255 String jsonRequest = gson.toJson(new ShadeStop());
256 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
257 return shadeDataFromJson(jsonResponse);
261 * Instructs the hub to jog a specific shade
263 * @param shadeId id of the shade to be jogged
264 * @return ShadeData class instance
265 * @throws HubInvalidResponseException if response is invalid
266 * @throws HubProcessingException if there is any processing error
267 * @throws HubMaintenanceException if the hub is down for maintenance
268 * @throws HubShadeTimeoutException if the shade did not respond to a request
270 public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
271 HubMaintenanceException, HubShadeTimeoutException {
272 String jsonRequest = gson.toJson(new ShadeJog());
273 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
274 return shadeDataFromJson(jsonResponse);
278 * Instructs the hub to calibrate a specific shade
280 * @param shadeId id of the shade to be calibrated
281 * @return ShadeData class instance
282 * @throws HubInvalidResponseException if response is invalid
283 * @throws HubProcessingException if there is any processing error
284 * @throws HubMaintenanceException if the hub is down for maintenance
285 * @throws HubShadeTimeoutException if the shade did not respond to a request
287 public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
288 HubMaintenanceException, HubShadeTimeoutException {
289 String jsonRequest = gson.toJson(new ShadeCalibrate());
290 String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest);
291 return shadeDataFromJson(jsonResponse);
295 * Fetches a JSON package that describes all scenes in the hub, and wraps it in
296 * a Scenes class instance
298 * @return Scenes class instance
299 * @throws HubInvalidResponseException if response is invalid
300 * @throws HubProcessingException if there is any processing error
301 * @throws HubMaintenanceException if the hub is down for maintenance
303 public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
304 String json = invoke(HttpMethod.GET, scenes, null, null);
306 Scenes scenes = gson.fromJson(json, Scenes.class);
307 if (scenes == null) {
308 throw new HubInvalidResponseException("Missing scenes response");
310 List<Scene> sceneData = scenes.sceneData;
311 if (sceneData == null) {
312 throw new HubInvalidResponseException("Missing 'scenes.sceneData' element");
315 } catch (JsonParseException e) {
316 throw new HubInvalidResponseException("Error parsing scenes response", e);
321 * Instructs the hub to execute a specific scene
323 * @param sceneId id of the scene to be executed
324 * @throws HubProcessingException if there is any processing error
325 * @throws HubMaintenanceException if the hub is down for maintenance
327 public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException {
328 invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null);
332 * Fetches a JSON package that describes all scene collections in the hub, and wraps it in
333 * a SceneCollections class instance
335 * @return SceneCollections class instance
336 * @throws HubInvalidResponseException if response is invalid
337 * @throws HubProcessingException if there is any processing error
338 * @throws HubMaintenanceException if the hub is down for maintenance
340 public SceneCollections getSceneCollections()
341 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
342 String json = invoke(HttpMethod.GET, sceneCollections, null, null);
344 SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
345 if (sceneCollections == null) {
346 throw new HubInvalidResponseException("Missing sceneCollections response");
348 List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
349 if (sceneCollectionData == null) {
350 throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element");
352 return sceneCollections;
353 } catch (JsonParseException e) {
354 throw new HubInvalidResponseException("Error parsing sceneCollections response", e);
359 * Instructs the hub to execute a specific scene collection
361 * @param sceneCollectionId id of the scene collection to be executed
362 * @throws HubProcessingException if there is any processing error
363 * @throws HubMaintenanceException if the hub is down for maintenance
365 public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException {
366 invoke(HttpMethod.GET, sceneCollectionActivate,
367 Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null);
371 * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in
372 * a ScheduledEvents class instance
374 * @return ScheduledEvents class instance
375 * @throws HubInvalidResponseException if response is invalid
376 * @throws HubProcessingException if there is any processing error
377 * @throws HubMaintenanceException if the hub is down for maintenance
379 public ScheduledEvents getScheduledEvents()
380 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
381 String json = invoke(HttpMethod.GET, scheduledEvents, null, null);
383 ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class);
384 if (scheduledEvents == null) {
385 throw new HubInvalidResponseException("Missing scheduledEvents response");
387 List<ScheduledEvent> scheduledEventData = scheduledEvents.scheduledEventData;
388 if (scheduledEventData == null) {
389 throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element");
391 return scheduledEvents;
392 } catch (JsonParseException e) {
393 throw new HubInvalidResponseException("Error parsing scheduledEvents response", e);
398 * Enables or disables a scheduled event in the hub.
400 * @param scheduledEventId id of the scheduled event to be enabled or disabled
401 * @param enable true to enable scheduled event, false to disable
402 * @throws HubInvalidResponseException if response is invalid
403 * @throws HubProcessingException if there is any processing error
404 * @throws HubMaintenanceException if the hub is down for maintenance
406 public void enableScheduledEvent(int scheduledEventId, boolean enable)
407 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
408 String uri = scheduledEvents + "/" + scheduledEventId;
409 String jsonResponse = invoke(HttpMethod.GET, uri, null, null);
411 JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject();
412 JsonElement scheduledEventElement = jsonObject.get("scheduledEvent");
413 if (scheduledEventElement == null) {
414 throw new HubInvalidResponseException("Missing 'scheduledEvent' element");
416 JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject();
417 scheduledEventObject.addProperty("enabled", enable);
418 invoke(HttpMethod.PUT, uri, null, jsonObject.toString());
419 } catch (JsonParseException | IllegalStateException e) {
420 throw new HubInvalidResponseException("Error parsing scheduledEvent response", e);
425 * Fetches a JSON package that describes all repeaters in the hub, and wraps it in
426 * a Repeaters class instance
428 * @return Repeaters class instance
429 * @throws HubInvalidResponseException if response is invalid
430 * @throws HubProcessingException if there is any processing error
431 * @throws HubMaintenanceException if the hub is down for maintenance
433 public Repeaters getRepeaters()
434 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
435 String json = invoke(HttpMethod.GET, repeaters, null, null);
437 Repeaters repeaters = gson.fromJson(json, Repeaters.class);
438 if (repeaters == null) {
439 throw new HubInvalidResponseException("Missing repeaters response");
441 List<RepeaterData> repeaterData = repeaters.repeaterData;
442 if (repeaterData == null) {
443 throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element");
446 } catch (JsonParseException e) {
447 throw new HubInvalidResponseException("Error parsing repeaters response", e);
452 * Fetches a JSON package that describes a specific repeater in the hub, and wraps it
453 * in a RepeaterData class instance
455 * @param repeaterId id of the repeater to be fetched
456 * @return RepeaterData class instance
457 * @throws HubInvalidResponseException if response is invalid
458 * @throws HubProcessingException if there is any processing error
459 * @throws HubMaintenanceException if the hub is down for maintenance
461 public RepeaterData getRepeater(int repeaterId)
462 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
463 String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null);
464 return repeaterDataFromJson(jsonResponse);
467 private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException {
469 Repeater repeater = gson.fromJson(json, Repeater.class);
470 if (repeater == null) {
471 throw new HubInvalidResponseException("Missing repeater response");
473 RepeaterData repeaterData = repeater.repeater;
474 if (repeaterData == null) {
475 throw new HubInvalidResponseException("Missing 'repeater.repeater' element");
478 } catch (JsonParseException e) {
479 throw new HubInvalidResponseException("Error parsing repeater response", e);
484 * Instructs the hub to identify a specific repeater by blinking
486 * @param repeaterId id of the repeater to be identified
487 * @return RepeaterData class instance
488 * @throws HubInvalidResponseException if response is invalid
489 * @throws HubProcessingException if there is any processing error
490 * @throws HubMaintenanceException if the hub is down for maintenance
492 public RepeaterData identifyRepeater(int repeaterId)
493 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
494 String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId,
495 Query.of("identify", Boolean.toString(true)), null);
496 return repeaterDataFromJson(jsonResponse);
500 * Enables or disables blinking for a repeater
502 * @param repeaterId id of the repeater for which to be enable or disable blinking
503 * @param enable true to enable blinking, false to disable
504 * @return RepeaterData class instance
505 * @throws HubInvalidResponseException if response is invalid
506 * @throws HubProcessingException if there is any processing error
507 * @throws HubMaintenanceException if the hub is down for maintenance
509 public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable)
510 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
511 String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable));
512 String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
513 return repeaterDataFromJson(jsonResponse);
517 * Sets color and brightness for a repeater
519 * @param repeaterId id of the repeater for which to set color and brightness
520 * @return RepeaterData class instance
521 * @throws HubInvalidResponseException if response is invalid
522 * @throws HubProcessingException if there is any processing error
523 * @throws HubMaintenanceException if the hub is down for maintenance
525 public RepeaterData setRepeaterColor(int repeaterId, Color color)
526 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
527 String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color));
528 String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
529 return repeaterDataFromJson(jsonResponse);
533 * Invoke a call on the hub server to retrieve information or send a command
535 * @param method GET or PUT
536 * @param url the host url to be called
537 * @param query the http query parameter
538 * @param jsonCommand the request command content (as a json string)
539 * @return the response content (as a json string)
540 * @throws HubMaintenanceException
541 * @throws HubProcessingException
543 private synchronized String invoke(HttpMethod method, String url, @Nullable Query query,
544 @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException {
545 if (logger.isTraceEnabled()) {
547 logger.trace("API command {} {}{}", method, url, query);
549 logger.trace("API command {} {}", method, url);
551 if (jsonCommand != null) {
552 logger.trace("JSON command = {}", jsonCommand);
555 Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*");
557 request.param(query.getKey(), query.getValue());
559 if (jsonCommand != null) {
560 request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand));
562 ContentResponse response;
564 response = request.send();
565 } catch (InterruptedException e) {
566 Thread.currentThread().interrupt();
567 throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage()));
568 } catch (TimeoutException | ExecutionException e) {
569 if (Instant.now().isBefore(maintenanceScheduledEnd)) {
570 // throw "softer" exception during maintenance window
571 logger.debug("Hub still undergoing maintenance");
572 throw new HubMaintenanceException("Hub still undergoing maintenance");
574 throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage()));
576 int statusCode = response.getStatus();
577 if (statusCode == HttpStatus.LOCKED_423) {
578 // set end of maintenance window, and throw a "softer" exception
579 maintenanceScheduledEnd = Instant.now().plusSeconds(maintenancePeriod);
580 logger.debug("Hub undergoing maintenance");
581 throw new HubMaintenanceException("Hub undergoing maintenance");
583 if (statusCode != HttpStatus.OK_200) {
584 logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason());
585 throw new HubProcessingException(String.format("HTTP %d error", statusCode));
587 String jsonResponse = response.getContentAsString();
588 if ("".equals(jsonResponse)) {
589 logger.warn("Hub returned no content");
590 throw new HubProcessingException("Missing response entity");
592 if (logger.isTraceEnabled()) {
593 logger.trace("JSON response = {}", jsonResponse);
599 * Fetches a JSON package that describes a specific shade in the hub, and wraps it
600 * in a Shade class instance
602 * @param shadeId id of the shade to be fetched
603 * @return ShadeData class instance
604 * @throws HubInvalidResponseException if response is invalid
605 * @throws HubProcessingException if there is any processing error
606 * @throws HubMaintenanceException if the hub is down for maintenance
607 * @throws HubShadeTimeoutException if the shade did not respond to a request
609 public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException,
610 HubMaintenanceException, HubShadeTimeoutException {
611 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null);
612 return shadeDataFromJson(jsonResponse);
616 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
617 * a specific shade's position; fetches a JSON package that describes that shade,
618 * and wraps it in a Shade class instance
620 * @param shadeId id of the shade to be refreshed
621 * @return ShadeData class instance
622 * @throws HubInvalidResponseException if response is invalid
623 * @throws HubProcessingException if there is any processing error
624 * @throws HubMaintenanceException if the hub is down for maintenance
625 * @throws HubShadeTimeoutException if the shade did not respond to a request
627 public ShadeData refreshShadePosition(int shadeId)
628 throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException {
629 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
630 Query.of("refresh", Boolean.toString(true)), null);
631 return shadeDataFromJson(jsonResponse);
635 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
636 * a specific shade's survey data, which will also refresh signal strength;
637 * fetches a JSON package that describes that survey, and wraps it in a Survey
640 * @param shadeId id of the shade to be surveyed
641 * @return List of SurveyData class instances
642 * @throws HubInvalidResponseException if response is invalid
643 * @throws HubProcessingException if there is any processing error
644 * @throws HubMaintenanceException if the hub is down for maintenance
646 public List<SurveyData> getShadeSurvey(int shadeId)
647 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
648 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
649 Query.of("survey", Boolean.toString(true)), null);
651 Survey survey = gson.fromJson(jsonResponse, Survey.class);
652 if (survey == null) {
653 throw new HubInvalidResponseException("Missing survey response");
655 List<SurveyData> surveyData = survey.surveyData;
656 if (surveyData == null) {
657 throw new HubInvalidResponseException("Missing 'survey.surveyData' element");
660 } catch (JsonParseException e) {
661 throw new HubInvalidResponseException("Error parsing survey response", e);
666 * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
667 * a specific shade's battery level; fetches a JSON package that describes that shade,
668 * and wraps it in a Shade class instance
670 * @param shadeId id of the shade to be refreshed
671 * @return ShadeData class instance
672 * @throws HubInvalidResponseException if response is invalid
673 * @throws HubProcessingException if there is any processing error
674 * @throws HubMaintenanceException if the hub is down for maintenance
675 * @throws HubShadeTimeoutException if the shade did not respond to a request
677 public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException,
678 HubMaintenanceException, HubShadeTimeoutException {
679 String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
680 Query.of("updateBatteryLevel", Boolean.toString(true)), null);
681 return shadeDataFromJson(jsonResponse);