From f82c2f9c71c59ce18054e137d4e4f0d04a88b848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0uvada?= Date: Fri, 27 Mar 2026 10:40:47 +0100 Subject: [PATCH 1/3] linux: up-device-supply: retain mouse classification for devices with mixed input capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miroslav Šuvada --- src/linux/up-device-supply.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/linux/up-device-supply.c b/src/linux/up-device-supply.c index ae4e696..1bcd89c 100644 --- a/src/linux/up-device-supply.c +++ b/src/linux/up-device-supply.c @@ -335,6 +335,9 @@ up_device_supply_sibling_discovered_guess_type (UpDevice *device, g_autoptr (GUdevDevice) parent_device = NULL; g_autoptr (GUdevDevice) parent_sibling = NULL; UpDeviceKind cur_type, new_type; + UpDeviceKind detected_type; + gboolean mixed_mouse_keyboard; + gboolean seen_mixed_mouse_keyboard; gboolean is_same_parent = FALSE; char *new_model_name; char *model_name; @@ -388,6 +391,7 @@ up_device_supply_sibling_discovered_guess_type (UpDevice *device, }; input = G_UDEV_DEVICE (sibling); + native_device = G_UDEV_DEVICE (up_device_get_native (device)); /* Do not process if we already have a "good" guess for the device type. */ g_object_get (device, "type", &cur_type, NULL); @@ -434,6 +438,12 @@ up_device_supply_sibling_discovered_guess_type (UpDevice *device, break; } } + detected_type = new_type; + mixed_mouse_keyboard = detected_type == UP_DEVICE_KIND_MOUSE && + g_udev_device_get_property_as_boolean (input, "ID_INPUT_KEYBOARD"); + if (mixed_mouse_keyboard) + g_object_set_data (G_OBJECT (device), "up-seen-mixed-mouse-keyboard", GINT_TO_POINTER (TRUE)); + seen_mixed_mouse_keyboard = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (device), "up-seen-mixed-mouse-keyboard")); for (i = 0; i < G_N_ELEMENTS (priority); i++) { if (priority[i] == cur_type || priority[i] == new_type) { @@ -442,6 +452,19 @@ up_device_supply_sibling_discovered_guess_type (UpDevice *device, } } + /* Some mice expose a mixed input node carrying both mouse and keyboard + * capabilities for extra buttons. Prefer mouse for that node and keep + * mouse stable if later siblings are keyboard-only. */ + if (detected_type == UP_DEVICE_KIND_MOUSE && + g_udev_device_get_property_as_boolean (input, "ID_INPUT_KEYBOARD") && + (cur_type == UP_DEVICE_KIND_KEYBOARD || new_type == UP_DEVICE_KIND_KEYBOARD)) + new_type = UP_DEVICE_KIND_MOUSE; + + if (seen_mixed_mouse_keyboard && + cur_type == UP_DEVICE_KIND_MOUSE && + detected_type == UP_DEVICE_KIND_KEYBOARD) + new_type = UP_DEVICE_KIND_MOUSE; + /* Match audio sub-type */ if (new_type == UP_DEVICE_KIND_OTHER_AUDIO) { const char *form_factor = g_udev_device_get_property (input, "SOUND_FORM_FACTOR"); @@ -469,7 +492,6 @@ up_device_supply_sibling_discovered_guess_type (UpDevice *device, } if (cur_type != new_type) { - native_device = G_UDEV_DEVICE (up_device_get_native (device)); parent_device = g_udev_device_get_parent (native_device); parent_sibling = g_udev_device_get_parent (input); From ecd2d4eb256be356a16031b66aee2f57e60bc0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0uvada?= Date: Fri, 27 Mar 2026 10:40:54 +0100 Subject: [PATCH 2/3] linux: integration-test: test for HID++ mouse with mixed input capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miroslav Šuvada --- src/linux/integration-test.py | 391 +++++++++++++++++++++++++++++++++- 1 file changed, 390 insertions(+), 1 deletion(-) diff --git a/src/linux/integration-test.py b/src/linux/integration-test.py index 9ceb582..6401a65 100755 --- a/src/linux/integration-test.py +++ b/src/linux/integration-test.py @@ -4124,7 +4124,14 @@ class Tests(dbusmock.DBusTestCase): "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A/input/input22", parent, [], - ["DEVNAME", "input/mouse3", "ID_INPUT_MOUSE", "1"], + [ + "DEVNAME", + "input/mouse3", + "ID_INPUT_MOUSE", + "1", + "ID_INPUT_KEYBOARD", + "1", + ], ) self.testbed.add_device( @@ -4174,6 +4181,388 @@ class Tests(dbusmock.DBusTestCase): ) self.stop_daemon() + def test_hidpp_mouse_with_keyboard_interface(self): + """HID++ mouse with extra keyboard interface stays classified as mouse""" + + parent = self.testbed.add_device( + "usb", "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2", None, [], [] + ) + parent = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009", + parent, + [], + [], + ) + dev = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A", + parent, + [], + [], + ) + + parent = dev + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A/input/input22", + parent, + [], + [ + "DEVNAME", + "input/mouse3", + "ID_INPUT_MOUSE", + "1", + "ID_INPUT_KEYBOARD", + "1", + ], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A/input/input23", + parent, + [], + ["DEVNAME", "input/event7", "ID_INPUT_KEYBOARD", "1"], + ) + + self.testbed.add_device( + "power_supply", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A/power_supply/hidpp_battery_3", + parent, + [ + "type", + "Battery", + "scope", + "Device", + "present", + "1", + "online", + "1", + "status", + "Discharging", + "capacity", + "30", + "serial_number", + "654321", + "model_name", + "Fancy Logitech mouse", + ], + [], + ) + + self.start_daemon() + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 1) + mousebat0_up = devs[0] + + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Model"), "Fancy Logitech mouse" + ) + self.assertAlmostEqual( + self.get_dbus_dev_property(mousebat0_up, "Percentage"), 30.0 + ) + self.assertEqual(self.get_dbus_dev_property(mousebat0_up, "PowerSupply"), False) + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Type"), UP_DEVICE_KIND_MOUSE + ) + self.assertEqual(self.get_dbus_dev_property(mousebat0_up, "Serial"), "654321") + self.assertEqual(self.get_dbus_property("OnBattery"), False) + self.assertEqual( + self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE + ) + self.stop_daemon() + + def test_hidpp_mouse_with_keyboard_interface_reverse_discovery(self): + """HID++ mouse remains a mouse when keyboard sibling appears first""" + + parent = self.testbed.add_device( + "usb", "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2", None, [], [] + ) + parent = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009", + parent, + [], + [], + ) + dev = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A", + parent, + [], + [], + ) + + parent = dev + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A/input/input22", + parent, + [], + ["DEVNAME", "input/event7", "ID_INPUT_KEYBOARD", "1"], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A/input/input23", + parent, + [], + [ + "DEVNAME", + "input/mouse3", + "ID_INPUT_MOUSE", + "1", + "ID_INPUT_KEYBOARD", + "1", + ], + ) + + self.testbed.add_device( + "power_supply", + "/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.2/0003:046D:C52B.0009/0003:046D:4101.000A/power_supply/hidpp_battery_3", + parent, + [ + "type", + "Battery", + "scope", + "Device", + "present", + "1", + "online", + "1", + "status", + "Discharging", + "capacity", + "30", + "serial_number", + "654321", + "model_name", + "Fancy Logitech mouse", + ], + [], + ) + + self.start_daemon() + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 1) + mousebat0_up = devs[0] + + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Model"), "Fancy Logitech mouse" + ) + self.assertAlmostEqual( + self.get_dbus_dev_property(mousebat0_up, "Percentage"), 30.0 + ) + self.assertEqual(self.get_dbus_dev_property(mousebat0_up, "PowerSupply"), False) + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Type"), UP_DEVICE_KIND_MOUSE + ) + self.assertEqual(self.get_dbus_dev_property(mousebat0_up, "Serial"), "654321") + self.assertEqual(self.get_dbus_property("OnBattery"), False) + self.assertEqual( + self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE + ) + self.stop_daemon() + + def test_hidpp_receiver_mouse_with_keyboard_interface(self): + """HID++ receiver mouse with mixed node stays classified as mouse""" + + usb_parent = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5", + None, + [], + [], + ) + intf = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2", + usb_parent, + [], + [], + ) + receiver = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004", + intf, + [], + [], + ) + mouse = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006", + receiver, + [], + [], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006/input/input72", + mouse, + [], + [ + "DEVNAME", + "input/event12", + "ID_INPUT_MOUSE", + "1", + "ID_INPUT_KEYBOARD", + "1", + ], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006/input/input73", + mouse, + [], + ["DEVNAME", "input/event13", "ID_INPUT_KEYBOARD", "1"], + ) + + self.testbed.add_device( + "power_supply", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006/power_supply/hidpp_battery_0", + mouse, + [ + "type", + "Battery", + "scope", + "Device", + "present", + "1", + "online", + "1", + "status", + "Discharging", + "capacity", + "69", + "model_name", + "Logitech G502 X PLUS", + ], + [], + ) + + self.start_daemon() + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 1) + mousebat0_up = devs[0] + + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Model"), "Logitech G502 X PLUS" + ) + self.assertAlmostEqual( + self.get_dbus_dev_property(mousebat0_up, "Percentage"), 69.0 + ) + self.assertEqual(self.get_dbus_dev_property(mousebat0_up, "PowerSupply"), False) + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Type"), UP_DEVICE_KIND_MOUSE + ) + self.assertEqual(self.get_dbus_property("OnBattery"), False) + self.assertEqual( + self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE + ) + self.stop_daemon() + + def test_hidpp_receiver_mouse_with_keyboard_interface_reverse_discovery(self): + """HID++ receiver mixed mouse/keyboard remains mouse when keyboard appears first""" + + usb_parent = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5", + None, + [], + [], + ) + intf = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2", + usb_parent, + [], + [], + ) + receiver = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004", + intf, + [], + [], + ) + mouse = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006", + receiver, + [], + [], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006/input/input73", + mouse, + [], + ["DEVNAME", "input/event13", "ID_INPUT_KEYBOARD", "1"], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006/input/input72", + mouse, + [], + [ + "DEVNAME", + "input/event12", + "ID_INPUT_MOUSE", + "1", + "ID_INPUT_KEYBOARD", + "1", + ], + ) + + self.testbed.add_device( + "power_supply", + "/devices/pci0000:00/0000:00:01.2/0000:01:00.0/0000:02:08.0/0000:0d:00.1/usb1/1-5/1-5:1.2/0003:046D:C53A.0004/0003:046D:4099.0006/power_supply/hidpp_battery_0", + mouse, + [ + "type", + "Battery", + "scope", + "Device", + "present", + "1", + "online", + "1", + "status", + "Discharging", + "capacity", + "69", + "model_name", + "Logitech G502 X PLUS", + ], + [], + ) + + self.start_daemon() + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 1) + mousebat0_up = devs[0] + + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Model"), "Logitech G502 X PLUS" + ) + self.assertAlmostEqual( + self.get_dbus_dev_property(mousebat0_up, "Percentage"), 69.0 + ) + self.assertEqual(self.get_dbus_dev_property(mousebat0_up, "PowerSupply"), False) + self.assertEqual( + self.get_dbus_dev_property(mousebat0_up, "Type"), UP_DEVICE_KIND_MOUSE + ) + self.assertEqual(self.get_dbus_property("OnBattery"), False) + self.assertEqual( + self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE + ) + self.stop_daemon() + def test_hidpp_two_mouses_unifying_receiver(self): """Upower shows wrong model name when a unifying receiver connects two mouses #309""" From 81455498ef84769a1a7ef5e77753b6dd69eea08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0uvada?= Date: Mon, 30 Mar 2026 13:59:50 +0200 Subject: [PATCH 3/3] linux: integration-test: tests for Razer mouse with mixed input capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miroslav Šuvada --- src/linux/integration-test.py | 214 ++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/src/linux/integration-test.py b/src/linux/integration-test.py index 6401a65..dc44050 100755 --- a/src/linux/integration-test.py +++ b/src/linux/integration-test.py @@ -4563,6 +4563,220 @@ class Tests(dbusmock.DBusTestCase): ) self.stop_daemon() + def test_razer_mouse_with_keyboard_interface(self): + """Razer mouse with mixed mouse/keyboard input stays classified as mouse""" + + usb_parent = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2", + None, + [], + ["DEVTYPE", "usb_device"], + ) + + intf0 = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0", + usb_parent, + [], + ["DEVTYPE", "usb_interface"], + ) + hid0 = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0/0003:1532:009C.003D", + intf0, + [], + ["HID_NAME", "Razer Razer DeathAdder V2 X HyperSpeed"], + ) + + intf2 = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.2", + usb_parent, + [], + ["DEVTYPE", "usb_interface"], + ) + hid2 = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.2/0003:1532:009C.003E", + intf2, + [], + ["HID_NAME", "Razer Razer DeathAdder V2 X HyperSpeed"], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0/0003:1532:009C.003D/input/input70", + hid0, + [], + [ + "DEVNAME", + "input/event10", + "ID_INPUT_MOUSE", + "1", + "ID_INPUT_KEYBOARD", + "1", + ], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.2/0003:1532:009C.003E/input/input71", + hid2, + [], + ["DEVNAME", "input/event11", "ID_INPUT_KEYBOARD", "1"], + ) + + self.testbed.add_device( + "power_supply", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0/0003:1532:009C.003D/power_supply/razermouse_battery_0", + hid0, + [ + "type", + "Battery", + "scope", + "Device", + "present", + "1", + "online", + "1", + "status", + "Discharging", + "capacity", + "69", + "model_name", + "Razer DeathAdder V2 X HyperSpeed", + ], + [], + ) + + self.start_daemon() + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 1) + razer_up = devs[0] + + self.assertEqual( + self.get_dbus_dev_property(razer_up, "Model"), + "Razer DeathAdder V2 X HyperSpeed", + ) + self.assertAlmostEqual(self.get_dbus_dev_property(razer_up, "Percentage"), 69.0) + self.assertEqual(self.get_dbus_dev_property(razer_up, "PowerSupply"), False) + self.assertEqual( + self.get_dbus_dev_property(razer_up, "Type"), UP_DEVICE_KIND_MOUSE + ) + self.assertEqual(self.get_dbus_property("OnBattery"), False) + self.assertEqual( + self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE + ) + self.stop_daemon() + + def test_razer_mouse_with_keyboard_interface_reverse_discovery(self): + """Razer mouse remains a mouse when keyboard-only sibling appears first""" + + usb_parent = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2", + None, + [], + ["DEVTYPE", "usb_device"], + ) + + intf0 = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0", + usb_parent, + [], + ["DEVTYPE", "usb_interface"], + ) + hid0 = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0/0003:1532:009C.003D", + intf0, + [], + ["HID_NAME", "Razer Razer DeathAdder V2 X HyperSpeed"], + ) + + intf2 = self.testbed.add_device( + "usb", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.2", + usb_parent, + [], + ["DEVTYPE", "usb_interface"], + ) + hid2 = self.testbed.add_device( + "hid", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.2/0003:1532:009C.003E", + intf2, + [], + ["HID_NAME", "Razer Razer DeathAdder V2 X HyperSpeed"], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.2/0003:1532:009C.003E/input/input71", + hid2, + [], + ["DEVNAME", "input/event11", "ID_INPUT_KEYBOARD", "1"], + ) + + self.testbed.add_device( + "input", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0/0003:1532:009C.003D/input/input70", + hid0, + [], + [ + "DEVNAME", + "input/event10", + "ID_INPUT_MOUSE", + "1", + "ID_INPUT_KEYBOARD", + "1", + ], + ) + + self.testbed.add_device( + "power_supply", + "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2.2/1-2.2.2:1.0/0003:1532:009C.003D/power_supply/razermouse_battery_0", + hid0, + [ + "type", + "Battery", + "scope", + "Device", + "present", + "1", + "online", + "1", + "status", + "Discharging", + "capacity", + "69", + "model_name", + "Razer DeathAdder V2 X HyperSpeed", + ], + [], + ) + + self.start_daemon() + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 1) + razer_up = devs[0] + + self.assertEqual( + self.get_dbus_dev_property(razer_up, "Model"), + "Razer DeathAdder V2 X HyperSpeed", + ) + self.assertAlmostEqual(self.get_dbus_dev_property(razer_up, "Percentage"), 69.0) + self.assertEqual(self.get_dbus_dev_property(razer_up, "PowerSupply"), False) + self.assertEqual( + self.get_dbus_dev_property(razer_up, "Type"), UP_DEVICE_KIND_MOUSE + ) + self.assertEqual(self.get_dbus_property("OnBattery"), False) + self.assertEqual( + self.get_dbus_display_property("WarningLevel"), UP_DEVICE_LEVEL_NONE + ) + self.stop_daemon() + def test_hidpp_two_mouses_unifying_receiver(self): """Upower shows wrong model name when a unifying receiver connects two mouses #309"""