SleepIQ (Sleep Number)

Hello -

Is there an integration for SleepIQ with Hubitat? There is a SmartThings integration.

I wondered if anyone has ported it over to Hubitat already?

Thanks!

I don't know if anyone has ported this, but what a great idea! I can see using the sleep IQ presence sensors to change the way motion lighting behaves in a bedroom.

Ex: If someone is in bed and motion changes to active, only turn on the small light in the corner. If no one in bed, turn on main bedroom lights.

We are going to be shopping for a new bed soon. I think you just sold me on a sleep number just based off the potential!

1 Like

That is precisely the sort of reason that I would love to see the SmartThings integration ported to Hubitat.

It is a phenomenal mattress. I got it with the adjustable frame/bed - which in my opinion is worth the expense. I used to be a light sleeper with some apnea - I get a solid 7-8 hours on most nights now.

1 Like

We have one, really like it

The adjustments are not real time from my experience, it checks about 5 mins after we get into bed then every 2 hours or so and also confirmed by support.

Curious to what others experience
Rick

@DeveloperDavidB I have try to import this and have it imported from Smartthings but I'm missing something and haven't figure it out yet?

Expression [MethodCallExpression] is not allowed: this.sleep(1000) at line number: 162

this is line 162 while(state.requestData == null) { sleep(1000) }
and once I put two // in front of this line it will save in Hubitat.

Then I go and install the app (SleepIQ Manager) and it will install but I keep getting this error?

Unexpected Error

An unexpected error has occurred trying to load the app. Check Logs for more information.

Error: Cannot invoke method size() on null object.

I have no idea what these mean and have research to try and find the answer but having no luck.
Any help would be greatly appreciated.

@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