added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="src/3rdparty/java"/>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry kind="src" output="target/test-classes" path="src/3rdparty/test">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.sleepiq</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,24 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons
== Third-party Content
The sources of libsleepiq-java are included in src/3rdparty/java and have been slightly adapted.
* License: Apache 2.0 License
* Project: https://github.com/syphr42/libsleepiq-java
* Source: https://github.com/syphr42/libsleepiq-java
This binding includes a class from Jersey in the src/3rdparty/java folder.
* License: CDDL License
* Project: https://eclipse-ee4j.github.io/jersey/

View File

@@ -0,0 +1,100 @@
# SleepIQ Binding
This binding integrates with the SleepIQ system from Select Comfort for Sleep Number beds.
## Introduction
SleepIQ is a service provided by Select Comfort and sold as an option for Sleep Number beds.
The system collects data about the bed (including individual air chamber data for dual chamber beds).
This information includes whether or not a sleeper is in bed, the current sleep number setting, the pressure of the air chamber, and its link status.
This data can then be analyzed for any number of purposes, including improving sleep.
## Supported Things
The SleepIQ cloud service acts as the bridge.
Each SleepIQ-enabled bed (regardless of the number of air chambers) is a Thing.
Currently, only dual-chamber beds are supported by this binding.
## Discovery
The SleepIQ cloud thing must be added manually with the username and password used to register with the service.
After that, beds are discovered automatically by querying the service.
## Binding Configuration
The binding requires no special configuration.
## Thing Configuration
### Bridge (Thing ID: "cloud")
The bridge requires a username and a password.
Optionally, you can also specify a polling interval.
To enable verbose logging of HTTP requests and responses regarding the cloud service, enable DEBUG level logging on ```SleepIQCloudHandler```.
| Configuration Parameter | Type | Description | Default |
|-------------------------|---------|--------------------------------------------------------|---------|
| username | text | Username of a registered SleepIQ account owner | |
| password | text | Password of a registered SleepIQ account owner | |
| pollingInterval | integer | Seconds between fetching values from the cloud service | 60 |
### Dual-Chamber Bed (Thing ID: "dualBed")
Each bed requires a bed ID as defined by the SleepIQ service.
| Configuration Parameter | Type | Description | Default |
|-------------------------|---------|----------------------------------------------|---------|
| bedId | text | The bed identifier identifies a specific bed | |
### Sample Thing Configuration
```
Bridge sleepiq:cloud:1 [ username="mail@example.com", password="password", pollingInterval=60, logging=false ]
{
Thing dualBed master [ bedId="-9999999999999999999" ]
Thing dualBed guest [ bedId="-8888888888888888888" ]
}
```
## Channels
### Dual-Chamber Bed
| Channel Group ID | Group Type | Description |
|------------------|------------|----------------------------|
| left | Chamber | The left side air chamber |
| right | Chamber | The right side air chamber |
### Chamber Channel Group
All channels within this group are read-only.
| Channel ID | Item Type | Description |
|----------------------|-----------|---------------------------------------------------------------------------------------------------------------------|
| inBed | Switch | The presence of a person or object on the chamber |
| sleepNumber | Number | The Sleep Number setting of the chamber |
| pressure | Number | The current pressure inside the chamber |
| lastLink | String | The amount of time that has passed since a connection was made from the chamber to the cloud service (D d HH:MM:SS) |
| alertId | Number | Identifier for an alert condition with the chamber |
| alertDetailedMessage | String | A detailed message describing an alert condition with the chamber |
## Items
Here is a sample item configuration:
```
Switch MasterBedroom_SleepIQ_InBed_Alice "In Bed [%s]" { channel="sleepiq:dualBed:1:master:left#inBed" }
Number MasterBedroom_SleepIQ_SleepNumber_Alice "Sleep Number [%s]" { channel="sleepiq:dualBed:1:master:left#sleepNumber" }
Number MasterBedroom_SleepIQ_Pressure_Alice "Pressure [%s]" { channel="sleepiq:dualBed:1:master:left#pressure" }
String MasterBedroom_SleepIQ_LastLink_Alice "Last Update [%s]" { channel="sleepiq:dualBed:1:master:left#lastLink" }
Number MasterBedroom_SleepIQ_AlertId_Alice "Alert ID [%s]" { channel="sleepiq:dualBed:1:master:left#alertId" }
String MasterBedroom_SleepIQ_AlertMessage_Alice "Alert Message [%s]" { channel="sleepiq:dualBed:1:master:left#alertDetailedMessage" }
Switch MasterBedroom_SleepIQ_InBed_Bob "In Bed [%s]" { channel="sleepiq:dualBed:1:master:right#inBed" }
Number MasterBedroom_SleepIQ_SleepNumber_Bob "Sleep Number [%s]" { channel="sleepiq:dualBed:1:master:right#sleepNumber" }
Number MasterBedroom_SleepIQ_Pressure_Bob "Pressure [%s]" { channel="sleepiq:dualBed:1:master:right#pressure" }
String MasterBedroom_SleepIQ_LastLink_Bob "Last Update [%s]" { channel="sleepiq:dualBed:1:master:right#lastLink" }
Number MasterBedroom_SleepIQ_AlertId_Bob "Alert ID [%s]" { channel="sleepiq:dualBed:1:master:right#alertId" }
String MasterBedroom_SleepIQ_AlertMessage_Bob "Alert Message [%s]" { channel="sleepiq:dualBed:1:master:right#alertDetailedMessage" }
```

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.sleepiq</artifactId>
<name>openHAB Add-ons :: Bundles :: SleepIQ Binding</name>
<properties>
<dep.noembedding>provider-gson</dep.noembedding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>add-source</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<sources>
<source>src/3rdparty/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-test-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/3rdparty/test</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api;
import org.openhab.binding.sleepiq.api.model.Failure;
public class BedNotFoundException extends SleepIQException
{
private static final long serialVersionUID = 1L;
public BedNotFoundException(Failure failure)
{
super(failure);
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api;
import java.net.URI;
import java.util.logging.Level;
/**
* This class represents configuration parameters for using {@link SleepIQ}.
*
* @author Gregory Moyer
*/
public class Configuration
{
private String username;
private String password;
private URI baseUri = URI.create("https://api.sleepiq.sleepnumber.com/rest");
private boolean logging = false;
/**
* Get the username on the account.
*
* @return the username
*/
public String getUsername()
{
return username;
}
/**
* Set the username on the account. This should be the username used to
* register with SleepIQ.
*
* @param username
* the value to set
*/
public void setUsername(String username)
{
this.username = username;
}
/**
* Set the username on the account. This should be the username used to
* register with SleepIQ.
*
* @param username
* the value to set
* @return this configuration instance
*/
public Configuration withUsername(String username)
{
setUsername(username);
return this;
}
/**
* Get the password on the account.
*
* @return the password
*/
public String getPassword()
{
return password;
}
/**
* Set the password on the account. This should be the password used to
* register with SleepIQ.
*
* @param password
* the value to set
*/
public void setPassword(String password)
{
this.password = password;
}
/**
* Set the password on the account. This should be the password used to
* register with SleepIQ.
*
* @param password
* the value to set
* @return this configuration instance
*/
public Configuration withPassword(String password)
{
setPassword(password);
return this;
}
/**
* Get the base URI of the SleepIQ cloud service.
*
* @return the base URI
*/
public URI getBaseUri()
{
return baseUri;
}
/**
* Set the base URI of the SleepIQ cloud service. It is unlikely that this
* will need to be changed from its default value.
*
* @param baseUri
* the value to set
*/
public void setBaseUri(URI baseUri)
{
this.baseUri = baseUri;
}
/**
* Set the base URI of the SleepIQ cloud service. It is unlikely that this
* will need to be changed from its default value.
*
* @param baseUri
* the value to set
* @return this configuration instance
*/
public Configuration withBaseUri(URI baseUri)
{
setBaseUri(baseUri);
return this;
}
/**
* Get the logging flag.
*
* @return the logging flag
*/
public boolean isLogging()
{
return logging;
}
/**
* Set the logging flag. When this is set to <code>true</code>, all requests
* and responses will be logged at the {@link Level#INFO} level. <b>This
* includes usernames and passwords!</b>
*
* @param logging
* the value to set
*/
public void setLogging(boolean logging)
{
this.logging = logging;
}
/**
* Set the logging flag. When this is set to <code>true</code>, all requests
* and responses will be logged at the {@link Level#INFO} level. <b>This
* includes usernames and passwords!</b>
*
* @param logging
* the value to set
* @return this configuration instance
*/
public Configuration withLogging(boolean logging)
{
setLogging(logging);
return this;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api;
import org.openhab.binding.sleepiq.api.model.Failure;
public class LoginException extends SleepIQException
{
private static final long serialVersionUID = 1L;
public LoginException(Failure failure)
{
super(failure);
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api;
import java.util.List;
import org.openhab.binding.sleepiq.api.impl.SleepIQImpl;
import org.openhab.binding.sleepiq.api.model.Bed;
import org.openhab.binding.sleepiq.api.model.FamilyStatus;
import org.openhab.binding.sleepiq.api.model.LoginInfo;
import org.openhab.binding.sleepiq.api.model.PauseMode;
import org.openhab.binding.sleepiq.api.model.Sleeper;
/**
* This interface is the main API to access the SleepIQ system.
*
* @author Gregory Moyer
*/
public interface SleepIQ
{
/**
* Login to the {@link Configuration configured} account. This method is not
* required to be called before other methods because all methods must
* ensure login before acting. However, when the only desired action is to
* login and not retrieve other data, this method is the most efficient
* option.
*
* @return basic information about the logged in user
* @throws UnauthorizedException
* if the credentials provided are not valid
* @throws LoginException
* if the login request fails for any reason other than bad
* credentials (including missing credentials)
*/
public LoginInfo login() throws LoginException;
/**
* Get a list of beds connected to the account.
*
* @return the list of beds
*/
public List<Bed> getBeds();
/**
* Get a list of people registered to this account for beds or bed positions
* (left or right side).
*
* @return the list of sleepers
*/
public List<Sleeper> getSleepers();
/**
* Get the status of all beds and all air chambers registered to this
* account.
*
* @return the complete status of beds on the account
*/
public FamilyStatus getFamilyStatus();
/**
* Get the status of "pause mode" (disabling SleepIQ data upload) for a
* specific bed. A bed in pause mode will send no information to the SleepIQ
* cloud services. For example, if a sleeper is in bed and disables SleepIQ
* (enables pause mode), the service will continue to report that the bed is
* occupied even after the sleeper exits the bed until pause mode is
* disabled.
*
* @param bedId
* the unique identifier of the bed to query
* @return the status of pause mode for the specified bed
* @throws BedNotFoundException
* if the bed identifier was not found on the account
*/
public PauseMode getPauseMode(String bedId) throws BedNotFoundException;
/**
* Create a default implementation instance of this interface. Each call to
* this method will create a new object.
*
* @param config
* the configuration to use for the new instance
* @return a concrete implementation of this interface
*/
public static SleepIQ create(Configuration config)
{
return new SleepIQImpl(config);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api;
import org.openhab.binding.sleepiq.api.model.Failure;
public class SleepIQException extends Exception
{
private static final long serialVersionUID = 1L;
private final Failure failure;
public SleepIQException(Failure failure)
{
super(failure.getError().getMessage());
this.failure = failure;
}
public Failure getFailure()
{
return failure;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api;
import org.openhab.binding.sleepiq.api.model.Failure;
public class UnauthorizedException extends LoginException
{
private static final long serialVersionUID = 1L;
public UnauthorizedException(Failure failure)
{
super(failure);
}
}

View File

@@ -0,0 +1,355 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2011-2015 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.openhab.binding.sleepiq.api.filter;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import javax.annotation.Priority;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
/**
* Universal logging filter.
* <p/>
* Can be used on client or server side. Has the highest priority.
*
* @author Pavel Bucek (pavel.bucek at oracle.com)
* @author Martin Matula
*/
@PreMatching
@Priority(Integer.MIN_VALUE)
public final class LoggingFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter,
ClientResponseFilter, WriterInterceptor {
public static final Charset UTF8 = Charset.forName("UTF-8");
private static final Logger LOGGER = Logger.getLogger(LoggingFilter.class.getName());
private static final String NOTIFICATION_PREFIX = "* ";
private static final String REQUEST_PREFIX = "> ";
private static final String RESPONSE_PREFIX = "< ";
private static final String ENTITY_LOGGER_PROPERTY = LoggingFilter.class.getName() + ".entityLogger";
private static final String LOGGING_ID_PROPERTY = LoggingFilter.class.getName() + ".id";
private static final Comparator<Map.Entry<String, List<String>>> COMPARATOR = new Comparator<Map.Entry<String, List<String>>>() {
@Override
public int compare(final Map.Entry<String, List<String>> o1, final Map.Entry<String, List<String>> o2) {
return o1.getKey().compareToIgnoreCase(o2.getKey());
}
};
private static final int DEFAULT_MAX_ENTITY_SIZE = 8 * 1024;
//
private final Logger logger;
private final AtomicLong _id = new AtomicLong(0);
private final boolean printEntity;
private final int maxEntitySize;
/**
* Create a logging filter logging the request and response to a default JDK
* logger, named as the fully qualified class name of this class. Entity
* logging is turned off by default.
*/
public LoggingFilter() {
this(LOGGER, false);
}
/**
* Create a logging filter with custom logger and custom settings of entity
* logging.
*
* @param logger the logger to log requests and responses.
* @param printEntity if true, entity will be logged as well up to the default maxEntitySize, which is 8KB
*/
public LoggingFilter(final Logger logger, final boolean printEntity) {
this.logger = logger;
this.printEntity = printEntity;
this.maxEntitySize = DEFAULT_MAX_ENTITY_SIZE;
}
/**
* Creates a logging filter with custom logger and entity logging turned on, but potentially limiting the size
* of entity to be buffered and logged.
*
* @param logger the logger to log requests and responses.
* @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
* logging filter will print (and buffer in memory) only the specified number of bytes
* and print "...more..." string at the end. Negative values are interpreted as zero.
*/
public LoggingFilter(final Logger logger, final int maxEntitySize) {
this.logger = logger;
this.printEntity = true;
this.maxEntitySize = Math.max(0, maxEntitySize);
}
private void log(final StringBuilder b) {
if (logger != null) {
logger.info(b.toString());
}
}
private StringBuilder prefixId(final StringBuilder b, final long id) {
b.append(Long.toString(id)).append(" ");
return b;
}
private void printRequestLine(final StringBuilder b, final String note, final long id, final String method,
final URI uri) {
prefixId(b, id).append(NOTIFICATION_PREFIX).append(note).append(" on thread ")
.append(Thread.currentThread().getName()).append("\n");
prefixId(b, id).append(REQUEST_PREFIX).append(method).append(" ").append(uri.toASCIIString()).append("\n");
}
private void printResponseLine(final StringBuilder b, final String note, final long id, final int status) {
prefixId(b, id).append(NOTIFICATION_PREFIX).append(note).append(" on thread ")
.append(Thread.currentThread().getName()).append("\n");
prefixId(b, id).append(RESPONSE_PREFIX).append(Integer.toString(status)).append("\n");
}
private void printPrefixedHeaders(final StringBuilder b, final long id, final String prefix,
final MultivaluedMap<String, String> headers) {
for (final Map.Entry<String, List<String>> headerEntry : getSortedHeaders(headers.entrySet())) {
final List<?> val = headerEntry.getValue();
final String header = headerEntry.getKey();
if (val.size() == 1) {
prefixId(b, id).append(prefix).append(header).append(": ").append(val.get(0)).append("\n");
} else {
final StringBuilder sb = new StringBuilder();
boolean add = false;
for (final Object s : val) {
if (add) {
sb.append(',');
}
add = true;
sb.append(s);
}
prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString()).append("\n");
}
}
}
private Set<Map.Entry<String, List<String>>> getSortedHeaders(final Set<Map.Entry<String, List<String>>> headers) {
final TreeSet<Map.Entry<String, List<String>>> sortedHeaders = new TreeSet<Map.Entry<String, List<String>>>(
COMPARATOR);
sortedHeaders.addAll(headers);
return sortedHeaders;
}
private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset)
throws IOException {
if (!stream.markSupported()) {
stream = new BufferedInputStream(stream);
}
stream.mark(maxEntitySize + 1);
final byte[] entity = new byte[maxEntitySize + 1];
final int entitySize = stream.read(entity);
b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
if (entitySize > maxEntitySize) {
b.append("...more...");
}
b.append('\n');
stream.reset();
return stream;
}
@Override
public void filter(final ClientRequestContext context) throws IOException {
final long id = _id.incrementAndGet();
context.setProperty(LOGGING_ID_PROPERTY, id);
final StringBuilder b = new StringBuilder();
printRequestLine(b, "Sending client request", id, context.getMethod(), context.getUri());
printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getStringHeaders());
if (printEntity && context.hasEntity()) {
final OutputStream stream = new LoggingStream(b, context.getEntityStream());
context.setEntityStream(stream);
context.setProperty(ENTITY_LOGGER_PROPERTY, stream);
// not calling log(b) here - it will be called by the interceptor
} else {
log(b);
}
}
@Override
public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext)
throws IOException {
final Object requestId = requestContext.getProperty(LOGGING_ID_PROPERTY);
final long id = requestId != null ? (Long) requestId : _id.incrementAndGet();
final StringBuilder b = new StringBuilder();
printResponseLine(b, "Client response received", id, responseContext.getStatus());
printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getHeaders());
if (printEntity && responseContext.hasEntity()) {
responseContext.setEntityStream(
logInboundEntity(b, responseContext.getEntityStream(), getCharset(responseContext.getMediaType())));
}
log(b);
}
@Override
public void filter(final ContainerRequestContext context) throws IOException {
final long id = _id.incrementAndGet();
context.setProperty(LOGGING_ID_PROPERTY, id);
final StringBuilder b = new StringBuilder();
printRequestLine(b, "Server has received a request", id, context.getMethod(),
context.getUriInfo().getRequestUri());
printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getHeaders());
if (printEntity && context.hasEntity()) {
context.setEntityStream(logInboundEntity(b, context.getEntityStream(), getCharset(context.getMediaType())));
}
log(b);
}
@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
throws IOException {
final Object requestId = requestContext.getProperty(LOGGING_ID_PROPERTY);
final long id = requestId != null ? (Long) requestId : _id.incrementAndGet();
final StringBuilder b = new StringBuilder();
printResponseLine(b, "Server responded with a response", id, responseContext.getStatus());
printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getStringHeaders());
if (printEntity && responseContext.hasEntity()) {
final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream());
responseContext.setEntityStream(stream);
requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream);
// not calling log(b) here - it will be called by the interceptor
} else {
log(b);
}
}
@Override
public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext)
throws IOException, WebApplicationException {
final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY);
writerInterceptorContext.proceed();
if (stream != null) {
log(stream.getStringBuilder(getCharset(writerInterceptorContext.getMediaType())));
}
}
private class LoggingStream extends FilterOutputStream {
private final StringBuilder b;
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
LoggingStream(final StringBuilder b, final OutputStream inner) {
super(inner);
this.b = b;
}
StringBuilder getStringBuilder(final Charset charset) {
// write entity to the builder
final byte[] entity = baos.toByteArray();
b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize), charset));
if (entity.length > maxEntitySize) {
b.append("...more...");
}
b.append('\n');
return b;
}
@Override
public void write(final int i) throws IOException {
if (baos.size() <= maxEntitySize) {
baos.write(i);
}
out.write(i);
}
}
/**
* Get the character set from a media type.
* <p>
* The character set is obtained from the media type parameter "charset".
* If the parameter is not present the {@link #UTF8} charset is utilized.
*
* @param m the media type.
* @return the character set.
*/
public static Charset getCharset(MediaType m) {
String name = (m == null) ? null : m.getParameters().get(MediaType.CHARSET_PARAMETER);
return (name == null) ? UTF8 : Charset.forName(name);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.impl;
import javax.ws.rs.client.Client;
import com.google.gson.Gson;
public abstract class AbstractClient
{
private volatile Client client;
private volatile Gson gson;
protected Client getClient()
{
if (client == null)
{
synchronized (this)
{
if (client == null)
{
client = createClient();
}
}
}
return client;
}
protected Gson getGson()
{
if (gson == null)
{
synchronized (this)
{
if (gson == null)
{
gson = createGson();
}
}
}
return gson;
}
protected abstract Client createClient();
protected Gson createGson()
{
return GsonGenerator.create();
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.impl;
public class Endpoints
{
private static final String LOGIN = "login";
private static final String BED = "bed";
private static final String SLEEPER = "sleeper";
private static final String FAMILY_STATUS = "familyStatus";
private static final String PAUSE_MODE = "pauseMode";
public static String login()
{
return LOGIN;
}
public static String bed()
{
return BED;
}
public static String sleeper()
{
return SLEEPER;
}
public static String familyStatus()
{
return FAMILY_STATUS;
}
public static String pauseMode()
{
return PAUSE_MODE;
}
// @formatter:off
private Endpoints() {}
// @formatter:on
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.impl;
import org.openhab.binding.sleepiq.api.impl.typeadapters.JSR310TypeAdapters;
import org.openhab.binding.sleepiq.api.impl.typeadapters.TimeSinceTypeAdapter;
import org.openhab.binding.sleepiq.api.model.TimeSince;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonGenerator
{
public static Gson create()
{
return create(false);
}
public static Gson create(boolean prettyPrint)
{
GsonBuilder builder = new GsonBuilder();
// add Java 8 Time API support
JSR310TypeAdapters.registerJSR310TypeAdapters(builder);
builder.registerTypeAdapter(TimeSince.class, new TimeSinceTypeAdapter());
if (prettyPrint)
{
builder.setPrettyPrinting();
}
return builder.create();
}
// @formatter:off
private GsonGenerator() {}
// @formatter:on
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.impl;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.openhab.binding.sleepiq.api.BedNotFoundException;
import org.openhab.binding.sleepiq.api.Configuration;
import org.openhab.binding.sleepiq.api.LoginException;
import org.openhab.binding.sleepiq.api.SleepIQ;
import org.openhab.binding.sleepiq.api.UnauthorizedException;
import org.openhab.binding.sleepiq.api.filter.LoggingFilter;
import org.openhab.binding.sleepiq.api.model.Bed;
import org.openhab.binding.sleepiq.api.model.BedsResponse;
import org.openhab.binding.sleepiq.api.model.Failure;
import org.openhab.binding.sleepiq.api.model.FamilyStatus;
import org.openhab.binding.sleepiq.api.model.LoginInfo;
import org.openhab.binding.sleepiq.api.model.LoginRequest;
import org.openhab.binding.sleepiq.api.model.PauseMode;
import org.openhab.binding.sleepiq.api.model.Sleeper;
import org.openhab.binding.sleepiq.api.model.SleepersResponse;
import org.openhab.binding.sleepiq.internal.GsonProvider;
public class SleepIQImpl extends AbstractClient implements SleepIQ
{
protected static final String PARAM_KEY = "_k";
protected static final String DATA_BED_ID = "bedId";
protected final Configuration config;
private volatile LoginInfo loginInfo;
public SleepIQImpl(Configuration config)
{
this.config = config;
}
@Override
public LoginInfo login() throws LoginException
{
if (loginInfo == null)
{
synchronized (this)
{
if (loginInfo == null)
{
Response response = getClient().target(config.getBaseUri())
.path(Endpoints.login())
.request(MediaType.APPLICATION_JSON_TYPE)
.put(Entity.json(new LoginRequest().withLogin(config.getUsername())
.withPassword(config.getPassword())));
if (isUnauthorized(response))
{
throw new UnauthorizedException(response.readEntity(Failure.class));
}
if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily()))
{
throw new LoginException(response.readEntity(Failure.class));
}
// add the received cookies to all future requests
getClient().register(new ClientRequestFilter()
{
@Override
public void filter(ClientRequestContext requestContext) throws IOException
{
List<Object> cookies = response.getCookies()
.values()
.stream()
.map(newCookie -> newCookie.toCookie())
.collect(Collectors.toList());
requestContext.getHeaders().put("Cookie", cookies);
}
});
loginInfo = response.readEntity(LoginInfo.class);
}
}
}
return loginInfo;
}
@Override
public List<Bed> getBeds()
{
return getSessionResponse(this::getBedsResponse).readEntity(BedsResponse.class).getBeds();
}
protected Response getBedsResponse(Map<String, Object> data) throws LoginException
{
LoginInfo login = login();
return getClient().target(config.getBaseUri())
.path(Endpoints.bed())
.queryParam(PARAM_KEY, login.getKey())
.request(MediaType.APPLICATION_JSON_TYPE)
.get();
}
@Override
public List<Sleeper> getSleepers()
{
return getSessionResponse(this::getSleepersResponse).readEntity(SleepersResponse.class)
.getSleepers();
}
protected Response getSleepersResponse(Map<String, Object> data) throws LoginException
{
LoginInfo login = login();
return getClient().target(config.getBaseUri())
.path(Endpoints.sleeper())
.queryParam(PARAM_KEY, login.getKey())
.request(MediaType.APPLICATION_JSON_TYPE)
.get();
}
@Override
public FamilyStatus getFamilyStatus()
{
return getSessionResponse(this::getFamilyStatusResponse).readEntity(FamilyStatus.class);
}
protected Response getFamilyStatusResponse(Map<String, Object> data) throws LoginException
{
LoginInfo login = login();
return getClient().target(config.getBaseUri())
.path(Endpoints.bed())
.path(Endpoints.familyStatus())
.queryParam(PARAM_KEY, login.getKey())
.request(MediaType.APPLICATION_JSON_TYPE)
.get();
}
@Override
public PauseMode getPauseMode(String bedId) throws BedNotFoundException
{
Map<String, Object> data = new HashMap<>();
data.put(DATA_BED_ID, bedId);
Response response = getSessionResponse(this::getPauseModeResponse, data);
if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily()))
{
throw new BedNotFoundException(response.readEntity(Failure.class));
}
return response.readEntity(PauseMode.class);
}
protected Response getPauseModeResponse(Map<String, Object> data) throws LoginException
{
LoginInfo login = login();
return getClient().target(config.getBaseUri())
.path(Endpoints.bed())
.path(data.get(DATA_BED_ID).toString())
.path(Endpoints.pauseMode())
.queryParam(PARAM_KEY, login.getKey())
.request(MediaType.APPLICATION_JSON_TYPE)
.get();
}
protected boolean isUnauthorized(Response response)
{
return Status.UNAUTHORIZED.getStatusCode() == response.getStatusInfo().getStatusCode();
}
protected synchronized void resetLogin()
{
loginInfo = null;
}
protected Response getSessionResponse(Request request)
{
return getSessionResponse(request, Collections.emptyMap());
}
protected Response getSessionResponse(Request request, Map<String, Object> data)
{
try
{
Response response = request.execute(data);
if (isUnauthorized(response))
{
// session timed out
response.close();
resetLogin();
response = request.execute(data);
}
return response;
}
catch (LoginException e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
protected Client createClient()
{
ClientBuilder builder = ClientBuilder.newBuilder();
// setup Gson (de)serialization
GsonProvider<Object> gsonProvider = new GsonProvider<>(getGson());
builder.register(gsonProvider);
// turn on logging if requested
if (config.isLogging())
{
builder.register(new LoggingFilter(Logger.getLogger(SleepIQImpl.class.getName()),
true));
}
return builder.build();
}
@FunctionalInterface
public static interface Request
{
public Response execute(Map<String, Object> data) throws LoginException;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.util.function.Function;
/**
* Abstract type adapter for jsr310 date-time types.
*
* @author Christophe Bornet
*/
abstract class DateTimeTypeAdapter<T> extends TemporalTypeAdapter<T> {
DateTimeTypeAdapter(Function<String, T> parseFunction) {
super(parseFunction);
}
@Override
public String preProcess(String in) {
if (in.endsWith("+0000")) {
return in.substring(0, in.length()-5) + "Z";
}
return in;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.Duration;
/**
* Type adapter for jsr310 {@link Duration} class.
*
* @author Christophe Bornet
*/
public class DurationTypeAdapter extends TemporalTypeAdapter<Duration> {
public DurationTypeAdapter() {
super(Duration::parse);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.Instant;
/**
* Type adapter for jsr310 {@link Instant} class.
*
* @author Christophe Bornet
*/
public class InstantTypeAdapter extends DateTimeTypeAdapter<Instant> {
public InstantTypeAdapter() {
super(Instant::parse);
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import com.google.gson.GsonBuilder;
import java.time.*;
/**
* Helper methods to register JSR310 type adapters.
*
* @author Christophe Bornet
*/
public class JSR310TypeAdapters {
private JSR310TypeAdapters() {
}
public static GsonBuilder registerDurationTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(Duration.class, new DurationTypeAdapter());
}
public static GsonBuilder registerInstantTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(Instant.class, new InstantTypeAdapter());
}
public static GsonBuilder registerLocalDateTimeTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTypeAdapter());
}
public static GsonBuilder registerLocalDateTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter());
}
public static GsonBuilder registerLocalTimeTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(LocalTime.class, new LocalTimeTypeAdapter());
}
public static GsonBuilder registerMonthDayTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(MonthDay.class, new MonthDayTypeAdapter());
}
public static GsonBuilder registerOffsetDateTimeTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter());
}
public static GsonBuilder registerOffsetTimeTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(OffsetTime.class, new OffsetTimeTypeAdapter());
}
public static GsonBuilder registerPeriodTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(Period.class, new PeriodTypeAdapter());
}
public static GsonBuilder registerYearMonthTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(YearMonth.class, new YearMonthTypeAdapter());
}
public static GsonBuilder registerYearTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(Year.class, new YearTypeAdapter());
}
public static GsonBuilder registerZonedDateTimeTypeAdapter(GsonBuilder gsonBuilder) {
return gsonBuilder.registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeTypeAdapter());
}
/**
* Helper method to register all the available JSR310 adapters at once.
* @param gsonBuilder the gsonBuilder on which all the JSR310 adapters must be registered.
* @return the gsonBuilder with the JSR310 adapters registered.
*/
public static GsonBuilder registerJSR310TypeAdapters(GsonBuilder gsonBuilder) {
registerDurationTypeAdapter(gsonBuilder);
registerInstantTypeAdapter(gsonBuilder);
registerLocalDateTimeTypeAdapter(gsonBuilder);
registerLocalDateTypeAdapter(gsonBuilder);
registerLocalTimeTypeAdapter(gsonBuilder);
registerMonthDayTypeAdapter(gsonBuilder);
registerOffsetDateTimeTypeAdapter(gsonBuilder);
registerOffsetTimeTypeAdapter(gsonBuilder);
registerPeriodTypeAdapter(gsonBuilder);
registerYearMonthTypeAdapter(gsonBuilder);
registerYearTypeAdapter(gsonBuilder);
registerZonedDateTimeTypeAdapter(gsonBuilder);
return gsonBuilder;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.LocalDateTime;
/**
* Type adapter for jsr310 {@link LocalDateTime} class.
*
* @author Christophe Bornet
*/
public class LocalDateTimeTypeAdapter extends DateTimeTypeAdapter<LocalDateTime> {
public LocalDateTimeTypeAdapter() {
super(LocalDateTime::parse);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.LocalDate;
/**
* Type adapter for jsr310 {@link LocalDate} class.
*
* @author Christophe Bornet
*/
public class LocalDateTypeAdapter extends TemporalTypeAdapter<LocalDate> {
public LocalDateTypeAdapter() {
super(LocalDate::parse);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* Type adapter for jsr310 {@link LocalTime} class.
*
* @author Christophe Bornet
*/
public class LocalTimeTypeAdapter extends TemporalTypeAdapter<LocalTime> {
public LocalTimeTypeAdapter() {
super(LocalTime::parse);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.MonthDay;
/**
* Type adapter for jsr310 {@link MonthDay} class.
*
* @author Christophe Bornet
*/
public class MonthDayTypeAdapter extends TemporalTypeAdapter<MonthDay> {
public MonthDayTypeAdapter() {
super(MonthDay::parse);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
/**
* Type adapter for jsr310 {@link OffsetDateTime} class.
*
* @author Christophe Bornet
*/
public class OffsetDateTimeTypeAdapter extends DateTimeTypeAdapter<OffsetDateTime> {
public OffsetDateTimeTypeAdapter() {
super(OffsetDateTime::parse);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.OffsetTime;
import java.time.format.DateTimeFormatter;
/**
* Type adapter for jsr310 {@link OffsetTime} class.
*
* @author Christophe Bornet
*/
public class OffsetTimeTypeAdapter extends TemporalTypeAdapter<OffsetTime> {
public OffsetTimeTypeAdapter() {
super(OffsetTime::parse);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.Period;
/**
* Type adapter for jsr310 {@link Period} class.
*
* @author Christophe Bornet
*/
public class PeriodTypeAdapter extends TemporalTypeAdapter<Period> {
public PeriodTypeAdapter() {
super(Period::parse);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.time.temporal.TemporalAccessor;
import java.util.Objects;
import java.util.function.Function;
/**
* Abstract type adapter for jsr310 date-time types.
*
* @author Christophe Bornet
*/
abstract class TemporalTypeAdapter<T> extends TypeAdapter<T> {
Function<String, T> parseFunction;
TemporalTypeAdapter(Function<String, T> parseFunction) {
Objects.requireNonNull(parseFunction);
this.parseFunction = parseFunction;
}
@Override
public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value.toString());
}
}
@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String temporalString = preProcess(in.nextString());
return parseFunction.apply(temporalString);
}
public String preProcess(String in) {
return in;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import org.openhab.binding.sleepiq.api.model.TimeSince;
public class TimeSinceTypeAdapter extends TemporalTypeAdapter<TimeSince>
{
public TimeSinceTypeAdapter()
{
super(TimeSince::parse);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.YearMonth;
/**
* Type adapter for jsr310 {@link YearMonth} class.
*
* @author Christophe Bornet
*/
public class YearMonthTypeAdapter extends TemporalTypeAdapter<YearMonth> {
public YearMonthTypeAdapter() {
super(YearMonth::parse);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.Year;
/**
* Type adapter for jsr310 {@link Year} class.
*
* @author Christophe Bornet
*/
public class YearTypeAdapter extends TemporalTypeAdapter<Year> {
public YearTypeAdapter() {
super(Year::parse);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2016 Gson Type Adapter Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported from https://github.com/google-gson/typeadapters/tree/master/jsr310/src
* and repackaged to avoid the default package.
*/
package org.openhab.binding.sleepiq.api.impl.typeadapters;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* Type adapter for jsr310 {@link ZonedDateTime} class.
*
* @author Christophe Bornet
*/
public class ZonedDateTimeTypeAdapter extends DateTimeTypeAdapter<ZonedDateTime> {
public ZonedDateTimeTypeAdapter() {
super(ZonedDateTime::parse);
}
}

View File

@@ -0,0 +1,472 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import java.time.ZonedDateTime;
import com.google.gson.annotations.SerializedName;
public class Bed
{
private ZonedDateTime registrationDate;
private String sleeperRightId;
private String base;
private Long returnRequestStatus;
private String size;
private String name;
private String serial;
@SerializedName("isKidsBed")
private Boolean kidsBed;
private Boolean dualSleep;
private String bedId;
private Long status;
private String sleeperLeftId;
private String version;
private String accountId;
private String timezone;
private String model;
private ZonedDateTime purchaseDate;
private String macAddress;
private String sku;
@SerializedName("zipcode")
private String zipCode;
private String reference;
public ZonedDateTime getRegistrationDate()
{
return registrationDate;
}
public void setRegistrationDate(ZonedDateTime registrationDate)
{
this.registrationDate = registrationDate;
}
public Bed withRegistrationDate(ZonedDateTime registrationDate)
{
setRegistrationDate(registrationDate);
return this;
}
public String getSleeperRightId()
{
return sleeperRightId;
}
public void setSleeperRightId(String sleeperRightId)
{
this.sleeperRightId = sleeperRightId;
}
public Bed withSleeperRightId(String sleeperRightId)
{
setSleeperRightId(sleeperRightId);
return this;
}
public String getBase()
{
return base;
}
public void setBase(String base)
{
this.base = base;
}
public Bed withBase(String base)
{
setBase(base);
return this;
}
public Long getReturnRequestStatus()
{
return returnRequestStatus;
}
public void setReturnRequestStatus(Long returnRequestStatus)
{
this.returnRequestStatus = returnRequestStatus;
}
public Bed withReturnRequestStatus(Long returnRequestStatus)
{
setReturnRequestStatus(returnRequestStatus);
return this;
}
public String getSize()
{
return size;
}
public void setSize(String size)
{
this.size = size;
}
public Bed withSize(String size)
{
setSize(size);
return this;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Bed withName(String name)
{
setName(name);
return this;
}
public String getSerial()
{
return serial;
}
public void setSerial(String serial)
{
this.serial = serial;
}
public Bed withSerial(String serial)
{
setSerial(serial);
return this;
}
public Boolean isKidsBed()
{
return kidsBed;
}
public void setKidsBed(Boolean kidsBed)
{
this.kidsBed = kidsBed;
}
public Bed withKidsBed(Boolean kidsBed)
{
setKidsBed(kidsBed);
return this;
}
public Boolean isDualSleep()
{
return dualSleep;
}
public void setDualSleep(Boolean dualSleep)
{
this.dualSleep = dualSleep;
}
public Bed withDualSleep(Boolean dualSleep)
{
setDualSleep(dualSleep);
return this;
}
public String getBedId()
{
return bedId;
}
public void setBedId(String bedId)
{
this.bedId = bedId;
}
public Bed withBedId(String bedId)
{
setBedId(bedId);
return this;
}
public Long getStatus()
{
return status;
}
public void setStatus(Long status)
{
this.status = status;
}
public Bed withStatus(Long status)
{
setStatus(status);
return this;
}
public String getSleeperLeftId()
{
return sleeperLeftId;
}
public void setSleeperLeftId(String sleeperLeftId)
{
this.sleeperLeftId = sleeperLeftId;
}
public Bed withSleeperLeftId(String sleeperLeftId)
{
setSleeperLeftId(sleeperLeftId);
return this;
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public Bed withVersion(String version)
{
setVersion(version);
return this;
}
public String getAccountId()
{
return accountId;
}
public void setAccountId(String accountId)
{
this.accountId = accountId;
}
public Bed withAccountId(String accountId)
{
setAccountId(accountId);
return this;
}
public String getTimezone()
{
return timezone;
}
public void setTimezone(String timezone)
{
this.timezone = timezone;
}
public Bed withTimezone(String timezone)
{
setTimezone(timezone);
return this;
}
public String getModel()
{
return model;
}
public void setModel(String model)
{
this.model = model;
}
public Bed withModel(String model)
{
setModel(model);
return this;
}
public ZonedDateTime getPurchaseDate()
{
return purchaseDate;
}
public void setPurchaseDate(ZonedDateTime purchaseDate)
{
this.purchaseDate = purchaseDate;
}
public Bed withPurchaseDate(ZonedDateTime purchaseDate)
{
setPurchaseDate(purchaseDate);
return this;
}
public String getMacAddress()
{
return macAddress;
}
public void setMacAddress(String macAddress)
{
this.macAddress = macAddress;
}
public Bed withMacAddress(String macAddress)
{
setMacAddress(macAddress);
return this;
}
public String getSku()
{
return sku;
}
public void setSku(String sku)
{
this.sku = sku;
}
public Bed withSku(String sku)
{
setSku(sku);
return this;
}
public String getZipCode()
{
return zipCode;
}
public void setZipCode(String zipCode)
{
this.zipCode = zipCode;
}
public Bed withZipCode(String zipCode)
{
setZipCode(zipCode);
return this;
}
public String getReference()
{
return reference;
}
public void setReference(String reference)
{
this.reference = reference;
}
public Bed withReference(String reference)
{
setReference(reference);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((bedId == null) ? 0 : bedId.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof Bed))
{
return false;
}
Bed other = (Bed)obj;
if (bedId == null)
{
if (other.bedId != null)
{
return false;
}
}
else if (!bedId.equals(other.bedId))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("Bed [registrationDate=");
builder.append(registrationDate);
builder.append(", sleeperRightId=");
builder.append(sleeperRightId);
builder.append(", base=");
builder.append(base);
builder.append(", returnRequestStatus=");
builder.append(returnRequestStatus);
builder.append(", size=");
builder.append(size);
builder.append(", name=");
builder.append(name);
builder.append(", serial=");
builder.append(serial);
builder.append(", kidsBed=");
builder.append(kidsBed);
builder.append(", dualSleep=");
builder.append(dualSleep);
builder.append(", bedId=");
builder.append(bedId);
builder.append(", status=");
builder.append(status);
builder.append(", sleeperLeftId=");
builder.append(sleeperLeftId);
builder.append(", version=");
builder.append(version);
builder.append(", accountId=");
builder.append(accountId);
builder.append(", timezone=");
builder.append(timezone);
builder.append(", model=");
builder.append(model);
builder.append(", purchaseDate=");
builder.append(purchaseDate);
builder.append(", macAddress=");
builder.append(macAddress);
builder.append(", sku=");
builder.append(sku);
builder.append(", zipCode=");
builder.append(zipCode);
builder.append(", reference=");
builder.append(reference);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,245 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import com.google.gson.annotations.SerializedName;
public class BedSideStatus
{
@SerializedName("isInBed")
private Boolean inBed;
private String alertDetailedMessage;
private Integer sleepNumber;
private Long alertId;
private TimeSince lastLink;
private Integer pressure; // appears to be in kPa
public Boolean isInBed()
{
return inBed;
}
public void setInBed(Boolean inBed)
{
this.inBed = inBed;
}
public BedSideStatus withInBed(Boolean inBed)
{
setInBed(inBed);
return this;
}
public String getAlertDetailedMessage()
{
return alertDetailedMessage;
}
public void setAlertDetailedMessage(String alertDetailedMessage)
{
this.alertDetailedMessage = alertDetailedMessage;
}
public BedSideStatus withAlertDetailedMessage(String alertDetailedMessage)
{
setAlertDetailedMessage(alertDetailedMessage);
return this;
}
public Integer getSleepNumber()
{
return sleepNumber;
}
public void setSleepNumber(Integer sleepNumber)
{
this.sleepNumber = sleepNumber;
}
public BedSideStatus withSleepNumber(Integer sleepNumber)
{
setSleepNumber(sleepNumber);
return this;
}
public Long getAlertId()
{
return alertId;
}
public void setAlertId(Long alertId)
{
this.alertId = alertId;
}
public BedSideStatus withAlertId(Long alertId)
{
setAlertId(alertId);
return this;
}
public TimeSince getLastLink()
{
return lastLink;
}
public void setLastLink(TimeSince lastLink)
{
this.lastLink = lastLink;
}
public BedSideStatus withLastLink(TimeSince lastLink)
{
setLastLink(lastLink);
return this;
}
public Integer getPressure()
{
return pressure;
}
public void setPressure(Integer pressure)
{
this.pressure = pressure;
}
public BedSideStatus withPressure(Integer pressure)
{
setPressure(pressure);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ ((alertDetailedMessage == null) ? 0 : alertDetailedMessage.hashCode());
result = prime * result + ((alertId == null) ? 0 : alertId.hashCode());
result = prime * result + ((inBed == null) ? 0 : inBed.hashCode());
result = prime * result + ((lastLink == null) ? 0 : lastLink.hashCode());
result = prime * result + ((pressure == null) ? 0 : pressure.hashCode());
result = prime * result + ((sleepNumber == null) ? 0 : sleepNumber.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof BedSideStatus))
{
return false;
}
BedSideStatus other = (BedSideStatus)obj;
if (alertDetailedMessage == null)
{
if (other.alertDetailedMessage != null)
{
return false;
}
}
else if (!alertDetailedMessage.equals(other.alertDetailedMessage))
{
return false;
}
if (alertId == null)
{
if (other.alertId != null)
{
return false;
}
}
else if (!alertId.equals(other.alertId))
{
return false;
}
if (inBed == null)
{
if (other.inBed != null)
{
return false;
}
}
else if (!inBed.equals(other.inBed))
{
return false;
}
if (lastLink == null)
{
if (other.lastLink != null)
{
return false;
}
}
else if (!lastLink.equals(other.lastLink))
{
return false;
}
if (pressure == null)
{
if (other.pressure != null)
{
return false;
}
}
else if (!pressure.equals(other.pressure))
{
return false;
}
if (sleepNumber == null)
{
if (other.sleepNumber != null)
{
return false;
}
}
else if (!sleepNumber.equals(other.sleepNumber))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("BedSideStatus [inBed=");
builder.append(inBed);
builder.append(", alertDetailedMessage=");
builder.append(alertDetailedMessage);
builder.append(", sleepNumber=");
builder.append(sleepNumber);
builder.append(", alertId=");
builder.append(alertId);
builder.append(", lastLink=");
builder.append(lastLink);
builder.append(", pressure=");
builder.append(pressure);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
public class BedStatus
{
private Long status;
private String bedId;
private BedSideStatus leftSide;
private BedSideStatus rightSide;
public Long getStatus()
{
return status;
}
public void setStatus(Long status)
{
this.status = status;
}
public BedStatus withStatus(Long status)
{
setStatus(status);
return this;
}
public String getBedId()
{
return bedId;
}
public void setBedId(String bedId)
{
this.bedId = bedId;
}
public BedStatus withBedId(String bedId)
{
setBedId(bedId);
return this;
}
public BedSideStatus getLeftSide()
{
return leftSide;
}
public void setLeftSide(BedSideStatus leftSide)
{
this.leftSide = leftSide;
}
public BedStatus withLeftSide(BedSideStatus leftSide)
{
setLeftSide(leftSide);
return this;
}
public BedSideStatus getRightSide()
{
return rightSide;
}
public void setRightSide(BedSideStatus rightSide)
{
this.rightSide = rightSide;
}
public BedStatus withRightSide(BedSideStatus rightSide)
{
setRightSide(rightSide);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((bedId == null) ? 0 : bedId.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof BedStatus))
{
return false;
}
BedStatus other = (BedStatus)obj;
if (bedId == null)
{
if (other.bedId != null)
{
return false;
}
}
else if (!bedId.equals(other.bedId))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("BedStatus [status=");
builder.append(status);
builder.append(", bedId=");
builder.append(bedId);
builder.append(", leftSide=");
builder.append(leftSide);
builder.append(", rightSide=");
builder.append(rightSide);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import java.util.List;
public class BedsResponse
{
private List<Bed> beds;
public List<Bed> getBeds()
{
return beds;
}
public void setBeds(List<Bed> beds)
{
this.beds = beds;
}
public BedsResponse withBeds(List<Bed> beds)
{
setBeds(beds);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((beds == null) ? 0 : beds.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof BedsResponse))
{
return false;
}
BedsResponse other = (BedsResponse)obj;
if (beds == null)
{
if (other.beds != null)
{
return false;
}
}
else if (!beds.equals(other.beds))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("BedsResponse [beds=");
builder.append(beds);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import com.google.gson.annotations.SerializedName;
public class Error
{
@SerializedName("Code")
private Long code;
@SerializedName("Message")
private String message;
public Long getCode()
{
return code;
}
public void setCode(Long code)
{
this.code = code;
}
public Error withCode(Long code)
{
setCode(code);
return this;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
public Error withMessage(String message)
{
setMessage(message);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((code == null) ? 0 : code.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof Error))
{
return false;
}
Error other = (Error)obj;
if (code == null)
{
if (other.code != null)
{
return false;
}
}
else if (!code.equals(other.code))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("Error [code=");
builder.append(code);
builder.append(", message=");
builder.append(message);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import com.google.gson.annotations.SerializedName;
public class Failure
{
@SerializedName("Error")
private Error error;
public Error getError()
{
return error;
}
public void setError(Error error)
{
this.error = error;
}
public Failure withError(Error error)
{
setError(error);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((error == null) ? 0 : error.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof Failure))
{
return false;
}
Failure other = (Failure)obj;
if (error == null)
{
if (other.error != null)
{
return false;
}
}
else if (!error.equals(other.error))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("Failure [error=");
builder.append(error);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import java.util.List;
public class FamilyStatus
{
private List<BedStatus> beds;
public List<BedStatus> getBeds()
{
return beds;
}
public void setBeds(List<BedStatus> beds)
{
this.beds = beds;
}
public FamilyStatus withBeds(List<BedStatus> beds)
{
setBeds(beds);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((beds == null) ? 0 : beds.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof FamilyStatus))
{
return false;
}
FamilyStatus other = (FamilyStatus)obj;
if (beds == null)
{
if (other.beds != null)
{
return false;
}
}
else if (!beds.equals(other.beds))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("FamilyStatus [beds=");
builder.append(beds);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
public class LoginInfo
{
private String userId;
private String key;
private Long registrationState;
private Long edpLoginStatus;
private String edpLoginMessage;
public String getUserId()
{
return userId;
}
public void setUserId(String userId)
{
this.userId = userId;
}
public LoginInfo withUserId(String userId)
{
setUserId(userId);
return this;
}
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public LoginInfo withKey(String key)
{
setKey(key);
return this;
}
public Long getRegistrationState()
{
return registrationState;
}
public void setRegistrationState(Long registrationState)
{
this.registrationState = registrationState;
}
public LoginInfo withRegistrationState(Long registrationState)
{
setRegistrationState(registrationState);
return this;
}
public Long getEdpLoginStatus()
{
return edpLoginStatus;
}
public void setEdpLoginStatus(Long edpLoginStatus)
{
this.edpLoginStatus = edpLoginStatus;
}
public LoginInfo withEdpLoginStatus(Long edpLoginStatus)
{
setEdpLoginStatus(edpLoginStatus);
return this;
}
public String getEdpLoginMessage()
{
return edpLoginMessage;
}
public void setEdpLoginMessage(String edpLoginMessage)
{
this.edpLoginMessage = edpLoginMessage;
}
public LoginInfo withEdpLoginMessage(String edpLoginMessage)
{
setEdpLoginMessage(edpLoginMessage);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + (int)(registrationState ^ (registrationState >>> 32));
result = prime * result + ((userId == null) ? 0 : userId.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof LoginInfo))
{
return false;
}
LoginInfo other = (LoginInfo)obj;
if (key == null)
{
if (other.key != null)
{
return false;
}
}
else if (!key.equals(other.key))
{
return false;
}
if (!registrationState.equals(other.registrationState))
{
return false;
}
if (userId == null)
{
if (other.userId != null)
{
return false;
}
}
else if (!userId.equals(other.userId))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("LoginInfo [userId=");
builder.append(userId);
builder.append(", key=");
builder.append(key);
builder.append(", registrationState=");
builder.append(registrationState);
builder.append(", edpLoginStatus=");
builder.append(edpLoginStatus);
builder.append(", edpLoginMessage=");
builder.append(edpLoginMessage);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
public class LoginRequest
{
private String login;
private String password;
public String getLogin()
{
return login;
}
public void setLogin(String login)
{
this.login = login;
}
public LoginRequest withLogin(String login)
{
setLogin(login);
return this;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public LoginRequest withPassword(String password)
{
setPassword(password);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((login == null) ? 0 : login.hashCode());
result = prime * result + ((password == null) ? 0 : password.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof LoginRequest))
{
return false;
}
LoginRequest other = (LoginRequest)obj;
if (login == null)
{
if (other.login != null)
{
return false;
}
}
else if (!login.equals(other.login))
{
return false;
}
if (password == null)
{
if (other.password != null)
{
return false;
}
}
else if (!password.equals(other.password))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("LoginRequest [login=");
builder.append(login);
builder.append(", password=");
builder.append(password);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
public class PauseMode
{
private String accountId;
private String bedId;
private String pauseMode;
public String getAccountId()
{
return accountId;
}
public void setAccountId(String accountId)
{
this.accountId = accountId;
}
public PauseMode withAccountId(String accountId)
{
setAccountId(accountId);
return this;
}
public String getBedId()
{
return bedId;
}
public void setBedId(String bedId)
{
this.bedId = bedId;
}
public PauseMode withBedId(String bedId)
{
setBedId(bedId);
return this;
}
public String getPauseMode()
{
return pauseMode;
}
public void setPauseMode(String pauseMode)
{
this.pauseMode = pauseMode;
}
public PauseMode withPauseMode(String pauseMode)
{
setPauseMode(pauseMode);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((accountId == null) ? 0 : accountId.hashCode());
result = prime * result + ((bedId == null) ? 0 : bedId.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof PauseMode))
{
return false;
}
PauseMode other = (PauseMode)obj;
if (accountId == null)
{
if (other.accountId != null)
{
return false;
}
}
else if (!accountId.equals(other.accountId))
{
return false;
}
if (bedId == null)
{
if (other.bedId != null)
{
return false;
}
}
else if (!bedId.equals(other.bedId))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("PauseMode [accountId=");
builder.append(accountId);
builder.append(", bedId=");
builder.append(bedId);
builder.append(", pauseMode=");
builder.append(pauseMode);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,509 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import com.google.gson.annotations.SerializedName;
public class Sleeper
{
private String firstName;
private Boolean active;
private Boolean emailValidated;
@SerializedName("isChild")
private Boolean child;
private String bedId;
private String birthYear;
private String zipCode;
private String timezone;
@SerializedName("isMale")
private Boolean male;
private Integer weight; // lbs
private String duration;
private String sleeperId;
private Integer height; // inches
private Long licenseVersion;
private String username;
private Integer birthMonth; // 0-based; 12 means not entered?
private Integer sleepGoal;
@SerializedName("isAccountOwner")
private Boolean accountOwner;
private String accountId;
private String email;
private String avatar;
private String lastLogin; // should be ZonedDateTime but provider passes string "null" when missing
private Integer side; // 0=left; 1=right
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public Sleeper withFirstName(String firstName)
{
setFirstName(firstName);
return this;
}
public Boolean isActive()
{
return active;
}
public void setActive(Boolean active)
{
this.active = active;
}
public Sleeper withActive(Boolean active)
{
setActive(active);
return this;
}
public Boolean isEmailValidated()
{
return emailValidated;
}
public void setEmailValidated(Boolean emailValidated)
{
this.emailValidated = emailValidated;
}
public Sleeper withEmailValidated(Boolean emailValidated)
{
setEmailValidated(emailValidated);
return this;
}
public Boolean isChild()
{
return child;
}
public void setChild(Boolean child)
{
this.child = child;
}
public Sleeper withChild(Boolean child)
{
setChild(child);
return this;
}
public String getBedId()
{
return bedId;
}
public void setBedId(String bedId)
{
this.bedId = bedId;
}
public Sleeper withBedId(String bedId)
{
setBedId(bedId);
return this;
}
public String getBirthYear()
{
return birthYear;
}
public void setBirthYear(String birthYear)
{
this.birthYear = birthYear;
}
public Sleeper withBirthYear(String birthYear)
{
setBirthYear(birthYear);
return this;
}
public String getZipCode()
{
return zipCode;
}
public void setZipCode(String zipCode)
{
this.zipCode = zipCode;
}
public Sleeper withZipCode(String zipCode)
{
setZipCode(zipCode);
return this;
}
public String getTimezone()
{
return timezone;
}
public void setTimezone(String timezone)
{
this.timezone = timezone;
}
public Sleeper withTimezone(String timezone)
{
setTimezone(timezone);
return this;
}
public Boolean isMale()
{
return male;
}
public void setMale(Boolean male)
{
this.male = male;
}
public Sleeper withMale(Boolean male)
{
setMale(male);
return this;
}
public Integer getWeight()
{
return weight;
}
public void setWeight(Integer weight)
{
this.weight = weight;
}
public Sleeper withWeight(Integer weight)
{
setWeight(weight);
return this;
}
public String getDuration()
{
return duration;
}
public void setDuration(String duration)
{
this.duration = duration;
}
public Sleeper withDuration(String duration)
{
setDuration(duration);
return this;
}
public String getSleeperId()
{
return sleeperId;
}
public void setSleeperId(String sleeperId)
{
this.sleeperId = sleeperId;
}
public Sleeper withSleeperId(String sleeperId)
{
setSleeperId(sleeperId);
return this;
}
public Integer getHeight()
{
return height;
}
public void setHeight(Integer height)
{
this.height = height;
}
public Sleeper withHeight(Integer height)
{
setHeight(height);
return this;
}
public Long getLicenseVersion()
{
return licenseVersion;
}
public void setLicenseVersion(Long licenseVersion)
{
this.licenseVersion = licenseVersion;
}
public Sleeper withLicenseVersion(Long licenseVersion)
{
setLicenseVersion(licenseVersion);
return this;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public Sleeper withUsername(String username)
{
setUsername(username);
return this;
}
public Integer getBirthMonth()
{
return birthMonth;
}
public void setBirthMonth(Integer birthMonth)
{
this.birthMonth = birthMonth;
}
public Sleeper withBirthMonth(Integer birthMonth)
{
setBirthMonth(birthMonth);
return this;
}
public Integer getSleepGoal()
{
return sleepGoal;
}
public void setSleepGoal(Integer sleepGoal)
{
this.sleepGoal = sleepGoal;
}
public Sleeper withSleepGoal(Integer sleepGoal)
{
setSleepGoal(sleepGoal);
return this;
}
public Boolean isAccountOwner()
{
return accountOwner;
}
public void setAccountOwner(Boolean accountOwner)
{
this.accountOwner = accountOwner;
}
public Sleeper withAccountOwner(Boolean accountOwner)
{
setAccountOwner(accountOwner);
return this;
}
public String getAccountId()
{
return accountId;
}
public void setAccountId(String accountId)
{
this.accountId = accountId;
}
public Sleeper withAccountId(String accountId)
{
setAccountId(accountId);
return this;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public Sleeper withEmail(String email)
{
setEmail(email);
return this;
}
public String getAvatar()
{
return avatar;
}
public void setAvatar(String avatar)
{
this.avatar = avatar;
}
public Sleeper withAvatar(String avatar)
{
setAvatar(avatar);
return this;
}
public String getLastLogin()
{
return lastLogin;
}
public void setLastLogin(String lastLogin)
{
this.lastLogin = lastLogin;
}
public Sleeper withLastLogin(String lastLogin)
{
setLastLogin(lastLogin);
return this;
}
public Integer getSide()
{
return side;
}
public void setSide(Integer side)
{
this.side = side;
}
public Sleeper withSide(Integer side)
{
setSide(side);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((sleeperId == null) ? 0 : sleeperId.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof Sleeper))
{
return false;
}
Sleeper other = (Sleeper)obj;
if (sleeperId == null)
{
if (other.sleeperId != null)
{
return false;
}
}
else if (!sleeperId.equals(other.sleeperId))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("Sleeper [firstName=");
builder.append(firstName);
builder.append(", active=");
builder.append(active);
builder.append(", emailValidated=");
builder.append(emailValidated);
builder.append(", child=");
builder.append(child);
builder.append(", bedId=");
builder.append(bedId);
builder.append(", birthYear=");
builder.append(birthYear);
builder.append(", zipCode=");
builder.append(zipCode);
builder.append(", timezone=");
builder.append(timezone);
builder.append(", male=");
builder.append(male);
builder.append(", weight=");
builder.append(weight);
builder.append(", duration=");
builder.append(duration);
builder.append(", sleeperId=");
builder.append(sleeperId);
builder.append(", height=");
builder.append(height);
builder.append(", licenseVersion=");
builder.append(licenseVersion);
builder.append(", username=");
builder.append(username);
builder.append(", birthMonth=");
builder.append(birthMonth);
builder.append(", sleepGoal=");
builder.append(sleepGoal);
builder.append(", accountOwner=");
builder.append(accountOwner);
builder.append(", accountId=");
builder.append(accountId);
builder.append(", email=");
builder.append(email);
builder.append(", avatar=");
builder.append(avatar);
builder.append(", lastLogin=");
builder.append(lastLogin);
builder.append(", side=");
builder.append(side);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import java.util.List;
public class SleepersResponse
{
private List<Sleeper> sleepers;
public List<Sleeper> getSleepers()
{
return sleepers;
}
public void setSleepers(List<Sleeper> sleepers)
{
this.sleepers = sleepers;
}
public SleepersResponse withSleepers(List<Sleeper> sleepers)
{
setSleepers(sleepers);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((sleepers == null) ? 0 : sleepers.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof SleepersResponse))
{
return false;
}
SleepersResponse other = (SleepersResponse)obj;
if (sleepers == null)
{
if (other.sleepers != null)
{
return false;
}
}
else if (!sleepers.equals(other.sleepers))
{
return false;
}
return true;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("SleepersResponse [sleepers=");
builder.append(sleepers);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import java.time.Duration;
import java.time.format.DateTimeParseException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TimeSince
{
private static final Pattern PATTERN = Pattern.compile("(([0-9]+) d )?([0-9]{2}):([0-9]{2}):([0-9]{2})",
Pattern.CASE_INSENSITIVE);
private Duration duration;
public Duration getDuration()
{
return duration;
}
public void setDuration(Duration duration)
{
this.duration = duration == null ? null : duration.abs();
}
public TimeSince withDuration(long days, long hours, long minutes, long seconds)
{
return withDuration(Duration.ofSeconds(TimeUnit.DAYS.toSeconds(days)
+ TimeUnit.HOURS.toSeconds(hours)
+ TimeUnit.MINUTES.toSeconds(minutes)
+ seconds));
}
public TimeSince withDuration(Duration duration)
{
setDuration(duration);
return this;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((duration == null) ? 0 : duration.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof TimeSince))
{
return false;
}
TimeSince other = (TimeSince)obj;
if (duration == null)
{
if (other.duration != null)
{
return false;
}
}
else if (!duration.equals(other.duration))
{
return false;
}
return true;
}
@Override
public String toString()
{
long totalDays = duration.toDays();
long totalHours = duration.toHours();
long totalMinutes = duration.toMinutes();
long totalSeconds = duration.getSeconds();
long hours = totalHours - TimeUnit.DAYS.toHours(totalDays);
long minutes = totalMinutes - TimeUnit.HOURS.toMinutes(totalHours);
long seconds = totalSeconds - TimeUnit.MINUTES.toSeconds(totalMinutes);
if (totalDays > 0)
{
return String.format("%d d %02d:%02d:%02d", totalDays, hours, minutes, seconds);
}
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
public static TimeSince parse(CharSequence text)
{
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (!matcher.matches())
{
throw new DateTimeParseException("Text cannot be parsed", text, 0);
}
String dayMatch = matcher.group(2);
String hourMatch = matcher.group(3);
String minuteMatch = matcher.group(4);
String secondMatch = matcher.group(5);
StringBuilder sb = new StringBuilder("P");
if (dayMatch != null)
{
sb.append(dayMatch).append('D');
}
sb.append('T')
.append(hourMatch)
.append('H')
.append(minuteMatch)
.append('M')
.append(secondMatch)
.append('S');
return new TimeSince().withDuration(Duration.parse(sb.toString()));
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.FileReader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.BedSideStatus;
import org.openhab.binding.sleepiq.api.model.TimeSince;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class BedSideStatusTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
BedSideStatus bedSideStatus = new BedSideStatus().withAlertDetailedMessage("No Alert")
.withAlertId(0L)
.withInBed(false)
.withLastLink(new TimeSince().withDuration(3,
5,
4,
38))
.withPressure(573)
.withSleepNumber(55);
assertEquals(readJson("bed-side-status.json"), gson.toJson(bedSideStatus));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("bed-side-status.json")))
{
BedSideStatus bedSideStatus = gson.fromJson(reader, BedSideStatus.class);
assertEquals("No Alert", bedSideStatus.getAlertDetailedMessage());
assertEquals(Long.valueOf(0L), bedSideStatus.getAlertId());
assertFalse(bedSideStatus.isInBed());
assertEquals(new TimeSince().withDuration(3, 5, 4, 38), bedSideStatus.getLastLink());
assertEquals(Integer.valueOf(573), bedSideStatus.getPressure());
assertEquals(Integer.valueOf(55), bedSideStatus.getSleepNumber());
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.FileReader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.BedSideStatus;
import org.openhab.binding.sleepiq.api.model.BedStatus;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class BedStatusTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
BedStatus bedStatus = new BedStatus().withBedId("-9999999999999999999")
.withLeftSide(new BedSideStatus().withInBed(true))
.withRightSide(new BedSideStatus().withInBed(false))
.withStatus(1L);
assertEquals(readJson("bed-status.json"), gson.toJson(bedStatus));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("bed-status.json")))
{
BedStatus bedStatus = gson.fromJson(reader, BedStatus.class);
assertEquals("-9999999999999999999", bedStatus.getBedId());
assertEquals(Long.valueOf(1L), bedStatus.getStatus());
BedSideStatus leftSide = bedStatus.getLeftSide();
assertNotNull(leftSide);
assertTrue(leftSide.isInBed());
BedSideStatus rightSide = bedStatus.getRightSide();
assertNotNull(rightSide);
assertFalse(rightSide.isInBed());
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import java.io.FileReader;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.Bed;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class BedTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
Bed bed = new Bed().withAccountId("-8888888888888888888")
.withBase("MODULAR")
.withBedId("-9999999999999999999")
.withDualSleep(true)
.withKidsBed(false)
.withMacAddress("AABBCCDDEEFF")
.withModel("P5")
.withName("Bed")
.withPurchaseDate(ZonedDateTime.of(2017,
2,
2,
0,
0,
1,
0,
ZoneId.of("Z").normalized()))
.withReference("55555555555-5")
.withRegistrationDate(ZonedDateTime.of(2017,
2,
17,
2,
14,
10,
0,
ZoneId.of("Z").normalized()))
.withReturnRequestStatus(0L)
.withSerial("")
.withSize("QUEEN")
.withSku("QP5")
.withSleeperLeftId("-2222222222222222222")
.withSleeperRightId("-1111111111111111111")
.withStatus(1L)
.withTimezone("US/Pacific")
.withVersion("")
.withZipCode("90210");
assertEquals(readJson("bed.json"), gson.toJson(bed));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("bed.json")))
{
Bed bed = gson.fromJson(reader, Bed.class);
assertEquals("-8888888888888888888", bed.getAccountId());
assertEquals("MODULAR", bed.getBase());
assertEquals("-9999999999999999999", bed.getBedId());
assertEquals(true, bed.isDualSleep());
assertEquals(false, bed.isKidsBed());
assertEquals("AABBCCDDEEFF", bed.getMacAddress());
assertEquals("P5", bed.getModel());
assertEquals("Bed", bed.getName());
assertEquals(ZonedDateTime.of(2017, 2, 2, 0, 0, 1, 0, ZoneId.of("Z").normalized()),
bed.getPurchaseDate());
assertEquals("55555555555-5", bed.getReference());
assertEquals(ZonedDateTime.of(2017, 2, 17, 2, 14, 10, 0, ZoneId.of("Z").normalized()),
bed.getRegistrationDate());
assertEquals(Long.valueOf(0L), bed.getReturnRequestStatus());
assertEquals("", bed.getSerial());
assertEquals("QUEEN", bed.getSize());
assertEquals("QP5", bed.getSku());
assertEquals("-2222222222222222222", bed.getSleeperLeftId());
assertEquals("-1111111111111111111", bed.getSleeperRightId());
assertEquals(Long.valueOf(1L), bed.getStatus());
assertEquals("US/Pacific", bed.getTimezone());
assertEquals("", bed.getVersion());
assertEquals("90210", bed.getZipCode());
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.FileReader;
import java.util.Arrays;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.Bed;
import org.openhab.binding.sleepiq.api.model.BedsResponse;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class BedsResponseTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
BedsResponse bedsResponse = new BedsResponse().withBeds(Arrays.asList(new Bed().withName("Bed1"),
new Bed().withName("Bed2")));
assertEquals(readJson("beds-response.json"), gson.toJson(bedsResponse));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("beds-response.json")))
{
BedsResponse bedsResponse = gson.fromJson(reader, BedsResponse.class);
List<Bed> beds = bedsResponse.getBeds();
assertNotNull(beds);
assertEquals(2, beds.size());
assertEquals("Bed1", beds.get(0).getName());
assertEquals("Bed2", beds.get(1).getName());
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.FileReader;
import java.util.Arrays;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.BedStatus;
import org.openhab.binding.sleepiq.api.model.FamilyStatus;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class FamilyStatusTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
FamilyStatus familyStatus = new FamilyStatus().withBeds(Arrays.asList(new BedStatus().withStatus(1L)));
assertEquals(readJson("family-status.json"), gson.toJson(familyStatus));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("family-status.json")))
{
FamilyStatus familyStatus = gson.fromJson(reader, FamilyStatus.class);
List<BedStatus> beds = familyStatus.getBeds();
assertNotNull(beds);
assertEquals(1, beds.size());
assertEquals(Long.valueOf(1L), beds.get(0).getStatus());
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import java.io.FileReader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.PauseMode;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class PauseModeTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
PauseMode pauseMode = new PauseMode().withAccountId("-8888888888888888888")
.withBedId("-9999999999999999999")
.withPauseMode("off");
assertEquals(readJson("pause-mode.json"), gson.toJson(pauseMode));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("pause-mode.json")))
{
PauseMode pauseMode = gson.fromJson(reader, PauseMode.class);
assertEquals("-8888888888888888888", pauseMode.getAccountId());
assertEquals("-9999999999999999999", pauseMode.getBedId());
assertEquals("off", pauseMode.getPauseMode());
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import java.io.FileReader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.Sleeper;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class SleeperTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
Sleeper sleeper = new Sleeper().withAccountId("-5555555555555555555")
.withAccountOwner(true)
.withActive(true)
.withAvatar("")
.withBedId("-9999999999999999999")
.withBirthMonth(6)
.withBirthYear("1970")
.withChild(false)
.withDuration("")
.withEmail("alice@domain.com")
.withEmailValidated(true)
.withFirstName("Alice")
.withHeight(64)
.withLastLogin("2017-02-17 20:19:36 CST")
.withLicenseVersion(6L)
.withMale(false)
.withSide(1)
.withSleeperId("-1111111111111111111")
.withSleepGoal(450)
.withTimezone("US/Pacific")
.withUsername("alice@domain.com")
.withWeight(110)
.withZipCode("90210");
assertEquals(readJson("sleeper.json"), gson.toJson(sleeper));
}
@Test
public void testSerializeLastLoginNull() throws Exception
{
Sleeper sleeper = new Sleeper().withLastLogin("null");
assertEquals(readJson("sleeper-lastlogin-null.json"), gson.toJson(sleeper));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("sleeper.json")))
{
Sleeper sleeper = gson.fromJson(reader, Sleeper.class);
assertEquals("-5555555555555555555", sleeper.getAccountId());
assertEquals(true, sleeper.isAccountOwner());
assertEquals(true, sleeper.isActive());
assertEquals("", sleeper.getAvatar());
assertEquals("-9999999999999999999", sleeper.getBedId());
assertEquals(Integer.valueOf(6), sleeper.getBirthMonth());
assertEquals("1970", sleeper.getBirthYear());
assertEquals(false, sleeper.isChild());
assertEquals("", sleeper.getDuration());
assertEquals("alice@domain.com", sleeper.getEmail());
assertEquals(true, sleeper.isEmailValidated());
assertEquals("Alice", sleeper.getFirstName());
assertEquals(Integer.valueOf(64), sleeper.getHeight());
assertEquals("2017-02-17 20:19:36 CST", sleeper.getLastLogin());
assertEquals(Long.valueOf(6L), sleeper.getLicenseVersion());
assertEquals(false, sleeper.isMale());
assertEquals(Integer.valueOf(1), sleeper.getSide());
assertEquals("-1111111111111111111", sleeper.getSleeperId());
assertEquals(Integer.valueOf(450), sleeper.getSleepGoal());
assertEquals("US/Pacific", sleeper.getTimezone());
assertEquals("alice@domain.com", sleeper.getUsername());
assertEquals(Integer.valueOf(110), sleeper.getWeight());
assertEquals("90210", sleeper.getZipCode());
}
}
@Test
public void testDeserializeLastLoginNull() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("sleeper-lastlogin-null.json")))
{
Sleeper sleeper = gson.fromJson(reader, Sleeper.class);
assertEquals("null", sleeper.getLastLogin());
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.FileReader;
import java.util.Arrays;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.impl.GsonGenerator;
import org.openhab.binding.sleepiq.api.model.Sleeper;
import org.openhab.binding.sleepiq.api.model.SleepersResponse;
import org.openhab.binding.sleepiq.api.test.AbstractTest;
import com.google.gson.Gson;
public class SleepersResponseTest extends AbstractTest
{
private static Gson gson;
@BeforeClass
public static void setUpBeforeClass()
{
gson = GsonGenerator.create(true);
}
@Test
public void testSerializeAllFields() throws Exception
{
SleepersResponse sleepersResponse = new SleepersResponse().withSleepers(Arrays.asList(new Sleeper().withFirstName("Alice"),
new Sleeper().withFirstName("Bob")));
assertEquals(readJson("sleepers-response.json"), gson.toJson(sleepersResponse));
}
@Test
public void testDeserializeAllFields() throws Exception
{
try (FileReader reader = new FileReader(getTestDataFile("sleepers-response.json")))
{
SleepersResponse sleepersResponse = gson.fromJson(reader, SleepersResponse.class);
List<Sleeper> sleepers = sleepersResponse.getSleepers();
assertNotNull(sleepers);
assertEquals(2, sleepers.size());
assertEquals("Alice", sleepers.get(0).getFirstName());
assertEquals("Bob", sleepers.get(1).getFirstName());
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.model;
import static org.junit.Assert.assertEquals;
import java.time.Duration;
import org.junit.Test;
import org.openhab.binding.sleepiq.api.model.TimeSince;
public class TimeSinceTest
{
@Test
public void testWithDuration()
{
assertEquals(new TimeSince().withDuration(0, 0, 0, 0).getDuration(),
new TimeSince().withDuration(Duration.parse("PT00H00M00S")).getDuration());
assertEquals(new TimeSince().withDuration(0, 2, 3, 4).getDuration(),
new TimeSince().withDuration(Duration.parse("PT02H03M04S")).getDuration());
assertEquals(new TimeSince().withDuration(0, 12, 34, 56).getDuration(),
new TimeSince().withDuration(Duration.parse("PT12H34M56S")).getDuration());
assertEquals(new TimeSince().withDuration(1, 2, 3, 4).getDuration(),
new TimeSince().withDuration(Duration.parse("P1DT02H03M04S")).getDuration());
assertEquals(new TimeSince().withDuration(12, 23, 34, 45).getDuration(),
new TimeSince().withDuration(Duration.parse("P12DT23H34M45S")).getDuration());
}
@Test
public void testToString()
{
assertEquals("00:00:00",
new TimeSince().withDuration(Duration.parse("PT00H00M00S")).toString());
assertEquals("02:03:04",
new TimeSince().withDuration(Duration.parse("PT02H03M04S")).toString());
assertEquals("12:34:56",
new TimeSince().withDuration(Duration.parse("PT12H34M56S")).toString());
assertEquals("1 d 02:03:04",
new TimeSince().withDuration(Duration.parse("P1DT02H03M04S")).toString());
assertEquals("12 d 23:34:45",
new TimeSince().withDuration(Duration.parse("P12DT23H34M45S")).toString());
}
@Test
public void testParse()
{
assertEquals(Duration.parse("PT00H00M00S"), TimeSince.parse("00:00:00").getDuration());
assertEquals(Duration.parse("PT2H3M4S"), TimeSince.parse("02:03:04").getDuration());
assertEquals(Duration.parse("PT12H34M56S"), TimeSince.parse("12:34:56").getDuration());
assertEquals(Duration.parse("P1DT2H3M4S"), TimeSince.parse("1 d 02:03:04").getDuration());
assertEquals(Duration.parse("P12DT23H34M45S"),
TimeSince.parse("12 d 23:34:45").getDuration());
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2017 Gregory Moyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openhab.binding.sleepiq.api.test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class AbstractTest {
private static final String RESOURCES_PATH = "src/test/resources/";
protected File getTestDataFile(String name) {
return getTestDataPath(name).toFile();
}
protected Path getTestDataPath(String name) {
String packageName = this.getClass().getPackage().getName();
List<String> paths = new ArrayList<>();
paths.addAll(Arrays.asList(packageName.split("\\.")));
paths.add(name);
return Paths.get(RESOURCES_PATH, paths.toArray(new String[paths.size()]));
}
protected String readJson(String jsonFileName) throws IOException {
return String.join("\n", Files.readAllLines(getTestDataPath(jsonFileName)));
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.sleepiq-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-sleepiq" description="SleepIQ Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.sleepiq/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import com.google.gson.Gson;
/**
* JSON reader/writer for Jersey using GSON.
*
* @author Simon Kaufmann - Initial contribution
*
* @param <T>
*/
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {
private final Gson gson;
public GsonProvider(Gson gson) {
this.gson = gson;
}
@Override
public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public void writeTo(T object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
try (OutputStream stream = entityStream) {
entityStream.write(gson.toJson(object).getBytes(StandardCharsets.UTF_8));
entityStream.flush();
}
}
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
try (InputStreamReader reader = new InputStreamReader(entityStream, StandardCharsets.UTF_8)) {
return gson.fromJson(reader, type);
}
}
}

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link SleepIQBinding} class defines common constants, which are
* used across the whole binding.
*
* @author Gregory Moyer - Initial contribution
*/
@NonNullByDefault
public class SleepIQBindingConstants {
public static final String BINDING_ID = "sleepiq";
// List of all Thing Type UIDs
// bridge
public static final ThingTypeUID THING_TYPE_CLOUD = new ThingTypeUID(BINDING_ID, "cloud");
// generic thing types
// public static final ThingTypeUID THING_TYPE_SINGLE_BED = new ThingTypeUID(BINDING_ID, "singleBed");
public static final ThingTypeUID THING_TYPE_DUAL_BED = new ThingTypeUID(BINDING_ID, "dualBed");
// List of all Channel ids
// public static final String CHANNEL_BED_IN_BED = "bed#inBed";
public static final String CHANNEL_LEFT_IN_BED = "left#inBed";
public static final String CHANNEL_RIGHT_IN_BED = "right#inBed";
// public static final String CHANNEL_BED_SLEEP_NUMBER = "bed#sleepNumber";
public static final String CHANNEL_LEFT_SLEEP_NUMBER = "left#sleepNumber";
public static final String CHANNEL_RIGHT_SLEEP_NUMBER = "right#sleepNumber";
// public static final String CHANNEL_BED_PRESSURE = "bed#pressure";
public static final String CHANNEL_LEFT_PRESSURE = "left#pressure";
public static final String CHANNEL_RIGHT_PRESSURE = "right#pressure";
// public static final String CHANNEL_BED_LAST_LINK = "bed#lastLink";
public static final String CHANNEL_LEFT_LAST_LINK = "left#lastLink";
public static final String CHANNEL_RIGHT_LAST_LINK = "right#lastLink";
// public static final String CHANNEL_BED_ALERT_ID = "bed#alertId";
public static final String CHANNEL_LEFT_ALERT_ID = "left#alertId";
public static final String CHANNEL_RIGHT_ALERT_ID = "right#alertId";
// public static final String CHANNEL_BED_ALERT_DETAILED_MESSAGE = "bed#alertDetailedMessage";
public static final String CHANNEL_LEFT_ALERT_DETAILED_MESSAGE = "left#alertDetailedMessage";
public static final String CHANNEL_RIGHT_ALERT_DETAILED_MESSAGE = "right#alertDetailedMessage";
// List of non-standard Properties
public static final String PROPERTY_BASE = "base";
public static final String PROPERTY_KIDS_BED = "kidsBed";
public static final String PROPERTY_MAC_ADDRESS = "macAddress";
public static final String PROPERTY_NAME = "name";
public static final String PROPERTY_PURCHASE_DATE = "purchaseDate";
public static final String PROPERTY_SIZE = "size";
public static final String PROPERTY_SKU = "sku";
private SleepIQBindingConstants() {
// utility class
}
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal;
/**
* The {@link SleepIQConfigStatusMessage} defines the keys to be used for configuration status messages.
*
* @author Gregory Moyer - Initial contribution
*
*/
public interface SleepIQConfigStatusMessage {
public static final String USERNAME_MISSING = "missing-username-configuration";
public static final String PASSWORD_MISSING = "missing-password-configuration";
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openhab.binding.sleepiq.internal.discovery.SleepIQBedDiscoveryService;
import org.openhab.binding.sleepiq.internal.handler.SleepIQCloudHandler;
import org.openhab.binding.sleepiq.internal.handler.SleepIQDualBedHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SleepIQHandlerFactory} is responsible for creating thing handlers.
*
* @author Gregory Moyer - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.sleepiq")
public class SleepIQHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Collections
.unmodifiableSet(Stream.concat(SleepIQCloudHandler.SUPPORTED_THING_TYPE_UIDS.stream(),
SleepIQDualBedHandler.SUPPORTED_THING_TYPE_UIDS.stream()).collect(Collectors.toSet()));
private final Logger logger = LoggerFactory.getLogger(SleepIQHandlerFactory.class);
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceReg = new HashMap<>();
@Override
public boolean supportsThingType(final ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(final Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (SleepIQCloudHandler.SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID)) {
logger.debug("Creating SleepIQ cloud thing handler");
SleepIQCloudHandler cloudHandler = new SleepIQCloudHandler((Bridge) thing);
registerBedDiscoveryService(cloudHandler);
return cloudHandler;
} else if (SleepIQDualBedHandler.SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID)) {
logger.debug("Creating SleepIQ dual bed thing handler");
return new SleepIQDualBedHandler(thing);
}
return null;
}
@Override
protected void removeHandler(final ThingHandler thingHandler) {
logger.debug("Removing SleepIQ thing handler");
if (thingHandler instanceof SleepIQCloudHandler) {
unregisterBedDiscoveryService((SleepIQCloudHandler) thingHandler);
}
}
/**
* Register the given cloud handler to participate in discovery of new beds.
*
* @param cloudHandler the cloud handler to register (must not be <code>null</code>)
*/
private synchronized void registerBedDiscoveryService(final SleepIQCloudHandler cloudHandler) {
logger.debug("Registering bed discovery service");
SleepIQBedDiscoveryService discoveryService = new SleepIQBedDiscoveryService(cloudHandler);
discoveryServiceReg.put(cloudHandler.getThing().getUID(),
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
}
/**
* Unregister the given cloud handler from participating in discovery of new beds.
*
* @param cloudHandler the cloud handler to unregister (must not be <code>null</code>)
*/
private synchronized void unregisterBedDiscoveryService(final SleepIQCloudHandler cloudHandler) {
ThingUID thingUID = cloudHandler.getThing().getUID();
ServiceRegistration<?> serviceReg = discoveryServiceReg.remove(thingUID);
if (serviceReg != null) {
logger.debug("Unregistering bed discovery service");
serviceReg.unregister();
}
}
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal.config;
/**
* Configuration class for a SleepIQ bed.
*
* @author Gregory Moyer - Initial contribution
*/
public class SleepIQBedConfiguration {
public static final String BED_ID = "bedId";
public String bedId;
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal.config;
/**
* Configuration class for the SleepIQ cloud.
*
* @author Gregory Moyer - Initial contribution
*/
public class SleepIQCloudConfiguration {
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
public static final String POLLING_INTERVAL = "pollingInterval";
public String username;
public String password;
public int pollingInterval;
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal.discovery;
import java.util.HashMap;
import java.util.Map;
import org.openhab.binding.sleepiq.api.model.Bed;
import org.openhab.binding.sleepiq.internal.SleepIQBindingConstants;
import org.openhab.binding.sleepiq.internal.config.SleepIQBedConfiguration;
import org.openhab.binding.sleepiq.internal.handler.SleepIQCloudHandler;
import org.openhab.binding.sleepiq.internal.handler.SleepIQDualBedHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SleepIQBedDiscoveryService} is responsible for processing the
* results of devices found through the SleepIQ cloud service.
*
* @author Gregory Moyer - Initial contribution
*/
public class SleepIQBedDiscoveryService extends AbstractDiscoveryService {
private static final int TIMEOUT = 60;
private final Logger logger = LoggerFactory.getLogger(SleepIQBedDiscoveryService.class);
private final SleepIQCloudHandler cloudHandler;
/**
* Discovers beds in the SleepIQ cloud service account.
*
* @param cloudHandler the cloud service handler (bridge)
*/
public SleepIQBedDiscoveryService(final SleepIQCloudHandler cloudHandler) {
super(SleepIQDualBedHandler.SUPPORTED_THING_TYPE_UIDS, TIMEOUT, true);
this.cloudHandler = cloudHandler;
}
@Override
protected void startBackgroundDiscovery() {
logger.debug("Starting background discovery for new beds");
startScan();
}
@Override
protected void startScan() {
logger.debug("Starting scan for new beds");
for (Bed bed : cloudHandler.getBeds()) {
// only dual chamber beds are supported currently
if (!bed.isDualSleep()) {
logger.info("Found a bed that is not dual chamber - currently unsupported");
continue;
}
ThingUID bridgeUID = cloudHandler.getThing().getUID();
ThingUID thingUID = new ThingUID(SleepIQBindingConstants.THING_TYPE_DUAL_BED, bridgeUID,
bed.getMacAddress());
// thing already exists
if (cloudHandler.getThing().getThing(thingUID) != null) {
continue;
}
logger.debug("New bed found with MAC address {}", bed.getMacAddress());
@SuppressWarnings({ "unchecked", "rawtypes" })
Map<String, Object> properties = (Map) cloudHandler.updateProperties(bed, new HashMap<>());
properties.put(SleepIQBedConfiguration.BED_ID, bed.getBedId());
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridgeUID).withLabel(bed.getName() + " - " + bed.getModel()).build();
thingDiscovered(discoveryResult);
}
stopScan();
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal.handler;
import org.openhab.binding.sleepiq.api.SleepIQ;
import org.openhab.binding.sleepiq.api.model.BedStatus;
/**
* The {@link BedStatusListener} is notified when a chamber is updated.
*
* @author Gregory Moyer - Initial contribution
*/
public interface BedStatusListener {
/**
* This method will be called whenever a new bed status is received by the cloud handler.
*
* @param cloud the cloud service that can be used to gather additional information
* @param status the status returned from the cloud service
*/
public void onBedStateChanged(SleepIQ cloud, BedStatus status);
}

View File

@@ -0,0 +1,281 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal.handler;
import static org.openhab.binding.sleepiq.internal.SleepIQBindingConstants.THING_TYPE_CLOUD;
import static org.openhab.binding.sleepiq.internal.config.SleepIQCloudConfiguration.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.sleepiq.api.Configuration;
import org.openhab.binding.sleepiq.api.LoginException;
import org.openhab.binding.sleepiq.api.SleepIQ;
import org.openhab.binding.sleepiq.api.UnauthorizedException;
import org.openhab.binding.sleepiq.api.model.Bed;
import org.openhab.binding.sleepiq.api.model.BedStatus;
import org.openhab.binding.sleepiq.api.model.FamilyStatus;
import org.openhab.binding.sleepiq.internal.SleepIQBindingConstants;
import org.openhab.binding.sleepiq.internal.SleepIQConfigStatusMessage;
import org.openhab.binding.sleepiq.internal.config.SleepIQCloudConfiguration;
import org.openhab.core.cache.ExpiringCache;
import org.openhab.core.config.core.status.ConfigStatusMessage;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SleepIQCloudHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Gregory Moyer - Initial contribution
*/
public class SleepIQCloudHandler extends ConfigStatusBridgeHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Collections.singleton(THING_TYPE_CLOUD);
private final Logger logger = LoggerFactory.getLogger(SleepIQCloudHandler.class);
private final List<BedStatusListener> bedStatusListeners = new CopyOnWriteArrayList<>();
private ExpiringCache<FamilyStatus> statusCache;
private ScheduledFuture<?> pollingJob;
private SleepIQ cloud;
public SleepIQCloudHandler(final Bridge bridge) {
super(bridge);
}
@Override
public void initialize() {
try {
logger.debug("Configuring bed status cache");
statusCache = new ExpiringCache<>(TimeUnit.SECONDS.toMillis(getPollingInterval() / 2),
() -> cloud.getFamilyStatus());
createCloudConnection();
logger.debug("Setting SleepIQ cloud online");
updateListenerManagement();
updateStatus(ThingStatus.ONLINE);
} catch (UnauthorizedException e) {
logger.debug("SleepIQ cloud authentication failed", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid SleepIQ credentials");
} catch (LoginException e) {
logger.debug("SleepIQ cloud login failed", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"SleepIQ cloud login failed: " + e.getMessage());
} catch (Exception e) {
logger.debug("Unexpected error while communicating with SleepIQ cloud", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Unable to connect to SleepIQ cloud: " + e.getMessage());
}
}
/**
* Create a new SleepIQ cloud service connection. If a connection already exists, it will be lost.
*
* @throws LoginException if there is an error while authenticating to the service
*/
private void createCloudConnection() throws LoginException {
logger.debug("Reading SleepIQ cloud binding configuration");
SleepIQCloudConfiguration bindingConfig = getConfigAs(SleepIQCloudConfiguration.class);
logger.debug("Creating SleepIQ client");
Configuration cloudConfig = new Configuration().withUsername(bindingConfig.username)
.withPassword(bindingConfig.password).withLogging(logger.isDebugEnabled());
cloud = SleepIQ.create(cloudConfig);
logger.debug("Authenticating at the SleepIQ cloud service");
cloud.login();
logger.info("Successfully authenticated at the SleepIQ cloud service");
}
@Override
public synchronized void dispose() {
logger.debug("Disposing SleepIQ cloud handler");
if (pollingJob != null && !pollingJob.isCancelled()) {
pollingJob.cancel(true);
pollingJob = null;
}
}
/**
* Start or stop a background polling job to look for bed status updates based on whether or not there are any
* listeners to notify.
*/
private synchronized void updateListenerManagement() {
if (!bedStatusListeners.isEmpty() && (pollingJob == null || pollingJob.isCancelled())) {
int pollingInterval = getPollingInterval();
pollingJob = scheduler.scheduleWithFixedDelay(this::refreshBedStatus, pollingInterval, pollingInterval,
TimeUnit.SECONDS);
} else if (bedStatusListeners.isEmpty() && pollingJob != null && !pollingJob.isCancelled()) {
pollingJob.cancel(true);
pollingJob = null;
}
}
/**
* Retrieve the polling interval for updating bed status.
*
* @return the polling interval in seconds
*/
private int getPollingInterval() {
return getConfigAs(SleepIQCloudConfiguration.class).pollingInterval;
}
/**
* Retrieve the latest status on all beds and update all registered listeners.
*/
public void refreshBedStatus() {
try {
FamilyStatus status = statusCache.getValue();
updateStatus(ThingStatus.ONLINE);
for (BedStatus bedStatus : status.getBeds()) {
bedStatusListeners.stream().forEach(l -> l.onBedStateChanged(cloud, bedStatus));
}
} catch (Exception e) {
logger.debug("Unexpected error while communicating with SleepIQ cloud", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Unable to connect to SleepIQ cloud: " + e.getMessage());
}
}
/**
* Register the given listener to receive bed status updates.
*
* @param listener the listener to register
*/
public void registerBedStatusListener(final BedStatusListener listener) {
if (listener == null) {
return;
}
bedStatusListeners.add(listener);
refreshBedStatus();
updateListenerManagement();
}
/**
* Unregister the given listener from further bed status updates.
*
* @param listener the listener to unregister
* @return <code>true</code> if listener was previously registered and is now unregistered; <code>false</code>
* otherwise
*/
public boolean unregisterBedStatusListener(final BedStatusListener listener) {
boolean result = bedStatusListeners.remove(listener);
if (result) {
updateListenerManagement();
}
return result;
}
@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
// cloud handler has no channels
}
@Override
public Collection<ConfigStatusMessage> getConfigStatus() {
Collection<ConfigStatusMessage> configStatusMessages = new ArrayList<>();
SleepIQCloudConfiguration config = getConfigAs(SleepIQCloudConfiguration.class);
String username = config.username;
String password = config.password;
if (StringUtils.isEmpty(username)) {
configStatusMessages.add(ConfigStatusMessage.Builder.error(USERNAME)
.withMessageKeySuffix(SleepIQConfigStatusMessage.USERNAME_MISSING).withArguments(USERNAME).build());
}
if (StringUtils.isEmpty(password)) {
configStatusMessages.add(ConfigStatusMessage.Builder.error(PASSWORD)
.withMessageKeySuffix(SleepIQConfigStatusMessage.PASSWORD_MISSING).withArguments(PASSWORD).build());
}
return configStatusMessages;
}
/**
* Get a list of all beds registered to the cloud service account.
*
* @return the list of beds (never <code>null</code>)
*/
public List<Bed> getBeds() {
return cloud.getBeds();
}
/**
* Get the {@link Bed} corresponding to the given identifier.
*
* @param bedId the bed identifier
* @return the identified {@link Bed} or <code>null</code> if no such bed exists
*/
public Bed getBed(final String bedId) {
for (Bed bed : getBeds()) {
if (bedId.equals(bed.getBedId())) {
return bed;
}
}
return null;
}
/**
* Update the given properties with attributes of the given bed. If no properties are given, a new map will be
* created.
*
* @param bed the source of data
* @param properties the properties to update (this may be <code>null</code>)
* @return the given map (or a new map if no map was given) with updated/set properties from the supplied bed
*/
public Map<String, String> updateProperties(final Bed bed, Map<String, String> properties) {
if (bed != null) {
properties.put(Thing.PROPERTY_MODEL_ID, bed.getModel());
properties.put(SleepIQBindingConstants.PROPERTY_BASE, bed.getBase());
if (bed.isKidsBed() != null) {
properties.put(SleepIQBindingConstants.PROPERTY_KIDS_BED, bed.isKidsBed().toString());
}
properties.put(SleepIQBindingConstants.PROPERTY_MAC_ADDRESS, bed.getMacAddress());
properties.put(SleepIQBindingConstants.PROPERTY_NAME, bed.getName());
if (bed.getPurchaseDate() != null) {
properties.put(SleepIQBindingConstants.PROPERTY_PURCHASE_DATE, bed.getPurchaseDate().toString());
}
properties.put(SleepIQBindingConstants.PROPERTY_SIZE, bed.getSize());
properties.put(SleepIQBindingConstants.PROPERTY_SKU, bed.getSku());
}
return properties;
}
}

View File

@@ -0,0 +1,162 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sleepiq.internal.handler;
import static org.openhab.binding.sleepiq.internal.SleepIQBindingConstants.*;
import java.util.Collections;
import java.util.Set;
import org.openhab.binding.sleepiq.api.SleepIQ;
import org.openhab.binding.sleepiq.api.model.Bed;
import org.openhab.binding.sleepiq.api.model.BedSideStatus;
import org.openhab.binding.sleepiq.api.model.BedStatus;
import org.openhab.binding.sleepiq.internal.config.SleepIQBedConfiguration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SleepIQDualBedHandler} is responsible for handling channel state updates from the cloud service.
*
* @author Gregory Moyer - Initial contribution
*/
public class SleepIQDualBedHandler extends BaseThingHandler implements BedStatusListener {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Collections.singleton(THING_TYPE_DUAL_BED);
private final Logger logger = LoggerFactory.getLogger(SleepIQDualBedHandler.class);
private volatile String bedId;
public SleepIQDualBedHandler(final Thing thing) {
super(thing);
}
@Override
public void initialize() {
logger.debug("Verifying SleepIQ cloud/bridge configuration");
Bridge bridge = getBridge();
if (bridge == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No cloud service bridge has been configured");
return;
}
ThingHandler handler = bridge.getHandler();
if (!(handler instanceof SleepIQCloudHandler)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Incorrect bridge thing found");
return;
}
logger.debug("Reading SleepIQ bed binding configuration");
bedId = getConfigAs(SleepIQBedConfiguration.class).bedId;
logger.debug("Registering SleepIQ bed status listener");
SleepIQCloudHandler cloudHandler = (SleepIQCloudHandler) handler;
cloudHandler.registerBedStatusListener(this);
if (ThingStatus.ONLINE != bridge.getStatus()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
} else {
updateProperties();
updateStatus(ThingStatus.ONLINE);
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
super.bridgeStatusChanged(bridgeStatusInfo);
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
updateProperties();
}
}
private void updateProperties() {
logger.debug("Updating SleepIQ bed properties for bed {}", bedId);
SleepIQCloudHandler cloudHandler = (SleepIQCloudHandler) getBridge().getHandler();
Bed bed = cloudHandler.getBed(bedId);
if (bed == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bed found with ID " + bedId);
return;
}
updateProperties(cloudHandler.updateProperties(bed, editProperties()));
}
@Override
public void dispose() {
logger.debug("Disposing bed handler for bed {}", bedId);
Bridge bridge = getBridge();
if (bridge != null) {
BridgeHandler bridgeHandler = bridge.getHandler();
if (bridgeHandler instanceof SleepIQCloudHandler) {
((SleepIQCloudHandler) bridgeHandler).unregisterBedStatusListener(this);
}
}
bedId = null;
}
@Override
public void onBedStateChanged(final SleepIQ cloud, final BedStatus status) {
if (!status.getBedId().equals(bedId)) {
return;
}
logger.debug("Updating left side status for bed {}", bedId);
BedSideStatus left = status.getLeftSide();
updateState(CHANNEL_LEFT_IN_BED, left.isInBed() ? OnOffType.ON : OnOffType.OFF);
updateState(CHANNEL_LEFT_SLEEP_NUMBER, new DecimalType(left.getSleepNumber()));
updateState(CHANNEL_LEFT_PRESSURE, new DecimalType(left.getPressure()));
updateState(CHANNEL_LEFT_LAST_LINK, new StringType(left.getLastLink().toString()));
updateState(CHANNEL_LEFT_ALERT_ID, new DecimalType(left.getAlertId()));
updateState(CHANNEL_LEFT_ALERT_DETAILED_MESSAGE, new StringType(left.getAlertDetailedMessage()));
logger.debug("Updating right side status for bed {}", bedId);
BedSideStatus right = status.getRightSide();
updateState(CHANNEL_RIGHT_IN_BED, right.isInBed() ? OnOffType.ON : OnOffType.OFF);
updateState(CHANNEL_RIGHT_SLEEP_NUMBER, new DecimalType(right.getSleepNumber()));
updateState(CHANNEL_RIGHT_PRESSURE, new DecimalType(right.getPressure()));
updateState(CHANNEL_RIGHT_LAST_LINK, new StringType(right.getLastLink().toString()));
updateState(CHANNEL_RIGHT_ALERT_ID, new DecimalType(right.getAlertId()));
updateState(CHANNEL_RIGHT_ALERT_DETAILED_MESSAGE, new StringType(right.getAlertDetailedMessage()));
}
@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
// all channels are read-only
if (command == RefreshType.REFRESH) {
SleepIQCloudHandler cloudHandler = (SleepIQCloudHandler) getBridge().getHandler();
cloudHandler.refreshBedStatus();
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="sleepiq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>SleepIQ Binding</name>
<description>This is the binding for the Sleep Number SleepIQ system.</description>
<author>Gregory Moyer</author>
</binding:binding>

View File

@@ -0,0 +1,3 @@
# configuration messages
config-status.error.missing-username-configuration=No username for the SleepIQ cloud has been provided.
config-status.error.missing-password-configuration=No password for the SleepIQ cloud has been provided.

View File

@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sleepiq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- SleepIQ Cloud Service -->
<bridge-type id="cloud">
<label>SleepIQ Cloud</label>
<description>The SleepIQ cloud service provides connections to all beds in an account.</description>
<properties>
<property name="vendor">Select Comfort</property>
</properties>
<config-description>
<parameter name="username" type="text">
<label>Username</label>
<description>Username of a registered SleepIQ account owner</description>
<required>true</required>
</parameter>
<parameter name="password" type="text">
<context>password</context>
<label>Password</label>
<description>Password of a registered SleepIQ account owner</description>
<required>false</required>
</parameter>
<parameter name="pollingInterval" type="integer" min="30" step="30">
<label>Polling Interval</label>
<description>Seconds between fetching values from the cloud service</description>
<required>true</required>
<default>60</default>
</parameter>
</config-description>
</bridge-type>
<!-- Bed thing with a single chamber -->
<!-- Single chamber bed support is disabled until test devices are available
<thing-type id="singleBed">
<supported-bridge-type-refs>
<bridge-type-ref id="cloud" />
</supported-bridge-type-refs>
<label>Single Chamber Bed</label>
<description>A Sleep Number bed with SleepIQ and a single air chamber</description>
<channel-groups>
<channel-group id="bed" typeId="chamberGroupType"/>
</channel-groups>
<config-description>
<parameter name="bedId" type="text">
<label>Bed ID</label>
<description>The bed identifier identifies a specific bed</description>
<required>true</required>
</parameter>
</config-description>
</thing-type>
-->
<!-- Bed thing with two separate chambers -->
<thing-type id="dualBed">
<supported-bridge-type-refs>
<bridge-type-ref id="cloud"/>
</supported-bridge-type-refs>
<label>Dual Chamber Bed</label>
<description>A Sleep Number bed with SleepIQ and two individual air chambers</description>
<channel-groups>
<channel-group id="left" typeId="chamberGroupType"/>
<channel-group id="right" typeId="chamberGroupType"/>
</channel-groups>
<config-description>
<parameter name="bedId" type="text">
<label>Bed ID</label>
<description>The bed identifier identifies a specific bed</description>
<required>true</required>
</parameter>
</config-description>
</thing-type>
<!-- The attributes of a chamber -->
<channel-group-type id="chamberGroupType">
<label>Chamber</label>
<channels>
<channel id="inBed" typeId="inBedType"/>
<channel id="sleepNumber" typeId="sleepNumberType"/>
<channel id="pressure" typeId="pressureType"/>
<channel id="lastLink" typeId="lastLinkType"/>
<channel id="alertId" typeId="alertIdType"/>
<channel id="alertDetailedMessage" typeId="alertDetailedMessageType"/>
</channels>
</channel-group-type>
<channel-type id="inBedType">
<item-type>Switch</item-type>
<label>In Bed</label>
<description>The presence of a person or object on the chamber</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="sleepNumberType">
<item-type>Number</item-type>
<label>Sleep Number</label>
<description>The Sleep Number setting of the chamber</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="pressureType">
<item-type>Number</item-type>
<label>Pressure</label>
<description>The current pressure inside the chamber</description>
<state pattern="%d kPa" readOnly="true"/>
</channel-type>
<channel-type id="lastLinkType">
<item-type>String</item-type>
<label>Last Link</label>
<description>The amount of time that has passed since a connection was made from the chamber to the cloud service (D d
HH:MM:SS)</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="alertIdType">
<item-type>Number</item-type>
<label>Alert ID</label>
<description>Identifier for an alert condition with the chamber</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="alertDetailedMessageType">
<item-type>String</item-type>
<label>Alert Detailed Message</label>
<description>A detailed message describing an alert condition with the chamber</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,8 @@
{
"isInBed": false,
"alertDetailedMessage": "No Alert",
"sleepNumber": 55,
"alertId": 0,
"lastLink": "3 d 05:04:38",
"pressure": 573
}

View File

@@ -0,0 +1,10 @@
{
"status": 1,
"bedId": "-9999999999999999999",
"leftSide": {
"isInBed": true
},
"rightSide": {
"isInBed": false
}
}

View File

@@ -0,0 +1,23 @@
{
"registrationDate": "2017-02-17T02:14:10Z",
"sleeperRightId": "-1111111111111111111",
"base": "MODULAR",
"returnRequestStatus": 0,
"size": "QUEEN",
"name": "Bed",
"serial": "",
"isKidsBed": false,
"dualSleep": true,
"bedId": "-9999999999999999999",
"status": 1,
"sleeperLeftId": "-2222222222222222222",
"version": "",
"accountId": "-8888888888888888888",
"timezone": "US/Pacific",
"model": "P5",
"purchaseDate": "2017-02-02T00:00:01Z",
"macAddress": "AABBCCDDEEFF",
"sku": "QP5",
"zipcode": "90210",
"reference": "55555555555-5"
}

View File

@@ -0,0 +1,10 @@
{
"beds": [
{
"name": "Bed1"
},
{
"name": "Bed2"
}
]
}

View File

@@ -0,0 +1,7 @@
{
"beds": [
{
"status": 1
}
]
}

View File

@@ -0,0 +1,5 @@
{
"accountId": "-8888888888888888888",
"bedId": "-9999999999999999999",
"pauseMode": "off"
}

View File

@@ -0,0 +1,25 @@
{
"firstName": "Alice",
"active": true,
"emailValidated": true,
"isChild": false,
"bedId": "-9999999999999999999",
"birthYear": "1970",
"zipCode": "90210",
"timezone": "US/Pacific",
"isMale": false,
"weight": 110,
"duration": "",
"sleeperId": "-1111111111111111111",
"height": 64,
"licenseVersion": 6,
"username": "alice@domain.com",
"birthMonth": 6,
"sleepGoal": 450,
"isAccountOwner": true,
"accountId": "-5555555555555555555",
"email": "alice@domain.com",
"avatar": "",
"lastLogin": "2017-02-17 20:19:36 CST",
"side": 1
}

View File

@@ -0,0 +1,10 @@
{
"sleepers": [
{
"firstName": "Alice"
},
{
"firstName": "Bob"
}
]
}