Пример #1
0
    def __init__(
        self,
        pduip="192.168.86.10",
        outlet=1,
        pdu_type="cyberpower",
        username="******",
        password="******",
        yamlfilename=None,
        board_name=None,
    ):
        self.pduip = pduip
        self.outlet = outlet
        self.pdu_type = pdu_type
        self.username = username
        self.password = password
        self.update_defaults_from_yaml(yamlfilename,
                                       __class__.__name__,
                                       board_name=board_name)

        if self.pdu_type == "cyberpower":
            if not self.pduip:
                raise Exception("pduip must be set for cyberpower config")
            self.pdu_dev = cpdu.CyberPowerPdu(self.pduip)
        elif self.pdu_type == "vesync":
            if not self.username:
                raise Exception("username must be set for vesync config")
            if not self.password:
                raise Exception("password must be set for vesync config")
            self.pdu_dev = VeSync(self.username, self.password)
            self.pdu_dev.login()
            self.pdu_dev.update()
        else:
            raise Exception("Unknown PDU type")
Пример #2
0
 def setUp(self):
     """Setup VeSync argument cases."""
     self.vesync_1 = VeSync('*****@*****.**', 'password', 'America/New_York')
     self.vesync_2 = VeSync('*****@*****.**', 'password')
     self.vesync_3 = VeSync('*****@*****.**', 'password', None)
     self.vesync_4 = VeSync('*****@*****.**', 'password')
     self.vesync_5 = VeSync('', '')
     self.vesync_6 = VeSync(None, None, None)
     self.vesync_7 = VeSync(None, 'password')
     self.vesync_8 = VeSync('*****@*****.**', None)
     self.vesync_9 = VeSync('*****@*****.**', 'password', 1)
Пример #3
0
 def setUp(self):
     """Instantiate VeSync classes."""
     self.vesync_1 = VeSync('*****@*****.**', 'password', 'America/New_York')
     self.vesync_2 = VeSync('*****@*****.**', 'password')
     self.vesync_3 = VeSync('*****@*****.**', 'password', None)
     self.vesync_4 = VeSync('*****@*****.**', 'password')
     self.vesync_5 = VeSync('', '')
     self.vesync_6 = VeSync(None, None, None)
     self.vesync_7 = VeSync(None, 'password')
     self.vesync_8 = VeSync('*****@*****.**', None)
     self.vesync_9 = VeSync('*****@*****.**', 'password', 1)
Пример #4
0
 def api_mock(self, caplog):
     self.mock_api_call = patch('pyvesync_v2.helpers.Helpers.call_api')
     self.mock_api = self.mock_api_call.start()
     self.mock_api.create_autospect()
     self.mock_api.return_value.ok = True
     self.vesync_obj = VeSync('*****@*****.**', 'pass')
     self.vesync_obj.enabled = True
     self.vesync_obj.login = True
     self.vesync_obj.token = 'sample_tk'
     self.vesync_obj.account_id = 'sample_actid'
     caplog.set_level(logging.DEBUG)
     yield
     self.mock_api_call.stop()
Пример #5
0
 def api_mock(self, caplog):
     """Mock call_api() and initialize VeSync object."""
     self.mock_api_call = patch.object(pyvesync_v2.helpers.Helpers,
                                       'call_api')
     self.mock_api = self.mock_api_call.start()
     self.mock_api.create_autospect()
     self.mock_api.return_value.ok = True
     self.vesync_obj = VeSync('*****@*****.**', 'pass')
     self.vesync_obj.enabled = True
     self.vesync_obj.login = True
     self.vesync_obj.tk = 'sample_tk'
     self.vesync_obj.account_id = 'sample_actid'
     caplog.set_level(logging.DEBUG)
     yield
     self.mock_api_call.stop()
Пример #6
0
class pdu(utils):
    """ Power Distribution Manager """
    def __init__(
        self,
        pduip="192.168.86.10",
        outlet=1,
        pdu_type="cyberpower",
        username="******",
        password="******",
        yamlfilename=None,
        board_name=None,
    ):
        self.pduip = pduip
        self.outlet = outlet
        self.pdu_type = pdu_type
        self.username = username
        self.password = password
        self.update_defaults_from_yaml(yamlfilename,
                                       __class__.__name__,
                                       board_name=board_name)

        if self.pdu_type == "cyberpower":
            if not self.pduip:
                raise Exception("pduip must be set for cyberpower config")
            self.pdu_dev = cpdu.CyberPowerPdu(self.pduip)
        elif self.pdu_type == "vesync":
            if not self.username:
                raise Exception("username must be set for vesync config")
            if not self.password:
                raise Exception("password must be set for vesync config")
            self.pdu_dev = VeSync(self.username, self.password)
            self.pdu_dev.login()
            self.pdu_dev.update()
        else:
            raise Exception("Unknown PDU type")

    def power_cycle_board(self):
        """ Power Cycle Board: OFF, wait 5 seconds, ON
        """
        if self.pdu_type == "cyberpower":
            self.pdu_dev.set_outlet_on(self.outlet, False)
        elif self.pdu_type == "vesync":
            self.pdu_dev.outlets[self.outlet].turn_off()
        time.sleep(5)
        if self.pdu_type == "cyberpower":
            self.pdu_dev.set_outlet_on(self.outlet, True)
        elif self.pdu_type == "vesync":
            self.pdu_dev.outlets[self.outlet].turn_on()
Пример #7
0
class TestVesync15ASwitch:
    """Tests for VeSync 15A Outlets."""
    @pytest.fixture()
    def api_mock(self, caplog):
        """Instantiate VeSync object and mock call_api."""
        self.mock_api_call = patch('pyvesync_v2.helpers.Helpers.call_api')
        self.mock_api = self.mock_api_call.start()
        self.mock_api.create_autospect()
        self.mock_api.return_value.ok = True
        self.vesync_obj = VeSync('*****@*****.**', 'pass')
        self.vesync_obj.enabled = True
        self.vesync_obj.login = True
        self.vesync_obj.token = 'sample_tk'
        self.vesync_obj.account_id = 'sample_actid'
        caplog.set_level(logging.DEBUG)
        yield
        self.mock_api_call.stop()

    def test_15aswitch_conf(self, api_mock):
        """Tests that 15A Outlet is instantiated properly."""
        self.mock_api.return_value = CORRECT_15A_LIST
        outlets = self.vesync_obj.get_devices()
        outlets = outlets[0]
        assert len(outlets) == 1
        vswitch15a = outlets[0]
        assert isinstance(vswitch15a, VeSyncOutlet15A)
        assert vswitch15a.device_name == "Name 15A Outlet"
        assert vswitch15a.device_type == "ESW15-USA"
        assert vswitch15a.cid == "15A-CID"
        assert vswitch15a.uuid == "UUID"

    def test_15a_details(self, api_mock):
        """Test 15A get_details()."""
        self.mock_api.return_value = CORRECT_15A_DETAILS
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.get_details()
        dev_details = vswitch15a.details
        assert vswitch15a.device_status == 'on'
        assert type(dev_details) == dict
        assert dev_details['active_time'] == 1
        assert dev_details['energy'] == 1
        assert dev_details['power'] == '1'
        assert dev_details['voltage'] == '1'
        assert vswitch15a.power == 1
        assert vswitch15a.voltage == 1
        assert vswitch15a.active_time == 1
        assert vswitch15a.energy_today == 1

    def test_15a_details_fail(self, caplog, api_mock):
        """Test 15A get_details with Code>0."""
        self.mock_api.return_value = BAD_15A_LIST
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.get_details()
        assert len(caplog.records) == 1
        assert 'details' in caplog.text

    def test_15a_no_details(self, caplog, api_mock):
        """Test 15A details return with no details and code=0."""
        bad_15a_details = {"code": 0, "deviceStatus": "on"}
        self.mock_api.return_value = (bad_15a_details, 200)
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.get_details()
        assert len(caplog.records) == 1

    def test_15a_onoff(self, caplog, api_mock):
        """Test 15A Device On/Off Methods."""
        self.mock_api.return_value = ({"code": 0}, 200)
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        head = helpers.req_headers(self.vesync_obj)
        body = helpers.req_body(self.vesync_obj, 'devicestatus')

        body['status'] = 'on'
        body['uuid'] = vswitch15a.uuid
        on = vswitch15a.turn_on()
        self.mock_api.assert_called_with('/15a/v1/device/devicestatus',
                                         'put',
                                         headers=head,
                                         json=body)
        assert on
        off = vswitch15a.turn_off()
        body['status'] = 'off'
        self.mock_api.assert_called_with('/15a/v1/device/devicestatus',
                                         'put',
                                         headers=head,
                                         json=body)
        assert off

    def test_15a_onoff_fail(self, api_mock):
        """Test 15A On/Off Fail with Code>0."""
        self.mock_api.return_value = ({"code": 1}, 400)
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        assert not vswitch15a.turn_on()
        assert not vswitch15a.turn_off()

    def test_15a_weekly(self, api_mock):
        """Test 15A get_weekly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.get_weekly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_week')
        body['uuid'] = vswitch15a.uuid
        self.mock_api.assert_called_with('/15a/v1/device/energyweek',
                                         'post',
                                         headers=helpers.req_headers(
                                             self.vesync_obj),
                                         json=body)
        energy_dict = vswitch15a.energy['week']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert vswitch15a.weekly_energy_total == 1

    def test_15a_monthly(self, api_mock):
        """Test 15A get_monthly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.get_monthly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_month')
        body['uuid'] = vswitch15a.uuid
        self.mock_api.assert_called_with('/15a/v1/device/energymonth',
                                         'post',
                                         headers=helpers.req_headers(
                                             self.vesync_obj),
                                         json=body)
        energy_dict = vswitch15a.energy['month']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert vswitch15a.monthly_energy_total == 1

    def test_15a_yearly(self, api_mock):
        """Test 15A get_yearly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.get_yearly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_year')
        body['uuid'] = vswitch15a.uuid
        self.mock_api.assert_called_with('/15a/v1/device/energyyear',
                                         'post',
                                         headers=helpers.req_headers(
                                             self.vesync_obj),
                                         json=body)
        energy_dict = vswitch15a.energy['year']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert vswitch15a.yearly_energy_total == 1

    def test_history_fail(self, caplog, api_mock):
        """Test 15A energy failure."""
        bad_history = {"code": 1}
        self.mock_api.return_value = (bad_history, 200)
        vswitch15a = VeSyncOutlet15A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.update_energy()
        assert len(caplog.records) == 1
        assert 'weekly' in caplog.text
        caplog.clear()
        vswitch15a.get_monthly_energy()
        assert len(caplog.records) == 1
        assert 'monthly' in caplog.text
        caplog.clear()
        vswitch15a.get_yearly_energy()
        assert len(caplog.records) == 1
        assert 'yearly' in caplog.text
Пример #8
0
class TestVesync(unittest.TestCase):
    """Test VeSync object initialization."""

    def setUp(self):
        """Setup VeSync argument cases."""
        self.vesync_1 = VeSync('*****@*****.**', 'password', 'America/New_York')
        self.vesync_2 = VeSync('*****@*****.**', 'password')
        self.vesync_3 = VeSync('*****@*****.**', 'password', None)
        self.vesync_4 = VeSync('*****@*****.**', 'password')
        self.vesync_5 = VeSync('', '')
        self.vesync_6 = VeSync(None, None, None)
        self.vesync_7 = VeSync(None, 'password')
        self.vesync_8 = VeSync('*****@*****.**', None)
        self.vesync_9 = VeSync('*****@*****.**', 'password', 1)

    def tearDown(self):
        """Clean up test."""
        pass

    def test_instance(self):
        """Test VeSync object is successfully initialized."""
        self.assertIsInstance(self.vesync_1, VeSync)

    def test_username(self):
        """Test invalid username arguments."""
        self.assertEqual(self.vesync_1.username, '*****@*****.**')
        self.assertEqual(self.vesync_5.username, '')
        self.assertEqual(self.vesync_6.username, None)

        self.vesync_1.username = '******'
        self.assertEqual(self.vesync_1.username, '*****@*****.**')

    def test_password(self):
        """Test invalid password arguments."""
        self.assertEqual(self.vesync_1.password, 'password')
        self.assertEqual(self.vesync_5.password, '')
        self.assertEqual(self.vesync_6.password, None)

        self.vesync_1.password = '******'
        self.assertEqual(self.vesync_1.password, 'other')

    def test_hash_password(self):
        """Test password hash method."""
        self.assertEqual(Helpers.hash_password(self.vesync_1.password),
                         '5f4dcc3b5aa765d61d8327deb882cf99')
        self.assertEqual(Helpers.hash_password(self.vesync_5.password),
                         'd41d8cd98f00b204e9800998ecf8427e')
        with self.assertRaises(AttributeError):
            Helpers.hash_password(self.vesync_6.password)

    def test_time_zone(self):
        """Test time zone argument handling."""
        self.assertEqual(self.vesync_1.time_zone, 'America/New_York')
        self.assertEqual(self.vesync_2.time_zone, 'America/New_York')
        self.assertEqual(self.vesync_3.time_zone, 'America/New_York')
        self.assertEqual(self.vesync_9.time_zone, 'America/New_York')

        self.vesync_1.time_zone = 'America/East'
        self.assertEqual(self.vesync_1.time_zone, 'America/East')

    def test_login(self):
        """Test login method."""
        mock_vesync = mock.Mock()
        mock_vesync.login.return_value = True
        self.assertTrue(mock_vesync.login())
        mock_vesync.login.return_value = False
        self.assertFalse(mock_vesync.login())

        with patch('pyvesync_v2.helpers.Helpers.call_api') as mocked_post:
            d = {"result": {"accountID": "12346536", "userType": "1",
                 "token": "somevaluehere"}, "code": 0}
            mocked_post.return_value = (d, 200)

            data = self.vesync_1.login()
            body = Helpers.req_body(self.vesync_1, 'login')
            body['email'] = self.vesync_1.username
            body['password'] = Helpers.hash_password(self.vesync_1.password)
            mocked_post.assert_called_with('/cloud/v1/user/login',
                                           'post',
                                           json=body)
            self.assertTrue(data)
Пример #9
0
class TestVesync7ASwitch(object):
    """Test 7A outlet API."""
    @pytest.fixture()
    def api_mock(self, caplog):
        """Mock call_api() and initialize VeSync object."""
        self.mock_api_call = patch.object(pyvesync_v2.helpers.Helpers,
                                          'call_api')
        self.mock_api = self.mock_api_call.start()
        self.mock_api.create_autospect()
        self.mock_api.return_value.ok = True
        self.vesync_obj = VeSync('*****@*****.**', 'pass')
        self.vesync_obj.enabled = True
        self.vesync_obj.login = True
        self.vesync_obj.tk = 'sample_tk'
        self.vesync_obj.account_id = 'sample_actid'
        caplog.set_level(logging.DEBUG)
        yield
        self.mock_api_call.stop()

    def test_7aswitch_conf(self, api_mock):
        """Test inizialization of 7A outlet."""
        self.mock_api.return_value = CORRECT_7A_LIST
        devs = self.vesync_obj.get_devices()
        assert len(devs) == 4
        vswitch7a = devs[0][0]
        assert isinstance(vswitch7a, VeSyncOutlet7A)
        assert vswitch7a.device_name == "Name 7A Outlet"
        assert vswitch7a.device_type == "wifi-switch-1.3"
        assert vswitch7a.cid == "7A-CID"
        assert vswitch7a.is_on

    def test_7a_details(self, api_mock):
        """Test get_details() method for 7A outlet."""
        self.mock_api.return_value = CORRECT_7A_DETAILS
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch7a.get_details()
        dev_details = vswitch7a.details
        assert vswitch7a.device_status == 'on'
        assert type(dev_details) == dict
        assert dev_details['active_time'] == 1
        assert dev_details['energy'] == 1
        assert vswitch7a.power == 1
        assert vswitch7a.voltage == 1

    def test_7a_no_devstatus(self, caplog, api_mock):
        """Test 7A outlet details response with no device status key."""
        bad_7a_details = {
            "deviceImg": "",
            "activeTime": 1,
            "energy": 1,
            "power": "1A:1A",
            "voltage": "1A:1A"
        }
        self.mock_api.return_value = (bad_7a_details, 200)
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch7a.get_details()
        assert len(caplog.records) == 2
        assert 'details' in caplog.text

    def test_7a_no_details(self, caplog, api_mock):
        """Test 7A outlet details response with unknown keys."""
        bad_7a_details = {"wrongdetails": "on"}
        self.mock_api.return_value = (bad_7a_details, 200)
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch7a.get_details()
        assert len(caplog.records) == 2

    def test_7a_onoff(self, caplog, api_mock):
        """Test 7A outlet on/off methods."""
        self.mock_api.return_value = ("response", 200)
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        on = vswitch7a.turn_on()
        head = helpers.req_headers(self.vesync_obj)
        self.mock_api.assert_called_with('/v1/wifi-switch-1.3/' +
                                         vswitch7a.cid + '/status/on',
                                         'put',
                                         headers=head)
        assert on
        off = vswitch7a.turn_off()
        self.mock_api.assert_called_with('/v1/wifi-switch-1.3/' +
                                         vswitch7a.cid + '/status/off',
                                         'put',
                                         headers=head)
        assert off

    def test_7a_onoff_fail(self, api_mock):
        """Test 7A outlet on/off methods that fail."""
        self.mock_api.return_value = ('response', 400)
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        assert not vswitch7a.turn_on()
        assert not vswitch7a.turn_off()

    def test_7a_weekly(self, api_mock):
        """Test 7A outlet weekly energy API call and energy dict."""
        self.mock_api.return_value = ENERGY_HISTORY
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch7a.get_weekly_energy()
        self.mock_api.assert_called_with(
            '/v1/device/' + vswitch7a.cid + '/energy/week',
            'get',
            headers=helpers.req_headers(self.vesync_obj))
        energy_dict = vswitch7a.energy['week']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]

    def test_7a_monthly(self, api_mock):
        """Test 7A outlet monthly energy API call and energy dict."""
        self.mock_api.return_value = ENERGY_HISTORY
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch7a.get_monthly_energy()
        self.mock_api.assert_called_with(
            '/v1/device/' + vswitch7a.cid + '/energy/month',
            'get',
            headers=helpers.req_headers(self.vesync_obj))
        energy_dict = vswitch7a.energy['month']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]

    def test_7a_yearly(self, api_mock):
        """Test 7A outlet yearly energy API call and energy dict."""
        self.mock_api.return_value = ENERGY_HISTORY
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch7a.get_yearly_energy()
        self.mock_api.assert_called_with(
            '/v1/device/' + vswitch7a.cid + '/energy/year',
            'get',
            headers=helpers.req_headers(self.vesync_obj))
        energy_dict = vswitch7a.energy['year']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]

    def test_history_fail(self, caplog, api_mock):
        """Test handling of energy update failure."""
        bad_history = {"code": 1}
        self.mock_api.return_value = (bad_history, 200)
        vswitch7a = VeSyncOutlet7A(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch7a.update_energy()
        assert len(caplog.records) == 2
        assert 'weekly' in caplog.text
        caplog.clear()
        vswitch7a.get_monthly_energy()
        assert len(caplog.records) == 2
        assert 'monthly' in caplog.text
        caplog.clear()
        vswitch7a.get_yearly_energy()
        assert len(caplog.records) == 2
        assert 'yearly' in caplog.text
Пример #10
0
class TestVesyncAirPurifier(object):
    """Air purifier tests."""
    @pytest.fixture()
    def api_mock(self, caplog):
        """Mock call_api and initialize VeSync object."""
        self.mock_api_call = patch('pyvesync_v2.helpers.Helpers.call_api')
        self.mock_api = self.mock_api_call.start()
        self.mock_api.create_autospect()
        self.mock_api.return_value.ok = True
        self.vesync_obj = VeSync('*****@*****.**', 'pass')
        self.vesync_obj.enabled = True
        self.vesync_obj.login = True
        self.vesync_obj.token = 'sample_tk'
        self.vesync_obj.account_id = 'sample_actid'
        caplog.set_level(logging.DEBUG)
        yield
        self.mock_api_call.stop()

    def test_airpur_conf(self, api_mock):
        """Tests that 15A Outlet is instantiated properly."""
        self.mock_api.return_value = CORRECT_LIST
        fans = self.vesync_obj.get_devices()
        fan = fans[2]
        assert len(fan) == 1
        fan = fan[0]
        assert isinstance(fan, VeSyncAir131)
        assert fan.device_name == "Name Air Purifier"
        assert fan.device_type == "LV-PUR131S"
        assert fan.cid == "AIRPUR-CID"
        assert fan.uuid == "UUID"

    def test_airpur_details(self, api_mock):
        """Test 15A get_details()."""
        self.mock_api.return_value = CORRECT_DETAILS
        fan = VeSyncAir131(DEV_LIST_DETAIL, self.vesync_obj)
        fan.get_details()
        dev_details = fan.details
        assert fan.device_status == 'on'
        assert type(dev_details) == dict
        assert dev_details['active_time'] == 1
        assert fan.filter_life == 100
        assert dev_details['screen_status'] == 'on'
        assert fan.mode == 'manual'
        assert dev_details['level'] == 1
        assert fan.fan_level == 1
        assert dev_details['air_quality'] == 'excellent'
        assert fan.air_quality == 'excellent'

    def test_airpur_details_fail(self, caplog, api_mock):
        """Test Air Purifier get_details with Code>0."""
        self.mock_api.return_value = BAD_LIST
        fan = VeSyncAir131(DEV_LIST_DETAIL, self.vesync_obj)
        fan.get_details()
        assert len(caplog.records) == 1
        assert 'details' in caplog.text

    def test_airpur_onoff(self, caplog, api_mock):
        """Test Air Purifier Device On/Off Methods."""
        self.mock_api.return_value = ({"code": 0}, 200)
        fan = VeSyncAir131(DEV_LIST_DETAIL, self.vesync_obj)
        head = helpers.req_headers(self.vesync_obj)
        body = helpers.req_body(self.vesync_obj, 'devicestatus')
        fan.device_status = 'off'
        body['status'] = 'on'
        body['uuid'] = fan.uuid
        on = fan.turn_on()
        self.mock_api.assert_called_with(
            '/131airPurifier/v1/device/deviceStatus',
            'put',
            json=body,
            headers=head)
        call_args = self.mock_api.call_args_list[0][0]
        assert call_args[0] == '/131airPurifier/v1/device/deviceStatus'
        assert call_args[1] == 'put'
        assert on
        fan.device_status = 'on'
        off = fan.turn_off()
        body['status'] = 'off'
        self.mock_api.assert_called_with(
            '/131airPurifier/v1/device/deviceStatus',
            'put',
            json=body,
            headers=head)
        assert off

    def test_airpur_onoff_fail(self, api_mock):
        """Test Air Purifier On/Off Fail with Code>0."""
        self.mock_api.return_value = ({"code": 1}, 400)
        vswitch15a = VeSyncAir131(DEV_LIST_DETAIL, self.vesync_obj)
        assert not vswitch15a.turn_on()
        assert not vswitch15a.turn_off()

    def test_airpur_fanspeed(self, caplog, api_mock):
        """Test changing fan speed of."""
        self.mock_api.return_value = ({'code': 0}, 200)
        fan = VeSyncAir131(DEV_LIST_DETAIL, self.vesync_obj)
        fan.details['level'] = 1
        b = fan.change_fan_speed()
        assert fan.fan_level == 2
        b = fan.change_fan_speed()
        assert fan.fan_level == 3
        b = fan.change_fan_speed()
        assert fan.fan_level == 1
        assert b
        b = fan.change_fan_speed(2)
        assert b
        assert fan.fan_level == 2

    def test_mode_toggle(self, caplog, api_mock):
        """Test changing modes on air purifier."""
        self.mock_api.return_value = ({'code': 0}, 200)
        fan = VeSyncAir131(DEV_LIST_DETAIL, self.vesync_obj)
        f = fan.auto_mode()
        assert f
        assert fan.mode == 'auto'
        f = fan.manual_mode()
        assert fan.mode == 'manual'
        assert f
        f = fan.sleep_mode()
        assert fan.mode == 'sleep'
        assert f
Пример #11
0
class TestVesyncWallSwitch(object):
    @pytest.fixture()
    def api_mock(self, caplog):
        self.mock_api_call = patch('pyvesync_v2.helpers.Helpers.call_api')
        self.mock_api = self.mock_api_call.start()
        self.mock_api.create_autospect()
        self.mock_api.return_value.ok = True
        self.vesync_obj = VeSync('*****@*****.**', 'pass')
        self.vesync_obj.enabled = True
        self.vesync_obj.login = True
        self.vesync_obj.token = 'sample_tk'
        self.vesync_obj.account_id = 'sample_actid'
        caplog.set_level(logging.DEBUG)
        yield
        self.mock_api_call.stop()

    def test_ws_conf(self, api_mock):
        """Tests that Wall Switch is instantiated properly"""
        self.mock_api.return_value = CORRECT_WS_LIST
        devices = self.vesync_obj.get_devices()
        switch = devices[1]
        assert len(switch) == 1
        wswitch = switch[0]
        assert isinstance(wswitch, VeSyncWallSwitch)
        assert wswitch.device_name == "Name Wall Switch"
        assert wswitch.device_type == "ESWL01"
        assert wswitch.cid == "WS-CID"
        assert wswitch.uuid == "UUID"

    def test_ws_details(self, api_mock):
        """Test WS get_details() """
        self.mock_api.return_value = CORRECT_WS_DETAILS
        wswitch = VeSyncWallSwitch(DEV_LIST_DETAIL, self.vesync_obj)
        wswitch.get_details()
        dev_details = wswitch.details
        assert wswitch.device_status == 'on'
        assert type(dev_details) == dict
        assert dev_details['active_time'] == 1
        assert wswitch.connection_status == 'online'

    def test_ws_details_fail(self, caplog, api_mock):
        """Test WS get_details with Code>0"""
        self.mock_api.return_value = BAD_LIST
        vswitch15a = VeSyncWallSwitch(DEV_LIST_DETAIL, self.vesync_obj)
        vswitch15a.get_details()
        assert len(caplog.records) == 1
        assert 'details' in caplog.text

    def test_ws_onoff(self, caplog, api_mock):
        """Test 15A Device On/Off Methods"""
        self.mock_api.return_value = ({"code": 0}, 200)
        wswitch = VeSyncWallSwitch(DEV_LIST_DETAIL, self.vesync_obj)
        head = helpers.req_headers(self.vesync_obj)
        body = helpers.req_body(self.vesync_obj, 'devicestatus')

        body['status'] = 'on'
        body['uuid'] = wswitch.uuid
        on = wswitch.turn_on()
        self.mock_api.assert_called_with(
            '/inwallswitch/v1/device/devicestatus',
            'put',
            headers=head,
            json=body)
        assert on
        off = wswitch.turn_off()
        body['status'] = 'off'
        self.mock_api.assert_called_with(
            '/inwallswitch/v1/device/devicestatus',
            'put',
            headers=head,
            json=body)
        assert off

    def test_ws_onoff_fail(self, api_mock):
        """Test ws On/Off Fail with Code>0"""
        self.mock_api.return_value = ({"code": 1}, 400)
        vswitch15a = VeSyncWallSwitch(DEV_LIST_DETAIL, self.vesync_obj)
        assert not vswitch15a.turn_on()
        assert not vswitch15a.turn_off()
Пример #12
0
class TestVesyncOutdoorPlug:
    """Test class for outdoor outlet."""
    @pytest.fixture()
    def api_mock(self, caplog):
        """Mock call_api and initialize VeSync object."""
        self.mock_api_call = patch('pyvesync_v2.helpers.Helpers.call_api')
        self.mock_api = self.mock_api_call.start()
        self.mock_api.create_autospec()
        self.mock_api.return_value.ok = True
        self.vesync_obj = VeSync('*****@*****.**', 'pass')
        self.vesync_obj.enabled = True
        self.vesync_obj.login = True
        self.vesync_obj.token = 'sample_tk'
        self.vesync_obj.account_id = 'sample_actid'
        caplog.set_level(logging.DEBUG)
        yield
        self.mock_api_call.stop()

    def test_outdoor_conf(self, api_mock):
        """Tests outdoor outlet is instantiated properly."""
        self.mock_api.return_value = CORRECT_OUTDOOR_LIST
        outlets = self.vesync_obj.get_devices()
        outlets = outlets[0]
        assert len(outlets) == 2
        outdoor_outlet = outlets[0]
        assert isinstance(outdoor_outlet, VeSyncOutdoorPlug)
        assert outdoor_outlet.device_type == 'ESO15-TB'
        assert outdoor_outlet.uuid == 'UUID'

    def test_outdoor_details(self, api_mock):
        """Tests retrieving outdoor outlet details."""
        self.mock_api.return_value = CORRECT_OUTDOOR_DETAILS
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        outdoor_outlet.get_details()
        dev_details = outdoor_outlet.details
        assert outdoor_outlet.device_status == 'on'
        assert isinstance(outdoor_outlet, VeSyncOutdoorPlug)
        assert dev_details['active_time'] == 1

    def test_outdoor_details_fail(self, caplog, api_mock):
        """Test outdoor outlet get_details response."""
        self.mock_api.return_value = BAD_OUTDOOR_LIST
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        outdoor_outlet.get_details()
        assert len(caplog.records) == 1
        assert 'details' in caplog.text

    def test_outdoor_outlet_onoff(self, caplog, api_mock):
        """Test Outdoor Outlet Device On/Off Methods."""
        self.mock_api.return_value = ({"code": 0}, 200)
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        head = helpers.req_headers(self.vesync_obj)
        body = helpers.req_body(self.vesync_obj, 'devicestatus')

        body['status'] = 'on'
        body['uuid'] = outdoor_outlet.uuid
        body['switchNo'] = outdoor_outlet.sub_device_no
        on = outdoor_outlet.turn_on()
        self.mock_api.assert_called_with(
            '/outdoorsocket15a/v1/device/devicestatus',
            'put',
            headers=head,
            json=body)
        assert on
        off = outdoor_outlet.turn_off()
        body['status'] = 'off'
        self.mock_api.assert_called_with(
            '/outdoorsocket15a/v1/device/devicestatus',
            'put',
            headers=head,
            json=body)
        assert off

    def test_outdoor_outlet_onoff_fail(self, api_mock):
        """Test outdoor outlet On/Off Fail with Code>0."""
        self.mock_api.return_value = ({"code": 1}, 400)
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        assert not outdoor_outlet.turn_on()
        assert not outdoor_outlet.turn_off()

    def test_outdoor_outlet_weekly(self, api_mock):
        """Test outdoor outlet get_weekly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        outdoor_outlet.get_weekly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_week')
        body['uuid'] = outdoor_outlet.uuid
        self.mock_api.assert_called_with(
            '/outdoorsocket15a/v1/device/energyweek',
            'post',
            headers=helpers.req_headers(self.vesync_obj),
            json=body)
        energy_dict = outdoor_outlet.energy['week']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert outdoor_outlet.weekly_energy_total == 1

    def test_outdoor_outlet_monthly(self, api_mock):
        """Test outdoor outlet get_monthly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        outdoor_outlet.get_monthly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_month')
        body['uuid'] = outdoor_outlet.uuid
        self.mock_api.assert_called_with(
            '/outdoorsocket15a/v1/device/energymonth',
            'post',
            headers=helpers.req_headers(self.vesync_obj),
            json=body)
        energy_dict = outdoor_outlet.energy['month']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert outdoor_outlet.monthly_energy_total == 1

    def test_outdoor_outlet_yearly(self, api_mock):
        """Test outdoor outlet get_yearly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        outdoor_outlet.get_yearly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_year')
        body['uuid'] = outdoor_outlet.uuid
        self.mock_api.assert_called_with(
            '/outdoorsocket15a/v1/device/energyyear',
            'post',
            headers=helpers.req_headers(self.vesync_obj),
            json=body)
        energy_dict = outdoor_outlet.energy['year']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert outdoor_outlet.yearly_energy_total == 1

    def test_history_fail(self, caplog, api_mock):
        """Test outdoor outlet energy failure."""
        bad_history = {"code": 1}
        self.mock_api.return_value = (bad_history, 200)
        outdoor_outlet = VeSyncOutdoorPlug(DEV_LIST_DETAIL, self.vesync_obj)
        outdoor_outlet.update_energy()
        assert len(caplog.records) == 1
        assert 'weekly' in caplog.text
        caplog.clear()
        outdoor_outlet.get_monthly_energy()
        assert len(caplog.records) == 1
        assert 'monthly' in caplog.text
        caplog.clear()
        outdoor_outlet.get_yearly_energy()
        assert len(caplog.records) == 1
        assert 'yearly' in caplog.text
Пример #13
0
from pyvesync_v2 import VeSync
import os

USER = os.getenv('VESYNC_USER')
PASSWORD= os.getenv('VESYNC_PASSWORD')

manager = VeSync(USER, PASSWORD, "America/New_York")
manager.login()

# Get/Update Devices from server - populate device lists
manager.update()

# Display outlet device information
for device in manager.outlets:
    print(device.display_json())
Пример #14
0
from pyvesync_v2 import VeSync
import os
import mailer
import time
import datetime

power_threshold = 30  # in Watts
sleeptime_seconds = 600
vsync_email = '*****@*****.**'
vesync_password = os.environ[
    'vesyncPassword']  # added as -e option on docker start.
sg_password = os.environ['sendgridKey']  # added as -e option on docker start.
logfile = 'rad_wagon_battery.log'

manager = VeSync(vsync_email, vesync_password, time_zone='US/Pacific')
manager.login()
manager.update()
manager.energy_update_interval = 60
my_switch = manager.outlets[0]

mail_from_larry = mailer.Sendgrid(sg_password, 'Larry Weinhouse',
                                  '*****@*****.**')


def poll_outlet(my_switch):
    try:
        manager.update_energy()
        my_switch.update()
        volts = my_switch.voltage
        power = my_switch.power
Пример #15
0
class TestVesync10ASwitch(object):
    """Test class for 10A outlets."""

    @pytest.fixture()
    def api_mock(self, caplog):
        """Mock call_api() method and initialize VeSync object."""
        self.mock_api_call = patch('pyvesync_v2.helpers.Helpers.call_api')
        self.mock_api = self.mock_api_call.start()
        self.mock_api.create_autospect()
        self.mock_api.return_value.ok = True
        self.vesync_obj = VeSync('*****@*****.**', 'pass')
        self.vesync_obj.enabled = True
        self.vesync_obj.login = True
        self.vesync_obj.token = 'sample_tk'
        self.vesync_obj.account_id = 'sample_actid'
        caplog.set_level(logging.DEBUG)
        yield
        self.mock_api_call.stop()

    @pytest.mark.parametrize('mock_return, devtype',
                             [(CORRECT_10AEU_LIST, 'ESW01-EU'),
                              (CORRECT_10AUS_LIST, 'ESW03-USA')])
    def test_10a_conf(self, mock_return, devtype, api_mock):
        """Tests that 10A US & EU Outlet is instantiated properly."""
        self.mock_api.return_value = mock_return
        outlets = self.vesync_obj.get_devices()
        outlets = outlets[0]
        assert len(outlets) == 1
        outlet = outlets[0]
        assert isinstance(outlet, VeSyncOutlet10A)
        assert outlet.device_name == "Name 10A Outlet"
        assert outlet.device_type == devtype
        assert outlet.cid == "10A-CID"
        assert outlet.uuid == "UUID"

    def test_10a_details(self, api_mock):
        """Test 10A get_details()."""
        self.mock_api.return_value = CORRECT_10A_DETAILS
        outlet = VeSyncOutlet10A(DEV_LIST_DETAIL_US, self.vesync_obj)
        outlet.get_details()
        dev_details = outlet.details
        assert outlet.device_status == 'on'
        assert type(dev_details) == dict
        assert dev_details['active_time'] == 1
        assert dev_details['energy'] == 1
        assert dev_details['power'] == '1'
        assert dev_details['voltage'] == '1'
        assert outlet.power == 1
        assert outlet.voltage == 1

    def test_10a_details_fail(self, caplog, api_mock):
        """Test 10A get_details with Code>0."""
        self.mock_api.return_value = BAD_10A_LIST
        out = VeSyncOutlet10A(DEV_LIST_DETAIL_EU, self.vesync_obj)
        out.get_details()
        assert len(caplog.records) == 1
        assert 'details' in caplog.text

    def test_10a_onoff(self, caplog, api_mock):
        """Test 10A Device On/Off Methods."""
        self.mock_api.return_value = ({"code": 0}, 200)
        out = VeSyncOutlet10A(DEV_LIST_DETAIL_EU, self.vesync_obj)
        head = helpers.req_headers(self.vesync_obj)
        body = helpers.req_body(self.vesync_obj, 'devicestatus')

        body['status'] = 'on'
        body['uuid'] = out.uuid
        on = out.turn_on()
        self.mock_api.assert_called_with('/10a/v1/device/devicestatus',
                                         'put',
                                         headers=head,
                                         json=body)
        assert on
        off = out.turn_off()
        body['status'] = 'off'
        self.mock_api.assert_called_with('/10a/v1/device/devicestatus',
                                         'put',
                                         headers=head,
                                         json=body)
        assert off

    def test_10a_onoff_fail(self, api_mock):
        """Test 10A On/Off Fail with Code>0."""
        self.mock_api.return_value = ({"code": 1}, 400)
        out = VeSyncOutlet10A(DEV_LIST_DETAIL_US, self.vesync_obj)
        assert not out.turn_on()
        assert not out.turn_off()

    def test_10a_weekly(self, api_mock):
        """Test 10A get_weekly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        out = VeSyncOutlet10A(DEV_LIST_DETAIL_EU, self.vesync_obj)
        out.get_weekly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_week')
        body['uuid'] = out.uuid
        self.mock_api.assert_called_with('/10a/v1/device/energyweek',
                                         'post',
                                         headers=helpers.req_headers(
                                             self.vesync_obj),
                                         json=body)
        energy_dict = out.energy['week']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert out.weekly_energy_total == 1

    def test_10a_monthly(self, api_mock):
        """Test 10A get_monthly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        out = VeSyncOutlet10A(DEV_LIST_DETAIL_EU, self.vesync_obj)
        out.get_monthly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_month')
        body['uuid'] = out.uuid
        self.mock_api.assert_called_with('/10a/v1/device/energymonth',
                                         'post',
                                         headers=helpers.req_headers(
                                             self.vesync_obj),
                                         json=body)
        energy_dict = out.energy['month']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert out.monthly_energy_total == 1

    def test_10a_yearly(self, api_mock):
        """Test 10A get_yearly_energy."""
        self.mock_api.return_value = ENERGY_HISTORY
        out = VeSyncOutlet10A(DEV_LIST_DETAIL_US, self.vesync_obj)
        out.get_yearly_energy()
        body = helpers.req_body(self.vesync_obj, 'energy_year')
        body['uuid'] = out.uuid
        self.mock_api.assert_called_with('/10a/v1/device/energyyear',
                                         'post',
                                         headers=helpers.req_headers(
                                             self.vesync_obj),
                                         json=body)
        energy_dict = out.energy['year']
        assert energy_dict['energy_consumption_of_today'] == 1
        assert energy_dict['cost_per_kwh'] == 1
        assert energy_dict['max_energy'] == 1
        assert energy_dict['total_energy'] == 1
        assert energy_dict['data'] == [1, 1]
        assert out.yearly_energy_total == 1

    def test_history_fail(self, caplog, api_mock):
        """Test 15A energy failure."""
        bad_history = {"code": 1}
        self.mock_api.return_value = (bad_history, 200)
        out = VeSyncOutlet10A(DEV_LIST_DETAIL_US, self.vesync_obj)
        out.update_energy()
        assert len(caplog.records) == 1
        assert 'weekly' in caplog.text
        caplog.clear()
        out.get_monthly_energy()
        assert len(caplog.records) == 1
        assert 'monthly' in caplog.text
        caplog.clear()
        out.get_yearly_energy()
        assert len(caplog.records) == 1
        assert 'yearly' in caplog.text
Пример #16
0
class TestVeSyncBulbESL100:
    """Tests for VeSync dimmable bulb."""
    @pytest.fixture()
    def api_mock(self, caplog):
        """Initilize the mock VeSync class and patch call_api."""
        self.mock_api_call = patch('pyvesync_v2.helpers.Helpers.call_api')
        self.mock_api = self.mock_api_call.start()
        self.mock_api.create_autospect()
        self.mock_api.return_value.ok = True
        self.vesync_obj = VeSync('*****@*****.**', 'pass')
        self.vesync_obj.enabled = True
        self.vesync_obj.login = True
        self.vesync_obj.token = 'sample_tk'
        self.vesync_obj.account_id = 'sample_actid'
        caplog.set_level(logging.DEBUG)
        yield
        self.mock_api_call.stop()

    def test_esl100_conf(self, api_mock):
        """Tests that Wall Switch is instantiated properly."""
        self.mock_api.return_value = DEV_LIST
        devices = self.vesync_obj.get_devices()
        bulbs = devices[3]
        assert len(bulbs) == 1
        bulb = bulbs[0]
        assert isinstance(bulb, VeSyncBulbESL100)
        assert bulb.device_name == "Etekcity Soft White Bulb"
        assert bulb.device_type == "ESL100"
        assert bulb.cid == "ESL100-CID"
        assert bulb.uuid == "UUID"

    def test_esl100_details(self, api_mock):
        """Test WS get_details()."""
        self.mock_api.return_value = DEVICE_DETAILS
        bulb = VeSyncBulbESL100(DEV_LIST_DETAIL, self.vesync_obj)
        bulb.get_details()
        dev_details = bulb.details
        assert bulb.device_status == 'on'
        assert isinstance(dev_details, dict)
        assert bulb.connection_status == 'online'

    def test_esl100_no_details(self, caplog, api_mock):
        """Test no device details for disconnected bulb."""
        self.mock_api.return_value = ({'code': 5}, 200)
        bulb = VeSyncBulbESL100(DEV_LIST_DETAIL, self.vesync_obj)
        bulb.update()
        assert len(caplog.records) == 2

    def test_esl100_onoff(self, caplog, api_mock):
        """Test power toggle for ESL100 bulb."""
        self.mock_api.return_value = ({'code': 0}, 200)
        bulb = VeSyncBulbESL100(DEV_LIST_DETAIL, self.vesync_obj)
        assert bulb.turn_off()
        assert bulb.turn_on()

    def test_brightness(self, api_mock):
        self.mock_api.return_value = ({'code': 0}, 200)
        bulb = VeSyncBulbESL100(DEV_LIST_DETAIL, self.vesync_obj)
        assert bulb.set_brightness(50)

    def test_invalid_brightness(self, caplog, api_mock):
        bulb = VeSyncBulbESL100(DEV_LIST_DETAIL, self.vesync_obj)
        assert not bulb.set_brightness(5000)

    def test_features(self, api_mock):
        bulb = VeSyncBulbESL100(DEV_LIST_DETAIL, self.vesync_obj)
        assert bulb.dimmable_feature
        assert not bulb.color_temp_feature
        assert not bulb.rgb_shift_feature