Add support for Canary component and platforms (#10306)
* Add Canary component * Made some change to how canary data is updated and stored * Updated to use py-canary:0.1.2 * Addressed flake8 warnings * Import canary API locally * Import canary API locally again * Addressed pylint errors * Updated requirements_all.txt * Fixed incorrect unit of measurement for air quality sensor * Added tests for Canary component and sensors * Updated canary component to handle exception better when initializing * Fixed tests * Fixed tests again * Addressed review comments * Fixed houndci error * Addressed comment about camera force update * Addressed comment regarding timeout when fetching camera image * Updated to use py-canary==0.2.2 * Increased update frequency to 30 seconds * Added support for Canary alarm control panel * Address review comments * Fixed houndci error * Fixed lint errors * Updated test to only test setup component / platform * Fixed flake error * Fixed failing test * Uptake py-canary:0.2.3 * canary.alarm_control_panel DISARM is now mapped to canary PRIVACY mode * Fixed failing tests * Removed unnecessary methods * Removed polling in canary camera component and update camera info when getting camera image * Added more tests to cover Canary sensors * Address review comments * Addressed review comment in tests * Fixed pylint errors * Excluded canary alarm_control_panel and camera from coverage calculation
This commit is contained in:
parent
929d49ed6f
commit
f892c3394b
10 changed files with 608 additions and 0 deletions
|
@ -264,6 +264,7 @@ omit =
|
|||
homeassistant/components/*/zoneminder.py
|
||||
|
||||
homeassistant/components/alarm_control_panel/alarmdotcom.py
|
||||
homeassistant/components/alarm_control_panel/canary.py
|
||||
homeassistant/components/alarm_control_panel/concord232.py
|
||||
homeassistant/components/alarm_control_panel/egardia.py
|
||||
homeassistant/components/alarm_control_panel/ialarm.py
|
||||
|
@ -285,6 +286,7 @@ omit =
|
|||
homeassistant/components/browser.py
|
||||
homeassistant/components/calendar/todoist.py
|
||||
homeassistant/components/camera/bloomsky.py
|
||||
homeassistant/components/camera/canary.py
|
||||
homeassistant/components/camera/ffmpeg.py
|
||||
homeassistant/components/camera/foscam.py
|
||||
homeassistant/components/camera/mjpeg.py
|
||||
|
|
92
homeassistant/components/alarm_control_panel/canary.py
Normal file
92
homeassistant/components/alarm_control_panel/canary.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
"""
|
||||
Support for Canary alarm.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/alarm_control_panel.canary/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.alarm_control_panel import AlarmControlPanel
|
||||
from homeassistant.components.canary import DATA_CANARY
|
||||
from homeassistant.const import STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY, \
|
||||
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_HOME
|
||||
|
||||
DEPENDENCIES = ['canary']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the Canary alarms."""
|
||||
data = hass.data[DATA_CANARY]
|
||||
devices = []
|
||||
|
||||
for location in data.locations:
|
||||
devices.append(CanaryAlarm(data, location.location_id))
|
||||
|
||||
add_devices(devices, True)
|
||||
|
||||
|
||||
class CanaryAlarm(AlarmControlPanel):
|
||||
"""Representation of a Canary alarm control panel."""
|
||||
|
||||
def __init__(self, data, location_id):
|
||||
"""Initialize a Canary security camera."""
|
||||
self._data = data
|
||||
self._location_id = location_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the alarm."""
|
||||
location = self._data.get_location(self._location_id)
|
||||
return location.name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
from canary.api import LOCATION_MODE_AWAY, LOCATION_MODE_HOME, \
|
||||
LOCATION_MODE_NIGHT
|
||||
|
||||
location = self._data.get_location(self._location_id)
|
||||
|
||||
if location.is_private:
|
||||
return STATE_ALARM_DISARMED
|
||||
|
||||
mode = location.mode
|
||||
if mode.name == LOCATION_MODE_AWAY:
|
||||
return STATE_ALARM_ARMED_AWAY
|
||||
elif mode.name == LOCATION_MODE_HOME:
|
||||
return STATE_ALARM_ARMED_HOME
|
||||
elif mode.name == LOCATION_MODE_NIGHT:
|
||||
return STATE_ALARM_ARMED_NIGHT
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
location = self._data.get_location(self._location_id)
|
||||
return {
|
||||
'private': location.is_private
|
||||
}
|
||||
|
||||
def alarm_disarm(self, code=None):
|
||||
"""Send disarm command."""
|
||||
location = self._data.get_location(self._location_id)
|
||||
self._data.set_location_mode(self._location_id, location.mode.name,
|
||||
True)
|
||||
|
||||
def alarm_arm_home(self, code=None):
|
||||
"""Send arm home command."""
|
||||
from canary.api import LOCATION_MODE_HOME
|
||||
self._data.set_location_mode(self._location_id, LOCATION_MODE_HOME)
|
||||
|
||||
def alarm_arm_away(self, code=None):
|
||||
"""Send arm away command."""
|
||||
from canary.api import LOCATION_MODE_AWAY
|
||||
self._data.set_location_mode(self._location_id, LOCATION_MODE_AWAY)
|
||||
|
||||
def alarm_arm_night(self, code=None):
|
||||
"""Send arm night command."""
|
||||
from canary.api import LOCATION_MODE_NIGHT
|
||||
self._data.set_location_mode(self._location_id, LOCATION_MODE_NIGHT)
|
95
homeassistant/components/camera/canary.py
Normal file
95
homeassistant/components/camera/canary.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
"""
|
||||
Support for Canary camera.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.canary/
|
||||
"""
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.components.canary import DATA_CANARY, DEFAULT_TIMEOUT
|
||||
|
||||
DEPENDENCIES = ['canary']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_MOTION_START_TIME = "motion_start_time"
|
||||
ATTR_MOTION_END_TIME = "motion_end_time"
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the Canary sensors."""
|
||||
data = hass.data[DATA_CANARY]
|
||||
devices = []
|
||||
|
||||
for location in data.locations:
|
||||
entries = data.get_motion_entries(location.location_id)
|
||||
if entries:
|
||||
devices.append(CanaryCamera(data, location.location_id,
|
||||
DEFAULT_TIMEOUT))
|
||||
|
||||
add_devices(devices, True)
|
||||
|
||||
|
||||
class CanaryCamera(Camera):
|
||||
"""An implementation of a Canary security camera."""
|
||||
|
||||
def __init__(self, data, location_id, timeout):
|
||||
"""Initialize a Canary security camera."""
|
||||
super().__init__()
|
||||
self._data = data
|
||||
self._location_id = location_id
|
||||
self._timeout = timeout
|
||||
|
||||
self._location = None
|
||||
self._motion_entry = None
|
||||
self._image_content = None
|
||||
|
||||
def camera_image(self):
|
||||
"""Update the status of the camera and return bytes of camera image."""
|
||||
self.update()
|
||||
return self._image_content
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this device."""
|
||||
return self._location.name
|
||||
|
||||
@property
|
||||
def is_recording(self):
|
||||
"""Return true if the device is recording."""
|
||||
return self._location.is_recording
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return device specific state attributes."""
|
||||
if self._motion_entry is None:
|
||||
return None
|
||||
|
||||
return {
|
||||
ATTR_MOTION_START_TIME: self._motion_entry.start_time,
|
||||
ATTR_MOTION_END_TIME: self._motion_entry.end_time,
|
||||
}
|
||||
|
||||
def update(self):
|
||||
"""Update the status of the camera."""
|
||||
self._data.update()
|
||||
self._location = self._data.get_location(self._location_id)
|
||||
|
||||
entries = self._data.get_motion_entries(self._location_id)
|
||||
if entries:
|
||||
current = entries[0]
|
||||
previous = self._motion_entry
|
||||
|
||||
if previous is None or previous.entry_id != current.entry_id:
|
||||
self._motion_entry = current
|
||||
self._image_content = requests.get(
|
||||
current.thumbnails[0].image_url,
|
||||
timeout=self._timeout).content
|
||||
|
||||
@property
|
||||
def motion_detection_enabled(self):
|
||||
"""Return the camera motion detection status."""
|
||||
return not self._location.is_recording
|
117
homeassistant/components/canary.py
Normal file
117
homeassistant/components/canary.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
"""
|
||||
Support for Canary.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/canary/
|
||||
"""
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
import voluptuous as vol
|
||||
from requests import ConnectTimeout, HTTPError
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
REQUIREMENTS = ['py-canary==0.2.3']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
NOTIFICATION_ID = 'canary_notification'
|
||||
NOTIFICATION_TITLE = 'Canary Setup'
|
||||
|
||||
DOMAIN = 'canary'
|
||||
DATA_CANARY = 'canary'
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
|
||||
DEFAULT_TIMEOUT = 10
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
CANARY_COMPONENTS = [
|
||||
'alarm_control_panel', 'camera', 'sensor'
|
||||
]
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the Canary component."""
|
||||
conf = config[DOMAIN]
|
||||
username = conf.get(CONF_USERNAME)
|
||||
password = conf.get(CONF_PASSWORD)
|
||||
timeout = conf.get(CONF_TIMEOUT)
|
||||
|
||||
try:
|
||||
hass.data[DATA_CANARY] = CanaryData(username, password, timeout)
|
||||
except (ConnectTimeout, HTTPError) as ex:
|
||||
_LOGGER.error("Unable to connect to Canary service: %s", str(ex))
|
||||
hass.components.persistent_notification.create(
|
||||
'Error: {}<br />'
|
||||
'You will need to restart hass after fixing.'
|
||||
''.format(ex),
|
||||
title=NOTIFICATION_TITLE,
|
||||
notification_id=NOTIFICATION_ID)
|
||||
return False
|
||||
|
||||
for component in CANARY_COMPONENTS:
|
||||
discovery.load_platform(hass, component, DOMAIN, {}, config)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class CanaryData(object):
|
||||
"""Get the latest data and update the states."""
|
||||
|
||||
def __init__(self, username, password, timeout):
|
||||
"""Init the Canary data object."""
|
||||
from canary.api import Api
|
||||
self._api = Api(username, password, timeout)
|
||||
|
||||
self._locations_by_id = {}
|
||||
self._readings_by_device_id = {}
|
||||
self._entries_by_location_id = {}
|
||||
|
||||
self.update()
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self, **kwargs):
|
||||
"""Get the latest data from py-canary."""
|
||||
for location in self._api.get_locations():
|
||||
location_id = location.location_id
|
||||
|
||||
self._locations_by_id[location_id] = location
|
||||
self._entries_by_location_id[location_id] = self._api.get_entries(
|
||||
location_id, entry_type="motion", limit=1)
|
||||
|
||||
for device in location.devices:
|
||||
if device.is_online:
|
||||
self._readings_by_device_id[device.device_id] = \
|
||||
self._api.get_latest_readings(device.device_id)
|
||||
|
||||
@property
|
||||
def locations(self):
|
||||
"""Return a list of locations."""
|
||||
return self._locations_by_id.values()
|
||||
|
||||
def get_motion_entries(self, location_id):
|
||||
"""Return a list of motion entries based on location_id."""
|
||||
return self._entries_by_location_id.get(location_id, [])
|
||||
|
||||
def get_location(self, location_id):
|
||||
"""Return a location based on location_id."""
|
||||
return self._locations_by_id.get(location_id, [])
|
||||
|
||||
def get_readings(self, device_id):
|
||||
"""Return a list of readings based on device_id."""
|
||||
return self._readings_by_device_id.get(device_id, [])
|
||||
|
||||
def set_location_mode(self, location_id, mode_name, is_private=False):
|
||||
"""Set location mode."""
|
||||
self._api.set_location_mode(location_id, mode_name, is_private)
|
||||
self.update(no_throttle=True)
|
85
homeassistant/components/sensor/canary.py
Normal file
85
homeassistant/components/sensor/canary.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
"""
|
||||
Support for Canary sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.canary/
|
||||
"""
|
||||
from homeassistant.components.canary import DATA_CANARY
|
||||
from homeassistant.const import TEMP_FAHRENHEIT, TEMP_CELSIUS
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
DEPENDENCIES = ['canary']
|
||||
|
||||
SENSOR_VALUE_PRECISION = 1
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the Canary sensors."""
|
||||
data = hass.data[DATA_CANARY]
|
||||
devices = []
|
||||
|
||||
from canary.api import SensorType
|
||||
for location in data.locations:
|
||||
for device in location.devices:
|
||||
if device.is_online:
|
||||
for sensor_type in SensorType:
|
||||
devices.append(CanarySensor(data, sensor_type, location,
|
||||
device))
|
||||
|
||||
add_devices(devices, True)
|
||||
|
||||
|
||||
class CanarySensor(Entity):
|
||||
"""Representation of a Canary sensor."""
|
||||
|
||||
def __init__(self, data, sensor_type, location, device):
|
||||
"""Initialize the sensor."""
|
||||
self._data = data
|
||||
self._sensor_type = sensor_type
|
||||
self._device_id = device.device_id
|
||||
self._is_celsius = location.is_celsius
|
||||
self._sensor_value = None
|
||||
|
||||
sensor_type_name = sensor_type.value.replace("_", " ").title()
|
||||
self._name = '{} {} {}'.format(location.name,
|
||||
device.name,
|
||||
sensor_type_name)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the Canary sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._sensor_value
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique ID of this sensor."""
|
||||
return "sensor_canary_{}_{}".format(self._device_id,
|
||||
self._sensor_type.value)
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement this sensor expresses itself in."""
|
||||
from canary.api import SensorType
|
||||
if self._sensor_type == SensorType.TEMPERATURE:
|
||||
return TEMP_CELSIUS if self._is_celsius else TEMP_FAHRENHEIT
|
||||
elif self._sensor_type == SensorType.HUMIDITY:
|
||||
return "%"
|
||||
elif self._sensor_type == SensorType.AIR_QUALITY:
|
||||
return ""
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
"""Get the latest state of the sensor."""
|
||||
self._data.update()
|
||||
|
||||
readings = self._data.get_readings(self._device_id)
|
||||
value = next((
|
||||
reading.value for reading in readings
|
||||
if reading.sensor_type == self._sensor_type), None)
|
||||
if value is not None:
|
||||
self._sensor_value = round(float(value), SENSOR_VALUE_PRECISION)
|
|
@ -581,6 +581,9 @@ pushetta==1.0.15
|
|||
# homeassistant.components.light.rpi_gpio_pwm
|
||||
pwmled==1.2.1
|
||||
|
||||
# homeassistant.components.canary
|
||||
py-canary==0.2.3
|
||||
|
||||
# homeassistant.components.sensor.cpuspeed
|
||||
py-cpuinfo==3.3.0
|
||||
|
||||
|
|
|
@ -115,6 +115,9 @@ pmsensor==0.4
|
|||
# homeassistant.components.prometheus
|
||||
prometheus_client==0.0.21
|
||||
|
||||
# homeassistant.components.canary
|
||||
py-canary==0.2.3
|
||||
|
||||
# homeassistant.components.zwave
|
||||
pydispatcher==2.0.5
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ TEST_REQUIREMENTS = (
|
|||
'pilight',
|
||||
'pmsensor',
|
||||
'prometheus_client',
|
||||
'py-canary',
|
||||
'pydispatcher',
|
||||
'PyJWT',
|
||||
'pylitejet',
|
||||
|
|
125
tests/components/sensor/test_canary.py
Normal file
125
tests/components/sensor/test_canary.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
"""The tests for the Canary sensor platform."""
|
||||
import copy
|
||||
import unittest
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from canary.api import SensorType
|
||||
from homeassistant.components import canary as base_canary
|
||||
from homeassistant.components.canary import DATA_CANARY
|
||||
from homeassistant.components.sensor import canary
|
||||
from homeassistant.components.sensor.canary import CanarySensor
|
||||
from tests.common import (get_test_home_assistant)
|
||||
from tests.components.test_canary import mock_device, mock_reading, \
|
||||
mock_location
|
||||
|
||||
VALID_CONFIG = {
|
||||
"canary": {
|
||||
"username": "foo@bar.org",
|
||||
"password": "bar",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TestCanarySensorSetup(unittest.TestCase):
|
||||
"""Test the Canary platform."""
|
||||
|
||||
DEVICES = []
|
||||
|
||||
def add_devices(self, devices, action):
|
||||
"""Mock add devices."""
|
||||
for device in devices:
|
||||
self.DEVICES.append(device)
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize values for this testcase class."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.config = copy.deepcopy(VALID_CONFIG)
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch('homeassistant.components.canary.CanaryData')
|
||||
def test_setup_sensors(self, mock_canary):
|
||||
"""Test the sensor setup."""
|
||||
base_canary.setup(self.hass, self.config)
|
||||
|
||||
online_device_at_home = mock_device(20, "Dining Room", True)
|
||||
offline_device_at_home = mock_device(21, "Front Yard", False)
|
||||
online_device_at_work = mock_device(22, "Office", True)
|
||||
|
||||
self.hass.data[DATA_CANARY] = mock_canary()
|
||||
self.hass.data[DATA_CANARY].locations = [
|
||||
mock_location("Home", True, devices=[online_device_at_home,
|
||||
offline_device_at_home]),
|
||||
mock_location("Work", True, devices=[online_device_at_work]),
|
||||
]
|
||||
|
||||
canary.setup_platform(self.hass, self.config, self.add_devices, None)
|
||||
|
||||
self.assertEqual(6, len(self.DEVICES))
|
||||
|
||||
def test_celsius_temperature_sensor(self):
|
||||
"""Test temperature sensor with celsius."""
|
||||
device = mock_device(10, "Family Room")
|
||||
location = mock_location("Home", True)
|
||||
|
||||
data = Mock()
|
||||
data.get_readings.return_value = [
|
||||
mock_reading(SensorType.TEMPERATURE, 21.1234)]
|
||||
|
||||
sensor = CanarySensor(data, SensorType.TEMPERATURE, location, device)
|
||||
sensor.update()
|
||||
|
||||
self.assertEqual("Home Family Room Temperature", sensor.name)
|
||||
self.assertEqual("sensor_canary_10_temperature", sensor.unique_id)
|
||||
self.assertEqual("°C", sensor.unit_of_measurement)
|
||||
self.assertEqual(21.1, sensor.state)
|
||||
|
||||
def test_fahrenheit_temperature_sensor(self):
|
||||
"""Test temperature sensor with fahrenheit."""
|
||||
device = mock_device(10, "Family Room")
|
||||
location = mock_location("Home", False)
|
||||
|
||||
data = Mock()
|
||||
data.get_readings.return_value = [
|
||||
mock_reading(SensorType.TEMPERATURE, 21.1567)]
|
||||
|
||||
sensor = CanarySensor(data, SensorType.TEMPERATURE, location, device)
|
||||
sensor.update()
|
||||
|
||||
self.assertEqual("Home Family Room Temperature", sensor.name)
|
||||
self.assertEqual("°F", sensor.unit_of_measurement)
|
||||
self.assertEqual(21.2, sensor.state)
|
||||
|
||||
def test_humidity_sensor(self):
|
||||
"""Test humidity sensor."""
|
||||
device = mock_device(10, "Family Room")
|
||||
location = mock_location("Home")
|
||||
|
||||
data = Mock()
|
||||
data.get_readings.return_value = [
|
||||
mock_reading(SensorType.HUMIDITY, 50.4567)]
|
||||
|
||||
sensor = CanarySensor(data, SensorType.HUMIDITY, location, device)
|
||||
sensor.update()
|
||||
|
||||
self.assertEqual("Home Family Room Humidity", sensor.name)
|
||||
self.assertEqual("%", sensor.unit_of_measurement)
|
||||
self.assertEqual(50.5, sensor.state)
|
||||
|
||||
def test_air_quality_sensor(self):
|
||||
"""Test air quality sensor."""
|
||||
device = mock_device(10, "Family Room")
|
||||
location = mock_location("Home")
|
||||
|
||||
data = Mock()
|
||||
data.get_readings.return_value = [
|
||||
mock_reading(SensorType.AIR_QUALITY, 50.4567)]
|
||||
|
||||
sensor = CanarySensor(data, SensorType.AIR_QUALITY, location, device)
|
||||
sensor.update()
|
||||
|
||||
self.assertEqual("Home Family Room Air Quality", sensor.name)
|
||||
self.assertEqual("", sensor.unit_of_measurement)
|
||||
self.assertEqual(50.5, sensor.state)
|
85
tests/components/test_canary.py
Normal file
85
tests/components/test_canary.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
"""The tests for the Canary component."""
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock, PropertyMock
|
||||
|
||||
import homeassistant.components.canary as canary
|
||||
from homeassistant import setup
|
||||
from tests.common import (
|
||||
get_test_home_assistant)
|
||||
|
||||
|
||||
def mock_device(device_id, name, is_online=True):
|
||||
"""Mock Canary Device class."""
|
||||
device = MagicMock()
|
||||
type(device).device_id = PropertyMock(return_value=device_id)
|
||||
type(device).name = PropertyMock(return_value=name)
|
||||
type(device).is_online = PropertyMock(return_value=is_online)
|
||||
return device
|
||||
|
||||
|
||||
def mock_location(name, is_celsius=True, devices=[]):
|
||||
"""Mock Canary Location class."""
|
||||
location = MagicMock()
|
||||
type(location).name = PropertyMock(return_value=name)
|
||||
type(location).is_celsius = PropertyMock(return_value=is_celsius)
|
||||
type(location).devices = PropertyMock(return_value=devices)
|
||||
return location
|
||||
|
||||
|
||||
def mock_reading(sensor_type, sensor_value):
|
||||
"""Mock Canary Reading class."""
|
||||
reading = MagicMock()
|
||||
type(reading).sensor_type = PropertyMock(return_value=sensor_type)
|
||||
type(reading).value = PropertyMock(return_value=sensor_value)
|
||||
return reading
|
||||
|
||||
|
||||
class TestCanary(unittest.TestCase):
|
||||
"""Tests the Canary component."""
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize values for this test case class."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch('homeassistant.components.canary.CanaryData.update')
|
||||
@patch('canary.api.Api.login')
|
||||
def test_setup_with_valid_config(self, mock_login, mock_update):
|
||||
"""Test setup component."""
|
||||
config = {
|
||||
"canary": {
|
||||
"username": "foo@bar.org",
|
||||
"password": "bar",
|
||||
}
|
||||
}
|
||||
|
||||
self.assertTrue(
|
||||
setup.setup_component(self.hass, canary.DOMAIN, config))
|
||||
|
||||
mock_update.assert_called_once_with()
|
||||
mock_login.assert_called_once_with()
|
||||
|
||||
def test_setup_with_missing_password(self):
|
||||
"""Test setup component."""
|
||||
config = {
|
||||
"canary": {
|
||||
"username": "foo@bar.org",
|
||||
}
|
||||
}
|
||||
|
||||
self.assertFalse(
|
||||
setup.setup_component(self.hass, canary.DOMAIN, config))
|
||||
|
||||
def test_setup_with_missing_username(self):
|
||||
"""Test setup component."""
|
||||
config = {
|
||||
"canary": {
|
||||
"password": "bar",
|
||||
}
|
||||
}
|
||||
|
||||
self.assertFalse(
|
||||
setup.setup_component(self.hass, canary.DOMAIN, config))
|
Loading…
Add table
Reference in a new issue