Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ omit =
homeassistant/components/openuv/__init__.py
homeassistant/components/openuv/binary_sensor.py
homeassistant/components/openuv/sensor.py
homeassistant/components/owlet/*
homeassistant/components/pilight/*
homeassistant/components/plum_lightpad/*
homeassistant/components/point/*
Expand Down
70 changes: 70 additions & 0 deletions homeassistant/components/owlet/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Support for Owlet baby monitors."""
import logging

import voluptuous as vol

from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, CONF_NAME)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import load_platform

from .const import SENSOR_MOVEMENT, SENSOR_BASE_STATION, SENSOR_HEART_RATE, \
SENSOR_OXYGEN_LEVEL

REQUIREMENTS = ['pyowlet==1.0.2']

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'owlet'

SENSOR_TYPES = [
SENSOR_OXYGEN_LEVEL,
SENSOR_HEART_RATE,
SENSOR_BASE_STATION,
SENSOR_MOVEMENT
]

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_NAME): cv.string,
}),
}, extra=vol.ALLOW_EXTRA)


def setup(hass, config):
"""Set up owlet component."""
from pyowlet.PyOwlet import PyOwlet

username = config[DOMAIN][CONF_USERNAME]
password = config[DOMAIN][CONF_PASSWORD]
name = config[DOMAIN].get(CONF_NAME)

try:
device = PyOwlet(username, password)
except KeyError:
_LOGGER.error('Owlet authentication failed. Please verify your '
'credentials are correct.')
return False

device.update_properties()

if not name:
name = '{}\'s Owlet'.format(device.baby_name)

hass.data[DOMAIN] = OwletDevice(device, name, SENSOR_TYPES)

load_platform(hass, 'sensor', DOMAIN, {}, config)
load_platform(hass, 'binary_sensor', DOMAIN, {}, config)

return True


class OwletDevice():
"""Represents a configured Owlet device."""

def __init__(self, device, name, monitor):
"""Initialize device."""
self.name = name
self.monitor = monitor
self.device = device
82 changes: 82 additions & 0 deletions homeassistant/components/owlet/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""Support for Owlet binary sensors."""
from datetime import timedelta

from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.owlet import DOMAIN as OWLET_DOMAIN
from homeassistant.util import dt as dt_util

from .const import SENSOR_BASE_STATION, SENSOR_MOVEMENT

SCAN_INTERVAL = timedelta(seconds=120)

BINARY_CONDITIONS = {
SENSOR_BASE_STATION: {
'name': 'Base Station',
'device_class': 'power'
},
SENSOR_MOVEMENT: {
'name': 'Movement',
'device_class': 'motion'
}
}


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up owlet binary sensor."""
if discovery_info is None:
return

device = hass.data[OWLET_DOMAIN]

entities = []
for condition in BINARY_CONDITIONS:
if condition in device.monitor:
entities.append(OwletBinarySensor(device, condition))

add_entities(entities, True)


class OwletBinarySensor(BinarySensorDevice):
"""Representation of owlet binary sensor."""

def __init__(self, device, condition):
"""Init owlet binary sensor."""
self._device = device
self._condition = condition
self._state = None
self._base_on = False
self._prop_expiration = None
self._is_charging = None

@property
def name(self):
"""Return sensor name."""
return '{} {}'.format(self._device.name,
BINARY_CONDITIONS[self._condition]['name'])

@property
def is_on(self):
"""Return current state of sensor."""
return self._state

@property
def device_class(self):
"""Return the device class."""
return BINARY_CONDITIONS[self._condition]['device_class']

def update(self):
"""Update state of sensor."""
self._base_on = self._device.device.base_station_on
self._prop_expiration = self._device.device.prop_expire_time
self._is_charging = self._device.device.charge_status > 0

# handle expired values
if self._prop_expiration < dt_util.now().timestamp():
self._state = False
return

if self._condition == 'movement':
if not self._base_on or self._is_charging:
return False

self._state = getattr(self._device.device, self._condition)
6 changes: 6 additions & 0 deletions homeassistant/components/owlet/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Constants for Owlet component."""
SENSOR_OXYGEN_LEVEL = 'oxygen_level'
SENSOR_HEART_RATE = 'heart_rate'

SENSOR_BASE_STATION = 'base_station_on'
SENSOR_MOVEMENT = 'movement'
103 changes: 103 additions & 0 deletions homeassistant/components/owlet/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Support for Owlet sensors."""
from datetime import timedelta

from homeassistant.components.owlet import DOMAIN as OWLET_DOMAIN
from homeassistant.helpers.entity import Entity
from homeassistant.util import dt as dt_util

from .const import SENSOR_HEART_RATE, SENSOR_OXYGEN_LEVEL

SCAN_INTERVAL = timedelta(seconds=120)

SENSOR_CONDITIONS = {
SENSOR_OXYGEN_LEVEL: {
'name': 'Oxygen Level',
'device_class': None
},
SENSOR_HEART_RATE: {
'name': 'Heart Rate',
'device_class': None
}
}


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up owlet binary sensor."""
if discovery_info is None:
return

device = hass.data[OWLET_DOMAIN]

entities = []
for condition in SENSOR_CONDITIONS:
if condition in device.monitor:
entities.append(OwletSensor(device, condition))

add_entities(entities, True)


class OwletSensor(Entity):
"""Representation of Owlet sensor."""

def __init__(self, device, condition):
"""Init owlet binary sensor."""
self._device = device
self._condition = condition
self._state = None
self._prop_expiration = None
self.is_charging = None
self.battery_level = None
self.sock_off = None
self.sock_connection = None
self._movement = None

@property
def name(self):
"""Return sensor name."""
return '{} {}'.format(self._device.name,
SENSOR_CONDITIONS[self._condition]['name'])

@property
def state(self):
"""Return current state of sensor."""
return self._state

@property
def device_class(self):
"""Return the device class."""
return SENSOR_CONDITIONS[self._condition]['device_class']

@property
def device_state_attributes(self):
"""Return state attributes."""
attributes = {
'battery_charging': self.is_charging,
'battery_level': self.battery_level,
'sock_off': self.sock_off,
'sock_connection': self.sock_connection
}

return attributes

def update(self):
"""Update state of sensor."""
self.is_charging = self._device.device.charge_status
self.battery_level = self._device.device.batt_level
self.sock_off = self._device.device.sock_off
self.sock_connection = self._device.device.sock_connection
self._movement = self._device.device.movement
self._prop_expiration = self._device.device.prop_expire_time

value = getattr(self._device.device, self._condition)

if self._condition == 'batt_level':
self._state = min(100, value)
return

if not self._device.device.base_station_on \
or self._device.device.charge_status > 0 \
or self._prop_expiration < dt_util.now().timestamp() \
or self._movement:
value = None

self._state = value
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,9 @@ pyotgw==0.4b1
# homeassistant.components.sensor.otp
pyotp==2.2.6

# homeassistant.components.owlet
pyowlet==1.0.2

# homeassistant.components.sensor.openweathermap
# homeassistant.components.weather.openweathermap
pyowm==2.10.0
Expand Down