Sleepiq single sleeper crash (#24941)

* Update sleepyq to 0.7

Fixes crash when working with a single sleeper.

* sleepiq: Handle null side definitions

These happen if no sleeper is defined for a side of the bed. Don't
create sensors for null sides; they'll crash every time we try to use
them.

* sleepiq: Fix urls mocked to match sleepyq 0.7

* sleepi: Fix test_sensor.TestSleepIQSensorSetup

Sleepyq 0.7 throws on empty strings, so we have to specify them.

* sleepiq: Test for ValueError thrown by sleepyq 0.7

* sleepiq: Drop no longer used HTTPError import

* sleepiq: Add tests for single sleeper case

* sleepiq: Shorten comments to not overflow line length

* sleepiq: Use formatted string literals for adding suffixes to test files

* sleepiq: Use str.format() for test suffixing
pull/25005/head
David Winn 2019-07-06 23:40:02 -07:00 committed by Pascal Vizeli
parent adbec5bffc
commit 628e12c944
11 changed files with 103 additions and 18 deletions

View File

@ -1,7 +1,6 @@
"""Support for SleepIQ from SleepNumber."""
import logging
from datetime import timedelta
from requests.exceptions import HTTPError
import voluptuous as vol
@ -53,7 +52,7 @@ def setup(hass, config):
try:
DATA = SleepIQData(client)
DATA.update()
except HTTPError:
except ValueError:
message = """
SleepIQ failed to login, double check your username and password"
"""

View File

@ -12,9 +12,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
data.update()
dev = list()
for bed_id, _ in data.beds.items():
for bed_id, bed in data.beds.items():
for side in sleepiq.SIDES:
dev.append(IsInBedBinarySensor(data, bed_id, side))
if getattr(bed, side) is not None:
dev.append(IsInBedBinarySensor(data, bed_id, side))
add_entities(dev)

View File

@ -3,7 +3,7 @@
"name": "Sleepiq",
"documentation": "https://www.home-assistant.io/components/sleepiq",
"requirements": [
"sleepyq==0.6"
"sleepyq==0.7"
],
"dependencies": [],
"codeowners": []

View File

@ -13,9 +13,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
data.update()
dev = list()
for bed_id, _ in data.beds.items():
for bed_id, bed in data.beds.items():
for side in sleepiq.SIDES:
dev.append(SleepNumberSensor(data, bed_id, side))
if getattr(bed, side) is not None:
dev.append(SleepNumberSensor(data, bed_id, side))
add_entities(dev)

View File

@ -1671,7 +1671,7 @@ skybellpy==0.4.0
slacker==0.13.0
# homeassistant.components.sleepiq
sleepyq==0.6
sleepyq==0.7
# homeassistant.components.xmpp
slixmpp==1.4.2

View File

@ -332,7 +332,7 @@ rxv==0.6.0
simplisafe-python==3.4.2
# homeassistant.components.sleepiq
sleepyq==0.6
sleepyq==0.7
# homeassistant.components.smhi
smhi-pkg==1.0.10

View File

@ -30,6 +30,7 @@ class TestSleepIQBinarySensorSetup(unittest.TestCase):
'username': self.username,
'password': self.password,
}
self.DEVICES = []
def tearDown(self): # pylint: disable=invalid-name
"""Stop everything that was started."""
@ -56,3 +57,21 @@ class TestSleepIQBinarySensorSetup(unittest.TestCase):
right_side = self.DEVICES[0]
assert 'SleepNumber ILE Test2 Is In Bed' == right_side.name
assert 'off' == right_side.state
@requests_mock.Mocker()
def test_setup_single(self, mock):
"""Test for successfully setting up the SleepIQ platform."""
mock_responses(mock, single=True)
setup_component(self.hass, 'sleepiq', {
'sleepiq': self.config})
sleepiq.setup_platform(self.hass,
self.config,
self.add_entities,
MagicMock())
assert 1 == len(self.DEVICES)
right_side = self.DEVICES[0]
assert 'SleepNumber ILE Test1 Is In Bed' == right_side.name
assert 'on' == right_side.state

View File

@ -10,21 +10,25 @@ import homeassistant.components.sleepiq as sleepiq
from tests.common import load_fixture, get_test_home_assistant
def mock_responses(mock):
def mock_responses(mock, single=False):
"""Mock responses for SleepIQ."""
base_url = 'https://api.sleepiq.sleepnumber.com/rest/'
base_url = 'https://prod-api.sleepiq.sleepnumber.com/rest/'
if single:
suffix = '-single'
else:
suffix = ''
mock.put(
base_url + 'login',
text=load_fixture('sleepiq-login.json'))
mock.get(
base_url + 'bed?_k=0987',
text=load_fixture('sleepiq-bed.json'))
text=load_fixture('sleepiq-bed{}.json'.format(suffix)))
mock.get(
base_url + 'sleeper?_k=0987',
text=load_fixture('sleepiq-sleeper.json'))
mock.get(
base_url + 'bed/familyStatus?_k=0987',
text=load_fixture('sleepiq-familystatus.json'))
text=load_fixture('sleepiq-familystatus{}.json'.format(suffix)))
class TestSleepIQ(unittest.TestCase):
@ -61,7 +65,7 @@ class TestSleepIQ(unittest.TestCase):
@requests_mock.Mocker()
def test_setup_login_failed(self, mock):
"""Test the setup if a bad username or password is given."""
mock.put('https://api.sleepiq.sleepnumber.com/rest/login',
mock.put('https://prod-api.sleepiq.sleepnumber.com/rest/login',
status_code=401,
json=load_fixture('sleepiq-login-failed.json'))

View File

@ -30,6 +30,7 @@ class TestSleepIQSensorSetup(unittest.TestCase):
'username': self.username,
'password': self.password,
}
self.DEVICES = []
def tearDown(self): # pylint: disable=invalid-name
"""Stop everything that was started."""
@ -41,10 +42,7 @@ class TestSleepIQSensorSetup(unittest.TestCase):
mock_responses(mock)
assert setup_component(self.hass, 'sleepiq', {
'sleepiq': {
'username': '',
'password': '',
}
'sleepiq': self.config
})
sleepiq.setup_platform(self.hass,
@ -60,3 +58,22 @@ class TestSleepIQSensorSetup(unittest.TestCase):
right_side = self.DEVICES[0]
assert 'SleepNumber ILE Test2 SleepNumber' == right_side.name
assert 80 == right_side.state
@requests_mock.Mocker()
def test_setup_sigle(self, mock):
"""Test for successfully setting up the SleepIQ platform."""
mock_responses(mock, single=True)
assert setup_component(self.hass, 'sleepiq', {
'sleepiq': self.config
})
sleepiq.setup_platform(self.hass,
self.config,
self.add_entities,
MagicMock())
assert 1 == len(self.DEVICES)
right_side = self.DEVICES[0]
assert 'SleepNumber ILE Test1 SleepNumber' == right_side.name
assert 40 == right_side.state

27
tests/fixtures/sleepiq-bed-single.json vendored Normal file
View File

@ -0,0 +1,27 @@
{
"beds" : [
{
"dualSleep" : false,
"base" : "FlexFit",
"sku" : "AILE",
"model" : "ILE",
"size" : "KING",
"isKidsBed" : false,
"sleeperRightId" : "-80",
"accountId" : "-32",
"bedId" : "-31",
"registrationDate" : "2016-07-22T14:00:58Z",
"serial" : null,
"reference" : "95000794555-1",
"macAddress" : "CD13A384BA51",
"version" : null,
"purchaseDate" : "2016-06-22T00:00:00Z",
"sleeperLeftId" : "0",
"zipcode" : "12345",
"returnRequestStatus" : 0,
"name" : "ILE",
"status" : 1,
"timezone" : "US/Eastern"
}
]
}

View File

@ -0,0 +1,17 @@
{
"beds" : [
{
"bedId" : "-31",
"rightSide" : {
"alertId" : 0,
"lastLink" : "00:00:00",
"isInBed" : true,
"sleepNumber" : 40,
"alertDetailedMessage" : "No Alert",
"pressure" : -16
},
"status" : 1,
"leftSide" : null
}
]
}