2 * Copyright 2017 Gregory Moyer
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.openhab.binding.sleepiq.api.impl;
18 import java.io.IOException;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.List;
23 import java.util.logging.Logger;
24 import java.util.stream.Collectors;
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;
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;
52 public class SleepIQImpl extends AbstractClient implements SleepIQ {
53 protected static final String PARAM_KEY = "_k";
55 protected static final String DATA_BED_ID = "bedId";
57 protected final Configuration config;
59 private volatile LoginInfo loginInfo;
61 private final ClientBuilder clientBuilder;
63 public SleepIQImpl(Configuration config, ClientBuilder clientBuilder) {
65 this.clientBuilder = clientBuilder;
69 public LoginInfo login() throws LoginException {
70 if (loginInfo == null) {
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())));
77 if (isUnauthorized(response)) {
78 throw new UnauthorizedException(response.readEntity(Failure.class));
81 if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) {
82 throw new LoginException(response.readEntity(Failure.class));
85 // add the received cookies to all future requests
86 getClient().register(new ClientRequestFilter() {
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);
95 loginInfo = response.readEntity(LoginInfo.class);
104 public List<Bed> getBeds() {
105 return getSessionResponse(this::getBedsResponse).readEntity(BedsResponse.class).getBeds();
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();
115 public List<Sleeper> getSleepers() {
116 return getSessionResponse(this::getSleepersResponse).readEntity(SleepersResponse.class).getSleepers();
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();
126 public FamilyStatus getFamilyStatus() {
127 return getSessionResponse(this::getFamilyStatusResponse).readEntity(FamilyStatus.class);
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();
137 public PauseMode getPauseMode(String bedId) throws BedNotFoundException {
138 Map<String, Object> data = new HashMap<>();
139 data.put(DATA_BED_ID, bedId);
141 Response response = getSessionResponse(this::getPauseModeResponse, data);
143 if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) {
144 throw new BedNotFoundException(response.readEntity(Failure.class));
147 return response.readEntity(PauseMode.class);
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();
157 protected boolean isUnauthorized(Response response) {
158 return Status.UNAUTHORIZED.getStatusCode() == response.getStatusInfo().getStatusCode();
161 protected synchronized void resetLogin() {
165 protected Response getSessionResponse(Request request) {
166 return getSessionResponse(request, Collections.emptyMap());
169 protected Response getSessionResponse(Request request, Map<String, Object> data) {
171 Response response = request.execute(data);
173 if (isUnauthorized(response)) {
177 response = request.execute(data);
181 } catch (LoginException e) {
182 throw new RuntimeException(e.getMessage(), e);
187 protected Client createClient() {
188 // setup Gson (de)serialization
189 GsonProvider<Object> gsonProvider = new GsonProvider<>(getGson());
190 clientBuilder.register(gsonProvider);
192 // turn on logging if requested
193 if (config.isLogging()) {
194 clientBuilder.register(new LoggingFilter(Logger.getLogger(SleepIQImpl.class.getName()), true));
197 return clientBuilder.build();
201 public static interface Request {
202 public Response execute(Map<String, Object> data) throws LoginException;