]> git.basschouten.com Git - openhab-addons.git/blob
666eb93388d926d114af32ca6e067971ba01daca
[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 {
54     protected static final String PARAM_KEY = "_k";
55
56     protected static final String DATA_BED_ID = "bedId";
57
58     protected final Configuration config;
59
60     private volatile LoginInfo loginInfo;
61
62     public SleepIQImpl(Configuration config)
63     {
64         this.config = config;
65     }
66
67     @Override
68     public LoginInfo login() throws LoginException
69     {
70         if (loginInfo == null)
71         {
72             synchronized (this)
73             {
74                 if (loginInfo == null)
75                 {
76                     Response response = getClient().target(config.getBaseUri())
77                                                    .path(Endpoints.login())
78                                                    .request(MediaType.APPLICATION_JSON_TYPE)
79                                                    .put(Entity.json(new LoginRequest().withLogin(config.getUsername())
80                                                                                       .withPassword(config.getPassword())));
81
82                     if (isUnauthorized(response))
83                     {
84                         throw new UnauthorizedException(response.readEntity(Failure.class));
85                     }
86
87                     if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily()))
88                     {
89                         throw new LoginException(response.readEntity(Failure.class));
90                     }
91
92                     // add the received cookies to all future requests
93                     getClient().register(new ClientRequestFilter()
94                     {
95                         @Override
96                         public void filter(ClientRequestContext requestContext) throws IOException
97                         {
98                             List<Object> cookies = response.getCookies()
99                                                            .values()
100                                                            .stream()
101                                                            .map(newCookie -> newCookie.toCookie())
102                                                            .collect(Collectors.toList());
103                             requestContext.getHeaders().put("Cookie", cookies);
104                         }
105                     });
106
107                     loginInfo = response.readEntity(LoginInfo.class);
108                 }
109             }
110         }
111
112         return loginInfo;
113     }
114
115     @Override
116     public List<Bed> getBeds()
117     {
118         return getSessionResponse(this::getBedsResponse).readEntity(BedsResponse.class).getBeds();
119     }
120
121     protected Response getBedsResponse(Map<String, Object> data) throws LoginException
122     {
123         LoginInfo login = login();
124         return getClient().target(config.getBaseUri())
125                           .path(Endpoints.bed())
126                           .queryParam(PARAM_KEY, login.getKey())
127                           .request(MediaType.APPLICATION_JSON_TYPE)
128                           .get();
129     }
130
131     @Override
132     public List<Sleeper> getSleepers()
133     {
134         return getSessionResponse(this::getSleepersResponse).readEntity(SleepersResponse.class)
135                                                             .getSleepers();
136     }
137
138     protected Response getSleepersResponse(Map<String, Object> data) throws LoginException
139     {
140         LoginInfo login = login();
141         return getClient().target(config.getBaseUri())
142                           .path(Endpoints.sleeper())
143                           .queryParam(PARAM_KEY, login.getKey())
144                           .request(MediaType.APPLICATION_JSON_TYPE)
145                           .get();
146     }
147
148     @Override
149     public FamilyStatus getFamilyStatus()
150     {
151         return getSessionResponse(this::getFamilyStatusResponse).readEntity(FamilyStatus.class);
152     }
153
154     protected Response getFamilyStatusResponse(Map<String, Object> data) throws LoginException
155     {
156         LoginInfo login = login();
157         return getClient().target(config.getBaseUri())
158                           .path(Endpoints.bed())
159                           .path(Endpoints.familyStatus())
160                           .queryParam(PARAM_KEY, login.getKey())
161                           .request(MediaType.APPLICATION_JSON_TYPE)
162                           .get();
163     }
164
165     @Override
166     public PauseMode getPauseMode(String bedId) throws BedNotFoundException
167     {
168         Map<String, Object> data = new HashMap<>();
169         data.put(DATA_BED_ID, bedId);
170
171         Response response = getSessionResponse(this::getPauseModeResponse, data);
172
173         if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily()))
174         {
175             throw new BedNotFoundException(response.readEntity(Failure.class));
176         }
177
178         return response.readEntity(PauseMode.class);
179     }
180
181     protected Response getPauseModeResponse(Map<String, Object> data) throws LoginException
182     {
183         LoginInfo login = login();
184         return getClient().target(config.getBaseUri())
185                           .path(Endpoints.bed())
186                           .path(data.get(DATA_BED_ID).toString())
187                           .path(Endpoints.pauseMode())
188                           .queryParam(PARAM_KEY, login.getKey())
189                           .request(MediaType.APPLICATION_JSON_TYPE)
190                           .get();
191     }
192
193     protected boolean isUnauthorized(Response response)
194     {
195         return Status.UNAUTHORIZED.getStatusCode() == response.getStatusInfo().getStatusCode();
196     }
197
198     protected synchronized void resetLogin()
199     {
200         loginInfo = null;
201     }
202
203     protected Response getSessionResponse(Request request)
204     {
205         return getSessionResponse(request, Collections.emptyMap());
206     }
207
208     protected Response getSessionResponse(Request request, Map<String, Object> data)
209     {
210         try
211         {
212             Response response = request.execute(data);
213
214             if (isUnauthorized(response))
215             {
216                 // session timed out
217                 response.close();
218                 resetLogin();
219                 response = request.execute(data);
220             }
221
222             return response;
223         }
224         catch (LoginException e)
225         {
226             throw new RuntimeException(e.getMessage(), e);
227         }
228     }
229
230     @Override
231     protected Client createClient()
232     {
233         ClientBuilder builder = ClientBuilder.newBuilder();
234
235         // setup Gson (de)serialization
236         GsonProvider<Object> gsonProvider = new GsonProvider<>(getGson());
237         builder.register(gsonProvider);
238
239         // turn on logging if requested
240         if (config.isLogging())
241         {
242             builder.register(new LoggingFilter(Logger.getLogger(SleepIQImpl.class.getName()),
243                                                true));
244         }
245
246         return builder.build();
247     }
248
249     @FunctionalInterface
250     public static interface Request
251     {
252         public Response execute(Map<String, Object> data) throws LoginException;
253     }
254 }