liquidctl/tests/test_commander_pro.py

1380 lines
44 KiB
Python

import pytest
from _testutils import MockHidapiDevice, Report, MockRuntimeStorage
from liquidctl.driver.commander_pro import _quoted, _prepare_profile, _fan_mode_desc, CommanderPro
from liquidctl.driver.hwmon import HwmonDevice
from liquidctl.error import NotSupportedByDevice
# hardcoded responce data expected for some of the calls:
# commander pro: firmware request (0.9.214)
# commander pro: bootloader req (2.3)
# commander pro: get temp config ( 3 sensors)
# commander pro: get fan configs (3 DC fans, 1 PWM fan) # note I have not tested it with pwm fans
# commander pro:
@pytest.fixture
def commanderProDeviceUnconnected():
device = MockHidapiDevice(vendor_id=0x1b1c, product_id=0x0c10, address='addr')
return CommanderPro(device, 'Corsair Commander Pro', 6, 4, 2)
@pytest.fixture
def lightingNodeProDeviceUnconnected():
device = MockHidapiDevice(vendor_id=0x1b1c, product_id=0x0c0b, address='addr')
return CommanderPro(device, 'Corsair Lighting Node Pro', 0, 0, 2)
@pytest.fixture
def commanderProDevice():
device = MockHidapiDevice(vendor_id=0x1b1c, product_id=0x0c10, address='addr')
pro = CommanderPro(device, 'Corsair Commander Pro', 6, 4, 2)
runtime_storage = MockRuntimeStorage(key_prefixes=['testing'])
pro.connect(runtime_storage=runtime_storage)
return pro
@pytest.fixture
def lightingNodeProDevice():
device = MockHidapiDevice(vendor_id=0x1b1c, product_id=0x0c0b, address='addr')
node = CommanderPro(device, 'Corsair Lighting Node Pro', 0, 0, 2)
runtime_storage = MockRuntimeStorage(key_prefixes=['testing'])
node.connect(runtime_storage=runtime_storage)
return node
@pytest.fixture
def lightingNodeCoreDevice():
device = MockHidapiDevice(vendor_id=0x1b1c, product_id=0x0c1a, address='addr')
node = CommanderPro(device, 'Corsair Lighting Node Core', 0, 0, 1)
runtime_storage = MockRuntimeStorage(key_prefixes=['testing'])
node.connect(runtime_storage=runtime_storage)
return node
# prepare profile
def test_prepare_profile_valid_max_rpm():
assert _prepare_profile([[10, 400], [20, 5000]], 60) == [(10, 400), (20, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000)]
def test_prepare_profile_add_max_rpm():
assert _prepare_profile([[10, 400]], 60) == [(10, 400), (60, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000)]
assert _prepare_profile([[10, 400], [20, 500], [30, 600], [40, 700], [50, 800]], 60) == [(10, 400), (20, 500), (30, 600), (40, 700), (50, 800), (60, 5000)]
def test_prepare_profile_missing_max_rpm():
with pytest.raises(ValueError):
_prepare_profile([[10, 400], [20, 500], [30, 600], [40, 700], [50, 800], [55, 900]], 60)
def test_prepare_profile_full_set():
assert _prepare_profile([[10, 400], [20, 500], [30, 600], [40, 700], [45, 2000], [50, 5000]], 60) == [(10, 400), (20, 500), (30, 600), (40, 700), (45, 2000), (50, 5000)]
def test_prepare_profile_too_many_points():
with pytest.raises(ValueError):
_prepare_profile([[10, 400], [20, 500], [30, 600], [40, 700], [50, 800], [55, 900]], 60)
def test_prepare_profile_no_points():
assert _prepare_profile([], 60) == [(60, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000)]
def test_prepare_profile_empty_list():
assert _prepare_profile([], 60) == [(60, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000)]
def test_prepare_profile_above_max_temp():
assert _prepare_profile([[10, 400], [70, 2000]], 60) == [(10, 400), (60, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000)]
def test_prepare_profile_temp_low():
assert _prepare_profile([[-10, 400], [70, 2000]], 60) == [(-10, 400), (60, 5000), (60, 5000), (60, 5000), (60, 5000), (60, 5000)]
def test_prepare_profile_max_temp():
assert _prepare_profile([], 100) == [(100, 5000), (100, 5000), (100, 5000), (100, 5000), (100, 5000), (100, 5000)]
# quoted
def test_quoted_empty():
assert _quoted() == ''
def test_quoted_single():
assert _quoted('one arg') == "'one arg'"
def test_quoted_valid():
assert _quoted('one', 'two') == "'one', 'two'"
def test_quoted_not_string():
assert _quoted('test', 500) == "'test', 500"
# fan modes
def test_get_fan_mode_description_auto():
assert _fan_mode_desc(0x00) == None
def test_get_fan_mode_description_unknown():
assert _fan_mode_desc(0x03) == None
assert _fan_mode_desc(0x04) == None
assert _fan_mode_desc(0x10) == None
assert _fan_mode_desc(0xff) == None
def test_get_fan_mode_description_dc():
assert _fan_mode_desc(0x01) == 'DC'
def test_get_fan_mode_description_pwm():
assert _fan_mode_desc(0x02) == 'PWM'
# class methods
def test_commander_constructor(commanderProDeviceUnconnected):
assert commanderProDeviceUnconnected._data is None
assert commanderProDeviceUnconnected._fan_names == ['fan1', 'fan2', 'fan3', 'fan4', 'fan5', 'fan6']
assert commanderProDeviceUnconnected._led_names == ['led1', 'led2']
assert commanderProDeviceUnconnected._temp_probs == 4
assert commanderProDeviceUnconnected._fan_count == 6
def test_lighting_constructor(lightingNodeProDeviceUnconnected):
assert lightingNodeProDeviceUnconnected._data is None
assert lightingNodeProDeviceUnconnected._fan_names == []
assert lightingNodeProDeviceUnconnected._led_names == ['led1', 'led2']
assert lightingNodeProDeviceUnconnected._temp_probs == 0
assert lightingNodeProDeviceUnconnected._fan_count == 0
def test_connect_commander(commanderProDeviceUnconnected):
commanderProDeviceUnconnected.connect()
assert commanderProDeviceUnconnected._data is not None
def test_connect_lighting(lightingNodeProDeviceUnconnected):
lightingNodeProDeviceUnconnected.connect()
assert lightingNodeProDeviceUnconnected._data is not None
@pytest.mark.parametrize('exp,fan_modes', [([(3, 0x01)], {'4': 'dc'}), ([(3, 0x00)], {'4': 'off'}), ([(4, 0x02)], {'5': 'pwm'}), ([(0, 0x01),(2, 0x02),(1, 0x01)], {'1': 'dc', '3':'pwm', '2': 'dc'})])
def test_initialize_commander_pro_fan_modes(commanderProDevice, exp, fan_modes, tmp_path):
responses = [
'000009d4000000000000000000000000', # firmware
'00000500000000000000000000000000', # bootloader
'00010100010000000000000000000000', # temp probes
'00010102000000000000000000000000', # fan set (throw away)
'00010102000000000000000000000000', # fan set (throw away)
'00010102000000000000000000000000', # fan set (throw away)
'00010102000000000000000000000000' # fan probes
]
for d in responses:
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(d)))
commanderProDevice.initialize(direct_access=True, fan_modes=fan_modes)
sent = commanderProDevice.device.sent
assert len(sent) == 4+len(exp)
for i in range(len(exp)):
assert sent[3+i].data[0] == 0x28
assert sent[3+i].data[2] == exp[i][0]
assert sent[3+i].data[3] == exp[i][1]
@pytest.mark.parametrize('has_hwmon,direct_access', [(False, False), (True, True), (True, False)])
def test_initialize_commander_pro(commanderProDevice, has_hwmon, direct_access, tmp_path):
if has_hwmon and not direct_access:
commanderProDevice._hwmon = HwmonDevice('mock_module', tmp_path)
(tmp_path / 'temp1_input').write_text('10000\n')
(tmp_path / 'temp2_input').write_text('20000\n')
(tmp_path / 'temp4_input').write_text('40000\n')
(tmp_path / 'fan1_input').write_text('1100\n')
(tmp_path / 'fan1_label').write_text('fan1 3pin\n')
(tmp_path / 'fan2_input').write_text('1200\n')
(tmp_path / 'fan2_label').write_text('fan1 3pin\n')
(tmp_path / 'fan3_input').write_text('1300\n')
(tmp_path / 'fan3_label').write_text('fan1 4pin\n')
else:
if has_hwmon:
commanderProDevice._hwmon = HwmonDevice('invalid_module', None)
responses = [
'000009d4000000000000000000000000', # firmware
'00000500000000000000000000000000', # bootloader
'00010100010000000000000000000000', # temp probes
'00010102000000000000000000000000' # fan probes
]
for d in responses:
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(d)))
res = commanderProDevice.initialize(direct_access=direct_access)
if has_hwmon and not direct_access:
assert len(res) == 10
i = 0
else:
assert len(res) == 12
assert res[0] == ('Firmware version', '0.9.212', '')
assert res[1] == ('Bootloader version', '0.5', '')
i = 2
assert res[i + 0] == ('Temperature probe 1', True, '')
assert res[i + 1] == ('Temperature probe 2', True, '')
assert res[i + 2] == ('Temperature probe 3', False, '')
assert res[i + 3] == ('Temperature probe 4', True, '')
assert res[i + 4] == ('Fan 1 control mode', 'DC', '')
assert res[i + 5] == ('Fan 2 control mode', 'DC', '')
assert res[i + 6] == ('Fan 3 control mode', 'PWM', '')
assert res[i + 7] == ('Fan 4 control mode', None, '')
assert res[i + 8] == ('Fan 5 control mode', None, '')
assert res[i + 9] == ('Fan 6 control mode', None, '')
data = commanderProDevice._data.load('fan_modes', None)
assert data is not None
assert len(data) == 6
assert data[0] == 0x01
assert data[1] == 0x01
assert data[2] == 0x02
assert data[3] == 0x00
assert data[4] == 0x00
assert data[5] == 0x00
data = commanderProDevice._data.load('temp_sensors_connected', None)
assert data is not None
assert len(data) == 4
assert data[0] == 0x01
assert data[1] == 0x01
assert data[2] == 0x00
assert data[3] == 0x01
def test_initialize_lighting_node(lightingNodeProDevice):
responses = [
'000009d4000000000000000000000000', # firmware
'00000500000000000000000000000000' # bootloader
]
for d in responses:
lightingNodeProDevice.device.preload_read(Report(0, bytes.fromhex(d)))
res = lightingNodeProDevice.initialize()
assert len(res) == 2
assert res[0][1] == '0.9.212'
assert res[1][1] == '0.5'
data = lightingNodeProDevice._data.load('fan_modes', None)
assert data is None
data = lightingNodeProDevice._data.load('temp_sensors_connected', None)
assert data is None
@pytest.mark.parametrize('has_hwmon,direct_access', [(False, False), (True, True), (True, False)])
def test_get_status_commander_pro(commanderProDevice, has_hwmon, direct_access, tmp_path):
if has_hwmon and not direct_access:
commanderProDevice._hwmon = HwmonDevice('mock_module', tmp_path)
(tmp_path / 'temp1_input').write_text('26910\n')
(tmp_path / 'temp2_input').write_text('29220\n')
(tmp_path / 'temp4_input').write_text('25740\n')
(tmp_path / 'fan1_input').write_text('940\n')
(tmp_path / 'fan1_label').write_text('fan1 3pin\n')
(tmp_path / 'fan2_input').write_text('939\n')
(tmp_path / 'fan2_label').write_text('fan1 3pin\n')
(tmp_path / 'fan3_input').write_text('987\n')
(tmp_path / 'fan3_label').write_text('fan1 4pin\n')
(tmp_path / 'in0_input').write_text('12066\n')
(tmp_path / 'in1_input').write_text('4965\n')
(tmp_path / 'in2_input').write_text('3359\n')
else:
if has_hwmon:
commanderProDevice._hwmon = HwmonDevice('invalid_module', None)
responses = [
'000a8300000000000000000000000000', # temp sensor 1
'000b6a00000000000000000000000000', # temp sensor 2
'000a0e00000000000000000000000000', # temp sensor 4
'0003ac00000000000000000000000000', # fan speed 1
'0003ab00000000000000000000000000', # fan speed 2
'0003db00000000000000000000000000', # fan speed 3
'002f2200000000000000000000000000', # get 12v
'00136500000000000000000000000000', # get 5v
'000d1f00000000000000000000000000', # get 3.3v
]
for d in responses:
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(d)))
commanderProDevice._data.store('fan_modes', [0x01, 0x01, 0x02, 0x00, 0x00, 0x00])
commanderProDevice._data.store('temp_sensors_connected', [0x01, 0x01, 0x00, 0x01])
res = commanderProDevice.get_status(direct_access=direct_access)
print(res)
assert len(res) == 9
# temp probes
assert res[0] == ('Temperature 1', pytest.approx(26.91, abs=1e-3), '°C')
assert res[1] == ('Temperature 2', pytest.approx(29.22, abs=1e-3), '°C')
assert res[2] == ('Temperature 4', pytest.approx(25.74, abs=1e-3), '°C')
# fans rpm
assert res[3] == ('Fan 1 speed', 940, 'rpm')
assert res[4] == ('Fan 2 speed', 939, 'rpm')
assert res[5] == ('Fan 3 speed', 987, 'rpm')
# voltages
assert res[6] == ('+12V rail', pytest.approx(12.066, abs=1e-3), 'V')
assert res[7] == ('+5V rail', pytest.approx(4.965, abs=1e-3), 'V')
assert res[8] == ('+3.3V rail', pytest.approx(3.359, abs=1e-3), 'V')
if not has_hwmon or direct_access:
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 9
assert sent[0].data[0] == 0x11
assert sent[1].data[0] == 0x11
assert sent[2].data[0] == 0x11
assert sent[3].data[0] == 0x21
assert sent[4].data[0] == 0x21
assert sent[5].data[0] == 0x21
assert sent[6].data[0] == 0x12
assert sent[7].data[0] == 0x12
assert sent[8].data[0] == 0x12
def test_get_status_lighting_pro(lightingNodeProDevice):
res = lightingNodeProDevice.get_status()
assert len(res) == 0
def test_get_temp_valid_sensor_commander(commanderProDevice):
response = '000a8300000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('temp_sensors_connected', [0x01, 0x01, 0x01, 0x01])
res = commanderProDevice._get_temp(1)
assert res == 26.91
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert sent[0].data[0] == 0x11
assert sent[0].data[1] == 1
def test_get_temp_invalid_sensor_low_commander(commanderProDevice):
response = '000a8300000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('temp_sensors_connected', [0x01, 0x01, 0x01, 0x01])
with pytest.raises(ValueError):
commanderProDevice._get_temp(-1)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_get_temp_invalid_sensor_high_commander(commanderProDevice):
response = '000a8300000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('temp_sensors_connected', [0x01, 0x01, 0x01, 0x01])
with pytest.raises(ValueError):
commanderProDevice._get_temp(4)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_get_temp_lighting(lightingNodeProDevice):
response = '000a8300000000000000000000000000'
lightingNodeProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
lightingNodeProDevice._data.store('temp_sensors_connected', [0x00, 0x00, 0x00, 0x00])
with pytest.raises(ValueError):
lightingNodeProDevice._get_temp(2)
# check the commands sent
sent = lightingNodeProDevice.device.sent
assert len(sent) == 0
def test_get_fan_rpm_valid_commander(commanderProDevice):
response = '0003ac00000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('fan_modes', [0x01, 0x01, 0x02, 0x00, 0x00, 0x00])
res = commanderProDevice._get_fan_rpm(1)
assert res == 940
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert sent[0].data[0] == 0x21
assert sent[0].data[1] == 1
def test_get_fan_rpm_invalid_low_commander(commanderProDevice):
response = '0003ac00000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('fan_modes', [0x01, 0x01, 0x02, 0x00, 0x00, 0x00])
with pytest.raises(ValueError):
commanderProDevice._get_fan_rpm(-1)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_get_fan_rpm_invalid_high_commander(commanderProDevice):
response = '0003ac00000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('fan_modes', [0x01, 0x01, 0x02, 0x00, 0x00, 0x00])
with pytest.raises(ValueError):
commanderProDevice._get_fan_rpm(7)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_get_fan_rpm_lighting(lightingNodeProDevice):
response = '0003ac00000000000000000000000000'
lightingNodeProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
with pytest.raises(ValueError):
lightingNodeProDevice._get_fan_rpm(7)
# check the commands sent
sent = lightingNodeProDevice.device.sent
assert len(sent) == 0
def test_get_hw_fan_channels_all(commanderProDevice):
res = commanderProDevice._get_hw_fan_channels('sync')
assert res == [0, 1, 2, 3, 4, 5]
def test_get_hw_fan_channels_lowercase(commanderProDevice):
res = commanderProDevice._get_hw_fan_channels('fan2')
assert res == [1]
@pytest.mark.parametrize('channel', [
'fan23', 'fan7', 'fan0', 'fan', 'led', 'led1', 'bob'
])
def test_get_hw_fan_channels_invalid(commanderProDevice, channel):
with pytest.raises(ValueError):
commanderProDevice._get_hw_fan_channels(channel)
@pytest.mark.parametrize('channel,expected', [
('led1', [0]), ('led2', [1]), ('sync', [0, 1])
])
def test_get_hw_led_channel_valid(commanderProDevice, channel, expected):
res = commanderProDevice._get_hw_led_channels(channel)
assert res == expected
@pytest.mark.parametrize('channel,expected', [('led', [0]), ('any', [0])])
def test_get_hw_led_channel_valid_node_core(lightingNodeCoreDevice, channel,
expected):
res = lightingNodeCoreDevice._get_hw_led_channels(channel)
assert res == expected
@pytest.mark.parametrize('channel', [
'led0', 'led3', 'led', 'fan', 'fan1', 'bob'
])
def test_get_hw_led_channels_invalid(commanderProDevice, channel):
with pytest.raises(ValueError):
commanderProDevice._get_hw_led_channels(channel)
def test_set_fixed_speed_low(commanderProDevice):
response = '00000000000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('fan_modes', [0x01, 0x01, 0x01, 0x01, 0x01, 0x01])
commanderProDevice.set_fixed_speed('fan4', -10)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert sent[0].data[0] == 0x23
assert sent[0].data[1] == 0x03
assert sent[0].data[2] == 0x00
def test_set_fixed_speed_high(commanderProDevice):
response = '00000000000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('fan_modes', [0x01, 0x01, 0x01, 0x01, 0x01, 0x01])
commanderProDevice.set_fixed_speed('fan3', 110)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert sent[0].data[0] == 0x23
assert sent[0].data[1] == 0x02
assert sent[0].data[2] == 0x64
def test_set_fixed_speed_valid(commanderProDevice):
response = '00000000000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('fan_modes', [0x01, 0x01, 0x01, 0x01, 0x01, 0x01])
commanderProDevice.set_fixed_speed('fan2', 50)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert sent[0].data[0] == 0x23
assert sent[0].data[1] == 0x01
assert sent[0].data[2] == 0x32
def test_set_fixed_speed_valid_unconfigured(commanderProDevice):
response = '00000000000000000000000000000000'
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
commanderProDevice._data.store('fan_modes', [0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
commanderProDevice.set_fixed_speed('fan2', 50)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_set_fixed_speed_valid_multi_fan(commanderProDevice):
responses = [
'00000000000000000000000000000000',
'00000000000000000000000000000000',
'00000000000000000000000000000000',
'00000000000000000000000000000000',
'00000000000000000000000000000000'
]
for d in responses:
commanderProDevice.device.preload_read(Report(0, bytes.fromhex(d)))
commanderProDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
commanderProDevice.set_fixed_speed('sync', 50)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 3
assert sent[0].data[0] == 0x23
assert sent[0].data[1] == 0x00
assert sent[0].data[2] == 0x32
assert sent[1].data[0] == 0x23
assert sent[1].data[1] == 0x02
assert sent[1].data[2] == 0x32
assert sent[2].data[0] == 0x23
assert sent[2].data[1] == 0x03
assert sent[2].data[2] == 0x32
def test_set_fixed_speed_lighting(lightingNodeProDevice):
response = '00000000000000000000000000000000'
lightingNodeProDevice.device.preload_read(Report(0, bytes.fromhex(response)))
with pytest.raises(NotSupportedByDevice):
lightingNodeProDevice.set_fixed_speed('sync', 50)
# check the commands sent
sent = lightingNodeProDevice.device.sent
assert len(sent) == 0
def test_set_speed_profile_valid_multi_fan(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(5):
commanderProDevice.device.preload_read(ignore)
commanderProDevice._data.store('temp_sensors_connected', [0x01, 0x01, 0x00, 0x01])
commanderProDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
commanderProDevice.set_speed_profile('sync', [(10, 500), (20, 1000)])
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 3
assert sent[0].data[0] == 0x25
assert sent[0].data[1] == 0x00
assert sent[0].data[2] == 0x00
assert sent[0].data[3] == 0x03
assert sent[0].data[4] == 0xe8
assert sent[0].data[15] == 0x01
assert sent[0].data[16] == 0xf4
assert sent[1].data[0] == 0x25
assert sent[1].data[1] == 0x02
assert sent[1].data[2] == 0x00
assert sent[2].data[0] == 0x25
assert sent[2].data[1] == 0x03
assert sent[2].data[2] == 0x00
def test_set_speed_profile_invalid_temp_sensor(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(5):
commanderProDevice.device.preload_read(ignore)
commanderProDevice._data.store('temp_sensors_connected', [0x01, 0x01, 0x00, 0x01])
commanderProDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
commanderProDevice.set_speed_profile('fan1', [(10, 500), (20, 1000)], temperature_sensor=10)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert sent[0].data[0] == 0x25
assert sent[0].data[1] == 0x00
assert sent[0].data[2] == 0x03
assert sent[0].data[3] == 0x03
assert sent[0].data[4] == 0xe8
assert sent[0].data[15] == 0x01
assert sent[0].data[16] == 0xf4
def test_set_speed_profile_no_temp_sensors(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(5):
commanderProDevice.device.preload_read(ignore)
commanderProDevice._data.store('temp_sensors_connected', [0x00, 0x00, 0x00, 0x00])
commanderProDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
with pytest.raises(ValueError):
commanderProDevice.set_speed_profile('sync', [(10, 500), (20, 1000)], temperature_sensor=1)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_set_speed_profile_valid(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(5):
commanderProDevice.device.preload_read(ignore)
commanderProDevice._data.store('temp_sensors_connected', [0x01, 0x01, 0x00, 0x01])
commanderProDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
commanderProDevice.set_speed_profile('fan3', [(10, 500), (20, 1000)])
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert sent[0].data[0] == 0x25
assert sent[0].data[1] == 0x02
assert sent[0].data[2] == 0x00
assert sent[0].data[3] == 0x03
assert sent[0].data[4] == 0xe8
assert sent[0].data[15] == 0x01
assert sent[0].data[16] == 0xf4
def test_set_speed_profile_node(lightingNodeProDevice):
ignore = Report(0, bytes(16))
for _ in range(5):
lightingNodeProDevice.device.preload_read(ignore)
lightingNodeProDevice._data.store('temp_sensors_connected', [0x01, 0x00, 0x00, 0x00])
lightingNodeProDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
with pytest.raises(NotSupportedByDevice):
lightingNodeProDevice.set_speed_profile('sync', [(10, 500), (20, 1000)])
# check the commands sent
sent = lightingNodeProDevice.device.sent
assert len(sent) == 0
def test_set_speed_profile_core(lightingNodeCoreDevice):
ignore = Report(0, bytes(16))
for _ in range(5):
lightingNodeCoreDevice.device.preload_read(ignore)
lightingNodeCoreDevice._data.store('temp_sensors_connected', [0x01, 0x00, 0x00, 0x00])
lightingNodeCoreDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
with pytest.raises(NotSupportedByDevice):
lightingNodeCoreDevice.set_speed_profile('sync', [(10, 500), (20, 1000)])
# check the commands sent
sent = lightingNodeCoreDevice.device.sent
assert len(sent) == 0
def test_set_speed_profile_valid_unconfigured(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(5):
commanderProDevice.device.preload_read(ignore)
commanderProDevice._data.store('temp_sensors_connected', [0x00, 0x00, 0x00, 0x00])
commanderProDevice._data.store('fan_modes', [0x01, 0x00, 0x01, 0x01, 0x00, 0x00])
with pytest.raises(ValueError):
commanderProDevice.set_speed_profile('fan2', [(10, 500), (20, 1000)])
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_set_color_hardware_clear(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
effect = {
'channel': 0x01,
'start_led': 0x00,
'num_leds': 0x0f,
'mode': 0x0a,
'speed': 0x00,
'direction': 0x00,
'random_colors': 0x00,
'colors': []
}
commanderProDevice._data.store('saved_effects', [effect])
commanderProDevice.set_color('led1', 'clear', [], )
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is None
def test_set_color_hardware_off(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
effect = {
'channel': 0x01,
'start_led': 0x00,
'num_leds': 0x0f,
'mode': 0x0a,
'speed': 0x00,
'direction': 0x00,
'random_colors': 0x00,
'colors': []
}
commanderProDevice._data.store('saved_effects', [effect])
commanderProDevice.set_color('led1', 'off', [])
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[4] == 0x04
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is None
@pytest.mark.parametrize('directionStr,expected',
[('forward', 0x01), ('backward', 0x00)])
def test_set_color_hardware_direction(commanderProDevice, directionStr, expected):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors, direction=directionStr)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[6] == expected
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_direction_default(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[6] == 0x01
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_speed_default(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[5] == 0x01
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
@pytest.mark.parametrize('speedStr,expected',
[('slow', 0x02), ('fast', 0x00), ('medium', 0x01)])
def test_set_color_hardware_speed(commanderProDevice, speedStr, expected):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors, speed=speedStr)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[5] == expected
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_default_start_end(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[2] == 0 # start led
assert sent[3].data[3] == 204 # num leds
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
@pytest.mark.parametrize('startLED,expected', [
(1, 0x00), (30, 0x1d), (92, 0x5b)
])
def test_set_color_hardware_start_set(commanderProDevice, startLED, expected):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors, start_led=startLED)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[2] == expected # start led
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
@pytest.mark.parametrize('numLED,expected', [
(1, 1), (30, 30), (96, 96), (203, 203), (204, 204), (205, 204) ])
def test_set_color_hardware_num_leds(commanderProDevice, numLED, expected):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors, start_led=1, maximum_leds=numLED)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[3] == expected # num leds
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_too_many_leds(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors, start_led=200, maximum_leds=50)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[2] == 0xc7 # start led
assert sent[3].data[3] == 5 # num led
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_too_few_leds(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors, start_led=1, maximum_leds=0)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[2] == 0x00 # start led
assert sent[3].data[3] == 0x01 # num led
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
@pytest.mark.parametrize('channel,expected',
[('led1', 0x00), ('led2', 0x01)])
def test_set_color_hardware_channel(commanderProDevice, channel, expected):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color(channel, 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[0].data[1] == expected
assert sent[1].data[1] == expected
assert sent[2].data[1] == expected
assert sent[3].data[0] == 0x35
assert sent[3].data[1] == expected
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_sync_channel(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(9):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('sync', 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 2*(3 + 1) + 1
assert sent[0].data[1] == 0
assert sent[1].data[1] == 0
assert sent[2].data[1] == 0
assert sent[3].data[1] == 1
assert sent[4].data[1] == 1
assert sent[5].data[1] == 1
assert sent[6].data[0] == 0x35
assert sent[6].data[1] == 0
assert sent[7].data[0] == 0x35
assert sent[7].data[1] == 1
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 2
def test_set_color_hardware_random_color(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = []
commanderProDevice.set_color('led1', 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[7] == 0x01
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_not_random_color(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
commanderProDevice.set_color('led1', 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[7] == 0x00
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_too_many_colors(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc], [0x00, 0x11, 0x22], [0x33, 0x44, 0x55]]
commanderProDevice.set_color('led1', 'fixed', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[7] == 0x00
assert sent[3].data[9] == 0xaa
assert sent[3].data[10] == 0xbb
assert sent[3].data[11] == 0xcc
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_too_few_colors(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
commanderProDevice.set_color('led1', 'fixed', [])
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[7] == 0x01
assert sent[3].data[9] == 0x00
assert sent[3].data[10] == 0x00
assert sent[3].data[11] == 0x00
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
@pytest.mark.parametrize('modeStr,expected', [
('rainbow', 0x00), ('color_shift', 0x01), ('color_pulse', 0x02),
('color_wave', 0x03), ('fixed', 0x04), ('visor', 0x06), ('marquee', 0x07),
('blink', 0x08), ('sequential', 0x09), ('rainbow2', 0x0a),
])
def test_set_color_hardware_valid_mode(commanderProDevice, modeStr, expected):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
commanderProDevice.set_color('led1', modeStr, [])
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 5
assert sent[3].data[0] == 0x35
assert sent[3].data[4] == expected
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 1
def test_set_color_hardware_invalid_mode(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
colors = [[0xaa, 0xbb, 0xcc]]
with pytest.raises(ValueError):
commanderProDevice.set_color('led1', 'invalid', colors)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is None
def test_set_color_hardware_multipe_commands(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
effect = {
'channel': 0x01,
'start_led': 0x00,
'num_leds': 0x0f,
'mode': 0x0a,
'speed': 0x00,
'direction': 0x00,
'random_colors': 0x00,
'colors': [0xaa, 0xbb, 0xcc]
}
commanderProDevice._data.store('saved_effects', [effect])
commanderProDevice.set_color('led1', 'fixed', [[0x00, 0x11, 0x22]], start_led=16, maximum_leds=5)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 6
assert sent[3].data[0] == 0x35
assert sent[4].data[0] == 0x35
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 2
def test_set_color_hardware_last_commands(commanderProDevice):
for i in range(15):
commanderProDevice.device.preload_read(Report(0, bytes.fromhex('00000000000000000000000000000000')))
effect = {
'channel': 0x01,
'start_led': 0x00,
'num_leds': 0x0f,
'mode': 0x0a,
'speed': 0x00,
'direction': 0x00,
'random_colors': 0x00,
'colors': [0xaa, 0xbb, 0xcc]
}
commanderProDevice._data.store('saved_effects', [effect, effect, effect, effect, effect, effect, effect])
commanderProDevice.set_color('led1', 'fixed', [[0x00, 0x11, 0x22]], start_led=16, maximum_leds=5)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 12
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 8
def test_set_color_hardware_max_commands(commanderProDevice):
for i in range(15):
commanderProDevice.device.preload_read(Report(0, bytes.fromhex('00000000000000000000000000000000')))
effect = {
'channel': 0x01,
'start_led': 0x00,
'num_leds': 0x0f,
'mode': 0x0a,
'speed': 0x00,
'direction': 0x00,
'random_colors': 0x00,
'colors': [0xaa, 0xbb, 0xcc]
}
commanderProDevice._data.store('saved_effects', [effect, effect, effect, effect, effect, effect, effect, effect])
commanderProDevice.set_color('led1', 'fixed', [[0x00, 0x11, 0x22]], start_led=16, maximum_leds=5)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
effects = commanderProDevice._data.load('saved_effects', default=None)
assert effects is not None
assert len(effects) == 8
def test_set_color_hardware_too_many_commands(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(6):
commanderProDevice.device.preload_read(ignore)
effect = {
'channel': 0x01,
'start_led': 0x00,
'num_leds': 0x0f,
'mode': 0x0a,
'speed': 0x00,
'direction': 0x00,
'random_colors': 0x00,
'colors': [0xaa, 0xbb, 0xcc]
}
commanderProDevice._data.store('saved_effects', [effect, effect, effect, effect, effect, effect, effect, effect, effect])
commanderProDevice.set_color('led1', 'fixed', [[0x00, 0x11, 0x22]], start_led=16, maximum_leds=5)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 0
def test_send_command_valid_data(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(2):
commanderProDevice.device.preload_read(ignore)
commanderProDevice._send_command(6, [255, 0, 20, 10, 15])
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert len(sent[0].data) == 64
assert sent[0].data[0] == 6
assert sent[0].data[1] == 255
def test_send_command_no_data(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(2):
commanderProDevice.device.preload_read(ignore)
commanderProDevice._send_command(6)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert len(sent[0].data) == 64
assert sent[0].data[0] == 6
assert sent[0].data[1] == 0
def test_send_command_data_too_long(commanderProDevice):
ignore = Report(0, bytes(16))
for _ in range(2):
commanderProDevice.device.preload_read(ignore)
data = bytearray(100)
commanderProDevice._send_command(3, data)
# check the commands sent
sent = commanderProDevice.device.sent
assert len(sent) == 1
assert len(sent[0].data) == 64
assert sent[0].data[0] == 3
assert sent[0].data[1] == 0