SleepIQ (Sleep Number)

@patrick did you ever get your Sleep By Number bed working in Hubitat?

I never got it working on ST. Never tried on Hubitat.

Thanks @patrick I have it working on ST but not on Hubitat.

I reach out to the Developer of the app for the SmartThings Sleep IQ app and now have it working in Hubitat. I have been using it for the last week and it works good. It works the same way that it did in Smartthings it shows presence in bed. There are two changes that you have to make and they are below.

Change 1: Before line 265 "httpPut(loginParams) { response ->" (in method doLogin) add a new line:
log.trace "doLogin() put request: $loginParams"

Change 2: Add two new fields to loginParams in the same method as above. Before line 257 "headers: [" add the new lines:
requestContentType: 'application/json',
contentType: 'application/json',

1 Like

That's really cool! Could you post your working driver to this thread or GitHub?

Thanks!!

I can here is the app code.

/**

  • Copyright 2015 Nathan Jacobson natecj@gmail.com
  • 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.

*/

definition(
name: "SleepIQ Manager",
namespace: "natecj",
author: "Nathan Jacobson",
description: "Manage sleepers across multiple beds through your SleepIQ account.",
category: "Convenience",
iconUrl: "",
iconX2Url: ""
)

preferences {
page name: "rootPage"
page name: "findDevicePage"
page name: "selectDevicePage"
page name: "createDevicePage"
}

def rootPage() {
log.trace "rootPage()"

def devices = getChildDevices()

dynamicPage(name: "rootPage", install: true, uninstall: true) {
section("Settings") {
input("login", "text", title: "Username", description: "Your SleepIQ username")
input("password", "password", title: "Password", description: "Your SleepIQ password")
input("interval", "number", title: "Refresh Interval", description: "How many minutes between refresh", defaultValue: 15)
}
section("Devices") {
if (devices.size() > 0) {
devices.each { device ->
paragraph title: device.label, "${device.currentBedId} / ${device.currentMode}"
}
}
href "findDevicePage", title: "Create New Device", description: null
}
section {
label title: "Assign a name", required: false
mode title: "Set for specific mode(s)", required: false
}
}
}

def statusText(status) {
"Current Status: " + (status ? "Present" : "Not Present")
}

def findDevicePage() {
log.trace "findDevicePage()"

def responseData = getBedData()
log.debug "Response Data: $responseData"

dynamicPage(name: "findDevicePage") {
if (responseData.beds.size() > 0) {
responseData.beds.each { bed ->
section("Bed: ${bed.bedId}") {
def leftStatus = bed.leftSide.isInBed
def rightStatus = bed.rightSide.isInBed
def bothStatus = leftStatus && rightStatus
def eitherStatus = leftStatus || rightStatus
href "selectDevicePage", title: "Both Sides", description: statusText(bothStatus), params: [bedId: bed.bedId, mode: "Both", status: bothStatus]
href "selectDevicePage", title: "Either Side", description: statusText(eitherStatus), params: [bedId: bed.bedId, mode: "Either", status: eitherStatus]
href "selectDevicePage", title: "Left Side", description: statusText(leftStatus), params: [bedId: bed.bedId, mode: "Left", status: leftStatus]
href "selectDevicePage", title: "Right Side", description: statusText(rightStatus), params: [bedId: bed.bedId, mode: "Right", status: rightStatus]
}
}
} else {
section {
paragraph "No Beds Found"
}
}
}
}

def selectDevicePage(params) {
log.trace "selectDevicePage()"

settings.newDeviceName = null

dynamicPage(name: "selectDevicePage") {
section {
paragraph "Bed ID: ${params.bedId}"
paragraph "Mode: ${params.mode}"
paragraph "Status: ${params.present ? 'Present' : 'Not Present'}"
input "newDeviceName", "text", title: "Device Name", description: "What do you want to call this presence sensor?", defaultValue: ""
}
section {
href "createDevicePage", title: "Create Device", description: null, params: [bedId: params.bedId, mode: params.mode, status: params.status]
}
}
}

def createDevicePage(params) {
log.trace "createDevicePage()"

def deviceId = "Sleepiq.${params.bedId}.${params.mode}"
def device = addChildDevice("natecj", "SleepIQ Presence Sensor", deviceId, null, [label: settings.newDeviceName])
device.setStatus(params.status)
device.setBedId(params.bedId)
device.setMode(params.mode)
settings.newDeviceName = null

dynamicPage(name: "selectDevicePage") {
section {
paragraph "Name: ${device.name}"
paragraph "Label: ${device.label}"
paragraph "Bed ID: ${device.curentBedId}"
paragraph "Mode: ${device.currentMode}"
paragraph "Presence: ${device.currentPresnce}"
}
section {
href "rootPage", title: "Back to Device List", description: null
}
}
}

def installed() {
log.trace "installed()"
initialize()
}

def updated() {
log.trace "updated()"
unsubscribe()
unschedule()
initialize()
}

def initialize() {
log.trace "initialize()"
refreshChildDevices()
schedule("* /${settings.interval} * * * ?", "refreshChildDevices")
}

def refreshChildDevices() {
log.trace "refreshChildDevices()"
getBedData()
}

def getBedData() {
log.trace "getBedData()"

// Make request and wait for completion
state.requestData = null
doStatus()
while(state.requestData == null) { sleep ( 1000 ) }
def requestData = state.requestData
state.requestData = null

// Process data
processBedData(requestData)

// Return data
requestData
}

def processBedData(responseData) {
if (!responseData || responseData.size() == 0) {
return
}
for(def device : getChildDevices()) {
for(def bed : responseData.beds) {
if (device.currentBedId == bed.bedId) {
def statusMap = [:]
statusMap["Both"] = bed.leftSide.isInBed && bed.rightSide.isInBed
statusMap["Either"] = bed.leftSide.isInBed || bed.rightSide.isInBed
statusMap["Left"] = bed.leftSide.isInBed
statusMap["Right"] = bed.rightSide.isInBed
if (statusMap.containsKey(device.currentMode)) {
log.debug "Setting ${device.label} (${device.currentMode}) to ${statusMap[device.currentMode] ? "Present" : "Not Present"}"
device.setStatus(statusMap[device.currentMode])
}
break
}
}
}
}

private def ApiHost() { "api.sleepiq.sleepnumber.com" }

private def ApiUriBase() { "https://api.sleepiq.sleepnumber.com" }

private def ApiUserAgent() { "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36" }

private def doStatus(alreadyLoggedIn = false) {
log.trace "doStatus()"

// Login if there isnt an active session
if (!state.session || !state.session?.key) {
if (alreadyLoggedIn) {
log.error "doStatus() Already attempted login, giving up for now"
} else {
doLogin()
}
return
}

// Make the request
try {
def statusParams = [
uri: ApiUriBase() + '/rest/bed/familyStatus?_k=' + state.session?.key,
headers: [
'Content-Type': 'application/json;charset=UTF-8',
'Host': ApiHost(),
'User-Agent': ApiUserAgent(),
'Cookie': state.session?.cookies,
'DNT': '1',
],
]
httpGet(statusParams) { response ->
if (response.status == 200){
log.trace "doStatus() Success - Request was successful: ($response.status) $response.data"
state.requestData = response.data
} else {
log.trace "doStatus() Failure - Request was unsuccessful: ($response.status) $response.data"
state.session = null
state.requestData = [:]
}
}
} catch(Exception e) {
if (alreadyLoggedIn) {
log.error "doStatus() Error ($e)"
} else {
log.trace "doStatus() Error ($e)"
doLogin()
}
}
}

private def doLogin() {
log.trace "doLogin()"
state.session = null
state.requestData = [:]
try {
def loginParams = [
uri: ApiUriBase() + '/rest/login',
requestContentType: 'application/json',
contentType: 'application/json',
headers: [
'Content-Type': 'application/json;charset=UTF-8',
'Host': ApiHost(),
'User-Agent': ApiUserAgent(),
'DNT': '1',
],
body: '{"login":"' + settings.login + '","password":"' + settings.password + '"}='
]
log.trace "doLogin() put request: $loginParams"
httpPut(loginParams) { response ->
if (response.status==200) {
log.trace "doLogin() Success - Request was successful: ($response.status) $response.data"
state.session = [:]
state.session.key = response.data.key
state.session.cookies = ''
response.getHeaders('Set-Cookie').each {
state.session.cookies = state.session.cookies + it.value.split(';')[0] + ';'
}
doStatus(true)
} else {
log.trace "doLogin() Failure - Request was unsuccessful: ($response.status) $response.data"
state.session = null
state.requestData = [:]
}
}
} catch(Exception e) {
log.error "doLogin() Error ($e)"
state.session = null
state.requestData = [:]
}
}

1 Like

Here is the driver.

/**

  • SleepIQ Presence Sensor
  • Copyright 2015 Nathan Jacobson
  • 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.

*/
metadata {
definition (name: "SleepIQ Presence Sensor", namespace: "natecj", author: "Nathan Jacobson") {
capability "Presence Sensor"
capability "Switch"
capability "Polling"

command "arrived"
command "departed"

attribute "bedId", "String"
attribute "mode", "enum", ["Both", "Either", "Left", "Right"]

command "setStatus", ["string"]
command "setBedId", ["string"]
command "setMode", ["string"]

}

simulator {
status "present": "presence: present"
status "not present": "presence: not present"
status "on": "switch: on"
status "off": "switch: not off"
}

/*
preferences {
section("Settings:") {
input("mode", title: "Mode", "enum", required: false, defaultValue: "Either", options: ["Left", "Right", "Both", "Either"], description: "The side(s) of the bed to monitor")
}
}
*/

tiles {
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
state("not present", label:'not present', icon:"st.presence.tile.not-present", backgroundColor:"#ffffff", action:"arrived")
state("present", label:'present', icon:"st.presence.tile.present", backgroundColor:"#53a7c0", action:"departed")
}
standardTile("refresh", "device.poll", inactiveLabel: false, decoration: "flat") {
state "default", action:"polling.poll", icon:"st.secondary.refresh"
}
valueTile("bedId", "device.bedId", width: 3, height: 1) {
state "default", label: '${currentValue}'
}
valueTile("mode", "device.mode", width: 1, height: 1) {
state "default", label: '${currentValue}'
}

main "presence"
details(["presence", "refresh", "mode", "bedId"])

}
}

def installed() {
log.trace 'installed()'
}

def updated() {
log.trace 'updated()'
}

def poll() {
log.trace "poll()"
parent.refreshChildDevices()
}

def parse(String description) {
log.trace "parse() - Description: ${description}"
def results = []
/*
def pair = description.split(":")
results = createEvent(name: pair[0].trim(), value: pair[1].trim())
//results = createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed")
*/
log.debug "parse() - Results: ${results.inspect()}"
results
}

def arrived() {
log.trace "arrived()"
sendEvent(name: "presence", value: "present")
sendEvent(name: "switch", value: "on")
}

def departed() {
log.trace "departed()"
sendEvent(name: "presence", value: "not present")
sendEvent(name: "switch", value: "off")
}

def on() {
log.trace "on()"
arrived()
}

def off() {
log.trace "off()"
departed()
}

def setStatus(val) {
log.trace "setStatus($val)"
if (val) {
arrived()
} else {
departed()
}
}

def setBedId(val) {
log.trace "setBedId($val)"
sendEvent(name: "bedId", value: val)
}

def setMode(val) {
log.trace "setMode($val)"
sendEvent(name: "mode", value: val)
}

1 Like

@leeonestop

Thanks a bunch!

@leeonestop

I can add the app just fine, but when I try to create device, it doesn't work with the following error:

For anyone else - I figured out the issue. Users have to be created for both sides of the bed using the SleepIQ app before adding a device.

I have users for both sides in the app so no concerns there

Where I tried to create a device for "Both" in the HE app, I got an error, however it did create the first device, which I discovered later.

I then went in and created the left side, got the same error, however it did create the device, now I have 2 for the same side

I then attempted to create the right side, got the same error, however the device was created

Then deleted the first left side device so I only have 1

Went back into the HE app and just selected done, it finally created the app in the HE portal

Same error every time



Rick

@leeonestop

Thank you for pursuing Nathan Jacobson and getting his Smartthings app/DTH ported to Hubitat.

The following screenshot of the conditional part of a rule, part of my wake up routines, indicates why I'm so happy with this. From tomorrow, if I'm still in bed 45 minutes after I'm supposed to be up , Alexa will start bugging me.

27%20PM

@aaiyar your welcome. Glad you got it working. I will try to see what cause this error and see if we can get it fix?

1 Like

I just repair line 162 on the app as I had extra spaces that should not have been there and may have been the cause of the errors and I have now remove the spaces, hopefully that will fix the issue.

@aaiyar, Ashok

I set this up a few days ago, have 1 device for me and my wife, yet I am not seeing anything in the event logs for either device

What am I missing?
Rick

@toy4rick are you pulling up the device and clicking the event button? I'm not seeing the mode or bedid on mine just the switch and presence.

Name Value Unit Description Text Source Type Date
switch off DEVICE 2019-06-08 08:30:00.557 AM EDT
presence not present DEVICE 2019-06-08 08:30:00.557 AM EDT
presence present DEVICE 2019-06-08 12:00:01.281 AM EDT
switch on DEVICE 2019-06-08 12:00:01.281 AM EDT
switch off DEVICE 2019-06-07 07:10:41.333 AM EDT
presence not present DEVICE 2019-06-07 07:10:41.333 AM EDT
switch on DEVICE 2019-06-06 10:50:00.643 PM EDT
presence present DEVICE 2019-06-06 10:50:00.637 PM EDT
switch off DEVICE 2019-06-06 10:45:00.787 PM EDT
presence not present DEVICE 2019-06-06 10:45:00.787 PM EDT

@toy4rick

I wish I could tell you why aren't seeing anything in the event log. Here's what the event log looks like for the side I sleep on:

Here's what the SleepIQ Manager setup screen looks like, after 4 sensors are created (R, L, Either, Both):

56%20AM

Just to confirm - you are using the modifications that @leeonestop posted, correct?

@aaiyar I just set a right and left not both or either? is there a benefit to setting up other beside the right and left???

There is for me. I roll in bed, sometimes starting on one side and ending up on the other. In that situation, I want my wake routine to be based on either side being occupied.

There is also a possibility that both sides will be occupied, and when that happens, I don't want my wake up routine to run.

@leeonestop, yes, using the event button

@aaiyar, I created left and right. why would i need to create both and/or either? I'm not following

By creating Both and Either, won't I know have 6 unique devices for a 2 person bed?

Rick