From 57dfe378a1ed6d45026cb189a36798c1ab22c5bb Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 3 Aug 2017 04:51:01 -0400 Subject: [PATCH] Add mochad light component (#8476) * Add mochad light component This commit adds a new component to control x10 dimmers/lights with mochad. * Create comm_type and address constants The comm_type and address conf constants are shared between all mochad devices because they are required information used for configuring a device. This commit moves the definition into const.py so they're consistent between all component types. --- homeassistant/components/light/mochad.py | 99 +++++++++++++++++++++++ homeassistant/components/mochad.py | 2 + homeassistant/components/switch/mochad.py | 9 +-- homeassistant/const.py | 1 + tests/components/light/test_mochad.py | 88 ++++++++++++++++++++ 5 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/light/mochad.py create mode 100644 tests/components/light/test_mochad.py diff --git a/homeassistant/components/light/mochad.py b/homeassistant/components/light/mochad.py new file mode 100644 index 00000000000..fffaa293188 --- /dev/null +++ b/homeassistant/components/light/mochad.py @@ -0,0 +1,99 @@ +""" +Contains functionality to use a X10 dimmer over Mochad. + +For more details about this platform, please refer to the documentation at +https://home.assistant.io/components/light.mochad/ +""" + +import logging + +import voluptuous as vol + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, PLATFORM_SCHEMA) +from homeassistant.components import mochad +from homeassistant.const import (CONF_NAME, CONF_PLATFORM, CONF_DEVICES, + CONF_ADDRESS) +from homeassistant.helpers import config_validation as cv + +DEPENDENCIES = ['mochad'] +_LOGGER = logging.getLogger(__name__) + + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_PLATFORM): mochad.DOMAIN, + CONF_DEVICES: [{ + vol.Optional(CONF_NAME): cv.string, + vol.Required(CONF_ADDRESS): cv.x10_address, + vol.Optional(mochad.CONF_COMM_TYPE): cv.string, + }] +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up X10 dimmers over a mochad controller.""" + devs = config.get(CONF_DEVICES) + add_devices([MochadLight( + hass, mochad.CONTROLLER.ctrl, dev) for dev in devs]) + return True + + +class MochadLight(Light): + """Representation of a X10 dimmer over Mochad.""" + + def __init__(self, hass, ctrl, dev): + """Initialize a Mochad Light Device.""" + from pymochad import device + + self._controller = ctrl + self._address = dev[CONF_ADDRESS] + self._name = dev.get(CONF_NAME, + 'x10_light_dev_{}'.format(self._address)) + self._comm_type = dev.get(mochad.CONF_COMM_TYPE, 'pl') + self.device = device.Device(ctrl, self._address, + comm_type=self._comm_type) + self._brightness = 0 + self._state = self._get_device_status() + + @property + def brightness(self): + """Return the birghtness of this light between 0..255.""" + return self._brightness + + def _get_device_status(self): + """Get the status of the light from mochad.""" + status = self.device.get_status().rstrip() + return status == 'on' + + @property + def name(self): + """Return the display name of this light.""" + return self._name + + @property + def is_on(self): + """Return true if the light is on.""" + return self._state + + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_BRIGHTNESS + + @property + def assumed_state(self): + """X10 devices are normally 1-way so we have to assume the state.""" + return True + + def turn_on(self, **kwargs): + """Send the command to turn the light on.""" + self._brightness = kwargs.get(ATTR_BRIGHTNESS, 255) + self.device.send_cmd("xdim {}".format(self._brightness)) + self._controller.read_data() + self._state = True + + def turn_off(self, **kwargs): + """Send the command to turn the light on.""" + self.device.send_cmd('off') + self._controller.read_data() + self._state = False diff --git a/homeassistant/components/mochad.py b/homeassistant/components/mochad.py index a42d142112d..165c43f488f 100644 --- a/homeassistant/components/mochad.py +++ b/homeassistant/components/mochad.py @@ -19,6 +19,8 @@ _LOGGER = logging.getLogger(__name__) CONTROLLER = None +CONF_COMM_TYPE = 'comm_type' + DOMAIN = 'mochad' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/switch/mochad.py b/homeassistant/components/switch/mochad.py index 21df1898fd2..a67b27a6a91 100644 --- a/homeassistant/components/switch/mochad.py +++ b/homeassistant/components/switch/mochad.py @@ -11,21 +11,20 @@ import voluptuous as vol from homeassistant.components import mochad from homeassistant.components.switch import SwitchDevice -from homeassistant.const import (CONF_NAME, CONF_PLATFORM) +from homeassistant.const import (CONF_NAME, CONF_DEVICES, + CONF_PLATFORM, CONF_ADDRESS) from homeassistant.helpers import config_validation as cv DEPENDENCIES = ['mochad'] _LOGGER = logging.getLogger(__name__) -CONF_ADDRESS = 'address' -CONF_DEVICES = 'devices' PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): mochad.DOMAIN, CONF_DEVICES: [{ vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_ADDRESS): cv.x10_address, - vol.Optional('comm_type'): cv.string, + vol.Optional(mochad.CONF_COMM_TYPE): cv.string, }] }) @@ -48,7 +47,7 @@ class MochadSwitch(SwitchDevice): self._controller = ctrl self._address = dev[CONF_ADDRESS] self._name = dev.get(CONF_NAME, 'x10_switch_dev_%s' % self._address) - self._comm_type = dev.get('comm_type', 'pl') + self._comm_type = dev.get(mochad.CONF_COMM_TYPE, 'pl') self.device = device.Device(ctrl, self._address, comm_type=self._comm_type) self._state = self._get_device_status() diff --git a/homeassistant/const.py b/homeassistant/const.py index c5e2993f510..fadd85e6b72 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -56,6 +56,7 @@ SUN_EVENT_SUNRISE = 'sunrise' # #### CONFIG #### CONF_ABOVE = 'above' CONF_ACCESS_TOKEN = 'access_token' +CONF_ADDRESS = 'address' CONF_AFTER = 'after' CONF_ALIAS = 'alias' CONF_API_KEY = 'api_key' diff --git a/tests/components/light/test_mochad.py b/tests/components/light/test_mochad.py new file mode 100644 index 00000000000..b1644effd57 --- /dev/null +++ b/tests/components/light/test_mochad.py @@ -0,0 +1,88 @@ +"""The tests for the mochad light platform.""" +import unittest +import unittest.mock as mock + +import pytest + +from homeassistant.components import light +from homeassistant.components.light import mochad +from homeassistant.setup import setup_component + +from tests.common import get_test_home_assistant + + +@pytest.fixture(autouse=True) +def pymochad_mock(): + """Mock pymochad.""" + with mock.patch.dict('sys.modules', { + 'pymochad': mock.MagicMock(), + }): + yield + + +class TestMochadSwitchSetup(unittest.TestCase): + """Test the mochad light.""" + + PLATFORM = mochad + COMPONENT = light + THING = 'light' + + def setUp(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + def tearDown(self): + """Stop everyhing that was started.""" + self.hass.stop() + + @mock.patch('homeassistant.components.light.mochad.MochadLight') + def test_setup_adds_proper_devices(self, mock_light): + """Test if setup adds devices.""" + good_config = { + 'mochad': {}, + 'light': { + 'platform': 'mochad', + 'devices': [ + { + 'name': 'Light1', + 'address': 'a1', + }, + ], + } + } + self.assertTrue(setup_component(self.hass, light.DOMAIN, good_config)) + + +class TestMochadLight(unittest.TestCase): + """Test for mochad light platform.""" + + def setUp(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + controller_mock = mock.MagicMock() + dev_dict = {'address': 'a1', 'name': 'fake_light'} + self.light = mochad.MochadLight(self.hass, controller_mock, + dev_dict) + + def teardown_method(self, method): + """Stop everything that was started.""" + self.hass.stop() + + def test_name(self): + """Test the name.""" + self.assertEqual('fake_light', self.light.name) + + def test_turn_on_with_no_brightness(self): + """Test turn_on.""" + self.light.turn_on() + self.light.device.send_cmd.assert_called_once_with('xdim 255') + + def test_turn_on_with_brightness(self): + """Test turn_on.""" + self.light.turn_on(brightness=45) + self.light.device.send_cmd.assert_called_once_with('xdim 45') + + def test_turn_off(self): + """Test turn_off.""" + self.light.turn_off() + self.light.device.send_cmd.assert_called_once_with('off')