diff --git a/homeassistant/components/binary_sensor/mercedesme.py b/homeassistant/components/binary_sensor/mercedesme.py old mode 100755 new mode 100644 index c817447f181..a6c8da56ce8 --- a/homeassistant/components/binary_sensor/mercedesme.py +++ b/homeassistant/components/binary_sensor/mercedesme.py @@ -1,94 +1,94 @@ -""" -Support for Mercedes cars with Mercedes ME. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.mercedesme/ -""" -import logging -import datetime - -from homeassistant.components.binary_sensor import (BinarySensorDevice) -from homeassistant.components.mercedesme import ( - DATA_MME, MercedesMeEntity, BINARY_SENSORS) - -DEPENDENCIES = ['mercedesme'] - -_LOGGER = logging.getLogger(__name__) - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the sensor platform.""" - data = hass.data[DATA_MME].data - - if not data.cars: - _LOGGER.error("No cars found. Check component log.") - return - - devices = [] - for car in data.cars: - for key, value in sorted(BINARY_SENSORS.items()): - devices.append(MercedesMEBinarySensor( - data, key, value[0], car["vin"], None)) - - add_devices(devices, True) - - -class MercedesMEBinarySensor(MercedesMeEntity, BinarySensorDevice): - """Representation of a Sensor.""" - - @property - def is_on(self): - """Return the state of the binary sensor.""" - return self._state - - @property - def device_state_attributes(self): - """Return the state attributes.""" - if self._internal_name == "windowsClosed": - return { - "window_front_left": self._car["windowStatusFrontLeft"], - "window_front_right": self._car["windowStatusFrontRight"], - "window_rear_left": self._car["windowStatusRearLeft"], - "window_rear_right": self._car["windowStatusRearRight"], - "original_value": self._car[self._internal_name], - "last_update": datetime.datetime.fromtimestamp( - self._car["lastUpdate"]).strftime('%Y-%m-%d %H:%M:%S'), - "car": self._car["license"] - } - elif self._internal_name == "tireWarningLight": - return { - "front_right_tire_pressure_kpa": - self._car["frontRightTirePressureKpa"], - "front_left_tire_pressure_kpa": - self._car["frontLeftTirePressureKpa"], - "rear_right_tire_pressure_kpa": - self._car["rearRightTirePressureKpa"], - "rear_left_tire_pressure_kpa": - self._car["rearLeftTirePressureKpa"], - "original_value": self._car[self._internal_name], - "last_update": datetime.datetime.fromtimestamp( - self._car["lastUpdate"] - ).strftime('%Y-%m-%d %H:%M:%S'), - "car": self._car["license"], - } - return { - "original_value": self._car[self._internal_name], - "last_update": datetime.datetime.fromtimestamp( - self._car["lastUpdate"]).strftime('%Y-%m-%d %H:%M:%S'), - "car": self._car["license"] - } - - def update(self): - """Fetch new state data for the sensor.""" - self._car = next( - car for car in self._data.cars if car["vin"] == self._vin) - - if self._internal_name == "windowsClosed": - self._state = bool(self._car[self._internal_name] == "CLOSED") - elif self._internal_name == "tireWarningLight": - self._state = bool(self._car[self._internal_name] != "INACTIVE") - else: - self._state = self._car[self._internal_name] is True - - _LOGGER.debug("Updated %s Value: %s IsOn: %s", - self._internal_name, self._state, self.is_on) +""" +Support for Mercedes cars with Mercedes ME. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.mercedesme/ +""" +import logging +import datetime + +from homeassistant.components.binary_sensor import (BinarySensorDevice) +from homeassistant.components.mercedesme import ( + DATA_MME, MercedesMeEntity, BINARY_SENSORS) + +DEPENDENCIES = ['mercedesme'] + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the sensor platform.""" + data = hass.data[DATA_MME].data + + if not data.cars: + _LOGGER.error("No cars found. Check component log.") + return + + devices = [] + for car in data.cars: + for key, value in sorted(BINARY_SENSORS.items()): + devices.append(MercedesMEBinarySensor( + data, key, value[0], car["vin"], None)) + + add_devices(devices, True) + + +class MercedesMEBinarySensor(MercedesMeEntity, BinarySensorDevice): + """Representation of a Sensor.""" + + @property + def is_on(self): + """Return the state of the binary sensor.""" + return self._state + + @property + def device_state_attributes(self): + """Return the state attributes.""" + if self._internal_name == "windowsClosed": + return { + "window_front_left": self._car["windowStatusFrontLeft"], + "window_front_right": self._car["windowStatusFrontRight"], + "window_rear_left": self._car["windowStatusRearLeft"], + "window_rear_right": self._car["windowStatusRearRight"], + "original_value": self._car[self._internal_name], + "last_update": datetime.datetime.fromtimestamp( + self._car["lastUpdate"]).strftime('%Y-%m-%d %H:%M:%S'), + "car": self._car["license"] + } + elif self._internal_name == "tireWarningLight": + return { + "front_right_tire_pressure_kpa": + self._car["frontRightTirePressureKpa"], + "front_left_tire_pressure_kpa": + self._car["frontLeftTirePressureKpa"], + "rear_right_tire_pressure_kpa": + self._car["rearRightTirePressureKpa"], + "rear_left_tire_pressure_kpa": + self._car["rearLeftTirePressureKpa"], + "original_value": self._car[self._internal_name], + "last_update": datetime.datetime.fromtimestamp( + self._car["lastUpdate"] + ).strftime('%Y-%m-%d %H:%M:%S'), + "car": self._car["license"], + } + return { + "original_value": self._car[self._internal_name], + "last_update": datetime.datetime.fromtimestamp( + self._car["lastUpdate"]).strftime('%Y-%m-%d %H:%M:%S'), + "car": self._car["license"] + } + + def update(self): + """Fetch new state data for the sensor.""" + self._car = next( + car for car in self._data.cars if car["vin"] == self._vin) + + if self._internal_name == "windowsClosed": + self._state = bool(self._car[self._internal_name] == "CLOSED") + elif self._internal_name == "tireWarningLight": + self._state = bool(self._car[self._internal_name] != "INACTIVE") + else: + self._state = self._car[self._internal_name] is True + + _LOGGER.debug("Updated %s Value: %s IsOn: %s", + self._internal_name, self._state, self.is_on) diff --git a/homeassistant/components/camera/xeoma.py b/homeassistant/components/camera/xeoma.py old mode 100755 new mode 100644 diff --git a/homeassistant/components/device_tracker/mercedesme.py b/homeassistant/components/device_tracker/mercedesme.py old mode 100755 new mode 100644 index ed516b738cc..0aa2be96290 --- a/homeassistant/components/device_tracker/mercedesme.py +++ b/homeassistant/components/device_tracker/mercedesme.py @@ -1,71 +1,71 @@ -""" -Support for Mercedes cars with Mercedes ME. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/device_tracker.mercedesme/ -""" -import logging -from datetime import timedelta - -from homeassistant.components.mercedesme import DATA_MME -from homeassistant.helpers.event import track_time_interval -from homeassistant.util import Throttle - -_LOGGER = logging.getLogger(__name__) - -DEPENDENCIES = ['mercedesme'] - -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30) - - -def setup_scanner(hass, config, see, discovery_info=None): - """Set up the Mercedes ME tracker.""" - if discovery_info is None: - return False - - data = hass.data[DATA_MME].data - - if not data.cars: - return False - - MercedesMEDeviceTracker(hass, config, see, data) - - return True - - -class MercedesMEDeviceTracker(object): - """A class representing a Mercedes ME device tracker.""" - - def __init__(self, hass, config, see, data): - """Initialize the Mercedes ME device tracker.""" - self.see = see - self.data = data - self.update_info() - - track_time_interval( - hass, self.update_info, MIN_TIME_BETWEEN_SCANS) - - @Throttle(MIN_TIME_BETWEEN_SCANS) - def update_info(self, now=None): - """Update the device info.""" - for device in self.data.cars: - _LOGGER.debug("Updating %s", device["vin"]) - location = self.data.get_location(device["vin"]) - if location is None: - return False - dev_id = device["vin"] - name = device["license"] - - lat = location['positionLat']['value'] - lon = location['positionLong']['value'] - attrs = { - 'trackr_id': dev_id, - 'id': dev_id, - 'name': name - } - self.see( - dev_id=dev_id, host_name=name, - gps=(lat, lon), attributes=attrs - ) - - return True +""" +Support for Mercedes cars with Mercedes ME. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/device_tracker.mercedesme/ +""" +import logging +from datetime import timedelta + +from homeassistant.components.mercedesme import DATA_MME +from homeassistant.helpers.event import track_time_interval +from homeassistant.util import Throttle + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['mercedesme'] + +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30) + + +def setup_scanner(hass, config, see, discovery_info=None): + """Set up the Mercedes ME tracker.""" + if discovery_info is None: + return False + + data = hass.data[DATA_MME].data + + if not data.cars: + return False + + MercedesMEDeviceTracker(hass, config, see, data) + + return True + + +class MercedesMEDeviceTracker(object): + """A class representing a Mercedes ME device tracker.""" + + def __init__(self, hass, config, see, data): + """Initialize the Mercedes ME device tracker.""" + self.see = see + self.data = data + self.update_info() + + track_time_interval( + hass, self.update_info, MIN_TIME_BETWEEN_SCANS) + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def update_info(self, now=None): + """Update the device info.""" + for device in self.data.cars: + _LOGGER.debug("Updating %s", device["vin"]) + location = self.data.get_location(device["vin"]) + if location is None: + return False + dev_id = device["vin"] + name = device["license"] + + lat = location['positionLat']['value'] + lon = location['positionLong']['value'] + attrs = { + 'trackr_id': dev_id, + 'id': dev_id, + 'name': name + } + self.see( + dev_id=dev_id, host_name=name, + gps=(lat, lon), attributes=attrs + ) + + return True diff --git a/homeassistant/components/mercedesme.py b/homeassistant/components/mercedesme.py old mode 100755 new mode 100644 index 0ac58e9c62e..a228486e2c8 --- a/homeassistant/components/mercedesme.py +++ b/homeassistant/components/mercedesme.py @@ -1,154 +1,154 @@ -""" -Support for MercedesME System. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/mercedesme/ -""" -import asyncio -import logging -from datetime import timedelta - -import voluptuous as vol -import homeassistant.helpers.config_validation as cv - -from homeassistant.const import ( - CONF_USERNAME, CONF_PASSWORD, CONF_SCAN_INTERVAL, LENGTH_KILOMETERS) -from homeassistant.helpers import discovery -from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, dispatcher_send) -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import track_time_interval - -REQUIREMENTS = ['mercedesmejsonpy==0.1.2'] - -_LOGGER = logging.getLogger(__name__) - -BINARY_SENSORS = { - 'doorsClosed': ['Doors closed'], - 'windowsClosed': ['Windows closed'], - 'locked': ['Doors locked'], - 'tireWarningLight': ['Tire Warning'] -} - -SENSORS = { - 'fuelLevelPercent': ['Fuel Level', '%'], - 'fuelRangeKm': ['Fuel Range', LENGTH_KILOMETERS], - 'latestTrip': ['Latest Trip', None], - 'odometerKm': ['Odometer', LENGTH_KILOMETERS], - 'serviceIntervalDays': ['Next Service', 'days'] -} - -DATA_MME = 'mercedesme' -DOMAIN = 'mercedesme' - -NOTIFICATION_ID = 'mercedesme_integration_notification' -NOTIFICATION_TITLE = 'Mercedes me integration setup' - -SIGNAL_UPDATE_MERCEDESME = "mercedesme_update" - - -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_SCAN_INTERVAL, default=30): - vol.All(cv.positive_int, vol.Clamp(min=10)) - }) -}, extra=vol.ALLOW_EXTRA) - - -def setup(hass, config): - """Set up MercedesMe System.""" - from mercedesmejsonpy.controller import Controller - from mercedesmejsonpy import Exceptions - - conf = config[DOMAIN] - username = conf.get(CONF_USERNAME) - password = conf.get(CONF_PASSWORD) - scan_interval = conf.get(CONF_SCAN_INTERVAL) - - try: - mercedesme_api = Controller(username, password, scan_interval) - if not mercedesme_api.is_valid_session: - raise Exceptions.MercedesMeException(500) - hass.data[DATA_MME] = MercedesMeHub(mercedesme_api) - except Exceptions.MercedesMeException as ex: - if ex.code == 401: - hass.components.persistent_notification.create( - "Error:
Please check username and password." - "You will need to restart Home Assistant after fixing.", - title=NOTIFICATION_TITLE, - notification_id=NOTIFICATION_ID) - else: - hass.components.persistent_notification.create( - "Error:
Can't communicate with Mercedes me API.
" - "Error code: {} Reason: {}" - "You will need to restart Home Assistant after fixing." - "".format(ex.code, ex.message), - title=NOTIFICATION_TITLE, - notification_id=NOTIFICATION_ID) - - _LOGGER.error("Unable to communicate with Mercedes me API: %s", - ex.message) - return False - - discovery.load_platform(hass, 'sensor', DOMAIN, {}, config) - discovery.load_platform(hass, 'device_tracker', DOMAIN, {}, config) - discovery.load_platform(hass, 'binary_sensor', DOMAIN, {}, config) - - def hub_refresh(event_time): - """Call Mercedes me API to refresh information.""" - _LOGGER.info("Updating Mercedes me component.") - hass.data[DATA_MME].data.update() - dispatcher_send(hass, SIGNAL_UPDATE_MERCEDESME) - - track_time_interval( - hass, - hub_refresh, - timedelta(seconds=scan_interval)) - - return True - - -class MercedesMeHub(object): - """Representation of a base MercedesMe device.""" - - def __init__(self, data): - """Initialize the entity.""" - self.data = data - - -class MercedesMeEntity(Entity): - """Entity class for MercedesMe devices.""" - - def __init__(self, data, internal_name, sensor_name, vin, unit): - """Initialize the MercedesMe entity.""" - self._car = None - self._data = data - self._state = False - self._name = sensor_name - self._internal_name = internal_name - self._unit = unit - self._vin = vin - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @asyncio.coroutine - def async_added_to_hass(self): - """Register callbacks.""" - async_dispatcher_connect( - self.hass, SIGNAL_UPDATE_MERCEDESME, self._update_callback) - - def _update_callback(self): - """Callback update method.""" - # If the method is made a callback this should be changed - # to the async version. Check core.callback - self.schedule_update_ha_state(True) - - @property - def unit_of_measurement(self): - """Return the unit of measurement.""" - return self._unit +""" +Support for MercedesME System. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/mercedesme/ +""" +import asyncio +import logging +from datetime import timedelta + +import voluptuous as vol +import homeassistant.helpers.config_validation as cv + +from homeassistant.const import ( + CONF_USERNAME, CONF_PASSWORD, CONF_SCAN_INTERVAL, LENGTH_KILOMETERS) +from homeassistant.helpers import discovery +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, dispatcher_send) +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.event import track_time_interval + +REQUIREMENTS = ['mercedesmejsonpy==0.1.2'] + +_LOGGER = logging.getLogger(__name__) + +BINARY_SENSORS = { + 'doorsClosed': ['Doors closed'], + 'windowsClosed': ['Windows closed'], + 'locked': ['Doors locked'], + 'tireWarningLight': ['Tire Warning'] +} + +SENSORS = { + 'fuelLevelPercent': ['Fuel Level', '%'], + 'fuelRangeKm': ['Fuel Range', LENGTH_KILOMETERS], + 'latestTrip': ['Latest Trip', None], + 'odometerKm': ['Odometer', LENGTH_KILOMETERS], + 'serviceIntervalDays': ['Next Service', 'days'] +} + +DATA_MME = 'mercedesme' +DOMAIN = 'mercedesme' + +NOTIFICATION_ID = 'mercedesme_integration_notification' +NOTIFICATION_TITLE = 'Mercedes me integration setup' + +SIGNAL_UPDATE_MERCEDESME = "mercedesme_update" + + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_SCAN_INTERVAL, default=30): + vol.All(cv.positive_int, vol.Clamp(min=10)) + }) +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up MercedesMe System.""" + from mercedesmejsonpy.controller import Controller + from mercedesmejsonpy import Exceptions + + conf = config[DOMAIN] + username = conf.get(CONF_USERNAME) + password = conf.get(CONF_PASSWORD) + scan_interval = conf.get(CONF_SCAN_INTERVAL) + + try: + mercedesme_api = Controller(username, password, scan_interval) + if not mercedesme_api.is_valid_session: + raise Exceptions.MercedesMeException(500) + hass.data[DATA_MME] = MercedesMeHub(mercedesme_api) + except Exceptions.MercedesMeException as ex: + if ex.code == 401: + hass.components.persistent_notification.create( + "Error:
Please check username and password." + "You will need to restart Home Assistant after fixing.", + title=NOTIFICATION_TITLE, + notification_id=NOTIFICATION_ID) + else: + hass.components.persistent_notification.create( + "Error:
Can't communicate with Mercedes me API.
" + "Error code: {} Reason: {}" + "You will need to restart Home Assistant after fixing." + "".format(ex.code, ex.message), + title=NOTIFICATION_TITLE, + notification_id=NOTIFICATION_ID) + + _LOGGER.error("Unable to communicate with Mercedes me API: %s", + ex.message) + return False + + discovery.load_platform(hass, 'sensor', DOMAIN, {}, config) + discovery.load_platform(hass, 'device_tracker', DOMAIN, {}, config) + discovery.load_platform(hass, 'binary_sensor', DOMAIN, {}, config) + + def hub_refresh(event_time): + """Call Mercedes me API to refresh information.""" + _LOGGER.info("Updating Mercedes me component.") + hass.data[DATA_MME].data.update() + dispatcher_send(hass, SIGNAL_UPDATE_MERCEDESME) + + track_time_interval( + hass, + hub_refresh, + timedelta(seconds=scan_interval)) + + return True + + +class MercedesMeHub(object): + """Representation of a base MercedesMe device.""" + + def __init__(self, data): + """Initialize the entity.""" + self.data = data + + +class MercedesMeEntity(Entity): + """Entity class for MercedesMe devices.""" + + def __init__(self, data, internal_name, sensor_name, vin, unit): + """Initialize the MercedesMe entity.""" + self._car = None + self._data = data + self._state = False + self._name = sensor_name + self._internal_name = internal_name + self._unit = unit + self._vin = vin + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @asyncio.coroutine + def async_added_to_hass(self): + """Register callbacks.""" + async_dispatcher_connect( + self.hass, SIGNAL_UPDATE_MERCEDESME, self._update_callback) + + def _update_callback(self): + """Callback update method.""" + # If the method is made a callback this should be changed + # to the async version. Check core.callback + self.schedule_update_ha_state(True) + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit diff --git a/homeassistant/components/remote/xiaomi_miio.py b/homeassistant/components/remote/xiaomi_miio.py old mode 100755 new mode 100644 diff --git a/homeassistant/components/sensor/mercedesme.py b/homeassistant/components/sensor/mercedesme.py old mode 100755 new mode 100644 index 21a63dd562d..bc368745e40 --- a/homeassistant/components/sensor/mercedesme.py +++ b/homeassistant/components/sensor/mercedesme.py @@ -1,83 +1,83 @@ -""" -Support for Mercedes cars with Mercedes ME. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.mercedesme/ -""" -import logging -import datetime - -from homeassistant.components.mercedesme import ( - DATA_MME, MercedesMeEntity, SENSORS) - - -DEPENDENCIES = ['mercedesme'] - -_LOGGER = logging.getLogger(__name__) - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the sensor platform.""" - if discovery_info is None: - return - - data = hass.data[DATA_MME].data - - if not data.cars: - return - - devices = [] - for car in data.cars: - for key, value in sorted(SENSORS.items()): - devices.append( - MercedesMESensor(data, key, value[0], car["vin"], value[1])) - - add_devices(devices, True) - - -class MercedesMESensor(MercedesMeEntity): - """Representation of a Sensor.""" - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - def update(self): - """Get the latest data and updates the states.""" - _LOGGER.debug("Updating %s", self._internal_name) - - self._car = next( - car for car in self._data.cars if car["vin"] == self._vin) - - if self._internal_name == "latestTrip": - self._state = self._car["latestTrip"]["id"] - else: - self._state = self._car[self._internal_name] - - @property - def device_state_attributes(self): - """Return the state attributes.""" - if self._internal_name == "latestTrip": - return { - "duration_seconds": - self._car["latestTrip"]["durationSeconds"], - "distance_traveled_km": - self._car["latestTrip"]["distanceTraveledKm"], - "started_at": datetime.datetime.fromtimestamp( - self._car["latestTrip"]["startedAt"] - ).strftime('%Y-%m-%d %H:%M:%S'), - "average_speed_km_per_hr": - self._car["latestTrip"]["averageSpeedKmPerHr"], - "finished": self._car["latestTrip"]["finished"], - "last_update": datetime.datetime.fromtimestamp( - self._car["lastUpdate"] - ).strftime('%Y-%m-%d %H:%M:%S'), - "car": self._car["license"] - } - - return { - "last_update": datetime.datetime.fromtimestamp( - self._car["lastUpdate"]).strftime('%Y-%m-%d %H:%M:%S'), - "car": self._car["license"] - } +""" +Support for Mercedes cars with Mercedes ME. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/sensor.mercedesme/ +""" +import logging +import datetime + +from homeassistant.components.mercedesme import ( + DATA_MME, MercedesMeEntity, SENSORS) + + +DEPENDENCIES = ['mercedesme'] + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the sensor platform.""" + if discovery_info is None: + return + + data = hass.data[DATA_MME].data + + if not data.cars: + return + + devices = [] + for car in data.cars: + for key, value in sorted(SENSORS.items()): + devices.append( + MercedesMESensor(data, key, value[0], car["vin"], value[1])) + + add_devices(devices, True) + + +class MercedesMESensor(MercedesMeEntity): + """Representation of a Sensor.""" + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + def update(self): + """Get the latest data and updates the states.""" + _LOGGER.debug("Updating %s", self._internal_name) + + self._car = next( + car for car in self._data.cars if car["vin"] == self._vin) + + if self._internal_name == "latestTrip": + self._state = self._car["latestTrip"]["id"] + else: + self._state = self._car[self._internal_name] + + @property + def device_state_attributes(self): + """Return the state attributes.""" + if self._internal_name == "latestTrip": + return { + "duration_seconds": + self._car["latestTrip"]["durationSeconds"], + "distance_traveled_km": + self._car["latestTrip"]["distanceTraveledKm"], + "started_at": datetime.datetime.fromtimestamp( + self._car["latestTrip"]["startedAt"] + ).strftime('%Y-%m-%d %H:%M:%S'), + "average_speed_km_per_hr": + self._car["latestTrip"]["averageSpeedKmPerHr"], + "finished": self._car["latestTrip"]["finished"], + "last_update": datetime.datetime.fromtimestamp( + self._car["lastUpdate"] + ).strftime('%Y-%m-%d %H:%M:%S'), + "car": self._car["license"] + } + + return { + "last_update": datetime.datetime.fromtimestamp( + self._car["lastUpdate"]).strftime('%Y-%m-%d %H:%M:%S'), + "car": self._car["license"] + } diff --git a/tests/components/calendar/test_google.py b/tests/components/calendar/test_google.py index 1de825efd99..62c8ea8854f 100644 --- a/tests/components/calendar/test_google.py +++ b/tests/components/calendar/test_google.py @@ -1,423 +1,423 @@ -"""The tests for the google calendar component.""" -# pylint: disable=protected-access -import logging -import unittest -from unittest.mock import patch - -import pytest - -import homeassistant.components.calendar as calendar_base -import homeassistant.components.calendar.google as calendar -import homeassistant.util.dt as dt_util -from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON -from homeassistant.helpers.template import DATE_STR_FORMAT -from tests.common import get_test_home_assistant - -TEST_PLATFORM = {calendar_base.DOMAIN: {CONF_PLATFORM: 'test'}} - -_LOGGER = logging.getLogger(__name__) - - -class TestComponentsGoogleCalendar(unittest.TestCase): - """Test the Google calendar.""" - - hass = None # HomeAssistant - - # pylint: disable=invalid-name - def setUp(self): - """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant() - - # Set our timezone to CST/Regina so we can check calculations - # This keeps UTC-6 all year round - dt_util.set_default_time_zone(dt_util.get_time_zone('America/Regina')) - - # pylint: disable=invalid-name - def tearDown(self): - """Stop everything that was started.""" - dt_util.set_default_time_zone(dt_util.get_time_zone('UTC')) - - self.hass.stop() - - @patch('homeassistant.components.calendar.google.GoogleCalendarData') - def test_all_day_event(self, mock_next_event): - """Test that we can create an event trigger on device.""" - week_from_today = dt_util.dt.date.today() \ - + dt_util.dt.timedelta(days=7) - event = { - 'summary': 'Test All Day Event', - 'start': { - 'date': week_from_today.isoformat() - }, - 'end': { - 'date': (week_from_today + dt_util.dt.timedelta(days=1)) - .isoformat() - }, - 'location': 'Test Cases', - 'description': 'We\'re just testing that all day events get setup ' - 'correctly', - 'kind': 'calendar#event', - 'created': '2016-06-23T16:37:57.000Z', - 'transparency': 'transparent', - 'updated': '2016-06-24T01:57:21.045Z', - 'reminders': {'useDefault': True}, - 'organizer': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True - }, - 'sequence': 0, - 'creator': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True - }, - 'id': '_c8rinwq863h45qnucyoi43ny8', - 'etag': '"2933466882090000"', - 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', - 'iCalUID': 'cydrevtfuybguinhomj@google.com', - 'status': 'confirmed' - } - - mock_next_event.return_value.event = event - - device_name = 'Test All Day' - - cal = calendar.GoogleCalendarEventDevice(self.hass, None, - '', {'name': device_name}) - - self.assertEqual(cal.name, device_name) - - self.assertEqual(cal.state, STATE_OFF) - - self.assertFalse(cal.offset_reached()) - - self.assertEqual(cal.device_state_attributes, { - 'message': event['summary'], - 'all_day': True, - 'offset_reached': False, - 'start_time': '{} 00:00:00'.format(event['start']['date']), - 'end_time': '{} 00:00:00'.format(event['end']['date']), - 'location': event['location'], - 'description': event['description'] - }) - - @patch('homeassistant.components.calendar.google.GoogleCalendarData') - def test_future_event(self, mock_next_event): - """Test that we can create an event trigger on device.""" - one_hour_from_now = dt_util.now() \ - + dt_util.dt.timedelta(minutes=30) - event = { - 'start': { - 'dateTime': one_hour_from_now.isoformat() - }, - 'end': { - 'dateTime': (one_hour_from_now - + dt_util.dt.timedelta(minutes=60)) - .isoformat() - }, - 'summary': 'Test Event in 30 minutes', - 'reminders': {'useDefault': True}, - 'id': 'aioehgni435lihje', - 'status': 'confirmed', - 'updated': '2016-11-05T15:52:07.329Z', - 'organizer': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True, - }, - 'created': '2016-11-05T15:52:07.000Z', - 'iCalUID': 'dsfohuygtfvgbhnuju@google.com', - 'sequence': 0, - 'creator': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - }, - 'etag': '"2956722254658000"', - 'kind': 'calendar#event', - 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', - } - mock_next_event.return_value.event = event - - device_name = 'Test Future Event' - device_id = 'test_future_event' - - cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, - {'name': device_name}) - - self.assertEqual(cal.name, device_name) - - self.assertEqual(cal.state, STATE_OFF) - - self.assertFalse(cal.offset_reached()) - - self.assertEqual(cal.device_state_attributes, { - 'message': event['summary'], - 'all_day': False, - 'offset_reached': False, - 'start_time': one_hour_from_now.strftime(DATE_STR_FORMAT), - 'end_time': - (one_hour_from_now + dt_util.dt.timedelta(minutes=60)) - .strftime(DATE_STR_FORMAT), - 'location': '', - 'description': '' - }) - - @patch('homeassistant.components.calendar.google.GoogleCalendarData') - def test_in_progress_event(self, mock_next_event): - """Test that we can create an event trigger on device.""" - middle_of_event = dt_util.now() \ - - dt_util.dt.timedelta(minutes=30) - event = { - 'start': { - 'dateTime': middle_of_event.isoformat() - }, - 'end': { - 'dateTime': (middle_of_event + dt_util.dt - .timedelta(minutes=60)) - .isoformat() - }, - 'summary': 'Test Event in Progress', - 'reminders': {'useDefault': True}, - 'id': 'aioehgni435lihje', - 'status': 'confirmed', - 'updated': '2016-11-05T15:52:07.329Z', - 'organizer': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True, - }, - 'created': '2016-11-05T15:52:07.000Z', - 'iCalUID': 'dsfohuygtfvgbhnuju@google.com', - 'sequence': 0, - 'creator': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - }, - 'etag': '"2956722254658000"', - 'kind': 'calendar#event', - 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', - } - - mock_next_event.return_value.event = event - - device_name = 'Test Event in Progress' - device_id = 'test_event_in_progress' - - cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, - {'name': device_name}) - - self.assertEqual(cal.name, device_name) - - self.assertEqual(cal.state, STATE_ON) - - self.assertFalse(cal.offset_reached()) - - self.assertEqual(cal.device_state_attributes, { - 'message': event['summary'], - 'all_day': False, - 'offset_reached': False, - 'start_time': middle_of_event.strftime(DATE_STR_FORMAT), - 'end_time': - (middle_of_event + dt_util.dt.timedelta(minutes=60)) - .strftime(DATE_STR_FORMAT), - 'location': '', - 'description': '' - }) - - @patch('homeassistant.components.calendar.google.GoogleCalendarData') - def test_offset_in_progress_event(self, mock_next_event): - """Test that we can create an event trigger on device.""" - middle_of_event = dt_util.now() \ - + dt_util.dt.timedelta(minutes=14) - event_summary = 'Test Event in Progress' - event = { - 'start': { - 'dateTime': middle_of_event.isoformat() - }, - 'end': { - 'dateTime': (middle_of_event + dt_util.dt - .timedelta(minutes=60)) - .isoformat() - }, - 'summary': '{} !!-15'.format(event_summary), - 'reminders': {'useDefault': True}, - 'id': 'aioehgni435lihje', - 'status': 'confirmed', - 'updated': '2016-11-05T15:52:07.329Z', - 'organizer': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True, - }, - 'created': '2016-11-05T15:52:07.000Z', - 'iCalUID': 'dsfohuygtfvgbhnuju@google.com', - 'sequence': 0, - 'creator': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - }, - 'etag': '"2956722254658000"', - 'kind': 'calendar#event', - 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', - } - - mock_next_event.return_value.event = event - - device_name = 'Test Event in Progress' - device_id = 'test_event_in_progress' - - cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, - {'name': device_name}) - - self.assertEqual(cal.name, device_name) - - self.assertEqual(cal.state, STATE_OFF) - - self.assertTrue(cal.offset_reached()) - - self.assertEqual(cal.device_state_attributes, { - 'message': event_summary, - 'all_day': False, - 'offset_reached': True, - 'start_time': middle_of_event.strftime(DATE_STR_FORMAT), - 'end_time': - (middle_of_event + dt_util.dt.timedelta(minutes=60)) - .strftime(DATE_STR_FORMAT), - 'location': '', - 'description': '' - }) - - @pytest.mark.skip - @patch('homeassistant.components.calendar.google.GoogleCalendarData') - def test_all_day_offset_in_progress_event(self, mock_next_event): - """Test that we can create an event trigger on device.""" - tomorrow = dt_util.dt.date.today() \ - + dt_util.dt.timedelta(days=1) - - event_summary = 'Test All Day Event Offset In Progress' - event = { - 'summary': '{} !!-25:0'.format(event_summary), - 'start': { - 'date': tomorrow.isoformat() - }, - 'end': { - 'date': (tomorrow + dt_util.dt.timedelta(days=1)) - .isoformat() - }, - 'location': 'Test Cases', - 'description': 'We\'re just testing that all day events get setup ' - 'correctly', - 'kind': 'calendar#event', - 'created': '2016-06-23T16:37:57.000Z', - 'transparency': 'transparent', - 'updated': '2016-06-24T01:57:21.045Z', - 'reminders': {'useDefault': True}, - 'organizer': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True - }, - 'sequence': 0, - 'creator': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True - }, - 'id': '_c8rinwq863h45qnucyoi43ny8', - 'etag': '"2933466882090000"', - 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', - 'iCalUID': 'cydrevtfuybguinhomj@google.com', - 'status': 'confirmed' - } - - mock_next_event.return_value.event = event - - device_name = 'Test All Day Offset In Progress' - device_id = 'test_all_day_offset_in_progress' - - cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, - {'name': device_name}) - - self.assertEqual(cal.name, device_name) - - self.assertEqual(cal.state, STATE_OFF) - - self.assertTrue(cal.offset_reached()) - - self.assertEqual(cal.device_state_attributes, { - 'message': event_summary, - 'all_day': True, - 'offset_reached': True, - 'start_time': '{} 06:00:00'.format(event['start']['date']), - 'end_time': '{} 06:00:00'.format(event['end']['date']), - 'location': event['location'], - 'description': event['description'] - }) - - @patch('homeassistant.components.calendar.google.GoogleCalendarData') - def test_all_day_offset_event(self, mock_next_event): - """Test that we can create an event trigger on device.""" - tomorrow = dt_util.dt.date.today() \ - + dt_util.dt.timedelta(days=2) - - offset_hours = (1 + dt_util.now().hour) - event_summary = 'Test All Day Event Offset' - event = { - 'summary': '{} !!-{}:0'.format(event_summary, offset_hours), - 'start': { - 'date': tomorrow.isoformat() - }, - 'end': { - 'date': (tomorrow + dt_util.dt.timedelta(days=1)) - .isoformat() - }, - 'location': 'Test Cases', - 'description': 'We\'re just testing that all day events get setup ' - 'correctly', - 'kind': 'calendar#event', - 'created': '2016-06-23T16:37:57.000Z', - 'transparency': 'transparent', - 'updated': '2016-06-24T01:57:21.045Z', - 'reminders': {'useDefault': True}, - 'organizer': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True - }, - 'sequence': 0, - 'creator': { - 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', - 'displayName': 'Organizer Name', - 'self': True - }, - 'id': '_c8rinwq863h45qnucyoi43ny8', - 'etag': '"2933466882090000"', - 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', - 'iCalUID': 'cydrevtfuybguinhomj@google.com', - 'status': 'confirmed' - } - - mock_next_event.return_value.event = event - - device_name = 'Test All Day Offset' - device_id = 'test_all_day_offset' - - cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, - {'name': device_name}) - - self.assertEqual(cal.name, device_name) - - self.assertEqual(cal.state, STATE_OFF) - - self.assertFalse(cal.offset_reached()) - - self.assertEqual(cal.device_state_attributes, { - 'message': event_summary, - 'all_day': True, - 'offset_reached': False, - 'start_time': '{} 00:00:00'.format(event['start']['date']), - 'end_time': '{} 00:00:00'.format(event['end']['date']), - 'location': event['location'], - 'description': event['description'] - }) +"""The tests for the google calendar component.""" +# pylint: disable=protected-access +import logging +import unittest +from unittest.mock import patch + +import pytest + +import homeassistant.components.calendar as calendar_base +import homeassistant.components.calendar.google as calendar +import homeassistant.util.dt as dt_util +from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON +from homeassistant.helpers.template import DATE_STR_FORMAT +from tests.common import get_test_home_assistant + +TEST_PLATFORM = {calendar_base.DOMAIN: {CONF_PLATFORM: 'test'}} + +_LOGGER = logging.getLogger(__name__) + + +class TestComponentsGoogleCalendar(unittest.TestCase): + """Test the Google calendar.""" + + hass = None # HomeAssistant + + # pylint: disable=invalid-name + def setUp(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + # Set our timezone to CST/Regina so we can check calculations + # This keeps UTC-6 all year round + dt_util.set_default_time_zone(dt_util.get_time_zone('America/Regina')) + + # pylint: disable=invalid-name + def tearDown(self): + """Stop everything that was started.""" + dt_util.set_default_time_zone(dt_util.get_time_zone('UTC')) + + self.hass.stop() + + @patch('homeassistant.components.calendar.google.GoogleCalendarData') + def test_all_day_event(self, mock_next_event): + """Test that we can create an event trigger on device.""" + week_from_today = dt_util.dt.date.today() \ + + dt_util.dt.timedelta(days=7) + event = { + 'summary': 'Test All Day Event', + 'start': { + 'date': week_from_today.isoformat() + }, + 'end': { + 'date': (week_from_today + dt_util.dt.timedelta(days=1)) + .isoformat() + }, + 'location': 'Test Cases', + 'description': 'We\'re just testing that all day events get setup ' + 'correctly', + 'kind': 'calendar#event', + 'created': '2016-06-23T16:37:57.000Z', + 'transparency': 'transparent', + 'updated': '2016-06-24T01:57:21.045Z', + 'reminders': {'useDefault': True}, + 'organizer': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True + }, + 'sequence': 0, + 'creator': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True + }, + 'id': '_c8rinwq863h45qnucyoi43ny8', + 'etag': '"2933466882090000"', + 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', + 'iCalUID': 'cydrevtfuybguinhomj@google.com', + 'status': 'confirmed' + } + + mock_next_event.return_value.event = event + + device_name = 'Test All Day' + + cal = calendar.GoogleCalendarEventDevice(self.hass, None, + '', {'name': device_name}) + + self.assertEqual(cal.name, device_name) + + self.assertEqual(cal.state, STATE_OFF) + + self.assertFalse(cal.offset_reached()) + + self.assertEqual(cal.device_state_attributes, { + 'message': event['summary'], + 'all_day': True, + 'offset_reached': False, + 'start_time': '{} 00:00:00'.format(event['start']['date']), + 'end_time': '{} 00:00:00'.format(event['end']['date']), + 'location': event['location'], + 'description': event['description'] + }) + + @patch('homeassistant.components.calendar.google.GoogleCalendarData') + def test_future_event(self, mock_next_event): + """Test that we can create an event trigger on device.""" + one_hour_from_now = dt_util.now() \ + + dt_util.dt.timedelta(minutes=30) + event = { + 'start': { + 'dateTime': one_hour_from_now.isoformat() + }, + 'end': { + 'dateTime': (one_hour_from_now + + dt_util.dt.timedelta(minutes=60)) + .isoformat() + }, + 'summary': 'Test Event in 30 minutes', + 'reminders': {'useDefault': True}, + 'id': 'aioehgni435lihje', + 'status': 'confirmed', + 'updated': '2016-11-05T15:52:07.329Z', + 'organizer': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True, + }, + 'created': '2016-11-05T15:52:07.000Z', + 'iCalUID': 'dsfohuygtfvgbhnuju@google.com', + 'sequence': 0, + 'creator': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + }, + 'etag': '"2956722254658000"', + 'kind': 'calendar#event', + 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', + } + mock_next_event.return_value.event = event + + device_name = 'Test Future Event' + device_id = 'test_future_event' + + cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, + {'name': device_name}) + + self.assertEqual(cal.name, device_name) + + self.assertEqual(cal.state, STATE_OFF) + + self.assertFalse(cal.offset_reached()) + + self.assertEqual(cal.device_state_attributes, { + 'message': event['summary'], + 'all_day': False, + 'offset_reached': False, + 'start_time': one_hour_from_now.strftime(DATE_STR_FORMAT), + 'end_time': + (one_hour_from_now + dt_util.dt.timedelta(minutes=60)) + .strftime(DATE_STR_FORMAT), + 'location': '', + 'description': '' + }) + + @patch('homeassistant.components.calendar.google.GoogleCalendarData') + def test_in_progress_event(self, mock_next_event): + """Test that we can create an event trigger on device.""" + middle_of_event = dt_util.now() \ + - dt_util.dt.timedelta(minutes=30) + event = { + 'start': { + 'dateTime': middle_of_event.isoformat() + }, + 'end': { + 'dateTime': (middle_of_event + dt_util.dt + .timedelta(minutes=60)) + .isoformat() + }, + 'summary': 'Test Event in Progress', + 'reminders': {'useDefault': True}, + 'id': 'aioehgni435lihje', + 'status': 'confirmed', + 'updated': '2016-11-05T15:52:07.329Z', + 'organizer': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True, + }, + 'created': '2016-11-05T15:52:07.000Z', + 'iCalUID': 'dsfohuygtfvgbhnuju@google.com', + 'sequence': 0, + 'creator': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + }, + 'etag': '"2956722254658000"', + 'kind': 'calendar#event', + 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', + } + + mock_next_event.return_value.event = event + + device_name = 'Test Event in Progress' + device_id = 'test_event_in_progress' + + cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, + {'name': device_name}) + + self.assertEqual(cal.name, device_name) + + self.assertEqual(cal.state, STATE_ON) + + self.assertFalse(cal.offset_reached()) + + self.assertEqual(cal.device_state_attributes, { + 'message': event['summary'], + 'all_day': False, + 'offset_reached': False, + 'start_time': middle_of_event.strftime(DATE_STR_FORMAT), + 'end_time': + (middle_of_event + dt_util.dt.timedelta(minutes=60)) + .strftime(DATE_STR_FORMAT), + 'location': '', + 'description': '' + }) + + @patch('homeassistant.components.calendar.google.GoogleCalendarData') + def test_offset_in_progress_event(self, mock_next_event): + """Test that we can create an event trigger on device.""" + middle_of_event = dt_util.now() \ + + dt_util.dt.timedelta(minutes=14) + event_summary = 'Test Event in Progress' + event = { + 'start': { + 'dateTime': middle_of_event.isoformat() + }, + 'end': { + 'dateTime': (middle_of_event + dt_util.dt + .timedelta(minutes=60)) + .isoformat() + }, + 'summary': '{} !!-15'.format(event_summary), + 'reminders': {'useDefault': True}, + 'id': 'aioehgni435lihje', + 'status': 'confirmed', + 'updated': '2016-11-05T15:52:07.329Z', + 'organizer': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True, + }, + 'created': '2016-11-05T15:52:07.000Z', + 'iCalUID': 'dsfohuygtfvgbhnuju@google.com', + 'sequence': 0, + 'creator': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + }, + 'etag': '"2956722254658000"', + 'kind': 'calendar#event', + 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', + } + + mock_next_event.return_value.event = event + + device_name = 'Test Event in Progress' + device_id = 'test_event_in_progress' + + cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, + {'name': device_name}) + + self.assertEqual(cal.name, device_name) + + self.assertEqual(cal.state, STATE_OFF) + + self.assertTrue(cal.offset_reached()) + + self.assertEqual(cal.device_state_attributes, { + 'message': event_summary, + 'all_day': False, + 'offset_reached': True, + 'start_time': middle_of_event.strftime(DATE_STR_FORMAT), + 'end_time': + (middle_of_event + dt_util.dt.timedelta(minutes=60)) + .strftime(DATE_STR_FORMAT), + 'location': '', + 'description': '' + }) + + @pytest.mark.skip + @patch('homeassistant.components.calendar.google.GoogleCalendarData') + def test_all_day_offset_in_progress_event(self, mock_next_event): + """Test that we can create an event trigger on device.""" + tomorrow = dt_util.dt.date.today() \ + + dt_util.dt.timedelta(days=1) + + event_summary = 'Test All Day Event Offset In Progress' + event = { + 'summary': '{} !!-25:0'.format(event_summary), + 'start': { + 'date': tomorrow.isoformat() + }, + 'end': { + 'date': (tomorrow + dt_util.dt.timedelta(days=1)) + .isoformat() + }, + 'location': 'Test Cases', + 'description': 'We\'re just testing that all day events get setup ' + 'correctly', + 'kind': 'calendar#event', + 'created': '2016-06-23T16:37:57.000Z', + 'transparency': 'transparent', + 'updated': '2016-06-24T01:57:21.045Z', + 'reminders': {'useDefault': True}, + 'organizer': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True + }, + 'sequence': 0, + 'creator': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True + }, + 'id': '_c8rinwq863h45qnucyoi43ny8', + 'etag': '"2933466882090000"', + 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', + 'iCalUID': 'cydrevtfuybguinhomj@google.com', + 'status': 'confirmed' + } + + mock_next_event.return_value.event = event + + device_name = 'Test All Day Offset In Progress' + device_id = 'test_all_day_offset_in_progress' + + cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, + {'name': device_name}) + + self.assertEqual(cal.name, device_name) + + self.assertEqual(cal.state, STATE_OFF) + + self.assertTrue(cal.offset_reached()) + + self.assertEqual(cal.device_state_attributes, { + 'message': event_summary, + 'all_day': True, + 'offset_reached': True, + 'start_time': '{} 06:00:00'.format(event['start']['date']), + 'end_time': '{} 06:00:00'.format(event['end']['date']), + 'location': event['location'], + 'description': event['description'] + }) + + @patch('homeassistant.components.calendar.google.GoogleCalendarData') + def test_all_day_offset_event(self, mock_next_event): + """Test that we can create an event trigger on device.""" + tomorrow = dt_util.dt.date.today() \ + + dt_util.dt.timedelta(days=2) + + offset_hours = (1 + dt_util.now().hour) + event_summary = 'Test All Day Event Offset' + event = { + 'summary': '{} !!-{}:0'.format(event_summary, offset_hours), + 'start': { + 'date': tomorrow.isoformat() + }, + 'end': { + 'date': (tomorrow + dt_util.dt.timedelta(days=1)) + .isoformat() + }, + 'location': 'Test Cases', + 'description': 'We\'re just testing that all day events get setup ' + 'correctly', + 'kind': 'calendar#event', + 'created': '2016-06-23T16:37:57.000Z', + 'transparency': 'transparent', + 'updated': '2016-06-24T01:57:21.045Z', + 'reminders': {'useDefault': True}, + 'organizer': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True + }, + 'sequence': 0, + 'creator': { + 'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com', + 'displayName': 'Organizer Name', + 'self': True + }, + 'id': '_c8rinwq863h45qnucyoi43ny8', + 'etag': '"2933466882090000"', + 'htmlLink': 'https://www.google.com/calendar/event?eid=*******', + 'iCalUID': 'cydrevtfuybguinhomj@google.com', + 'status': 'confirmed' + } + + mock_next_event.return_value.event = event + + device_name = 'Test All Day Offset' + device_id = 'test_all_day_offset' + + cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id, + {'name': device_name}) + + self.assertEqual(cal.name, device_name) + + self.assertEqual(cal.state, STATE_OFF) + + self.assertFalse(cal.offset_reached()) + + self.assertEqual(cal.device_state_attributes, { + 'message': event_summary, + 'all_day': True, + 'offset_reached': False, + 'start_time': '{} 00:00:00'.format(event['start']['date']), + 'end_time': '{} 00:00:00'.format(event['end']['date']), + 'location': event['location'], + 'description': event['description'] + }) diff --git a/tests/components/cloud/__init__.py b/tests/components/cloud/__init__.py index 707e49f670f..7a4e9f2950e 100644 --- a/tests/components/cloud/__init__.py +++ b/tests/components/cloud/__init__.py @@ -1 +1 @@ -"""Tests for the cloud component.""" +"""Tests for the cloud component.""" diff --git a/tests/components/device_tracker/test_xiaomi.py b/tests/components/device_tracker/test_xiaomi.py index 94a4566a17b..19f25b514db 100644 --- a/tests/components/device_tracker/test_xiaomi.py +++ b/tests/components/device_tracker/test_xiaomi.py @@ -1,265 +1,265 @@ -"""The tests for the Xiaomi router device tracker platform.""" -import logging -import unittest -from unittest import mock -from unittest.mock import patch - -import requests - -from homeassistant.components.device_tracker import DOMAIN, xiaomi as xiaomi -from homeassistant.components.device_tracker.xiaomi import get_scanner -from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, - CONF_PLATFORM) -from tests.common import get_test_home_assistant - -_LOGGER = logging.getLogger(__name__) - -INVALID_USERNAME = 'bob' -TOKEN_TIMEOUT_USERNAME = 'tok' -URL_AUTHORIZE = 'http://192.168.0.1/cgi-bin/luci/api/xqsystem/login' -URL_LIST_END = 'api/misystem/devicelist' - -FIRST_CALL = True - - -def mocked_requests(*args, **kwargs): - """Mock requests.get invocations.""" - class MockResponse: - """Class to represent a mocked response.""" - - def __init__(self, json_data, status_code): - """Initialize the mock response class.""" - self.json_data = json_data - self.status_code = status_code - - def json(self): - """Return the json of the response.""" - return self.json_data - - @property - def content(self): - """Return the content of the response.""" - return self.json() - - def raise_for_status(self): - """Raise an HTTPError if status is not 200.""" - if self.status_code != 200: - raise requests.HTTPError(self.status_code) - - data = kwargs.get('data') - global FIRST_CALL - - if data and data.get('username', None) == INVALID_USERNAME: - # deliver an invalid token - return MockResponse({ - "code": "401", - "msg": "Invalid token" - }, 200) - elif data and data.get('username', None) == TOKEN_TIMEOUT_USERNAME: - # deliver an expired token - return MockResponse({ - "url": "/cgi-bin/luci/;stok=ef5860/web/home", - "token": "timedOut", - "code": "0" - }, 200) - elif str(args[0]).startswith(URL_AUTHORIZE): - # deliver an authorized token - return MockResponse({ - "url": "/cgi-bin/luci/;stok=ef5860/web/home", - "token": "ef5860", - "code": "0" - }, 200) - elif str(args[0]).endswith("timedOut/" + URL_LIST_END) \ - and FIRST_CALL is True: - FIRST_CALL = False - # deliver an error when called with expired token - return MockResponse({ - "code": "401", - "msg": "Invalid token" - }, 200) - elif str(args[0]).endswith(URL_LIST_END): - # deliver the device list - return MockResponse({ - "mac": "1C:98:EC:0E:D5:A4", - "list": [ - { - "mac": "23:83:BF:F6:38:A0", - "oname": "12255ff", - "isap": 0, - "parent": "", - "authority": { - "wan": 1, - "pridisk": 0, - "admin": 1, - "lan": 0 - }, - "push": 0, - "online": 1, - "name": "Device1", - "times": 0, - "ip": [ - { - "downspeed": "0", - "online": "496957", - "active": 1, - "upspeed": "0", - "ip": "192.168.0.25" - } - ], - "statistics": { - "downspeed": "0", - "online": "496957", - "upspeed": "0" - }, - "icon": "", - "type": 1 - }, - { - "mac": "1D:98:EC:5E:D5:A6", - "oname": "CdddFG58", - "isap": 0, - "parent": "", - "authority": { - "wan": 1, - "pridisk": 0, - "admin": 1, - "lan": 0 - }, - "push": 0, - "online": 1, - "name": "Device2", - "times": 0, - "ip": [ - { - "downspeed": "0", - "online": "347325", - "active": 1, - "upspeed": "0", - "ip": "192.168.0.3" - } - ], - "statistics": { - "downspeed": "0", - "online": "347325", - "upspeed": "0" - }, - "icon": "", - "type": 0 - }, - ], - "code": 0 - }, 200) - else: - _LOGGER.debug('UNKNOWN ROUTE') - - -class TestXiaomiDeviceScanner(unittest.TestCase): - """Xiaomi device scanner test class.""" - - def setUp(self): - """Initialize values for this testcase class.""" - self.hass = get_test_home_assistant() - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - @mock.patch( - 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', - return_value=mock.MagicMock()) - def test_config(self, xiaomi_mock): - """Testing minimal configuration.""" - config = { - DOMAIN: xiaomi.PLATFORM_SCHEMA({ - CONF_PLATFORM: xiaomi.DOMAIN, - CONF_HOST: '192.168.0.1', - CONF_PASSWORD: 'passwordTest' - }) - } - xiaomi.get_scanner(self.hass, config) - self.assertEqual(xiaomi_mock.call_count, 1) - self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN])) - call_arg = xiaomi_mock.call_args[0][0] - self.assertEqual(call_arg['username'], 'admin') - self.assertEqual(call_arg['password'], 'passwordTest') - self.assertEqual(call_arg['host'], '192.168.0.1') - self.assertEqual(call_arg['platform'], 'device_tracker') - - @mock.patch( - 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', - return_value=mock.MagicMock()) - def test_config_full(self, xiaomi_mock): - """Testing full configuration.""" - config = { - DOMAIN: xiaomi.PLATFORM_SCHEMA({ - CONF_PLATFORM: xiaomi.DOMAIN, - CONF_HOST: '192.168.0.1', - CONF_USERNAME: 'alternativeAdminName', - CONF_PASSWORD: 'passwordTest' - }) - } - xiaomi.get_scanner(self.hass, config) - self.assertEqual(xiaomi_mock.call_count, 1) - self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN])) - call_arg = xiaomi_mock.call_args[0][0] - self.assertEqual(call_arg['username'], 'alternativeAdminName') - self.assertEqual(call_arg['password'], 'passwordTest') - self.assertEqual(call_arg['host'], '192.168.0.1') - self.assertEqual(call_arg['platform'], 'device_tracker') - - @patch('requests.get', side_effect=mocked_requests) - @patch('requests.post', side_effect=mocked_requests) - def test_invalid_credential(self, mock_get, mock_post): - """"Testing invalid credential handling.""" - config = { - DOMAIN: xiaomi.PLATFORM_SCHEMA({ - CONF_PLATFORM: xiaomi.DOMAIN, - CONF_HOST: '192.168.0.1', - CONF_USERNAME: INVALID_USERNAME, - CONF_PASSWORD: 'passwordTest' - }) - } - self.assertIsNone(get_scanner(self.hass, config)) - - @patch('requests.get', side_effect=mocked_requests) - @patch('requests.post', side_effect=mocked_requests) - def test_valid_credential(self, mock_get, mock_post): - """"Testing valid refresh.""" - config = { - DOMAIN: xiaomi.PLATFORM_SCHEMA({ - CONF_PLATFORM: xiaomi.DOMAIN, - CONF_HOST: '192.168.0.1', - CONF_USERNAME: 'admin', - CONF_PASSWORD: 'passwordTest' - }) - } - scanner = get_scanner(self.hass, config) - self.assertIsNotNone(scanner) - self.assertEqual(2, len(scanner.scan_devices())) - self.assertEqual("Device1", - scanner.get_device_name("23:83:BF:F6:38:A0")) - self.assertEqual("Device2", - scanner.get_device_name("1D:98:EC:5E:D5:A6")) - - @patch('requests.get', side_effect=mocked_requests) - @patch('requests.post', side_effect=mocked_requests) - def test_token_timed_out(self, mock_get, mock_post): - """"Testing refresh with a timed out token. - - New token is requested and list is downloaded a second time. - """ - config = { - DOMAIN: xiaomi.PLATFORM_SCHEMA({ - CONF_PLATFORM: xiaomi.DOMAIN, - CONF_HOST: '192.168.0.1', - CONF_USERNAME: TOKEN_TIMEOUT_USERNAME, - CONF_PASSWORD: 'passwordTest' - }) - } - scanner = get_scanner(self.hass, config) - self.assertIsNotNone(scanner) - self.assertEqual(2, len(scanner.scan_devices())) - self.assertEqual("Device1", - scanner.get_device_name("23:83:BF:F6:38:A0")) - self.assertEqual("Device2", - scanner.get_device_name("1D:98:EC:5E:D5:A6")) +"""The tests for the Xiaomi router device tracker platform.""" +import logging +import unittest +from unittest import mock +from unittest.mock import patch + +import requests + +from homeassistant.components.device_tracker import DOMAIN, xiaomi as xiaomi +from homeassistant.components.device_tracker.xiaomi import get_scanner +from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, + CONF_PLATFORM) +from tests.common import get_test_home_assistant + +_LOGGER = logging.getLogger(__name__) + +INVALID_USERNAME = 'bob' +TOKEN_TIMEOUT_USERNAME = 'tok' +URL_AUTHORIZE = 'http://192.168.0.1/cgi-bin/luci/api/xqsystem/login' +URL_LIST_END = 'api/misystem/devicelist' + +FIRST_CALL = True + + +def mocked_requests(*args, **kwargs): + """Mock requests.get invocations.""" + class MockResponse: + """Class to represent a mocked response.""" + + def __init__(self, json_data, status_code): + """Initialize the mock response class.""" + self.json_data = json_data + self.status_code = status_code + + def json(self): + """Return the json of the response.""" + return self.json_data + + @property + def content(self): + """Return the content of the response.""" + return self.json() + + def raise_for_status(self): + """Raise an HTTPError if status is not 200.""" + if self.status_code != 200: + raise requests.HTTPError(self.status_code) + + data = kwargs.get('data') + global FIRST_CALL + + if data and data.get('username', None) == INVALID_USERNAME: + # deliver an invalid token + return MockResponse({ + "code": "401", + "msg": "Invalid token" + }, 200) + elif data and data.get('username', None) == TOKEN_TIMEOUT_USERNAME: + # deliver an expired token + return MockResponse({ + "url": "/cgi-bin/luci/;stok=ef5860/web/home", + "token": "timedOut", + "code": "0" + }, 200) + elif str(args[0]).startswith(URL_AUTHORIZE): + # deliver an authorized token + return MockResponse({ + "url": "/cgi-bin/luci/;stok=ef5860/web/home", + "token": "ef5860", + "code": "0" + }, 200) + elif str(args[0]).endswith("timedOut/" + URL_LIST_END) \ + and FIRST_CALL is True: + FIRST_CALL = False + # deliver an error when called with expired token + return MockResponse({ + "code": "401", + "msg": "Invalid token" + }, 200) + elif str(args[0]).endswith(URL_LIST_END): + # deliver the device list + return MockResponse({ + "mac": "1C:98:EC:0E:D5:A4", + "list": [ + { + "mac": "23:83:BF:F6:38:A0", + "oname": "12255ff", + "isap": 0, + "parent": "", + "authority": { + "wan": 1, + "pridisk": 0, + "admin": 1, + "lan": 0 + }, + "push": 0, + "online": 1, + "name": "Device1", + "times": 0, + "ip": [ + { + "downspeed": "0", + "online": "496957", + "active": 1, + "upspeed": "0", + "ip": "192.168.0.25" + } + ], + "statistics": { + "downspeed": "0", + "online": "496957", + "upspeed": "0" + }, + "icon": "", + "type": 1 + }, + { + "mac": "1D:98:EC:5E:D5:A6", + "oname": "CdddFG58", + "isap": 0, + "parent": "", + "authority": { + "wan": 1, + "pridisk": 0, + "admin": 1, + "lan": 0 + }, + "push": 0, + "online": 1, + "name": "Device2", + "times": 0, + "ip": [ + { + "downspeed": "0", + "online": "347325", + "active": 1, + "upspeed": "0", + "ip": "192.168.0.3" + } + ], + "statistics": { + "downspeed": "0", + "online": "347325", + "upspeed": "0" + }, + "icon": "", + "type": 0 + }, + ], + "code": 0 + }, 200) + else: + _LOGGER.debug('UNKNOWN ROUTE') + + +class TestXiaomiDeviceScanner(unittest.TestCase): + """Xiaomi device scanner test class.""" + + def setUp(self): + """Initialize values for this testcase class.""" + self.hass = get_test_home_assistant() + + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + + @mock.patch( + 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', + return_value=mock.MagicMock()) + def test_config(self, xiaomi_mock): + """Testing minimal configuration.""" + config = { + DOMAIN: xiaomi.PLATFORM_SCHEMA({ + CONF_PLATFORM: xiaomi.DOMAIN, + CONF_HOST: '192.168.0.1', + CONF_PASSWORD: 'passwordTest' + }) + } + xiaomi.get_scanner(self.hass, config) + self.assertEqual(xiaomi_mock.call_count, 1) + self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN])) + call_arg = xiaomi_mock.call_args[0][0] + self.assertEqual(call_arg['username'], 'admin') + self.assertEqual(call_arg['password'], 'passwordTest') + self.assertEqual(call_arg['host'], '192.168.0.1') + self.assertEqual(call_arg['platform'], 'device_tracker') + + @mock.patch( + 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', + return_value=mock.MagicMock()) + def test_config_full(self, xiaomi_mock): + """Testing full configuration.""" + config = { + DOMAIN: xiaomi.PLATFORM_SCHEMA({ + CONF_PLATFORM: xiaomi.DOMAIN, + CONF_HOST: '192.168.0.1', + CONF_USERNAME: 'alternativeAdminName', + CONF_PASSWORD: 'passwordTest' + }) + } + xiaomi.get_scanner(self.hass, config) + self.assertEqual(xiaomi_mock.call_count, 1) + self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN])) + call_arg = xiaomi_mock.call_args[0][0] + self.assertEqual(call_arg['username'], 'alternativeAdminName') + self.assertEqual(call_arg['password'], 'passwordTest') + self.assertEqual(call_arg['host'], '192.168.0.1') + self.assertEqual(call_arg['platform'], 'device_tracker') + + @patch('requests.get', side_effect=mocked_requests) + @patch('requests.post', side_effect=mocked_requests) + def test_invalid_credential(self, mock_get, mock_post): + """"Testing invalid credential handling.""" + config = { + DOMAIN: xiaomi.PLATFORM_SCHEMA({ + CONF_PLATFORM: xiaomi.DOMAIN, + CONF_HOST: '192.168.0.1', + CONF_USERNAME: INVALID_USERNAME, + CONF_PASSWORD: 'passwordTest' + }) + } + self.assertIsNone(get_scanner(self.hass, config)) + + @patch('requests.get', side_effect=mocked_requests) + @patch('requests.post', side_effect=mocked_requests) + def test_valid_credential(self, mock_get, mock_post): + """"Testing valid refresh.""" + config = { + DOMAIN: xiaomi.PLATFORM_SCHEMA({ + CONF_PLATFORM: xiaomi.DOMAIN, + CONF_HOST: '192.168.0.1', + CONF_USERNAME: 'admin', + CONF_PASSWORD: 'passwordTest' + }) + } + scanner = get_scanner(self.hass, config) + self.assertIsNotNone(scanner) + self.assertEqual(2, len(scanner.scan_devices())) + self.assertEqual("Device1", + scanner.get_device_name("23:83:BF:F6:38:A0")) + self.assertEqual("Device2", + scanner.get_device_name("1D:98:EC:5E:D5:A6")) + + @patch('requests.get', side_effect=mocked_requests) + @patch('requests.post', side_effect=mocked_requests) + def test_token_timed_out(self, mock_get, mock_post): + """"Testing refresh with a timed out token. + + New token is requested and list is downloaded a second time. + """ + config = { + DOMAIN: xiaomi.PLATFORM_SCHEMA({ + CONF_PLATFORM: xiaomi.DOMAIN, + CONF_HOST: '192.168.0.1', + CONF_USERNAME: TOKEN_TIMEOUT_USERNAME, + CONF_PASSWORD: 'passwordTest' + }) + } + scanner = get_scanner(self.hass, config) + self.assertIsNotNone(scanner) + self.assertEqual(2, len(scanner.scan_devices())) + self.assertEqual("Device1", + scanner.get_device_name("23:83:BF:F6:38:A0")) + self.assertEqual("Device2", + scanner.get_device_name("1D:98:EC:5E:D5:A6")) diff --git a/tests/components/emulated_hue/test_init.py b/tests/components/emulated_hue/test_init.py index 25bcbc1dd55..06613f1336a 100644 --- a/tests/components/emulated_hue/test_init.py +++ b/tests/components/emulated_hue/test_init.py @@ -1,128 +1,128 @@ -"""Test the Emulated Hue component.""" -import json - -from unittest.mock import patch, Mock, mock_open - -from homeassistant.components.emulated_hue import Config, _LOGGER - - -def test_config_google_home_entity_id_to_number(): - """Test config adheres to the type.""" - conf = Config(Mock(), { - 'type': 'google_home' - }) - - mop = mock_open(read_data=json.dumps({'1': 'light.test2'})) - handle = mop() - - with patch('homeassistant.util.json.open', mop, create=True): - number = conf.entity_id_to_number('light.test') - assert number == '2' - assert handle.write.call_count == 1 - assert json.loads(handle.write.mock_calls[0][1][0]) == { - '1': 'light.test2', - '2': 'light.test', - } - - number = conf.entity_id_to_number('light.test') - assert number == '2' - assert handle.write.call_count == 1 - - number = conf.entity_id_to_number('light.test2') - assert number == '1' - assert handle.write.call_count == 1 - - entity_id = conf.number_to_entity_id('1') - assert entity_id == 'light.test2' - - -def test_config_google_home_entity_id_to_number_altered(): - """Test config adheres to the type.""" - conf = Config(Mock(), { - 'type': 'google_home' - }) - - mop = mock_open(read_data=json.dumps({'21': 'light.test2'})) - handle = mop() - - with patch('homeassistant.util.json.open', mop, create=True): - number = conf.entity_id_to_number('light.test') - assert number == '22' - assert handle.write.call_count == 1 - assert json.loads(handle.write.mock_calls[0][1][0]) == { - '21': 'light.test2', - '22': 'light.test', - } - - number = conf.entity_id_to_number('light.test') - assert number == '22' - assert handle.write.call_count == 1 - - number = conf.entity_id_to_number('light.test2') - assert number == '21' - assert handle.write.call_count == 1 - - entity_id = conf.number_to_entity_id('21') - assert entity_id == 'light.test2' - - -def test_config_google_home_entity_id_to_number_empty(): - """Test config adheres to the type.""" - conf = Config(Mock(), { - 'type': 'google_home' - }) - - mop = mock_open(read_data='') - handle = mop() - - with patch('homeassistant.util.json.open', mop, create=True): - number = conf.entity_id_to_number('light.test') - assert number == '1' - assert handle.write.call_count == 1 - assert json.loads(handle.write.mock_calls[0][1][0]) == { - '1': 'light.test', - } - - number = conf.entity_id_to_number('light.test') - assert number == '1' - assert handle.write.call_count == 1 - - number = conf.entity_id_to_number('light.test2') - assert number == '2' - assert handle.write.call_count == 2 - - entity_id = conf.number_to_entity_id('2') - assert entity_id == 'light.test2' - - -def test_config_alexa_entity_id_to_number(): - """Test config adheres to the type.""" - conf = Config(None, { - 'type': 'alexa' - }) - - number = conf.entity_id_to_number('light.test') - assert number == 'light.test' - - number = conf.entity_id_to_number('light.test') - assert number == 'light.test' - - number = conf.entity_id_to_number('light.test2') - assert number == 'light.test2' - - entity_id = conf.number_to_entity_id('light.test') - assert entity_id == 'light.test' - - -def test_warning_config_google_home_listen_port(): - """Test we warn when non-default port is used for Google Home.""" - with patch.object(_LOGGER, 'warning') as mock_warn: - Config(None, { - 'type': 'google_home', - 'host_ip': '123.123.123.123', - 'listen_port': 8300 - }) - - assert mock_warn.called - assert mock_warn.mock_calls[0][1][0] == \ - "When targeting Google Home, listening port has to be port 80" +"""Test the Emulated Hue component.""" +import json + +from unittest.mock import patch, Mock, mock_open + +from homeassistant.components.emulated_hue import Config, _LOGGER + + +def test_config_google_home_entity_id_to_number(): + """Test config adheres to the type.""" + conf = Config(Mock(), { + 'type': 'google_home' + }) + + mop = mock_open(read_data=json.dumps({'1': 'light.test2'})) + handle = mop() + + with patch('homeassistant.util.json.open', mop, create=True): + number = conf.entity_id_to_number('light.test') + assert number == '2' + assert handle.write.call_count == 1 + assert json.loads(handle.write.mock_calls[0][1][0]) == { + '1': 'light.test2', + '2': 'light.test', + } + + number = conf.entity_id_to_number('light.test') + assert number == '2' + assert handle.write.call_count == 1 + + number = conf.entity_id_to_number('light.test2') + assert number == '1' + assert handle.write.call_count == 1 + + entity_id = conf.number_to_entity_id('1') + assert entity_id == 'light.test2' + + +def test_config_google_home_entity_id_to_number_altered(): + """Test config adheres to the type.""" + conf = Config(Mock(), { + 'type': 'google_home' + }) + + mop = mock_open(read_data=json.dumps({'21': 'light.test2'})) + handle = mop() + + with patch('homeassistant.util.json.open', mop, create=True): + number = conf.entity_id_to_number('light.test') + assert number == '22' + assert handle.write.call_count == 1 + assert json.loads(handle.write.mock_calls[0][1][0]) == { + '21': 'light.test2', + '22': 'light.test', + } + + number = conf.entity_id_to_number('light.test') + assert number == '22' + assert handle.write.call_count == 1 + + number = conf.entity_id_to_number('light.test2') + assert number == '21' + assert handle.write.call_count == 1 + + entity_id = conf.number_to_entity_id('21') + assert entity_id == 'light.test2' + + +def test_config_google_home_entity_id_to_number_empty(): + """Test config adheres to the type.""" + conf = Config(Mock(), { + 'type': 'google_home' + }) + + mop = mock_open(read_data='') + handle = mop() + + with patch('homeassistant.util.json.open', mop, create=True): + number = conf.entity_id_to_number('light.test') + assert number == '1' + assert handle.write.call_count == 1 + assert json.loads(handle.write.mock_calls[0][1][0]) == { + '1': 'light.test', + } + + number = conf.entity_id_to_number('light.test') + assert number == '1' + assert handle.write.call_count == 1 + + number = conf.entity_id_to_number('light.test2') + assert number == '2' + assert handle.write.call_count == 2 + + entity_id = conf.number_to_entity_id('2') + assert entity_id == 'light.test2' + + +def test_config_alexa_entity_id_to_number(): + """Test config adheres to the type.""" + conf = Config(None, { + 'type': 'alexa' + }) + + number = conf.entity_id_to_number('light.test') + assert number == 'light.test' + + number = conf.entity_id_to_number('light.test') + assert number == 'light.test' + + number = conf.entity_id_to_number('light.test2') + assert number == 'light.test2' + + entity_id = conf.number_to_entity_id('light.test') + assert entity_id == 'light.test' + + +def test_warning_config_google_home_listen_port(): + """Test we warn when non-default port is used for Google Home.""" + with patch.object(_LOGGER, 'warning') as mock_warn: + Config(None, { + 'type': 'google_home', + 'host_ip': '123.123.123.123', + 'listen_port': 8300 + }) + + assert mock_warn.called + assert mock_warn.mock_calls[0][1][0] == \ + "When targeting Google Home, listening port has to be port 80" diff --git a/tests/components/light/test_mqtt_json.py b/tests/components/light/test_mqtt_json.py index 5cb0d0cdc1b..a06f8e7d093 100644 --- a/tests/components/light/test_mqtt_json.py +++ b/tests/components/light/test_mqtt_json.py @@ -1,579 +1,579 @@ -"""The tests for the MQTT JSON light platform. - -Configuration with RGB, brightness, color temp, effect, white value and XY: - -light: - platform: mqtt_json - name: mqtt_json_light_1 - state_topic: "home/rgb1" - command_topic: "home/rgb1/set" - brightness: true - color_temp: true - effect: true - rgb: true - white_value: true - xy: true - -Configuration with RGB, brightness, color temp, effect, white value: - -light: - platform: mqtt_json - name: mqtt_json_light_1 - state_topic: "home/rgb1" - command_topic: "home/rgb1/set" - brightness: true - color_temp: true - effect: true - rgb: true - white_value: true - -Configuration with RGB, brightness, color temp and effect: - -light: - platform: mqtt_json - name: mqtt_json_light_1 - state_topic: "home/rgb1" - command_topic: "home/rgb1/set" - brightness: true - color_temp: true - effect: true - rgb: true - -Configuration with RGB, brightness and color temp: - -light: - platform: mqtt_json - name: mqtt_json_light_1 - state_topic: "home/rgb1" - command_topic: "home/rgb1/set" - brightness: true - rgb: true - color_temp: true - -Configuration with RGB, brightness: - -light: - platform: mqtt_json - name: mqtt_json_light_1 - state_topic: "home/rgb1" - command_topic: "home/rgb1/set" - brightness: true - rgb: true - -Config without RGB: - -light: - platform: mqtt_json - name: mqtt_json_light_1 - state_topic: "home/rgb1" - command_topic: "home/rgb1/set" - brightness: true - -Config without RGB and brightness: - -light: - platform: mqtt_json - name: mqtt_json_light_1 - state_topic: "home/rgb1" - command_topic: "home/rgb1/set" - -Config with brightness and scale: - -light: - platform: mqtt_json - name: test - state_topic: "mqtt_json_light_1" - command_topic: "mqtt_json_light_1/set" - brightness: true - brightness_scale: 99 -""" - -import json -import unittest - -from homeassistant.setup import setup_component -from homeassistant.const import ( - STATE_ON, STATE_OFF, STATE_UNAVAILABLE, ATTR_ASSUMED_STATE, - ATTR_SUPPORTED_FEATURES) -import homeassistant.components.light as light -from tests.common import ( - get_test_home_assistant, mock_mqtt_component, fire_mqtt_message, - assert_setup_component) - - -class TestLightMQTTJSON(unittest.TestCase): - """Test the MQTT JSON light.""" - - def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant() - self.mock_publish = mock_mqtt_component(self.hass) - - def tearDown(self): # pylint: disable=invalid-name - """Stop everything that was started.""" - self.hass.stop() - - def test_fail_setup_if_no_command_topic(self): \ - # pylint: disable=invalid-name - """Test if setup fails with no command topic.""" - with assert_setup_component(0, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - } - }) - self.assertIsNone(self.hass.states.get('light.test')) - - def test_no_color_brightness_color_temp_white_val_if_no_topics(self): \ - # pylint: disable=invalid-name - """Test for no RGB, brightness, color temp, effect, white val or XY.""" - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertEqual(40, state.attributes.get(ATTR_SUPPORTED_FEATURES)) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('color_temp')) - self.assertIsNone(state.attributes.get('effect')) - self.assertIsNone(state.attributes.get('white_value')) - self.assertIsNone(state.attributes.get('xy_color')) - - fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"ON"}') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('color_temp')) - self.assertIsNone(state.attributes.get('effect')) - self.assertIsNone(state.attributes.get('white_value')) - self.assertIsNone(state.attributes.get('xy_color')) - - def test_controlling_state_via_topic(self): \ - # pylint: disable=invalid-name - """Test the controlling of the state via topic.""" - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'brightness': True, - 'color_temp': True, - 'effect': True, - 'rgb': True, - 'white_value': True, - 'xy': True, - 'qos': '0' - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertEqual(255, state.attributes.get(ATTR_SUPPORTED_FEATURES)) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('color_temp')) - self.assertIsNone(state.attributes.get('effect')) - self.assertIsNone(state.attributes.get('white_value')) - self.assertIsNone(state.attributes.get('xy_color')) - self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) - - # Turn on the light, full white - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"color":{"r":255,"g":255,"b":255,' - '"x":0.123,"y":0.123},' - '"brightness":255,' - '"color_temp":155,' - '"effect":"colorloop",' - '"white_value":150}') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) - self.assertEqual(255, state.attributes.get('brightness')) - self.assertEqual(155, state.attributes.get('color_temp')) - self.assertEqual('colorloop', state.attributes.get('effect')) - self.assertEqual(150, state.attributes.get('white_value')) - self.assertEqual([0.123, 0.123], state.attributes.get('xy_color')) - - # Turn the light off - fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"OFF"}') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"brightness":100}') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.hass.block_till_done() - self.assertEqual(100, - light_state.attributes['brightness']) - - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"color":{"r":125,"g":125,"b":125}}') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.assertEqual([125, 125, 125], - light_state.attributes.get('rgb_color')) - - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"color":{"x":0.135,"y":0.135}}') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.assertEqual([0.135, 0.135], - light_state.attributes.get('xy_color')) - - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"color_temp":155}') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.assertEqual(155, light_state.attributes.get('color_temp')) - - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"effect":"colorloop"}') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.assertEqual('colorloop', light_state.attributes.get('effect')) - - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"white_value":155}') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.assertEqual(155, light_state.attributes.get('white_value')) - - def test_sending_mqtt_commands_and_optimistic(self): \ - # pylint: disable=invalid-name - """Test the sending of command in optimistic mode.""" - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'command_topic': 'test_light_rgb/set', - 'brightness': True, - 'color_temp': True, - 'effect': True, - 'rgb': True, - 'white_value': True, - 'qos': 2 - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertEqual(191, state.attributes.get(ATTR_SUPPORTED_FEATURES)) - self.assertTrue(state.attributes.get(ATTR_ASSUMED_STATE)) - - light.turn_on(self.hass, 'light.test') - self.hass.block_till_done() - - self.assertEqual(('test_light_rgb/set', '{"state": "ON"}', 2, False), - self.mock_publish.mock_calls[-2][1]) - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - - light.turn_off(self.hass, 'light.test') - self.hass.block_till_done() - - self.assertEqual(('test_light_rgb/set', '{"state": "OFF"}', 2, False), - self.mock_publish.mock_calls[-2][1]) - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - - light.turn_on(self.hass, 'light.test', - brightness=50, color_temp=155, effect='colorloop', - white_value=170) - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(2, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - # Get the sent message - message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) - self.assertEqual(50, message_json["brightness"]) - self.assertEqual(155, message_json["color_temp"]) - self.assertEqual('colorloop', message_json["effect"]) - self.assertEqual(170, message_json["white_value"]) - self.assertEqual("ON", message_json["state"]) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual(50, state.attributes['brightness']) - self.assertEqual(155, state.attributes['color_temp']) - self.assertEqual('colorloop', state.attributes['effect']) - self.assertEqual(170, state.attributes['white_value']) - - def test_flash_short_and_long(self): \ - # pylint: disable=invalid-name - """Test for flash length being sent when included.""" - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'flash_time_short': 5, - 'flash_time_long': 15, - 'qos': 0 - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertEqual(40, state.attributes.get(ATTR_SUPPORTED_FEATURES)) - - light.turn_on(self.hass, 'light.test', flash="short") - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - # Get the sent message - message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) - self.assertEqual(5, message_json["flash"]) - self.assertEqual("ON", message_json["state"]) - - light.turn_on(self.hass, 'light.test', flash="long") - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - # Get the sent message - message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) - self.assertEqual(15, message_json["flash"]) - self.assertEqual("ON", message_json["state"]) - - def test_transition(self): - """Test for transition time being sent when included.""" - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'qos': 0 - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertEqual(40, state.attributes.get(ATTR_SUPPORTED_FEATURES)) - - light.turn_on(self.hass, 'light.test', transition=10) - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - # Get the sent message - message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) - self.assertEqual(10, message_json["transition"]) - self.assertEqual("ON", message_json["state"]) - - # Transition back off - light.turn_off(self.hass, 'light.test', transition=10) - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - # Get the sent message - message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) - self.assertEqual(10, message_json["transition"]) - self.assertEqual("OFF", message_json["state"]) - - def test_brightness_scale(self): - """Test for brightness scaling.""" - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_bright_scale', - 'command_topic': 'test_light_bright_scale/set', - 'brightness': True, - 'brightness_scale': 99 - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertIsNone(state.attributes.get('brightness')) - self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) - - # Turn on the light - fire_mqtt_message(self.hass, 'test_light_bright_scale', - '{"state":"ON"}') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual(255, state.attributes.get('brightness')) - - # Turn on the light with brightness - fire_mqtt_message(self.hass, 'test_light_bright_scale', - '{"state":"ON",' - '"brightness": 99}') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual(255, state.attributes.get('brightness')) - - def test_invalid_color_brightness_and_white_values(self): \ - # pylint: disable=invalid-name - """Test that invalid color/brightness/white values are ignored.""" - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'brightness': True, - 'rgb': True, - 'white_value': True, - 'qos': '0' - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertEqual(185, state.attributes.get(ATTR_SUPPORTED_FEATURES)) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('white_value')) - self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) - - # Turn on the light - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"color":{"r":255,"g":255,"b":255},' - '"brightness": 255,' - '"white_value": 255}') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) - self.assertEqual(255, state.attributes.get('brightness')) - self.assertEqual(255, state.attributes.get('white_value')) - - # Bad color values - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"color":{"r":"bad","g":"val","b":"test"}}') - self.hass.block_till_done() - - # Color should not have changed - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) - - # Bad brightness values - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"brightness": "badValue"}') - self.hass.block_till_done() - - # Brightness should not have changed - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual(255, state.attributes.get('brightness')) - - # Bad white value - fire_mqtt_message(self.hass, 'test_light_rgb', - '{"state":"ON",' - '"white_value": "badValue"}') - self.hass.block_till_done() - - # White value should not have changed - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual(255, state.attributes.get('white_value')) - - def test_default_availability_payload(self): - """Test availability by default payload with defined topic.""" - self.assertTrue(setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'availability_topic': 'availability-topic' - } - })) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'online') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertNotEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'offline') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) - - def test_custom_availability_payload(self): - """Test availability by custom payload with defined topic.""" - self.assertTrue(setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_json', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'availability_topic': 'availability-topic', - 'payload_available': 'good', - 'payload_not_available': 'nogood' - } - })) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'good') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertNotEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'nogood') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) +"""The tests for the MQTT JSON light platform. + +Configuration with RGB, brightness, color temp, effect, white value and XY: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + color_temp: true + effect: true + rgb: true + white_value: true + xy: true + +Configuration with RGB, brightness, color temp, effect, white value: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + color_temp: true + effect: true + rgb: true + white_value: true + +Configuration with RGB, brightness, color temp and effect: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + color_temp: true + effect: true + rgb: true + +Configuration with RGB, brightness and color temp: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + rgb: true + color_temp: true + +Configuration with RGB, brightness: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + rgb: true + +Config without RGB: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + +Config without RGB and brightness: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + +Config with brightness and scale: + +light: + platform: mqtt_json + name: test + state_topic: "mqtt_json_light_1" + command_topic: "mqtt_json_light_1/set" + brightness: true + brightness_scale: 99 +""" + +import json +import unittest + +from homeassistant.setup import setup_component +from homeassistant.const import ( + STATE_ON, STATE_OFF, STATE_UNAVAILABLE, ATTR_ASSUMED_STATE, + ATTR_SUPPORTED_FEATURES) +import homeassistant.components.light as light +from tests.common import ( + get_test_home_assistant, mock_mqtt_component, fire_mqtt_message, + assert_setup_component) + + +class TestLightMQTTJSON(unittest.TestCase): + """Test the MQTT JSON light.""" + + def setUp(self): # pylint: disable=invalid-name + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + self.mock_publish = mock_mqtt_component(self.hass) + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + + def test_fail_setup_if_no_command_topic(self): \ + # pylint: disable=invalid-name + """Test if setup fails with no command topic.""" + with assert_setup_component(0, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + } + }) + self.assertIsNone(self.hass.states.get('light.test')) + + def test_no_color_brightness_color_temp_white_val_if_no_topics(self): \ + # pylint: disable=invalid-name + """Test for no RGB, brightness, color temp, effect, white val or XY.""" + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertEqual(40, state.attributes.get(ATTR_SUPPORTED_FEATURES)) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) + + fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"ON"}') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) + + def test_controlling_state_via_topic(self): \ + # pylint: disable=invalid-name + """Test the controlling of the state via topic.""" + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'brightness': True, + 'color_temp': True, + 'effect': True, + 'rgb': True, + 'white_value': True, + 'xy': True, + 'qos': '0' + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertEqual(255, state.attributes.get(ATTR_SUPPORTED_FEATURES)) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) + self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) + + # Turn on the light, full white + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color":{"r":255,"g":255,"b":255,' + '"x":0.123,"y":0.123},' + '"brightness":255,' + '"color_temp":155,' + '"effect":"colorloop",' + '"white_value":150}') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) + self.assertEqual(255, state.attributes.get('brightness')) + self.assertEqual(155, state.attributes.get('color_temp')) + self.assertEqual('colorloop', state.attributes.get('effect')) + self.assertEqual(150, state.attributes.get('white_value')) + self.assertEqual([0.123, 0.123], state.attributes.get('xy_color')) + + # Turn the light off + fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"OFF"}') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"brightness":100}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(100, + light_state.attributes['brightness']) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color":{"r":125,"g":125,"b":125}}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual([125, 125, 125], + light_state.attributes.get('rgb_color')) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color":{"x":0.135,"y":0.135}}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual([0.135, 0.135], + light_state.attributes.get('xy_color')) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color_temp":155}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual(155, light_state.attributes.get('color_temp')) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"effect":"colorloop"}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual('colorloop', light_state.attributes.get('effect')) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"white_value":155}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual(155, light_state.attributes.get('white_value')) + + def test_sending_mqtt_commands_and_optimistic(self): \ + # pylint: disable=invalid-name + """Test the sending of command in optimistic mode.""" + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'brightness': True, + 'color_temp': True, + 'effect': True, + 'rgb': True, + 'white_value': True, + 'qos': 2 + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertEqual(191, state.attributes.get(ATTR_SUPPORTED_FEATURES)) + self.assertTrue(state.attributes.get(ATTR_ASSUMED_STATE)) + + light.turn_on(self.hass, 'light.test') + self.hass.block_till_done() + + self.assertEqual(('test_light_rgb/set', '{"state": "ON"}', 2, False), + self.mock_publish.mock_calls[-2][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + + light.turn_off(self.hass, 'light.test') + self.hass.block_till_done() + + self.assertEqual(('test_light_rgb/set', '{"state": "OFF"}', 2, False), + self.mock_publish.mock_calls[-2][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + light.turn_on(self.hass, 'light.test', + brightness=50, color_temp=155, effect='colorloop', + white_value=170) + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(2, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + # Get the sent message + message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) + self.assertEqual(50, message_json["brightness"]) + self.assertEqual(155, message_json["color_temp"]) + self.assertEqual('colorloop', message_json["effect"]) + self.assertEqual(170, message_json["white_value"]) + self.assertEqual("ON", message_json["state"]) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(50, state.attributes['brightness']) + self.assertEqual(155, state.attributes['color_temp']) + self.assertEqual('colorloop', state.attributes['effect']) + self.assertEqual(170, state.attributes['white_value']) + + def test_flash_short_and_long(self): \ + # pylint: disable=invalid-name + """Test for flash length being sent when included.""" + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'flash_time_short': 5, + 'flash_time_long': 15, + 'qos': 0 + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertEqual(40, state.attributes.get(ATTR_SUPPORTED_FEATURES)) + + light.turn_on(self.hass, 'light.test', flash="short") + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + # Get the sent message + message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) + self.assertEqual(5, message_json["flash"]) + self.assertEqual("ON", message_json["state"]) + + light.turn_on(self.hass, 'light.test', flash="long") + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + # Get the sent message + message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) + self.assertEqual(15, message_json["flash"]) + self.assertEqual("ON", message_json["state"]) + + def test_transition(self): + """Test for transition time being sent when included.""" + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'qos': 0 + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertEqual(40, state.attributes.get(ATTR_SUPPORTED_FEATURES)) + + light.turn_on(self.hass, 'light.test', transition=10) + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + # Get the sent message + message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) + self.assertEqual(10, message_json["transition"]) + self.assertEqual("ON", message_json["state"]) + + # Transition back off + light.turn_off(self.hass, 'light.test', transition=10) + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + # Get the sent message + message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) + self.assertEqual(10, message_json["transition"]) + self.assertEqual("OFF", message_json["state"]) + + def test_brightness_scale(self): + """Test for brightness scaling.""" + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_bright_scale', + 'command_topic': 'test_light_bright_scale/set', + 'brightness': True, + 'brightness_scale': 99 + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('brightness')) + self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) + + # Turn on the light + fire_mqtt_message(self.hass, 'test_light_bright_scale', + '{"state":"ON"}') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(255, state.attributes.get('brightness')) + + # Turn on the light with brightness + fire_mqtt_message(self.hass, 'test_light_bright_scale', + '{"state":"ON",' + '"brightness": 99}') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(255, state.attributes.get('brightness')) + + def test_invalid_color_brightness_and_white_values(self): \ + # pylint: disable=invalid-name + """Test that invalid color/brightness/white values are ignored.""" + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'brightness': True, + 'rgb': True, + 'white_value': True, + 'qos': '0' + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertEqual(185, state.attributes.get(ATTR_SUPPORTED_FEATURES)) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) + + # Turn on the light + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color":{"r":255,"g":255,"b":255},' + '"brightness": 255,' + '"white_value": 255}') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) + self.assertEqual(255, state.attributes.get('brightness')) + self.assertEqual(255, state.attributes.get('white_value')) + + # Bad color values + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color":{"r":"bad","g":"val","b":"test"}}') + self.hass.block_till_done() + + # Color should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) + + # Bad brightness values + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"brightness": "badValue"}') + self.hass.block_till_done() + + # Brightness should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(255, state.attributes.get('brightness')) + + # Bad white value + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"white_value": "badValue"}') + self.hass.block_till_done() + + # White value should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(255, state.attributes.get('white_value')) + + def test_default_availability_payload(self): + """Test availability by default payload with defined topic.""" + self.assertTrue(setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'availability_topic': 'availability-topic' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'online') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertNotEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'offline') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) + + def test_custom_availability_payload(self): + """Test availability by custom payload with defined topic.""" + self.assertTrue(setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_json', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'availability_topic': 'availability-topic', + 'payload_available': 'good', + 'payload_not_available': 'nogood' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'good') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertNotEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'nogood') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) diff --git a/tests/components/light/test_mqtt_template.py b/tests/components/light/test_mqtt_template.py index be1f119fc14..0df9d8136e1 100644 --- a/tests/components/light/test_mqtt_template.py +++ b/tests/components/light/test_mqtt_template.py @@ -1,524 +1,524 @@ -"""The tests for the MQTT Template light platform. - -Configuration example with all features: - -light: - platform: mqtt_template - name: mqtt_template_light_1 - state_topic: 'home/rgb1' - command_topic: 'home/rgb1/set' - command_on_template: > - on,{{ brightness|d }},{{ red|d }}-{{ green|d }}-{{ blue|d }} - command_off_template: 'off' - state_template: '{{ value.split(",")[0] }}' - brightness_template: '{{ value.split(",")[1] }}' - color_temp_template: '{{ value.split(",")[2] }}' - white_value_template: '{{ value.split(",")[3] }}' - red_template: '{{ value.split(",")[4].split("-")[0] }}' - green_template: '{{ value.split(",")[4].split("-")[1] }}' - blue_template: '{{ value.split(",")[4].split("-")[2] }}' - -If your light doesn't support brightness feature, omit `brightness_template`. - -If your light doesn't support color temp feature, omit `color_temp_template`. - -If your light doesn't support white value feature, omit `white_value_template`. - -If your light doesn't support RGB feature, omit `(red|green|blue)_template`. -""" -import unittest - -from homeassistant.setup import setup_component -from homeassistant.const import ( - STATE_ON, STATE_OFF, STATE_UNAVAILABLE, ATTR_ASSUMED_STATE) -import homeassistant.components.light as light -from tests.common import ( - get_test_home_assistant, mock_mqtt_component, fire_mqtt_message, - assert_setup_component) - - -class TestLightMQTTTemplate(unittest.TestCase): - """Test the MQTT Template light.""" - - def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant() - self.mock_publish = mock_mqtt_component(self.hass) - - def tearDown(self): # pylint: disable=invalid-name - """Stop everything that was started.""" - self.hass.stop() - - def test_setup_fails(self): \ - # pylint: disable=invalid-name - """Test that setup fails with missing required configuration items.""" - with assert_setup_component(0, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - } - }) - self.assertIsNone(self.hass.states.get('light.test')) - - def test_state_change_via_topic(self): \ - # pylint: disable=invalid-name - """Test state change via topic.""" - with assert_setup_component(1, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,' - '{{ brightness|d }},' - '{{ color_temp|d }},' - '{{ white_value|d }},' - '{{ red|d }}-' - '{{ green|d }}-' - '{{ blue|d }}', - 'command_off_template': 'off', - 'state_template': '{{ value.split(",")[0] }}' - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('color_temp')) - self.assertIsNone(state.attributes.get('white_value')) - self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) - - fire_mqtt_message(self.hass, 'test_light_rgb', 'on') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('color_temp')) - self.assertIsNone(state.attributes.get('white_value')) - - def test_state_brightness_color_effect_temp_white_change_via_topic(self): \ - # pylint: disable=invalid-name - """Test state, bri, color, effect, color temp, white val change.""" - with assert_setup_component(1, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'effect_list': ['rainbow', 'colorloop'], - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,' - '{{ brightness|d }},' - '{{ color_temp|d }},' - '{{ white_value|d }},' - '{{ red|d }}-' - '{{ green|d }}-' - '{{ blue|d }},' - '{{ effect|d }}', - 'command_off_template': 'off', - 'state_template': '{{ value.split(",")[0] }}', - 'brightness_template': '{{ value.split(",")[1] }}', - 'color_temp_template': '{{ value.split(",")[2] }}', - 'white_value_template': '{{ value.split(",")[3] }}', - 'red_template': '{{ value.split(",")[4].' - 'split("-")[0] }}', - 'green_template': '{{ value.split(",")[4].' - 'split("-")[1] }}', - 'blue_template': '{{ value.split(",")[4].' - 'split("-")[2] }}', - 'effect_template': '{{ value.split(",")[5] }}' - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('effect')) - self.assertIsNone(state.attributes.get('color_temp')) - self.assertIsNone(state.attributes.get('white_value')) - self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) - - # turn on the light, full white - fire_mqtt_message(self.hass, 'test_light_rgb', - 'on,255,145,123,255-128-64,') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual([255, 128, 64], state.attributes.get('rgb_color')) - self.assertEqual(255, state.attributes.get('brightness')) - self.assertEqual(145, state.attributes.get('color_temp')) - self.assertEqual(123, state.attributes.get('white_value')) - self.assertIsNone(state.attributes.get('effect')) - - # turn the light off - fire_mqtt_message(self.hass, 'test_light_rgb', 'off') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - - # lower the brightness - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,100') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.hass.block_till_done() - self.assertEqual(100, light_state.attributes['brightness']) - - # change the color temp - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,195') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.hass.block_till_done() - self.assertEqual(195, light_state.attributes['color_temp']) - - # change the color - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,,41-42-43') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.assertEqual([41, 42, 43], light_state.attributes.get('rgb_color')) - - # change the white value - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,134') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.hass.block_till_done() - self.assertEqual(134, light_state.attributes['white_value']) - - # change the effect - fire_mqtt_message(self.hass, 'test_light_rgb', - 'on,,,,41-42-43,rainbow') - self.hass.block_till_done() - - light_state = self.hass.states.get('light.test') - self.assertEqual('rainbow', light_state.attributes.get('effect')) - - def test_optimistic(self): \ - # pylint: disable=invalid-name - """Test optimistic mode.""" - with assert_setup_component(1, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,' - '{{ brightness|d }},' - '{{ color_temp|d }},' - '{{ white_value|d }},' - '{{ red|d }}-' - '{{ green|d }}-' - '{{ blue|d }}', - 'command_off_template': 'off', - 'qos': 2 - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertTrue(state.attributes.get(ATTR_ASSUMED_STATE)) - - # turn on the light - light.turn_on(self.hass, 'light.test') - self.hass.block_till_done() - - self.assertEqual(('test_light_rgb/set', 'on,,,,--', 2, False), - self.mock_publish.mock_calls[-2][1]) - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - - # turn the light off - light.turn_off(self.hass, 'light.test') - self.hass.block_till_done() - - self.assertEqual(('test_light_rgb/set', 'off', 2, False), - self.mock_publish.mock_calls[-2][1]) - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - - # turn on the light with brightness, color - light.turn_on(self.hass, 'light.test', brightness=50, - rgb_color=[75, 75, 75]) - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - - # check the payload - payload = self.mock_publish.mock_calls[-2][1][1] - self.assertEqual('on,50,,,75-75-75', payload) - - # turn on the light with color temp and white val - light.turn_on(self.hass, 'light.test', color_temp=200, white_value=139) - self.hass.block_till_done() - - payload = self.mock_publish.mock_calls[-2][1][1] - self.assertEqual('on,,200,139,--', payload) - - self.assertEqual(2, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - - # check the state - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual((75, 75, 75), state.attributes['rgb_color']) - self.assertEqual(50, state.attributes['brightness']) - self.assertEqual(200, state.attributes['color_temp']) - self.assertEqual(139, state.attributes['white_value']) - - def test_flash(self): \ - # pylint: disable=invalid-name - """Test flash.""" - with assert_setup_component(1, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,{{ flash }}', - 'command_off_template': 'off', - 'qos': 0 - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - - # short flash - light.turn_on(self.hass, 'light.test', flash='short') - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - - # check the payload - payload = self.mock_publish.mock_calls[-2][1][1] - self.assertEqual('on,short', payload) - - # long flash - light.turn_on(self.hass, 'light.test', flash='long') - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - - # check the payload - payload = self.mock_publish.mock_calls[-2][1][1] - self.assertEqual('on,long', payload) - - def test_transition(self): - """Test for transition time being sent when included.""" - with assert_setup_component(1, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,{{ transition }}', - 'command_off_template': 'off,{{ transition|d }}' - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - - # transition on - light.turn_on(self.hass, 'light.test', transition=10) - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - - # check the payload - payload = self.mock_publish.mock_calls[-2][1][1] - self.assertEqual('on,10', payload) - - # transition off - light.turn_off(self.hass, 'light.test', transition=4) - self.hass.block_till_done() - - self.assertEqual('test_light_rgb/set', - self.mock_publish.mock_calls[-2][1][0]) - self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) - self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) - - # check the payload - payload = self.mock_publish.mock_calls[-2][1][1] - self.assertEqual('off,4', payload) - - def test_invalid_values(self): \ - # pylint: disable=invalid-name - """Test that invalid values are ignored.""" - with assert_setup_component(1, light.DOMAIN): - assert setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'effect_list': ['rainbow', 'colorloop'], - 'state_topic': 'test_light_rgb', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,' - '{{ brightness|d }},' - '{{ color_temp|d }},' - '{{ red|d }}-' - '{{ green|d }}-' - '{{ blue|d }},' - '{{ effect|d }}', - 'command_off_template': 'off', - 'state_template': '{{ value.split(",")[0] }}', - 'brightness_template': '{{ value.split(",")[1] }}', - 'color_temp_template': '{{ value.split(",")[2] }}', - 'white_value_template': '{{ value.split(",")[3] }}', - 'red_template': '{{ value.split(",")[4].' - 'split("-")[0] }}', - 'green_template': '{{ value.split(",")[4].' - 'split("-")[1] }}', - 'blue_template': '{{ value.split(",")[4].' - 'split("-")[2] }}', - 'effect_template': '{{ value.split(",")[5] }}', - } - }) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_OFF, state.state) - self.assertIsNone(state.attributes.get('rgb_color')) - self.assertIsNone(state.attributes.get('brightness')) - self.assertIsNone(state.attributes.get('color_temp')) - self.assertIsNone(state.attributes.get('effect')) - self.assertIsNone(state.attributes.get('white_value')) - self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) - - # turn on the light, full white - fire_mqtt_message(self.hass, 'test_light_rgb', - 'on,255,215,222,255-255-255,rainbow') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - self.assertEqual(255, state.attributes.get('brightness')) - self.assertEqual(215, state.attributes.get('color_temp')) - self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) - self.assertEqual(222, state.attributes.get('white_value')) - self.assertEqual('rainbow', state.attributes.get('effect')) - - # bad state value - fire_mqtt_message(self.hass, 'test_light_rgb', 'offf') - self.hass.block_till_done() - - # state should not have changed - state = self.hass.states.get('light.test') - self.assertEqual(STATE_ON, state.state) - - # bad brightness values - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,off,255-255-255') - self.hass.block_till_done() - - # brightness should not have changed - state = self.hass.states.get('light.test') - self.assertEqual(255, state.attributes.get('brightness')) - - # bad color temp values - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,off,255-255-255') - self.hass.block_till_done() - - # color temp should not have changed - state = self.hass.states.get('light.test') - self.assertEqual(215, state.attributes.get('color_temp')) - - # bad color values - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,255,a-b-c') - self.hass.block_till_done() - - # color should not have changed - state = self.hass.states.get('light.test') - self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) - - # bad white value values - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,off,255-255-255') - self.hass.block_till_done() - - # white value should not have changed - state = self.hass.states.get('light.test') - self.assertEqual(222, state.attributes.get('white_value')) - - # bad effect value - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,255,a-b-c,white') - self.hass.block_till_done() - - # effect should not have changed - state = self.hass.states.get('light.test') - self.assertEqual('rainbow', state.attributes.get('effect')) - - def test_default_availability_payload(self): - """Test availability by default payload with defined topic.""" - self.assertTrue(setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,{{ transition }}', - 'command_off_template': 'off,{{ transition|d }}', - 'availability_topic': 'availability-topic' - } - })) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'online') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertNotEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'offline') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) - - def test_custom_availability_payload(self): - """Test availability by custom payload with defined topic.""" - self.assertTrue(setup_component(self.hass, light.DOMAIN, { - light.DOMAIN: { - 'platform': 'mqtt_template', - 'name': 'test', - 'command_topic': 'test_light_rgb/set', - 'command_on_template': 'on,{{ transition }}', - 'command_off_template': 'off,{{ transition|d }}', - 'availability_topic': 'availability-topic', - 'payload_available': 'good', - 'payload_not_available': 'nogood' - } - })) - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'good') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertNotEqual(STATE_UNAVAILABLE, state.state) - - fire_mqtt_message(self.hass, 'availability-topic', 'nogood') - self.hass.block_till_done() - - state = self.hass.states.get('light.test') - self.assertEqual(STATE_UNAVAILABLE, state.state) +"""The tests for the MQTT Template light platform. + +Configuration example with all features: + +light: + platform: mqtt_template + name: mqtt_template_light_1 + state_topic: 'home/rgb1' + command_topic: 'home/rgb1/set' + command_on_template: > + on,{{ brightness|d }},{{ red|d }}-{{ green|d }}-{{ blue|d }} + command_off_template: 'off' + state_template: '{{ value.split(",")[0] }}' + brightness_template: '{{ value.split(",")[1] }}' + color_temp_template: '{{ value.split(",")[2] }}' + white_value_template: '{{ value.split(",")[3] }}' + red_template: '{{ value.split(",")[4].split("-")[0] }}' + green_template: '{{ value.split(",")[4].split("-")[1] }}' + blue_template: '{{ value.split(",")[4].split("-")[2] }}' + +If your light doesn't support brightness feature, omit `brightness_template`. + +If your light doesn't support color temp feature, omit `color_temp_template`. + +If your light doesn't support white value feature, omit `white_value_template`. + +If your light doesn't support RGB feature, omit `(red|green|blue)_template`. +""" +import unittest + +from homeassistant.setup import setup_component +from homeassistant.const import ( + STATE_ON, STATE_OFF, STATE_UNAVAILABLE, ATTR_ASSUMED_STATE) +import homeassistant.components.light as light +from tests.common import ( + get_test_home_assistant, mock_mqtt_component, fire_mqtt_message, + assert_setup_component) + + +class TestLightMQTTTemplate(unittest.TestCase): + """Test the MQTT Template light.""" + + def setUp(self): # pylint: disable=invalid-name + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + self.mock_publish = mock_mqtt_component(self.hass) + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + + def test_setup_fails(self): \ + # pylint: disable=invalid-name + """Test that setup fails with missing required configuration items.""" + with assert_setup_component(0, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + } + }) + self.assertIsNone(self.hass.states.get('light.test')) + + def test_state_change_via_topic(self): \ + # pylint: disable=invalid-name + """Test state change via topic.""" + with assert_setup_component(1, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,' + '{{ brightness|d }},' + '{{ color_temp|d }},' + '{{ white_value|d }},' + '{{ red|d }}-' + '{{ green|d }}-' + '{{ blue|d }}', + 'command_off_template': 'off', + 'state_template': '{{ value.split(",")[0] }}' + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) + + fire_mqtt_message(self.hass, 'test_light_rgb', 'on') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('white_value')) + + def test_state_brightness_color_effect_temp_white_change_via_topic(self): \ + # pylint: disable=invalid-name + """Test state, bri, color, effect, color temp, white val change.""" + with assert_setup_component(1, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'effect_list': ['rainbow', 'colorloop'], + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,' + '{{ brightness|d }},' + '{{ color_temp|d }},' + '{{ white_value|d }},' + '{{ red|d }}-' + '{{ green|d }}-' + '{{ blue|d }},' + '{{ effect|d }}', + 'command_off_template': 'off', + 'state_template': '{{ value.split(",")[0] }}', + 'brightness_template': '{{ value.split(",")[1] }}', + 'color_temp_template': '{{ value.split(",")[2] }}', + 'white_value_template': '{{ value.split(",")[3] }}', + 'red_template': '{{ value.split(",")[4].' + 'split("-")[0] }}', + 'green_template': '{{ value.split(",")[4].' + 'split("-")[1] }}', + 'blue_template': '{{ value.split(",")[4].' + 'split("-")[2] }}', + 'effect_template': '{{ value.split(",")[5] }}' + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('effect')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) + + # turn on the light, full white + fire_mqtt_message(self.hass, 'test_light_rgb', + 'on,255,145,123,255-128-64,') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual([255, 128, 64], state.attributes.get('rgb_color')) + self.assertEqual(255, state.attributes.get('brightness')) + self.assertEqual(145, state.attributes.get('color_temp')) + self.assertEqual(123, state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('effect')) + + # turn the light off + fire_mqtt_message(self.hass, 'test_light_rgb', 'off') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + # lower the brightness + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,100') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(100, light_state.attributes['brightness']) + + # change the color temp + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,195') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(195, light_state.attributes['color_temp']) + + # change the color + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,,41-42-43') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual([41, 42, 43], light_state.attributes.get('rgb_color')) + + # change the white value + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,134') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(134, light_state.attributes['white_value']) + + # change the effect + fire_mqtt_message(self.hass, 'test_light_rgb', + 'on,,,,41-42-43,rainbow') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual('rainbow', light_state.attributes.get('effect')) + + def test_optimistic(self): \ + # pylint: disable=invalid-name + """Test optimistic mode.""" + with assert_setup_component(1, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,' + '{{ brightness|d }},' + '{{ color_temp|d }},' + '{{ white_value|d }},' + '{{ red|d }}-' + '{{ green|d }}-' + '{{ blue|d }}', + 'command_off_template': 'off', + 'qos': 2 + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertTrue(state.attributes.get(ATTR_ASSUMED_STATE)) + + # turn on the light + light.turn_on(self.hass, 'light.test') + self.hass.block_till_done() + + self.assertEqual(('test_light_rgb/set', 'on,,,,--', 2, False), + self.mock_publish.mock_calls[-2][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + + # turn the light off + light.turn_off(self.hass, 'light.test') + self.hass.block_till_done() + + self.assertEqual(('test_light_rgb/set', 'off', 2, False), + self.mock_publish.mock_calls[-2][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + # turn on the light with brightness, color + light.turn_on(self.hass, 'light.test', brightness=50, + rgb_color=[75, 75, 75]) + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + + # check the payload + payload = self.mock_publish.mock_calls[-2][1][1] + self.assertEqual('on,50,,,75-75-75', payload) + + # turn on the light with color temp and white val + light.turn_on(self.hass, 'light.test', color_temp=200, white_value=139) + self.hass.block_till_done() + + payload = self.mock_publish.mock_calls[-2][1][1] + self.assertEqual('on,,200,139,--', payload) + + self.assertEqual(2, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + + # check the state + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual((75, 75, 75), state.attributes['rgb_color']) + self.assertEqual(50, state.attributes['brightness']) + self.assertEqual(200, state.attributes['color_temp']) + self.assertEqual(139, state.attributes['white_value']) + + def test_flash(self): \ + # pylint: disable=invalid-name + """Test flash.""" + with assert_setup_component(1, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,{{ flash }}', + 'command_off_template': 'off', + 'qos': 0 + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + # short flash + light.turn_on(self.hass, 'light.test', flash='short') + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + + # check the payload + payload = self.mock_publish.mock_calls[-2][1][1] + self.assertEqual('on,short', payload) + + # long flash + light.turn_on(self.hass, 'light.test', flash='long') + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + + # check the payload + payload = self.mock_publish.mock_calls[-2][1][1] + self.assertEqual('on,long', payload) + + def test_transition(self): + """Test for transition time being sent when included.""" + with assert_setup_component(1, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,{{ transition }}', + 'command_off_template': 'off,{{ transition|d }}' + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + # transition on + light.turn_on(self.hass, 'light.test', transition=10) + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + + # check the payload + payload = self.mock_publish.mock_calls[-2][1][1] + self.assertEqual('on,10', payload) + + # transition off + light.turn_off(self.hass, 'light.test', transition=4) + self.hass.block_till_done() + + self.assertEqual('test_light_rgb/set', + self.mock_publish.mock_calls[-2][1][0]) + self.assertEqual(0, self.mock_publish.mock_calls[-2][1][2]) + self.assertEqual(False, self.mock_publish.mock_calls[-2][1][3]) + + # check the payload + payload = self.mock_publish.mock_calls[-2][1][1] + self.assertEqual('off,4', payload) + + def test_invalid_values(self): \ + # pylint: disable=invalid-name + """Test that invalid values are ignored.""" + with assert_setup_component(1, light.DOMAIN): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'effect_list': ['rainbow', 'colorloop'], + 'state_topic': 'test_light_rgb', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,' + '{{ brightness|d }},' + '{{ color_temp|d }},' + '{{ red|d }}-' + '{{ green|d }}-' + '{{ blue|d }},' + '{{ effect|d }}', + 'command_off_template': 'off', + 'state_template': '{{ value.split(",")[0] }}', + 'brightness_template': '{{ value.split(",")[1] }}', + 'color_temp_template': '{{ value.split(",")[2] }}', + 'white_value_template': '{{ value.split(",")[3] }}', + 'red_template': '{{ value.split(",")[4].' + 'split("-")[0] }}', + 'green_template': '{{ value.split(",")[4].' + 'split("-")[1] }}', + 'blue_template': '{{ value.split(",")[4].' + 'split("-")[2] }}', + 'effect_template': '{{ value.split(",")[5] }}', + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('rgb_color')) + self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) + + # turn on the light, full white + fire_mqtt_message(self.hass, 'test_light_rgb', + 'on,255,215,222,255-255-255,rainbow') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(255, state.attributes.get('brightness')) + self.assertEqual(215, state.attributes.get('color_temp')) + self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) + self.assertEqual(222, state.attributes.get('white_value')) + self.assertEqual('rainbow', state.attributes.get('effect')) + + # bad state value + fire_mqtt_message(self.hass, 'test_light_rgb', 'offf') + self.hass.block_till_done() + + # state should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + + # bad brightness values + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,off,255-255-255') + self.hass.block_till_done() + + # brightness should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(255, state.attributes.get('brightness')) + + # bad color temp values + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,off,255-255-255') + self.hass.block_till_done() + + # color temp should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(215, state.attributes.get('color_temp')) + + # bad color values + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,255,a-b-c') + self.hass.block_till_done() + + # color should not have changed + state = self.hass.states.get('light.test') + self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) + + # bad white value values + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,off,255-255-255') + self.hass.block_till_done() + + # white value should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(222, state.attributes.get('white_value')) + + # bad effect value + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,255,a-b-c,white') + self.hass.block_till_done() + + # effect should not have changed + state = self.hass.states.get('light.test') + self.assertEqual('rainbow', state.attributes.get('effect')) + + def test_default_availability_payload(self): + """Test availability by default payload with defined topic.""" + self.assertTrue(setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,{{ transition }}', + 'command_off_template': 'off,{{ transition|d }}', + 'availability_topic': 'availability-topic' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'online') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertNotEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'offline') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) + + def test_custom_availability_payload(self): + """Test availability by custom payload with defined topic.""" + self.assertTrue(setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt_template', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'command_on_template': 'on,{{ transition }}', + 'command_off_template': 'off,{{ transition|d }}', + 'availability_topic': 'availability-topic', + 'payload_available': 'good', + 'payload_not_available': 'nogood' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'good') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertNotEqual(STATE_UNAVAILABLE, state.state) + + fire_mqtt_message(self.hass, 'availability-topic', 'nogood') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_UNAVAILABLE, state.state) diff --git a/tests/components/sensor/test_hddtemp.py b/tests/components/sensor/test_hddtemp.py index 3be35f3281c..1b65af7fd7e 100644 --- a/tests/components/sensor/test_hddtemp.py +++ b/tests/components/sensor/test_hddtemp.py @@ -1,216 +1,216 @@ -"""The tests for the hddtemp platform.""" -import socket - -import unittest -from unittest.mock import patch - -from homeassistant.setup import setup_component - -from tests.common import get_test_home_assistant - -VALID_CONFIG_MINIMAL = { - 'sensor': { - 'platform': 'hddtemp', - } -} - -VALID_CONFIG_NAME = { - 'sensor': { - 'platform': 'hddtemp', - 'name': 'FooBar', - } -} - -VALID_CONFIG_ONE_DISK = { - 'sensor': { - 'platform': 'hddtemp', - 'disks': [ - '/dev/sdd1', - ], - } -} - -VALID_CONFIG_WRONG_DISK = { - 'sensor': { - 'platform': 'hddtemp', - 'disks': [ - '/dev/sdx1', - ], - } -} - -VALID_CONFIG_MULTIPLE_DISKS = { - 'sensor': { - 'platform': 'hddtemp', - 'host': 'foobar.local', - 'disks': [ - '/dev/sda1', - '/dev/sdb1', - '/dev/sdc1', - ], - } -} - -VALID_CONFIG_HOST = { - 'sensor': { - 'platform': 'hddtemp', - 'host': 'alice.local', - } -} - -VALID_CONFIG_HOST_UNREACHABLE = { - 'sensor': { - 'platform': 'hddtemp', - 'host': 'bob.local', - } -} - - -class TelnetMock(): - """Mock class for the telnetlib.Telnet object.""" - - def __init__(self, host, port, timeout=0): - """Initialize Telnet object.""" - self.host = host - self.port = port - self.timeout = timeout - self.sample_data = bytes('|/dev/sda1|WDC WD30EZRX-12DC0B0|29|C|' + - '|/dev/sdb1|WDC WD15EADS-11P7B2|32|C|' + - '|/dev/sdc1|WDC WD20EARX-22MMMB0|29|C|' + - '|/dev/sdd1|WDC WD15EARS-00Z5B1|89|F|', - 'ascii') - - def read_all(self): - """Return sample values.""" - if self.host == 'alice.local': - raise ConnectionRefusedError - elif self.host == 'bob.local': - raise socket.gaierror - else: - return self.sample_data - return None - - -class TestHDDTempSensor(unittest.TestCase): - """Test the hddtemp sensor.""" - - def setUp(self): - """Set up things to run when tests begin.""" - self.hass = get_test_home_assistant() - self.config = VALID_CONFIG_ONE_DISK - self.reference = {'/dev/sda1': {'device': '/dev/sda1', - 'temperature': '29', - 'unit_of_measurement': '°C', - 'model': 'WDC WD30EZRX-12DC0B0', }, - '/dev/sdb1': {'device': '/dev/sdb1', - 'temperature': '32', - 'unit_of_measurement': '°C', - 'model': 'WDC WD15EADS-11P7B2', }, - '/dev/sdc1': {'device': '/dev/sdc1', - 'temperature': '29', - 'unit_of_measurement': '°C', - 'model': 'WDC WD20EARX-22MMMB0', }, - '/dev/sdd1': {'device': '/dev/sdd1', - 'temperature': '32', - 'unit_of_measurement': '°C', - 'model': 'WDC WD15EARS-00Z5B1', }, } - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - @patch('telnetlib.Telnet', new=TelnetMock) - def test_hddtemp_min_config(self): - """Test minimal hddtemp configuration.""" - assert setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL) - - entity = self.hass.states.all()[0].entity_id - state = self.hass.states.get(entity) - - reference = self.reference[state.attributes.get('device')] - - self.assertEqual(state.state, reference['temperature']) - self.assertEqual(state.attributes.get('device'), reference['device']) - self.assertEqual(state.attributes.get('model'), reference['model']) - self.assertEqual(state.attributes.get('unit_of_measurement'), - reference['unit_of_measurement']) - self.assertEqual(state.attributes.get('friendly_name'), - 'HD Temperature ' + reference['device']) - - @patch('telnetlib.Telnet', new=TelnetMock) - def test_hddtemp_rename_config(self): - """Test hddtemp configuration with different name.""" - assert setup_component(self.hass, 'sensor', VALID_CONFIG_NAME) - - entity = self.hass.states.all()[0].entity_id - state = self.hass.states.get(entity) - - reference = self.reference[state.attributes.get('device')] - - self.assertEqual(state.attributes.get('friendly_name'), - 'FooBar ' + reference['device']) - - @patch('telnetlib.Telnet', new=TelnetMock) - def test_hddtemp_one_disk(self): - """Test hddtemp one disk configuration.""" - assert setup_component(self.hass, 'sensor', VALID_CONFIG_ONE_DISK) - - state = self.hass.states.get('sensor.hd_temperature_devsdd1') - - reference = self.reference[state.attributes.get('device')] - - self.assertEqual(state.state, reference['temperature']) - self.assertEqual(state.attributes.get('device'), reference['device']) - self.assertEqual(state.attributes.get('model'), reference['model']) - self.assertEqual(state.attributes.get('unit_of_measurement'), - reference['unit_of_measurement']) - self.assertEqual(state.attributes.get('friendly_name'), - 'HD Temperature ' + reference['device']) - - @patch('telnetlib.Telnet', new=TelnetMock) - def test_hddtemp_wrong_disk(self): - """Test hddtemp wrong disk configuration.""" - assert setup_component(self.hass, 'sensor', VALID_CONFIG_WRONG_DISK) - - self.assertEqual(len(self.hass.states.all()), 1) - state = self.hass.states.get('sensor.hd_temperature_devsdx1') - self.assertEqual(state.attributes.get('friendly_name'), - 'HD Temperature ' + '/dev/sdx1') - - @patch('telnetlib.Telnet', new=TelnetMock) - def test_hddtemp_multiple_disks(self): - """Test hddtemp multiple disk configuration.""" - assert setup_component(self.hass, - 'sensor', VALID_CONFIG_MULTIPLE_DISKS) - - for sensor in ['sensor.hd_temperature_devsda1', - 'sensor.hd_temperature_devsdb1', - 'sensor.hd_temperature_devsdc1']: - - state = self.hass.states.get(sensor) - - reference = self.reference[state.attributes.get('device')] - - self.assertEqual(state.state, - reference['temperature']) - self.assertEqual(state.attributes.get('device'), - reference['device']) - self.assertEqual(state.attributes.get('model'), - reference['model']) - self.assertEqual(state.attributes.get('unit_of_measurement'), - reference['unit_of_measurement']) - self.assertEqual(state.attributes.get('friendly_name'), - 'HD Temperature ' + reference['device']) - - @patch('telnetlib.Telnet', new=TelnetMock) - def test_hddtemp_host_refused(self): - """Test hddtemp if host unreachable.""" - assert setup_component(self.hass, 'sensor', VALID_CONFIG_HOST) - self.assertEqual(len(self.hass.states.all()), 0) - - @patch('telnetlib.Telnet', new=TelnetMock) - def test_hddtemp_host_unreachable(self): - """Test hddtemp if host unreachable.""" - assert setup_component(self.hass, 'sensor', - VALID_CONFIG_HOST_UNREACHABLE) - self.assertEqual(len(self.hass.states.all()), 0) +"""The tests for the hddtemp platform.""" +import socket + +import unittest +from unittest.mock import patch + +from homeassistant.setup import setup_component + +from tests.common import get_test_home_assistant + +VALID_CONFIG_MINIMAL = { + 'sensor': { + 'platform': 'hddtemp', + } +} + +VALID_CONFIG_NAME = { + 'sensor': { + 'platform': 'hddtemp', + 'name': 'FooBar', + } +} + +VALID_CONFIG_ONE_DISK = { + 'sensor': { + 'platform': 'hddtemp', + 'disks': [ + '/dev/sdd1', + ], + } +} + +VALID_CONFIG_WRONG_DISK = { + 'sensor': { + 'platform': 'hddtemp', + 'disks': [ + '/dev/sdx1', + ], + } +} + +VALID_CONFIG_MULTIPLE_DISKS = { + 'sensor': { + 'platform': 'hddtemp', + 'host': 'foobar.local', + 'disks': [ + '/dev/sda1', + '/dev/sdb1', + '/dev/sdc1', + ], + } +} + +VALID_CONFIG_HOST = { + 'sensor': { + 'platform': 'hddtemp', + 'host': 'alice.local', + } +} + +VALID_CONFIG_HOST_UNREACHABLE = { + 'sensor': { + 'platform': 'hddtemp', + 'host': 'bob.local', + } +} + + +class TelnetMock(): + """Mock class for the telnetlib.Telnet object.""" + + def __init__(self, host, port, timeout=0): + """Initialize Telnet object.""" + self.host = host + self.port = port + self.timeout = timeout + self.sample_data = bytes('|/dev/sda1|WDC WD30EZRX-12DC0B0|29|C|' + + '|/dev/sdb1|WDC WD15EADS-11P7B2|32|C|' + + '|/dev/sdc1|WDC WD20EARX-22MMMB0|29|C|' + + '|/dev/sdd1|WDC WD15EARS-00Z5B1|89|F|', + 'ascii') + + def read_all(self): + """Return sample values.""" + if self.host == 'alice.local': + raise ConnectionRefusedError + elif self.host == 'bob.local': + raise socket.gaierror + else: + return self.sample_data + return None + + +class TestHDDTempSensor(unittest.TestCase): + """Test the hddtemp sensor.""" + + def setUp(self): + """Set up things to run when tests begin.""" + self.hass = get_test_home_assistant() + self.config = VALID_CONFIG_ONE_DISK + self.reference = {'/dev/sda1': {'device': '/dev/sda1', + 'temperature': '29', + 'unit_of_measurement': '°C', + 'model': 'WDC WD30EZRX-12DC0B0', }, + '/dev/sdb1': {'device': '/dev/sdb1', + 'temperature': '32', + 'unit_of_measurement': '°C', + 'model': 'WDC WD15EADS-11P7B2', }, + '/dev/sdc1': {'device': '/dev/sdc1', + 'temperature': '29', + 'unit_of_measurement': '°C', + 'model': 'WDC WD20EARX-22MMMB0', }, + '/dev/sdd1': {'device': '/dev/sdd1', + 'temperature': '32', + 'unit_of_measurement': '°C', + 'model': 'WDC WD15EARS-00Z5B1', }, } + + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + + @patch('telnetlib.Telnet', new=TelnetMock) + def test_hddtemp_min_config(self): + """Test minimal hddtemp configuration.""" + assert setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL) + + entity = self.hass.states.all()[0].entity_id + state = self.hass.states.get(entity) + + reference = self.reference[state.attributes.get('device')] + + self.assertEqual(state.state, reference['temperature']) + self.assertEqual(state.attributes.get('device'), reference['device']) + self.assertEqual(state.attributes.get('model'), reference['model']) + self.assertEqual(state.attributes.get('unit_of_measurement'), + reference['unit_of_measurement']) + self.assertEqual(state.attributes.get('friendly_name'), + 'HD Temperature ' + reference['device']) + + @patch('telnetlib.Telnet', new=TelnetMock) + def test_hddtemp_rename_config(self): + """Test hddtemp configuration with different name.""" + assert setup_component(self.hass, 'sensor', VALID_CONFIG_NAME) + + entity = self.hass.states.all()[0].entity_id + state = self.hass.states.get(entity) + + reference = self.reference[state.attributes.get('device')] + + self.assertEqual(state.attributes.get('friendly_name'), + 'FooBar ' + reference['device']) + + @patch('telnetlib.Telnet', new=TelnetMock) + def test_hddtemp_one_disk(self): + """Test hddtemp one disk configuration.""" + assert setup_component(self.hass, 'sensor', VALID_CONFIG_ONE_DISK) + + state = self.hass.states.get('sensor.hd_temperature_devsdd1') + + reference = self.reference[state.attributes.get('device')] + + self.assertEqual(state.state, reference['temperature']) + self.assertEqual(state.attributes.get('device'), reference['device']) + self.assertEqual(state.attributes.get('model'), reference['model']) + self.assertEqual(state.attributes.get('unit_of_measurement'), + reference['unit_of_measurement']) + self.assertEqual(state.attributes.get('friendly_name'), + 'HD Temperature ' + reference['device']) + + @patch('telnetlib.Telnet', new=TelnetMock) + def test_hddtemp_wrong_disk(self): + """Test hddtemp wrong disk configuration.""" + assert setup_component(self.hass, 'sensor', VALID_CONFIG_WRONG_DISK) + + self.assertEqual(len(self.hass.states.all()), 1) + state = self.hass.states.get('sensor.hd_temperature_devsdx1') + self.assertEqual(state.attributes.get('friendly_name'), + 'HD Temperature ' + '/dev/sdx1') + + @patch('telnetlib.Telnet', new=TelnetMock) + def test_hddtemp_multiple_disks(self): + """Test hddtemp multiple disk configuration.""" + assert setup_component(self.hass, + 'sensor', VALID_CONFIG_MULTIPLE_DISKS) + + for sensor in ['sensor.hd_temperature_devsda1', + 'sensor.hd_temperature_devsdb1', + 'sensor.hd_temperature_devsdc1']: + + state = self.hass.states.get(sensor) + + reference = self.reference[state.attributes.get('device')] + + self.assertEqual(state.state, + reference['temperature']) + self.assertEqual(state.attributes.get('device'), + reference['device']) + self.assertEqual(state.attributes.get('model'), + reference['model']) + self.assertEqual(state.attributes.get('unit_of_measurement'), + reference['unit_of_measurement']) + self.assertEqual(state.attributes.get('friendly_name'), + 'HD Temperature ' + reference['device']) + + @patch('telnetlib.Telnet', new=TelnetMock) + def test_hddtemp_host_refused(self): + """Test hddtemp if host unreachable.""" + assert setup_component(self.hass, 'sensor', VALID_CONFIG_HOST) + self.assertEqual(len(self.hass.states.all()), 0) + + @patch('telnetlib.Telnet', new=TelnetMock) + def test_hddtemp_host_unreachable(self): + """Test hddtemp if host unreachable.""" + assert setup_component(self.hass, 'sensor', + VALID_CONFIG_HOST_UNREACHABLE) + self.assertEqual(len(self.hass.states.all()), 0) diff --git a/tests/components/switch/test_wake_on_lan.py b/tests/components/switch/test_wake_on_lan.py index d7945218e73..167c3bb35ac 100644 --- a/tests/components/switch/test_wake_on_lan.py +++ b/tests/components/switch/test_wake_on_lan.py @@ -1,192 +1,192 @@ -"""The tests for the wake on lan switch platform.""" -import unittest -from unittest.mock import patch - -from homeassistant.setup import setup_component -from homeassistant.const import STATE_ON, STATE_OFF -import homeassistant.components.switch as switch - -from tests.common import get_test_home_assistant, mock_service - - -TEST_STATE = None - - -def send_magic_packet(*macs, **kwargs): - """Fake call for sending magic packets.""" - return - - -def call(cmd, stdout, stderr): - """Return fake subprocess return codes.""" - if cmd[5] == 'validhostname' and TEST_STATE: - return 0 - return 2 - - -def system(): - """Fake system call to test the windows platform.""" - return 'Windows' - - -class TestWOLSwitch(unittest.TestCase): - """Test the wol switch.""" - - def setUp(self): - """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant() - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - @patch('wakeonlan.send_magic_packet', new=send_magic_packet) - @patch('subprocess.call', new=call) - def test_valid_hostname(self): - """Test with valid hostname.""" - global TEST_STATE - TEST_STATE = False - self.assertTrue(setup_component(self.hass, switch.DOMAIN, { - 'switch': { - 'platform': 'wake_on_lan', - 'mac_address': '00-01-02-03-04-05', - 'host': 'validhostname', - } - })) - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_OFF, state.state) - - TEST_STATE = True - - switch.turn_on(self.hass, 'switch.wake_on_lan') - self.hass.block_till_done() - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_ON, state.state) - - switch.turn_off(self.hass, 'switch.wake_on_lan') - self.hass.block_till_done() - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_ON, state.state) - - @patch('wakeonlan.send_magic_packet', new=send_magic_packet) - @patch('subprocess.call', new=call) - @patch('platform.system', new=system) - def test_valid_hostname_windows(self): - """Test with valid hostname on windows.""" - global TEST_STATE - TEST_STATE = False - self.assertTrue(setup_component(self.hass, switch.DOMAIN, { - 'switch': { - 'platform': 'wake_on_lan', - 'mac_address': '00-01-02-03-04-05', - 'host': 'validhostname', - } - })) - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_OFF, state.state) - - TEST_STATE = True - - switch.turn_on(self.hass, 'switch.wake_on_lan') - self.hass.block_till_done() - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_ON, state.state) - - @patch('wakeonlan.send_magic_packet', new=send_magic_packet) - @patch('subprocess.call', new=call) - def test_minimal_config(self): - """Test with minimal config.""" - self.assertTrue(setup_component(self.hass, switch.DOMAIN, { - 'switch': { - 'platform': 'wake_on_lan', - 'mac_address': '00-01-02-03-04-05', - } - })) - - @patch('wakeonlan.send_magic_packet', new=send_magic_packet) - @patch('subprocess.call', new=call) - def test_broadcast_config(self): - """Test with broadcast address config.""" - self.assertTrue(setup_component(self.hass, switch.DOMAIN, { - 'switch': { - 'platform': 'wake_on_lan', - 'mac_address': '00-01-02-03-04-05', - 'broadcast_address': '255.255.255.255', - } - })) - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_OFF, state.state) - - switch.turn_on(self.hass, 'switch.wake_on_lan') - self.hass.block_till_done() - - @patch('wakeonlan.send_magic_packet', new=send_magic_packet) - @patch('subprocess.call', new=call) - def test_off_script(self): - """Test with turn off script.""" - global TEST_STATE - TEST_STATE = False - self.assertTrue(setup_component(self.hass, switch.DOMAIN, { - 'switch': { - 'platform': 'wake_on_lan', - 'mac_address': '00-01-02-03-04-05', - 'host': 'validhostname', - 'turn_off': { - 'service': 'shell_command.turn_off_TARGET', - }, - } - })) - calls = mock_service(self.hass, 'shell_command', 'turn_off_TARGET') - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_OFF, state.state) - - TEST_STATE = True - - switch.turn_on(self.hass, 'switch.wake_on_lan') - self.hass.block_till_done() - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_ON, state.state) - assert len(calls) == 0 - - TEST_STATE = False - - switch.turn_off(self.hass, 'switch.wake_on_lan') - self.hass.block_till_done() - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_OFF, state.state) - assert len(calls) == 1 - - @patch('wakeonlan.send_magic_packet', new=send_magic_packet) - @patch('subprocess.call', new=call) - @patch('platform.system', new=system) - def test_invalid_hostname_windows(self): - """Test with invalid hostname on windows.""" - global TEST_STATE - TEST_STATE = False - self.assertTrue(setup_component(self.hass, switch.DOMAIN, { - 'switch': { - 'platform': 'wake_on_lan', - 'mac_address': '00-01-02-03-04-05', - 'host': 'invalidhostname', - } - })) - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_OFF, state.state) - - TEST_STATE = True - - switch.turn_on(self.hass, 'switch.wake_on_lan') - self.hass.block_till_done() - - state = self.hass.states.get('switch.wake_on_lan') - self.assertEqual(STATE_OFF, state.state) +"""The tests for the wake on lan switch platform.""" +import unittest +from unittest.mock import patch + +from homeassistant.setup import setup_component +from homeassistant.const import STATE_ON, STATE_OFF +import homeassistant.components.switch as switch + +from tests.common import get_test_home_assistant, mock_service + + +TEST_STATE = None + + +def send_magic_packet(*macs, **kwargs): + """Fake call for sending magic packets.""" + return + + +def call(cmd, stdout, stderr): + """Return fake subprocess return codes.""" + if cmd[5] == 'validhostname' and TEST_STATE: + return 0 + return 2 + + +def system(): + """Fake system call to test the windows platform.""" + return 'Windows' + + +class TestWOLSwitch(unittest.TestCase): + """Test the wol switch.""" + + def setUp(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + + @patch('wakeonlan.send_magic_packet', new=send_magic_packet) + @patch('subprocess.call', new=call) + def test_valid_hostname(self): + """Test with valid hostname.""" + global TEST_STATE + TEST_STATE = False + self.assertTrue(setup_component(self.hass, switch.DOMAIN, { + 'switch': { + 'platform': 'wake_on_lan', + 'mac_address': '00-01-02-03-04-05', + 'host': 'validhostname', + } + })) + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_OFF, state.state) + + TEST_STATE = True + + switch.turn_on(self.hass, 'switch.wake_on_lan') + self.hass.block_till_done() + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_ON, state.state) + + switch.turn_off(self.hass, 'switch.wake_on_lan') + self.hass.block_till_done() + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_ON, state.state) + + @patch('wakeonlan.send_magic_packet', new=send_magic_packet) + @patch('subprocess.call', new=call) + @patch('platform.system', new=system) + def test_valid_hostname_windows(self): + """Test with valid hostname on windows.""" + global TEST_STATE + TEST_STATE = False + self.assertTrue(setup_component(self.hass, switch.DOMAIN, { + 'switch': { + 'platform': 'wake_on_lan', + 'mac_address': '00-01-02-03-04-05', + 'host': 'validhostname', + } + })) + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_OFF, state.state) + + TEST_STATE = True + + switch.turn_on(self.hass, 'switch.wake_on_lan') + self.hass.block_till_done() + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_ON, state.state) + + @patch('wakeonlan.send_magic_packet', new=send_magic_packet) + @patch('subprocess.call', new=call) + def test_minimal_config(self): + """Test with minimal config.""" + self.assertTrue(setup_component(self.hass, switch.DOMAIN, { + 'switch': { + 'platform': 'wake_on_lan', + 'mac_address': '00-01-02-03-04-05', + } + })) + + @patch('wakeonlan.send_magic_packet', new=send_magic_packet) + @patch('subprocess.call', new=call) + def test_broadcast_config(self): + """Test with broadcast address config.""" + self.assertTrue(setup_component(self.hass, switch.DOMAIN, { + 'switch': { + 'platform': 'wake_on_lan', + 'mac_address': '00-01-02-03-04-05', + 'broadcast_address': '255.255.255.255', + } + })) + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_OFF, state.state) + + switch.turn_on(self.hass, 'switch.wake_on_lan') + self.hass.block_till_done() + + @patch('wakeonlan.send_magic_packet', new=send_magic_packet) + @patch('subprocess.call', new=call) + def test_off_script(self): + """Test with turn off script.""" + global TEST_STATE + TEST_STATE = False + self.assertTrue(setup_component(self.hass, switch.DOMAIN, { + 'switch': { + 'platform': 'wake_on_lan', + 'mac_address': '00-01-02-03-04-05', + 'host': 'validhostname', + 'turn_off': { + 'service': 'shell_command.turn_off_TARGET', + }, + } + })) + calls = mock_service(self.hass, 'shell_command', 'turn_off_TARGET') + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_OFF, state.state) + + TEST_STATE = True + + switch.turn_on(self.hass, 'switch.wake_on_lan') + self.hass.block_till_done() + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_ON, state.state) + assert len(calls) == 0 + + TEST_STATE = False + + switch.turn_off(self.hass, 'switch.wake_on_lan') + self.hass.block_till_done() + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_OFF, state.state) + assert len(calls) == 1 + + @patch('wakeonlan.send_magic_packet', new=send_magic_packet) + @patch('subprocess.call', new=call) + @patch('platform.system', new=system) + def test_invalid_hostname_windows(self): + """Test with invalid hostname on windows.""" + global TEST_STATE + TEST_STATE = False + self.assertTrue(setup_component(self.hass, switch.DOMAIN, { + 'switch': { + 'platform': 'wake_on_lan', + 'mac_address': '00-01-02-03-04-05', + 'host': 'invalidhostname', + } + })) + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_OFF, state.state) + + TEST_STATE = True + + switch.turn_on(self.hass, 'switch.wake_on_lan') + self.hass.block_till_done() + + state = self.hass.states.get('switch.wake_on_lan') + self.assertEqual(STATE_OFF, state.state)