]> git.basschouten.com Git - openhab-addons.git/blob
df1b85a387bd756dd42e102d933d98666a2b6ba1
[openhab-addons.git] /
1 /*
2  * Copyright 2017 Gregory Moyer
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.openhab.binding.sleepiq.api.impl;
17
18 import java.io.IOException;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.logging.Logger;
24 import java.util.stream.Collectors;
25
26 import javax.ws.rs.client.Client;
27 import javax.ws.rs.client.ClientBuilder;
28 import javax.ws.rs.client.ClientRequestContext;
29 import javax.ws.rs.client.ClientRequestFilter;
30 import javax.ws.rs.client.Entity;
31 import javax.ws.rs.core.MediaType;
32 import javax.ws.rs.core.Response;
33 import javax.ws.rs.core.Response.Status;
34
35 import org.openhab.binding.sleepiq.api.BedNotFoundException;
36 import org.openhab.binding.sleepiq.api.Configuration;
37 import org.openhab.binding.sleepiq.api.LoginException;
38 import org.openhab.binding.sleepiq.api.SleepIQ;
39 import org.openhab.binding.sleepiq.api.UnauthorizedException;
40 import org.openhab.binding.sleepiq.api.filter.LoggingFilter;
41 import org.openhab.binding.sleepiq.api.model.Bed;
42 import org.openhab.binding.sleepiq.api.model.BedsResponse;
43 import org.openhab.binding.sleepiq.api.model.Failure;
44 import org.openhab.binding.sleepiq.api.model.FamilyStatus;
45 import org.openhab.binding.sleepiq.api.model.LoginInfo;
46 import org.openhab.binding.sleepiq.api.model.LoginRequest;
47 import org.openhab.binding.sleepiq.api.model.PauseMode;
48 import org.openhab.binding.sleepiq.api.model.Sleeper;
49 import org.openhab.binding.sleepiq.api.model.SleepersResponse;
50 import org.openhab.binding.sleepiq.internal.GsonProvider;
51
52 public class SleepIQImpl extends AbstractClient implements SleepIQ {
53     protected static final String PARAM_KEY = "_k";
54
55     protected static final String DATA_BED_ID = "bedId";
56
57     protected final Configuration config;
58
59     private volatile LoginInfo loginInfo;
60
61     private final ClientBuilder clientBuilder;
62
63     public SleepIQImpl(Configuration config, ClientBuilder clientBuilder) {
64         this.config = config;
65         this.clientBuilder = clientBuilder;
66     }
67
68     @Override
69     public LoginInfo login() throws LoginException {
70         if (loginInfo == null) {
71             synchronized (this) {
72                 if (loginInfo == null) {
73                     Response response = getClient().target(config.getBaseUri()).path(Endpoints.login())
74                             .request(MediaType.APPLICATION_JSON_TYPE).put(Entity.json(new LoginRequest()
75                                     .withLogin(config.getUsername()).withPassword(config.getPassword())));
76
77                     if (isUnauthorized(response)) {
78                         throw new UnauthorizedException(response.readEntity(Failure.class));
79                     }
80
81                     if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) {
82                         throw new LoginException(response.readEntity(Failure.class));
83                     }
84
85                     // add the received cookies to all future requests
86                     getClient().register(new ClientRequestFilter() {
87                         @Override
88                         public void filter(ClientRequestContext requestContext) throws IOException {
89                             List<Object> cookies = response.getCookies().values().stream()
90                                     .map(newCookie -> newCookie.toCookie()).collect(Collectors.toList());
91                             requestContext.getHeaders().put("Cookie", cookies);
92                         }
93                     });
94
95                     loginInfo = response.readEntity(LoginInfo.class);
96                 }
97             }
98         }
99
100         return loginInfo;
101     }
102
103     @Override
104     public List<Bed> getBeds() {
105         return getSessionResponse(this::getBedsResponse).readEntity(BedsResponse.class).getBeds();
106     }
107
108     protected Response getBedsResponse(Map<String, Object> data) throws LoginException {
109         LoginInfo login = login();
110         return getClient().target(config.getBaseUri()).path(Endpoints.bed()).queryParam(PARAM_KEY, login.getKey())
111                 .request(MediaType.APPLICATION_JSON_TYPE).get();
112     }
113
114     @Override
115     public List<Sleeper> getSleepers() {
116         return getSessionResponse(this::getSleepersResponse).readEntity(SleepersResponse.class).getSleepers();
117     }
118
119     protected Response getSleepersResponse(Map<String, Object> data) throws LoginException {
120         LoginInfo login = login();
121         return getClient().target(config.getBaseUri()).path(Endpoints.sleeper()).queryParam(PARAM_KEY, login.getKey())
122                 .request(MediaType.APPLICATION_JSON_TYPE).get();
123     }
124
125     @Override
126     public FamilyStatus getFamilyStatus() {
127         return getSessionResponse(this::getFamilyStatusResponse).readEntity(FamilyStatus.class);
128     }
129
130     protected Response getFamilyStatusResponse(Map<String, Object> data) throws LoginException {
131         LoginInfo login = login();
132         return getClient().target(config.getBaseUri()).path(Endpoints.bed()).path(Endpoints.familyStatus())
133                 .queryParam(PARAM_KEY, login.getKey()).request(MediaType.APPLICATION_JSON_TYPE).get();
134     }
135
136     @Override
137     public PauseMode getPauseMode(String bedId) throws BedNotFoundException {
138         Map<String, Object> data = new HashMap<>();
139         data.put(DATA_BED_ID, bedId);
140
141         Response response = getSessionResponse(this::getPauseModeResponse, data);
142
143         if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) {
144             throw new BedNotFoundException(response.readEntity(Failure.class));
145         }
146
147         return response.readEntity(PauseMode.class);
148     }
149
150     protected Response getPauseModeResponse(Map<String, Object> data) throws LoginException {
151         LoginInfo login = login();
152         return getClient().target(config.getBaseUri()).path(Endpoints.bed()).path(data.get(DATA_BED_ID).toString())
153                 .path(Endpoints.pauseMode()).queryParam(PARAM_KEY, login.getKey())
154                 .request(MediaType.APPLICATION_JSON_TYPE).get();
155     }
156
157     protected boolean isUnauthorized(Response response) {
158         return Status.UNAUTHORIZED.getStatusCode() == response.getStatusInfo().getStatusCode();
159     }
160
161     protected synchronized void resetLogin() {
162         loginInfo = null;
163     }
164
165     protected Response getSessionResponse(Request request) {
166         return getSessionResponse(request, Collections.emptyMap());
167     }
168
169     protected Response getSessionResponse(Request request, Map<String, Object> data) {
170         try {
171             Response response = request.execute(data);
172
173             if (isUnauthorized(response)) {
174                 // session timed out
175                 response.close();
176                 resetLogin();
177                 response = request.execute(data);
178             }
179
180             return response;
181         } catch (LoginException e) {
182             throw new RuntimeException(e.getMessage(), e);
183         }
184     }
185
186     @Override
187     protected Client createClient() {
188         // setup Gson (de)serialization
189         GsonProvider<Object> gsonProvider = new GsonProvider<>(getGson());
190         clientBuilder.register(gsonProvider);
191
192         // turn on logging if requested
193         if (config.isLogging()) {
194             clientBuilder.register(new LoggingFilter(Logger.getLogger(SleepIQImpl.class.getName()), true));
195         }
196
197         return clientBuilder.build();
198     }
199
200     @FunctionalInterface
201     public static interface Request {
202         public Response execute(Map<String, Object> data) throws LoginException;
203     }
204 }