]> git.basschouten.com Git - openhab-addons.git/blob
f8fedf26f03eafc02cf0ac6bd5a5e0427b17be7e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.innogysmarthome.internal.client;
14
15 import static org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants.*;
16 import static org.openhab.binding.innogysmarthome.internal.client.Constants.*;
17
18 import java.io.IOException;
19 import java.net.URI;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.TimeoutException;
28
29 import org.apache.commons.lang.StringUtils;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.eclipse.jetty.client.HttpClient;
33 import org.eclipse.jetty.client.api.ContentResponse;
34 import org.eclipse.jetty.client.api.Request;
35 import org.eclipse.jetty.client.util.StringContentProvider;
36 import org.eclipse.jetty.http.HttpHeader;
37 import org.eclipse.jetty.http.HttpMethod;
38 import org.eclipse.jetty.http.HttpStatus;
39 import org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants;
40 import org.openhab.binding.innogysmarthome.internal.client.entity.StatusResponse;
41 import org.openhab.binding.innogysmarthome.internal.client.entity.action.Action;
42 import org.openhab.binding.innogysmarthome.internal.client.entity.action.ShutterAction;
43 import org.openhab.binding.innogysmarthome.internal.client.entity.action.StateActionSetter;
44 import org.openhab.binding.innogysmarthome.internal.client.entity.capability.Capability;
45 import org.openhab.binding.innogysmarthome.internal.client.entity.capability.CapabilityState;
46 import org.openhab.binding.innogysmarthome.internal.client.entity.device.Device;
47 import org.openhab.binding.innogysmarthome.internal.client.entity.device.DeviceState;
48 import org.openhab.binding.innogysmarthome.internal.client.entity.device.Gateway;
49 import org.openhab.binding.innogysmarthome.internal.client.entity.device.State;
50 import org.openhab.binding.innogysmarthome.internal.client.entity.error.ErrorResponse;
51 import org.openhab.binding.innogysmarthome.internal.client.entity.link.Link;
52 import org.openhab.binding.innogysmarthome.internal.client.entity.location.Location;
53 import org.openhab.binding.innogysmarthome.internal.client.entity.message.Message;
54 import org.openhab.binding.innogysmarthome.internal.client.exception.ApiException;
55 import org.openhab.binding.innogysmarthome.internal.client.exception.AuthenticationException;
56 import org.openhab.binding.innogysmarthome.internal.client.exception.ControllerOfflineException;
57 import org.openhab.binding.innogysmarthome.internal.client.exception.InvalidActionTriggeredException;
58 import org.openhab.binding.innogysmarthome.internal.client.exception.RemoteAccessNotAllowedException;
59 import org.openhab.binding.innogysmarthome.internal.client.exception.ServiceUnavailableException;
60 import org.openhab.binding.innogysmarthome.internal.client.exception.SessionExistsException;
61 import org.openhab.binding.innogysmarthome.internal.client.exception.SessionNotFoundException;
62 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
63 import org.openhab.core.auth.client.oauth2.OAuthClientService;
64 import org.openhab.core.auth.client.oauth2.OAuthException;
65 import org.openhab.core.auth.client.oauth2.OAuthResponseException;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import com.google.gson.Gson;
70 import com.google.gson.GsonBuilder;
71 import com.google.gson.JsonSyntaxException;
72
73 /**
74  * The main client that handles the communication with the innogy SmartHome API service.
75  *
76  * @author Oliver Kuhl - Initial contribution
77  * @author Hilbrand Bouwkamp - Refactored to use openHAB http and oauth2 libraries
78  *
79  */
80 @NonNullByDefault
81 public class InnogyClient {
82
83     private static final String BEARER = "Bearer ";
84     private static final String CONTENT_TYPE = "application/json";
85     private static final int HTTP_REQUEST_TIMEOUT_SECONDS = 10;
86     private static final int HTTP_REQUEST_IDLE_TIMEOUT_SECONDS = 20;
87
88     private final Logger logger = LoggerFactory.getLogger(InnogyClient.class);
89
90     /**
91      * date format as used in json in API. Example: 2016-07-11T10:55:52.3863424Z
92      */
93     private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
94
95     private final Gson gson = new GsonBuilder().setDateFormat(DATE_FORMAT).create();
96     private final OAuthClientService oAuthService;
97     private final HttpClient httpClient;
98     private @Nullable Gateway bridgeDetails;
99     private String configVersion = "";
100
101     public InnogyClient(final OAuthClientService oAuthService, final HttpClient httpClient) {
102         this.oAuthService = oAuthService;
103         this.httpClient = httpClient;
104     }
105
106     /**
107      * @return the bridgeInfo
108      */
109     public @Nullable Gateway getBridgeDetails() {
110         return bridgeDetails;
111     }
112
113     /**
114      * Gets the status
115      *
116      * As the API returns the details of the SmartHome controller (SHC), the data is saved in {@link #bridgeDetails} and
117      * the {@link #configVersion} is set.
118      *
119      * @throws SessionExistsException thrown, if a session already exists
120      * @throws IOException
121      * @throws ApiException
122      */
123     public void refreshStatus() throws IOException, ApiException, AuthenticationException {
124         logger.debug("Get innogy SmartHome status...");
125         final StatusResponse status = executeGet(API_URL_STATUS, StatusResponse.class);
126
127         bridgeDetails = status.gateway;
128         configVersion = bridgeDetails.getConfigVersion();
129
130         logger.debug("innogy SmartHome Status loaded. Configuration version is {}.", configVersion);
131     }
132
133     /**
134      * Executes a HTTP GET request with default headers and returns data as object of type T.
135      *
136      * @param url
137      * @param clazz type of data to return
138      * @return
139      * @throws IOException
140      * @throws AuthenticationException
141      * @throws ApiException
142      */
143     private <T> T executeGet(final String url, final Class<T> clazz)
144             throws IOException, AuthenticationException, ApiException {
145         final ContentResponse response = request(httpClient.newRequest(url).method(HttpMethod.GET));
146
147         return gson.fromJson(response.getContentAsString(), clazz);
148     }
149
150     /**
151      * Executes a HTTP GET request with default headers and returns data as List of type T.
152      *
153      * @param url
154      * @param clazz array type of data to return as list
155      * @throws IOException
156      * @throws AuthenticationException
157      * @throws ApiException
158      */
159     private <T> List<T> executeGetList(final String url, final Class<T[]> clazz)
160             throws IOException, AuthenticationException, ApiException {
161         return Arrays.asList(executeGet(url, clazz));
162     }
163
164     /**
165      * Executes a HTTP POST request with the given {@link Action} as content.
166      *
167      * @param url
168      * @param action
169      * @return
170      * @throws IOException
171      * @throws AuthenticationException
172      * @throws ApiException
173      */
174     private ContentResponse executePost(final String url, final Action action)
175             throws IOException, AuthenticationException, ApiException {
176         final String json = gson.toJson(action);
177         logger.debug("Action {} JSON: {}", action.getType(), json);
178
179         return request(httpClient.newRequest(url).method(HttpMethod.POST)
180                 .content(new StringContentProvider(json), CONTENT_TYPE).accept(CONTENT_TYPE));
181     }
182
183     private ContentResponse request(final Request request) throws IOException, AuthenticationException, ApiException {
184         final ContentResponse response;
185         try {
186             final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
187
188             response = request.header(HttpHeader.ACCEPT, CONTENT_TYPE)
189                     .header(HttpHeader.AUTHORIZATION, BEARER + accessTokenResponse.getAccessToken())
190                     .idleTimeout(HTTP_REQUEST_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
191                     .timeout(HTTP_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS).send();
192         } catch (InterruptedException | TimeoutException | ExecutionException e) {
193             throw new IOException(e);
194         }
195         handleResponseErrors(response, request.getURI());
196         return response;
197     }
198
199     public AccessTokenResponse getAccessTokenResponse() throws AuthenticationException, IOException {
200         final AccessTokenResponse accessTokenResponse;
201         try {
202             accessTokenResponse = oAuthService.getAccessTokenResponse();
203         } catch (OAuthException | OAuthResponseException e) {
204             throw new AuthenticationException("Error fetching access token: " + e.getMessage());
205         }
206         if (accessTokenResponse == null || StringUtils.isBlank(accessTokenResponse.getAccessToken())) {
207             throw new AuthenticationException("No innogy accesstoken. Is this thing authorized?");
208         }
209         return accessTokenResponse;
210     }
211
212     /**
213      * Handles errors from the {@link ContentResponse} and throws the following errors:
214      *
215      * @param response
216      * @param uri uri of api call made
217      * @throws SessionExistsException
218      * @throws SessionNotFoundException
219      * @throws ControllerOfflineException thrown, if the innogy SmartHome controller (SHC) is offline.
220      * @throws IOException
221      * @throws ApiException
222      * @throws AuthenticationException
223      */
224     private void handleResponseErrors(final ContentResponse response, final URI uri)
225             throws IOException, ApiException, AuthenticationException {
226         String content = "";
227
228         switch (response.getStatus()) {
229             case HttpStatus.OK_200:
230                 logger.debug("Statuscode is OK: [{}]", uri);
231                 return;
232             case HttpStatus.SERVICE_UNAVAILABLE_503:
233                 logger.debug("innogy service is unavailabe (503).");
234                 throw new ServiceUnavailableException("innogy service is unavailabe (503).");
235             default:
236                 logger.debug("Statuscode {} is NOT OK: [{}]", response.getStatus(), uri);
237                 try {
238                     content = response.getContentAsString();
239                     logger.trace("Response error content: {}", content);
240                     final ErrorResponse error = gson.fromJson(content, ErrorResponse.class);
241
242                     if (error == null) {
243                         logger.debug("Error without JSON message, code: {} / message: {}", response.getStatus(),
244                                 response.getReason());
245                         throw new ApiException("Error code: " + response.getStatus());
246                     }
247
248                     switch (error.getCode()) {
249                         case ErrorResponse.ERR_SESSION_EXISTS:
250                             logger.debug("Session exists: {}", error);
251                             throw new SessionExistsException(error.getDescription());
252                         case ErrorResponse.ERR_SESSION_NOT_FOUND:
253                             logger.debug("Session not found: {}", error);
254                             throw new SessionNotFoundException(error.getDescription());
255                         case ErrorResponse.ERR_CONTROLLER_OFFLINE:
256                             logger.debug("Controller offline: {}", error);
257                             throw new ControllerOfflineException(error.getDescription());
258                         case ErrorResponse.ERR_REMOTE_ACCESS_NOT_ALLOWED:
259                             logger.debug(
260                                     "Remote access not allowed. Access is allowed only from the SHC device network.");
261                             throw new RemoteAccessNotAllowedException(
262                                     "Remote access not allowed. Access is allowed only from the SHC device network.");
263                         case ErrorResponse.ERR_INVALID_ACTION_TRIGGERED:
264                             logger.debug("Invalid action triggered. Message: {}", error.getMessages());
265                             throw new InvalidActionTriggeredException(error.getDescription());
266                         default:
267                             logger.debug("Unknown error: {}", error);
268                             throw new ApiException("Unknown error: " + error);
269                     }
270                 } catch (final JsonSyntaxException e) {
271                     throw new ApiException("Invalid JSON syntax in error response: " + content);
272                 }
273         }
274     }
275
276     /**
277      * Sets a new state of a SwitchActuator.
278      *
279      * @param capabilityId
280      * @param state
281      * @throws IOException
282      * @throws ApiException
283      */
284     public void setSwitchActuatorState(final String capabilityId, final boolean state)
285             throws IOException, ApiException, AuthenticationException {
286         executePost(API_URL_ACTION, new StateActionSetter(capabilityId, Capability.TYPE_SWITCHACTUATOR, state));
287     }
288
289     /**
290      * Sets the dimmer level of a DimmerActuator.
291      *
292      * @param capabilityId
293      * @param dimLevel
294      * @throws IOException
295      * @throws ApiException
296      */
297     public void setDimmerActuatorState(final String capabilityId, final int dimLevel)
298             throws IOException, ApiException, AuthenticationException {
299         executePost(API_URL_ACTION, new StateActionSetter(capabilityId, Capability.TYPE_DIMMERACTUATOR, dimLevel));
300     }
301
302     /**
303      * Sets the roller shutter level of a RollerShutterActuator.
304      *
305      * @param capabilityId
306      * @param rollerShutterLevel
307      * @throws IOException
308      * @throws ApiException
309      * @throws AuthenticationException
310      */
311     public void setRollerShutterActuatorState(final String capabilityId, final int rollerShutterLevel)
312             throws IOException, ApiException, AuthenticationException {
313         executePost(API_URL_ACTION,
314                 new StateActionSetter(capabilityId, Capability.TYPE_ROLLERSHUTTERACTUATOR, rollerShutterLevel));
315     }
316
317     /**
318      * Starts or stops moving a RollerShutterActuator
319      *
320      * @param capabilityId
321      * @param rollerShutterAction
322      * @throws IOException
323      * @throws ApiException
324      * @throws AuthenticationException
325      */
326     public void setRollerShutterAction(final String capabilityId,
327             final ShutterAction.ShutterActions rollerShutterAction)
328             throws IOException, ApiException, AuthenticationException {
329         executePost(API_URL_ACTION, new ShutterAction(capabilityId, rollerShutterAction));
330     }
331
332     /**
333      * Sets a new state of a VariableActuator.
334      *
335      * @param capabilityId
336      * @param state
337      * @throws IOException
338      * @throws ApiException
339      */
340     public void setVariableActuatorState(final String capabilityId, final boolean state)
341             throws IOException, ApiException, AuthenticationException {
342         executePost(API_URL_ACTION, new StateActionSetter(capabilityId, Capability.TYPE_VARIABLEACTUATOR, state));
343     }
344
345     /**
346      * Sets the point temperature.
347      *
348      * @param capabilityId
349      * @param pointTemperature
350      * @throws IOException
351      * @throws ApiException
352      */
353     public void setPointTemperatureState(final String capabilityId, final double pointTemperature)
354             throws IOException, ApiException, AuthenticationException {
355         executePost(API_URL_ACTION,
356                 new StateActionSetter(capabilityId, Capability.TYPE_THERMOSTATACTUATOR, pointTemperature));
357     }
358
359     /**
360      * Sets the operation mode to "Auto" or "Manu".
361      *
362      * @param capabilityId
363      * @param autoMode
364      * @throws IOException
365      * @throws ApiException
366      */
367     public void setOperationMode(final String capabilityId, final boolean autoMode)
368             throws IOException, ApiException, AuthenticationException {
369         executePost(API_URL_ACTION,
370                 new StateActionSetter(capabilityId, Capability.TYPE_THERMOSTATACTUATOR,
371                         autoMode ? CapabilityState.STATE_VALUE_OPERATION_MODE_AUTO
372                                 : CapabilityState.STATE_VALUE_OPERATION_MODE_MANUAL));
373     }
374
375     /**
376      * Sets the alarm state.
377      *
378      * @param capabilityId
379      * @param alarmState
380      * @throws IOException
381      * @throws ApiException
382      */
383     public void setAlarmActuatorState(final String capabilityId, final boolean alarmState)
384             throws IOException, ApiException, AuthenticationException {
385         executePost(API_URL_ACTION, new StateActionSetter(capabilityId, Capability.TYPE_ALARMACTUATOR, alarmState));
386     }
387
388     /**
389      * Load the device and returns a {@link List} of {@link Device}s..
390      *
391      * @return List of Devices
392      * @throws IOException
393      * @throws ApiException
394      */
395     public List<Device> getDevices() throws IOException, ApiException, AuthenticationException {
396         logger.debug("Loading innogy devices...");
397         return executeGetList(API_URL_DEVICE, Device[].class);
398     }
399
400     /**
401      * Loads the {@link Device} with the given deviceId.
402      *
403      * @param deviceId
404      * @return
405      * @throws IOException
406      * @throws ApiException
407      */
408     public Device getDeviceById(final String deviceId) throws IOException, ApiException, AuthenticationException {
409         logger.debug("Loading device with id {}...", deviceId);
410         return executeGet(API_URL_DEVICE_ID.replace("{id}", deviceId), Device.class);
411     }
412
413     /**
414      * Returns a {@link List} of all {@link Device}s with the full configuration details, {@link Capability}s and
415      * states. Calling this may take a while...
416      *
417      * @return
418      * @throws IOException
419      * @throws ApiException
420      */
421     public List<Device> getFullDevices() throws IOException, ApiException, AuthenticationException {
422         // LOCATIONS
423         final List<Location> locationList = getLocations();
424         final Map<String, Location> locationMap = new HashMap<>();
425         for (final Location l : locationList) {
426             locationMap.put(l.getId(), l);
427         }
428
429         // CAPABILITIES
430         final List<Capability> capabilityList = getCapabilities();
431         final Map<String, Capability> capabilityMap = new HashMap<>();
432         for (final Capability c : capabilityList) {
433             capabilityMap.put(c.getId(), c);
434         }
435
436         // CAPABILITY STATES
437         final List<CapabilityState> capabilityStateList = getCapabilityStates();
438         final Map<String, CapabilityState> capabilityStateMap = new HashMap<>();
439         for (final CapabilityState cs : capabilityStateList) {
440             capabilityStateMap.put(cs.getId(), cs);
441         }
442
443         // DEVICE STATES
444         final List<DeviceState> deviceStateList = getDeviceStates();
445         final Map<String, DeviceState> deviceStateMap = new HashMap<>();
446         for (final DeviceState es : deviceStateList) {
447             deviceStateMap.put(es.getId(), es);
448         }
449
450         // MESSAGES
451         final List<Message> messageList = getMessages();
452         final Map<String, List<Message>> deviceMessageMap = new HashMap<>();
453         for (final Message m : messageList) {
454             if (m.getDevices() != null && !m.getDevices().isEmpty()) {
455                 final String deviceId = m.getDevices().get(0).replace("/device/", "");
456                 List<Message> ml;
457                 if (deviceMessageMap.containsKey(deviceId)) {
458                     ml = deviceMessageMap.get(deviceId);
459                 } else {
460                     ml = new ArrayList<>();
461                 }
462                 ml.add(m);
463                 deviceMessageMap.put(deviceId, ml);
464             }
465         }
466
467         // DEVICES
468         final List<Device> deviceList = getDevices();
469         for (final Device d : deviceList) {
470             if (InnogyBindingConstants.BATTERY_POWERED_DEVICES.contains(d.getType())) {
471                 d.setIsBatteryPowered(true);
472             }
473
474             // location
475             d.setLocation(locationMap.get(d.getLocationId()));
476             final HashMap<String, Capability> deviceCapabilityMap = new HashMap<>();
477
478             // capabilities and their states
479             for (final String cl : d.getCapabilityLinkList()) {
480                 final Capability c = capabilityMap.get(Link.getId(cl));
481                 final String capabilityId = c.getId();
482                 final CapabilityState capabilityState = capabilityStateMap.get(capabilityId);
483                 c.setCapabilityState(capabilityState);
484                 deviceCapabilityMap.put(capabilityId, c);
485             }
486             d.setCapabilityMap(deviceCapabilityMap);
487
488             // device states
489             d.setDeviceState(deviceStateMap.get(d.getId()));
490
491             // messages
492             if (deviceMessageMap.containsKey(d.getId())) {
493                 d.setMessageList(deviceMessageMap.get(d.getId()));
494                 for (final Message m : d.getMessageList()) {
495                     switch (m.getType()) {
496                         case Message.TYPE_DEVICE_LOW_BATTERY:
497                             d.setLowBattery(true);
498                             d.setLowBatteryMessageId(m.getId());
499                             break;
500                     }
501                 }
502             }
503         }
504
505         return deviceList;
506     }
507
508     /**
509      * Returns the {@link Device} with the given deviceId with full configuration details, {@link Capability}s and
510      * states. Calling this may take a little bit longer...
511      *
512      * @param deviceId
513      * @return
514      * @throws IOException
515      * @throws ApiException
516      */
517     public Device getFullDeviceById(final String deviceId) throws IOException, ApiException, AuthenticationException {
518         // LOCATIONS
519         final List<Location> locationList = getLocations();
520         final Map<String, Location> locationMap = new HashMap<>();
521         for (final Location l : locationList) {
522             locationMap.put(l.getId(), l);
523         }
524
525         // CAPABILITIES FOR DEVICE
526         final List<Capability> capabilityList = getCapabilitiesForDevice(deviceId);
527         final Map<String, Capability> capabilityMap = new HashMap<>();
528         for (final Capability c : capabilityList) {
529             capabilityMap.put(c.getId(), c);
530         }
531
532         // CAPABILITY STATES
533         final List<CapabilityState> capabilityStateList = getCapabilityStates();
534         final Map<String, CapabilityState> capabilityStateMap = new HashMap<>();
535         for (final CapabilityState cs : capabilityStateList) {
536             capabilityStateMap.put(cs.getId(), cs);
537         }
538
539         // DEVICE STATE
540         final State state = getDeviceStateByDeviceId(deviceId);
541         final DeviceState deviceState = new DeviceState();
542         deviceState.setId(deviceId);
543         deviceState.setState(state);
544
545         // MESSAGES
546         final List<Message> messageList = getMessages();
547         final List<Message> ml = new ArrayList<>();
548         final String deviceIdPath = "/device/" + deviceId;
549
550         for (final Message m : messageList) {
551             logger.trace("Message Type {} with ID {}", m.getType(), m.getId());
552             if (m.getDevices() != null && !m.getDevices().isEmpty()) {
553                 for (final String li : m.getDevices()) {
554                     if (deviceIdPath.equals(li)) {
555                         ml.add(m);
556                     }
557                 }
558             }
559         }
560
561         // DEVICE
562         final Device d = getDeviceById(deviceId);
563         if (BATTERY_POWERED_DEVICES.contains(d.getType())) {
564             d.setIsBatteryPowered(true);
565             d.setLowBattery(false);
566         }
567
568         // location
569         d.setLocation(locationMap.get(d.getLocationId()));
570
571         // capabilities and their states
572         final HashMap<String, Capability> deviceCapabilityMap = new HashMap<>();
573         for (final String cl : d.getCapabilityLinkList()) {
574
575             final Capability c = capabilityMap.get(Link.getId(cl));
576             c.setCapabilityState(capabilityStateMap.get(c.getId()));
577             deviceCapabilityMap.put(c.getId(), c);
578
579         }
580         d.setCapabilityMap(deviceCapabilityMap);
581
582         // device states
583         d.setDeviceState(deviceState);
584
585         // messages
586         if (!ml.isEmpty()) {
587             d.setMessageList(ml);
588             for (final Message m : d.getMessageList()) {
589                 switch (m.getType()) {
590                     case Message.TYPE_DEVICE_LOW_BATTERY:
591                         d.setLowBattery(true);
592                         d.setLowBatteryMessageId(m.getId());
593                         break;
594                 }
595             }
596         }
597
598         return d;
599     }
600
601     /**
602      * Loads the states for all {@link Device}s.
603      *
604      * @return
605      * @throws IOException
606      * @throws ApiException
607      */
608     public List<DeviceState> getDeviceStates() throws IOException, ApiException, AuthenticationException {
609         logger.debug("Loading device states...");
610         return executeGetList(API_URL_DEVICE_STATES, DeviceState[].class);
611     }
612
613     /**
614      * Loads the device state for the given deviceId.
615      *
616      * @param deviceId
617      * @return
618      * @throws IOException
619      * @throws ApiException
620      */
621     public State getDeviceStateByDeviceId(final String deviceId)
622             throws IOException, ApiException, AuthenticationException {
623         logger.debug("Loading device states for device id {}...", deviceId);
624         return executeGet(API_URL_DEVICE_ID_STATE.replace("{id}", deviceId), State.class);
625     }
626
627     /**
628      * Loads the locations and returns a {@link List} of {@link Location}s.
629      *
630      * @return a List of Devices
631      * @throws IOException
632      * @throws ApiException
633      */
634     public List<Location> getLocations() throws IOException, ApiException, AuthenticationException {
635         logger.debug("Loading locations...");
636         return executeGetList(API_URL_LOCATION, Location[].class);
637     }
638
639     /**
640      * Loads and returns a {@link List} of {@link Capability}s for the given deviceId.
641      *
642      * @param deviceId the id of the {@link Device}
643      * @return
644      * @throws IOException
645      * @throws ApiException
646      */
647     public List<Capability> getCapabilitiesForDevice(final String deviceId)
648             throws IOException, ApiException, AuthenticationException {
649         logger.debug("Loading capabilities for device {}...", deviceId);
650         return executeGetList(API_URL_DEVICE_CAPABILITIES.replace("{id}", deviceId), Capability[].class);
651     }
652
653     /**
654      * Loads and returns a {@link List} of all {@link Capability}s.
655      *
656      * @return
657      * @throws IOException
658      * @throws ApiException
659      */
660     public List<Capability> getCapabilities() throws IOException, ApiException, AuthenticationException {
661         logger.debug("Loading capabilities...");
662         return executeGetList(API_URL_CAPABILITY, Capability[].class);
663     }
664
665     /**
666      * Loads and returns a {@link List} of all {@link Capability}States.
667      *
668      * @return
669      * @throws IOException
670      * @throws ApiException
671      */
672     public List<CapabilityState> getCapabilityStates() throws IOException, ApiException, AuthenticationException {
673         logger.debug("Loading capability states...");
674         return executeGetList(API_URL_CAPABILITY_STATES, CapabilityState[].class);
675     }
676
677     /**
678      * Returns a {@link List} of all {@link Message}s.
679      *
680      * @return
681      * @throws IOException
682      * @throws ApiException
683      */
684     public List<Message> getMessages() throws IOException, ApiException, AuthenticationException {
685         logger.debug("Loading messages...");
686         return executeGetList(API_URL_MESSAGE, Message[].class);
687     }
688
689     /**
690      * @return the configVersion
691      */
692     public String getConfigVersion() {
693         return configVersion;
694     }
695
696     /**
697      * @param configVersion the configVersion to set
698      */
699     public void setConfigVersion(final String configVersion) {
700         this.configVersion = configVersion;
701     }
702 }