Compare commits
975 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a108b3ab7 | ||
|
|
fac6cd6840 | ||
|
|
edf610d235 | ||
|
|
dac9c1bd8e | ||
|
|
352375aa9a | ||
|
|
44722560d2 | ||
|
|
101e24c7bf | ||
|
|
b7e8e646b5 | ||
|
|
97fa7de28a | ||
|
|
302720b0db | ||
|
|
813c238704 | ||
|
|
354c914bd2 | ||
|
|
574aee0cc7 | ||
|
|
a3cdcf4cd1 | ||
|
|
592ced0a53 | ||
|
|
3a2593798e | ||
|
|
67b7d46844 | ||
|
|
5411e43b77 | ||
|
|
66dc0bd285 | ||
|
|
f641554ed5 | ||
|
|
beec4c2130 | ||
|
|
f5bf488ffc | ||
|
|
1deaa09142 | ||
|
|
88a0dfff6f | ||
|
|
f28da6e7c4 | ||
|
|
5290980ac8 | ||
|
|
4d23d3abac | ||
|
|
0c4e757cf9 | ||
|
|
d487a0d889 | ||
|
|
65824a4c08 | ||
|
|
58a1c7ffb0 | ||
|
|
1f896d2b9e | ||
|
|
377c954933 | ||
|
|
4744ce47b2 | ||
|
|
b01b74f2a8 | ||
|
|
6282ae05e8 | ||
|
|
b5e1f47c50 | ||
|
|
8ffaeb112f | ||
|
|
2189d4a35b | ||
|
|
706ee2990f | ||
|
|
06875fb9ab | ||
|
|
f6b0cc59ed | ||
|
|
e3aacdbf05 | ||
|
|
45dc81b4c4 | ||
|
|
0324fd9a1e | ||
|
|
ca9f9b3447 | ||
|
|
624247d378 | ||
|
|
5f8dbdeb6d | ||
|
|
2bdb844557 | ||
|
|
422f336d70 | ||
|
|
563ef0d8ef | ||
|
|
3a85c4f64b | ||
|
|
4056f5b9d3 | ||
|
|
442a7d18a5 | ||
|
|
79be4b20d7 | ||
|
|
b2f3b56c31 | ||
|
|
072f932206 | ||
|
|
d5a3a4fe7e | ||
|
|
a49b7938e0 | ||
|
|
1eef88eb44 | ||
|
|
eda1399330 | ||
|
|
8dbc4b5707 | ||
|
|
687a28d2c2 | ||
|
|
f733390bd1 | ||
|
|
ac6fa9643f | ||
|
|
f28053a09a | ||
|
|
49145ae272 | ||
|
|
af26870e83 | ||
|
|
007163a563 | ||
|
|
a9acdbe47a | ||
|
|
955df30c7f | ||
|
|
23c0397764 | ||
|
|
789f5f05c3 | ||
|
|
ea2c22c015 | ||
|
|
1c09b9c45d | ||
|
|
dd9626f7a4 | ||
|
|
35909afb0d | ||
|
|
8e67bbbc11 | ||
|
|
a5813e4e23 | ||
|
|
d439d43be6 | ||
|
|
11465f2b2a | ||
|
|
079f2dacd4 | ||
|
|
7c6bd4202d | ||
|
|
1740a9a8c4 | ||
|
|
0cadc3247e | ||
|
|
e23de2ee9f | ||
|
|
8fb52a3f37 | ||
|
|
3edac85b90 | ||
|
|
64c76e51ef | ||
|
|
02cac598a0 | ||
|
|
02a47b4003 | ||
|
|
4afe8c893b | ||
|
|
5afa094175 | ||
|
|
196705ba1f | ||
|
|
4c46a999f7 | ||
|
|
7bd31c0d95 | ||
|
|
dc2a1adf26 | ||
|
|
e89b50cdf2 | ||
|
|
d08e0a9574 | ||
|
|
22d37c4c1c | ||
|
|
546ccb7ac2 | ||
|
|
1ab03d4c9f | ||
|
|
a6fcb75aa8 | ||
|
|
907c5ab075 | ||
|
|
515f994ab8 | ||
|
|
25905c5d77 | ||
|
|
babf0f4ded | ||
|
|
41664b8e21 | ||
|
|
5c080a7831 | ||
|
|
5aa4fa0239 | ||
|
|
4f4e610635 | ||
|
|
39791be440 | ||
|
|
2814350f9f | ||
|
|
4a28b48275 | ||
|
|
97474a21de | ||
|
|
719106bf0f | ||
|
|
0a735a32ed | ||
|
|
bd26b9340d | ||
|
|
f45ae5ffad | ||
|
|
0520dde990 | ||
|
|
e53781ec6e | ||
|
|
1bc189e2fe | ||
|
|
91bcfa28ea | ||
|
|
64c1de32da | ||
|
|
56762b5494 | ||
|
|
77e21a0b63 | ||
|
|
d6fa5269c5 | ||
|
|
0198d58dc7 | ||
|
|
c30f0a79bd | ||
|
|
89ee83b906 | ||
|
|
0bef7b84f8 | ||
|
|
c3e90bc73e | ||
|
|
814b7f30ba | ||
|
|
8039c035a7 | ||
|
|
430b70711b | ||
|
|
82915eea4b | ||
|
|
995544396c | ||
|
|
4678f52293 | ||
|
|
26c5a5f9ba | ||
|
|
c6ccc0638d | ||
|
|
6ba8c6c3fc | ||
|
|
9087f4187c | ||
|
|
673c81c096 | ||
|
|
43bfa80753 | ||
|
|
3884ce14d7 | ||
|
|
4fafd4f6be | ||
|
|
0bec0e02b1 | ||
|
|
59c0367283 | ||
|
|
63f523dac6 | ||
|
|
4822975acb | ||
|
|
c389c9c2b7 | ||
|
|
76e70799aa | ||
|
|
2a94cc9233 | ||
|
|
ae9e862f19 | ||
|
|
12f4e187d6 | ||
|
|
51dd2a5d28 | ||
|
|
6981555804 | ||
|
|
3583759d3a | ||
|
|
c92a889b68 | ||
|
|
b9fb005c9c | ||
|
|
a06ea6de11 | ||
|
|
a868b477e1 | ||
|
|
c799e038de | ||
|
|
8468be2126 | ||
|
|
1ff9c3a40b | ||
|
|
9b201f6b0d | ||
|
|
afda6f9f66 | ||
|
|
5cc0b8854d | ||
|
|
f88951c56d | ||
|
|
2aab7f71fd | ||
|
|
6376980213 | ||
|
|
1bb08d011d | ||
|
|
966c55e69d | ||
|
|
6b14004a6f | ||
|
|
7a4f7bbb84 | ||
|
|
155e2f8c40 | ||
|
|
6a1968beff | ||
|
|
f4557233f1 | ||
|
|
a597b81f5a | ||
|
|
a587ae65d1 | ||
|
|
6522b5fef0 | ||
|
|
7f78f0c580 | ||
|
|
127a8053d8 | ||
|
|
6c0acee645 | ||
|
|
60c52eb13f | ||
|
|
8bac8d68f4 | ||
|
|
087a5d2c42 | ||
|
|
481511b6c2 | ||
|
|
21d98294bd | ||
|
|
ebbb54758b | ||
|
|
c07c115ee3 | ||
|
|
8c02320795 | ||
|
|
932164458a | ||
|
|
de79bf1b5c | ||
|
|
5944103aef | ||
|
|
ab4226e066 | ||
|
|
a66121db0d | ||
|
|
eacfdea37f | ||
|
|
68b1f4e413 | ||
|
|
947035a7b7 | ||
|
|
e54134cda9 | ||
|
|
1609c3e98a | ||
|
|
ed9aa76547 | ||
|
|
b8529030e6 | ||
|
|
1412a07899 | ||
|
|
f79ad67ea7 | ||
|
|
dd8018a4d5 | ||
|
|
350784eb70 | ||
|
|
f0fc63e5b7 | ||
|
|
51375d4510 | ||
|
|
2c0ea34694 | ||
|
|
867edf8516 | ||
|
|
dcd1a88250 | ||
|
|
8f95dd323d | ||
|
|
336b778398 | ||
|
|
572c7f5e59 | ||
|
|
6b12b7935f | ||
|
|
ade2fbacec | ||
|
|
a59368f3e7 | ||
|
|
d021d87656 | ||
|
|
5d0d353c74 | ||
|
|
e00d680fc9 | ||
|
|
59b7285fdf | ||
|
|
988bdc2c8d | ||
|
|
1a67b33860 | ||
|
|
d43bffc9d9 | ||
|
|
f8079f4f9d | ||
|
|
47e7f997d5 | ||
|
|
8c0cf9fe9b | ||
|
|
fa8bd83ba3 | ||
|
|
697fb999b4 | ||
|
|
aeb943fc9e | ||
|
|
434af009cb | ||
|
|
cc010cb653 | ||
|
|
e450e959a6 | ||
|
|
10f3421021 | ||
|
|
8eba60d617 | ||
|
|
e3d658ea10 | ||
|
|
ef1c051e10 | ||
|
|
bc566718b4 | ||
|
|
22719f21fc | ||
|
|
c51daa922c | ||
|
|
632d8804be | ||
|
|
a4b7194490 | ||
|
|
ea80c6d639 | ||
|
|
a01e4b2efe | ||
|
|
801bdfa224 | ||
|
|
410d100dc6 | ||
|
|
7078750a3f | ||
|
|
360c92f6b0 | ||
|
|
2377fef3a8 | ||
|
|
684afa871e | ||
|
|
dd2f884504 | ||
|
|
9dd05bb727 | ||
|
|
e96a0fbfbb | ||
|
|
f9618dd0ab | ||
|
|
db0656967b | ||
|
|
53ec751cf1 | ||
|
|
45cc161cc7 | ||
|
|
1ccd2d1856 | ||
|
|
d9d37edc2f | ||
|
|
ae405871fb | ||
|
|
f4700f07ce | ||
|
|
d2d19a615d | ||
|
|
c36cc506b6 | ||
|
|
7180693f89 | ||
|
|
aa08356823 | ||
|
|
197ff7365d | ||
|
|
a0c6a6ce59 | ||
|
|
934ecb821d | ||
|
|
f0cf949336 | ||
|
|
ecd245e8dc | ||
|
|
f65f000d38 | ||
|
|
3b60f205f7 | ||
|
|
d6c93cfcfe | ||
|
|
cbd71f9d24 | ||
|
|
45d5119a5a | ||
|
|
773cb7f45f | ||
|
|
d74e3a9b92 | ||
|
|
fe1fba7ac6 | ||
|
|
4e20ef61c8 | ||
|
|
a750b32d14 | ||
|
|
69006c48c6 | ||
|
|
b12d03dfe3 | ||
|
|
ddbf93fb24 | ||
|
|
65c55b789c | ||
|
|
d83530bee3 | ||
|
|
e6c02f5c52 | ||
|
|
122d76d061 | ||
|
|
fb1442dbcb | ||
|
|
0b8141f092 | ||
|
|
137c32262b | ||
|
|
7c726738b1 | ||
|
|
2570f584b9 | ||
|
|
4c1f568fab | ||
|
|
27bd71253e | ||
|
|
9a9a82016f | ||
|
|
aa03cef61f | ||
|
|
c14e30b6f5 | ||
|
|
b5d43cdc3c | ||
|
|
1edd8a577b | ||
|
|
358e0958bb | ||
|
|
fa5fba796b | ||
|
|
ce215228f3 | ||
|
|
28e43395ab | ||
|
|
e09231c05a | ||
|
|
f29de0f3dd | ||
|
|
ba540338e2 | ||
|
|
2442fee341 | ||
|
|
bbadd3e755 | ||
|
|
2fdce2f938 | ||
|
|
f1fad2d16b | ||
|
|
8a5700b44e | ||
|
|
e046455a05 | ||
|
|
ddbc27486c | ||
|
|
2041007b38 | ||
|
|
7b7e17819e | ||
|
|
f855cd60e1 | ||
|
|
883ed9549d | ||
|
|
d1858f747b | ||
|
|
aa7d1b6410 | ||
|
|
bc3a98ddb2 | ||
|
|
5b01f375e0 | ||
|
|
dd2755909d | ||
|
|
b052ab9ef0 | ||
|
|
9c768d60a1 | ||
|
|
d1d0ab85ff | ||
|
|
e1eee2e078 | ||
|
|
5c38f90cd6 | ||
|
|
45d49b9de8 | ||
|
|
4ccb8ab26d | ||
|
|
ab162583e4 | ||
|
|
e25b47aa1c | ||
|
|
63a98819a6 | ||
|
|
27d3d80d30 | ||
|
|
478381a994 | ||
|
|
51a15a960f | ||
|
|
92967eed23 | ||
|
|
57338303e8 | ||
|
|
e735ce10f0 | ||
|
|
d75b6d2f2b | ||
|
|
bd1470dad7 | ||
|
|
15f97681ef | ||
|
|
7075ce910a | ||
|
|
5ba816dd38 | ||
|
|
a515cc3860 | ||
|
|
73344cbf26 | ||
|
|
cf27328d13 | ||
|
|
869f1e4791 | ||
|
|
e2d65a690e | ||
|
|
3141f06472 | ||
|
|
b88cf5a07b | ||
|
|
a344ad9867 | ||
|
|
76e327d2fa | ||
|
|
0ae0ef195a | ||
|
|
b1f74a1280 | ||
|
|
30431fbfb8 | ||
|
|
fb4c055fbf | ||
|
|
3ea51fe1a2 | ||
|
|
afd36764c2 | ||
|
|
b694809394 | ||
|
|
66296265ba | ||
|
|
952495ce04 | ||
|
|
29eb377267 | ||
|
|
47345e1dcb | ||
|
|
36fc6de760 | ||
|
|
6c0aca8c63 | ||
|
|
bd93689109 | ||
|
|
4e5d4853d6 | ||
|
|
bb2361a986 | ||
|
|
7c8d4815f0 | ||
|
|
0f05589cd6 | ||
|
|
21db60a14e | ||
|
|
091c28ca8b | ||
|
|
9af353dd32 | ||
|
|
981ff00ca3 | ||
|
|
ec0197df3d | ||
|
|
2111107ff4 | ||
|
|
2b08c5e0c9 | ||
|
|
cc8a6c4ab5 | ||
|
|
437e17cf37 | ||
|
|
22191d7d0c | ||
|
|
ba890c2d2e | ||
|
|
9a6cdd9cee | ||
|
|
32324f3e66 | ||
|
|
ffe297b239 | ||
|
|
7fe5983f7c | ||
|
|
d5f14bb5f8 | ||
|
|
f3049ed451 | ||
|
|
8485494ea7 | ||
|
|
7a75813a84 | ||
|
|
35830e3d50 | ||
|
|
46fd4dac56 | ||
|
|
ebd8635f84 | ||
|
|
efbf43b472 | ||
|
|
8a6d8e2ed1 | ||
|
|
bff99799ab | ||
|
|
1434b2a50d | ||
|
|
c3fdbfb643 | ||
|
|
a9ebac264f | ||
|
|
60a591ab6b | ||
|
|
ab3a6b50d4 | ||
|
|
b193b39701 | ||
|
|
806d67a72f | ||
|
|
9c465cd998 | ||
|
|
024a71b618 | ||
|
|
f144816256 | ||
|
|
f0eeea8625 | ||
|
|
72819b6130 | ||
|
|
23e4f32e11 | ||
|
|
243d1548dc | ||
|
|
c3bc56f306 | ||
|
|
288c607069 | ||
|
|
55c307d0e5 | ||
|
|
69febfcea5 | ||
|
|
9934755566 | ||
|
|
a7561eaa9b | ||
|
|
689661169a | ||
|
|
eac40abc3e | ||
|
|
372e4e0949 | ||
|
|
e42f7f1393 | ||
|
|
3860d4a56b | ||
|
|
3b75b69970 | ||
|
|
a4f0eab855 | ||
|
|
f54ea27d97 | ||
|
|
db48fe1b40 | ||
|
|
d0a0e902a7 | ||
|
|
95a6412d5c | ||
|
|
5faecbf4f3 | ||
|
|
5af1719384 | ||
|
|
6a66370ffe | ||
|
|
c0874220d2 | ||
|
|
9c67b1b494 | ||
|
|
22656d5b82 | ||
|
|
d55963caba | ||
|
|
413cebe40f | ||
|
|
140ffae01a | ||
|
|
68251777fb | ||
|
|
188b20a4f1 | ||
|
|
5fba1db11c | ||
|
|
21b1a722fc | ||
|
|
715c50685b | ||
|
|
dd0ba17351 | ||
|
|
576dd4cdd4 | ||
|
|
a2a0380401 | ||
|
|
f1a85f45bd | ||
|
|
200cf48df4 | ||
|
|
14b5993dc3 | ||
|
|
585ba93588 | ||
|
|
e30a692a6e | ||
|
|
edc563e0e4 | ||
|
|
969ca59948 | ||
|
|
53abd22176 | ||
|
|
f449b37bda | ||
|
|
c97f4082dd | ||
|
|
5e6761c61b | ||
|
|
b2c87b4f80 | ||
|
|
58fcb074ff | ||
|
|
dbff65a4d1 | ||
|
|
a7ac589692 | ||
|
|
3d4570cd1f | ||
|
|
b6bc35ed3e | ||
|
|
5d4d0c07ad | ||
|
|
15cb97c56e | ||
|
|
f902b32755 | ||
|
|
fcfc7cd6fc | ||
|
|
c3718d22d6 | ||
|
|
2829acd6f5 | ||
|
|
c35ae8ffc9 | ||
|
|
f7159e9338 | ||
|
|
4074fb7750 | ||
|
|
444169d84b | ||
|
|
5d5f97a776 | ||
|
|
a8c5d3bc24 | ||
|
|
dd2a18d60e | ||
|
|
a3f9860181 | ||
|
|
1fed000855 | ||
|
|
61dfefde94 | ||
|
|
454fbcbc6e | ||
|
|
8112e90ded | ||
|
|
a4c0ad3d73 | ||
|
|
30f2afacb7 | ||
|
|
2a27c1d14e | ||
|
|
cccf4ec428 | ||
|
|
38c76393b1 | ||
|
|
ce9b10dc2d | ||
|
|
13e89dc219 | ||
|
|
16af3a9acc | ||
|
|
c690b2c9e9 | ||
|
|
43ab629a26 | ||
|
|
b83a14e16f | ||
|
|
a966bbff6f | ||
|
|
141a5c3dee | ||
|
|
994bb07bc4 | ||
|
|
24f658d8fe | ||
|
|
49d77a2f90 | ||
|
|
876cf35954 | ||
|
|
46c0448854 | ||
|
|
d78c7ed9be | ||
|
|
51d0ccf654 | ||
|
|
9c8252b59f | ||
|
|
488a78cc52 | ||
|
|
0e6d088372 | ||
|
|
d4d1a4f8e2 | ||
|
|
34801bfd1e | ||
|
|
948b4c4d51 | ||
|
|
85efe92d30 | ||
|
|
b01636e05d | ||
|
|
c0a7838429 | ||
|
|
eda4112501 | ||
|
|
dbb9025e26 | ||
|
|
beee0bc8c2 | ||
|
|
1bd6fd512b | ||
|
|
ab8421b2f2 | ||
|
|
1be56dd072 | ||
|
|
c023f81f1a | ||
|
|
47f64d40de | ||
|
|
d0b8233c9a | ||
|
|
39534d11dd | ||
|
|
ca2f5f927c | ||
|
|
2ab040591c | ||
|
|
8b26759731 | ||
|
|
0e43287386 | ||
|
|
7221ceb7b4 | ||
|
|
66416ff4cc | ||
|
|
0d89d1e6c8 | ||
|
|
81765ee971 | ||
|
|
b72273b2a9 | ||
|
|
e697a2d6cf | ||
|
|
94d07eed53 | ||
|
|
64ce3872b4 | ||
|
|
508444526a | ||
|
|
faa6de3b75 | ||
|
|
77d2ae5249 | ||
|
|
e2f94a9e0e | ||
|
|
ba55e4d9bd | ||
|
|
8e9ff939f3 | ||
|
|
bd3198f6f0 | ||
|
|
3319feeb73 | ||
|
|
47bcd4478c | ||
|
|
065b8628d2 | ||
|
|
87e2f1cad7 | ||
|
|
373b07f271 | ||
|
|
58f598fc88 | ||
|
|
8f44c294d7 | ||
|
|
f65079ad6d | ||
|
|
edce56cd20 | ||
|
|
cd437c3809 | ||
|
|
007cbef086 | ||
|
|
a03cc9ce64 | ||
|
|
3275aa4c12 | ||
|
|
a0f880fbba | ||
|
|
833f087fdf | ||
|
|
9a2a28e0aa | ||
|
|
bf5fc42f07 | ||
|
|
fb495fd2fc | ||
|
|
90ab7af069 | ||
|
|
0914e4c48d | ||
|
|
3436055c7f | ||
|
|
20aa797e96 | ||
|
|
5ebc2634ae | ||
|
|
94464a40b8 | ||
|
|
1194abf125 | ||
|
|
ceba698678 | ||
|
|
b1e9480f5a | ||
|
|
eaa38c858c | ||
|
|
4504045bc6 | ||
|
|
a9ea69b9a1 | ||
|
|
9a8d089c73 | ||
|
|
0d56cfbded | ||
|
|
9896875180 | ||
|
|
a1c04ce39f | ||
|
|
da541e000a | ||
|
|
e490162e4f | ||
|
|
a0b7d39f83 | ||
|
|
c464e049bf | ||
|
|
daad9b5d5f | ||
|
|
2682722cb1 | ||
|
|
8d5718178f | ||
|
|
1d438f098f | ||
|
|
1d305db29e | ||
|
|
c2a549c114 | ||
|
|
f43e298ac7 | ||
|
|
c25b769578 | ||
|
|
0cf1f1983d | ||
|
|
39ec568ab1 | ||
|
|
5b94ebae34 | ||
|
|
a857b5fc43 | ||
|
|
eb8791ec47 | ||
|
|
ff6da43007 | ||
|
|
d42fdf8a4a | ||
|
|
adab25ad44 | ||
|
|
90b057938b | ||
|
|
fc5c72e164 | ||
|
|
bd5c03812f | ||
|
|
6b0b7ea823 | ||
|
|
dd24ed5fe0 | ||
|
|
f64942b51d | ||
|
|
375db9996f | ||
|
|
20dfc063ff | ||
|
|
70c341e268 | ||
|
|
dc59c79bd3 | ||
|
|
422cd26b92 | ||
|
|
cec892ce88 | ||
|
|
d5374b9f51 | ||
|
|
51e2d965cf | ||
|
|
a6c8f2212a | ||
|
|
b3aac40246 | ||
|
|
fb138b77ff | ||
|
|
273284da39 | ||
|
|
7102229937 | ||
|
|
be80ba3b3b | ||
|
|
4410cb6dc6 | ||
|
|
df8df2bffa | ||
|
|
ae4c921300 | ||
|
|
3d1aa6698d | ||
|
|
613b115eb4 | ||
|
|
e19b690bd5 | ||
|
|
888be8e8a8 | ||
|
|
5785896007 | ||
|
|
8d522de7e7 | ||
|
|
81ae4c8d4d | ||
|
|
8cd3b8fdef | ||
|
|
c1aa341a7a | ||
|
|
f9c192c47a | ||
|
|
cdc6da844e | ||
|
|
fd35f23af7 | ||
|
|
c04851f64e | ||
|
|
3a63c3ad43 | ||
|
|
431f1c97cf | ||
|
|
1b68a3d5a9 | ||
|
|
c797808a40 | ||
|
|
d810ccba84 | ||
|
|
2fea5d9af2 | ||
|
|
1d9a3bf23a | ||
|
|
3d8c764eb0 | ||
|
|
eb317f0d46 | ||
|
|
27de1aea11 | ||
|
|
92f7e761ee | ||
|
|
b2f1786eff | ||
|
|
f8369e7c63 | ||
|
|
f095a74f2a | ||
|
|
4e3fccb009 | ||
|
|
a2995ed3c5 | ||
|
|
5edff9235b | ||
|
|
31e134c1e0 | ||
|
|
47e22b788f | ||
|
|
798628fdf8 | ||
|
|
ca9825d262 | ||
|
|
1a36ec65ee | ||
|
|
d07a20e6f8 | ||
|
|
783b317281 | ||
|
|
4bc525f250 | ||
|
|
11cfc7ea09 | ||
|
|
a806f2672d | ||
|
|
2fee88e54b | ||
|
|
7e1a2a6e4f | ||
|
|
d5b311760e | ||
|
|
dffe6f8b91 | ||
|
|
b4bca4670b | ||
|
|
150c43f41f | ||
|
|
9fc11df229 | ||
|
|
56a7a960c3 | ||
|
|
cd44cc6396 | ||
|
|
4af714f1dd | ||
|
|
836719587c | ||
|
|
2d6fab6e14 | ||
|
|
0ae58a3346 | ||
|
|
f32e958984 | ||
|
|
0703d927ca | ||
|
|
62b5deb77d | ||
|
|
82e718b3d1 | ||
|
|
41409c9b94 | ||
|
|
57c759773a | ||
|
|
9d6402a4f7 | ||
|
|
a29609df21 | ||
|
|
04ea8293a8 | ||
|
|
bde54aba3b | ||
|
|
687e1be3e5 | ||
|
|
a44d9b7ca8 | ||
|
|
febf571d84 | ||
|
|
818ece9f10 | ||
|
|
ec10c9c70c | ||
|
|
8852d50971 | ||
|
|
908f6763be | ||
|
|
03e20842fb | ||
|
|
084913ac91 | ||
|
|
d9801e2d57 | ||
|
|
29fe913fd8 | ||
|
|
5b68f9623e | ||
|
|
2a6662472d | ||
|
|
5cf6777340 | ||
|
|
17863c85b6 | ||
|
|
237d0f9d9a | ||
|
|
b98e27e464 | ||
|
|
d01d9edb78 | ||
|
|
0a86683392 | ||
|
|
b2b4febd31 | ||
|
|
ad67e6eaee | ||
|
|
ed5ce48f65 | ||
|
|
2e351bfc78 | ||
|
|
f324b97e8b | ||
|
|
46544e1cbe | ||
|
|
ffab6c0e12 | ||
|
|
10e736386b | ||
|
|
a1ecebf5bf | ||
|
|
996597dcb1 | ||
|
|
e09ad373b3 | ||
|
|
ddf7d34982 | ||
|
|
db9a4e4402 | ||
|
|
39a75a6792 | ||
|
|
d78484ff38 | ||
|
|
8900ac9a1f | ||
|
|
7df2e8af97 | ||
|
|
2ed723dfc2 | ||
|
|
087177274d | ||
|
|
59459ebbd6 | ||
|
|
2d981cd737 | ||
|
|
4c4e6fd6f9 | ||
|
|
c52f2fc069 | ||
|
|
eb3e2566e9 | ||
|
|
c186573775 | ||
|
|
3d9d8d6efc | ||
|
|
1ed82fc7d5 | ||
|
|
162228aff3 | ||
|
|
dd051f4c6e | ||
|
|
e59645b347 | ||
|
|
cbdc0bd99b | ||
|
|
b4fc36701a | ||
|
|
f2f00e3017 | ||
|
|
c706fbc6e7 | ||
|
|
88fb8458c1 | ||
|
|
982f298fc4 | ||
|
|
4a9a05718e | ||
|
|
b99a851af3 | ||
|
|
f452d6f5b7 | ||
|
|
b98b94de91 | ||
|
|
b18aee9dba | ||
|
|
bfe7bcf1b4 | ||
|
|
55bb732bb0 | ||
|
|
465fea8a16 | ||
|
|
149758ccab | ||
|
|
16095544cb | ||
|
|
37265f25d8 | ||
|
|
c61eb3f039 | ||
|
|
39862034e1 | ||
|
|
f938b29040 | ||
|
|
984b92815c | ||
|
|
82c4b0007c | ||
|
|
f4a3f699e8 | ||
|
|
9c4055a046 | ||
|
|
088493dc4f | ||
|
|
479ab1fb7b | ||
|
|
749cde6e72 | ||
|
|
7317fad754 | ||
|
|
83a29328c7 | ||
|
|
788fb145af | ||
|
|
b1b1a9b5de | ||
|
|
941a36a385 | ||
|
|
3ef3c23a29 | ||
|
|
39e630cece | ||
|
|
b2a62c2dd7 | ||
|
|
26dd3a2ca8 | ||
|
|
c328d6f6c6 | ||
|
|
44c79d71d7 | ||
|
|
ecd90d605a | ||
|
|
b6bd4b9da0 | ||
|
|
6ae11f862c | ||
|
|
c224141355 | ||
|
|
28c35633d3 | ||
|
|
6e36e33b22 | ||
|
|
093cca9d21 | ||
|
|
410c19dd78 | ||
|
|
4a7be89be6 | ||
|
|
51305e0a21 | ||
|
|
fda3398440 | ||
|
|
340ff0107c | ||
|
|
8374a58dc1 | ||
|
|
3690863a27 | ||
|
|
078d003cfe | ||
|
|
b6ab795878 | ||
|
|
7e81dede2f | ||
|
|
3482257b15 | ||
|
|
9f3ab8d3dd | ||
|
|
8f2ee555ec | ||
|
|
db53de2233 | ||
|
|
3596faed2f | ||
|
|
9e13d134d3 | ||
|
|
7f229005a6 | ||
|
|
e3a887f36c | ||
|
|
bca8e64574 | ||
|
|
3873d7099c | ||
|
|
48b176f49a | ||
|
|
8b8b387f2a | ||
|
|
f459d9b953 | ||
|
|
b276fbab90 | ||
|
|
e73f076324 | ||
|
|
131f8f7f45 | ||
|
|
c3b73964d5 | ||
|
|
790fc7c04b | ||
|
|
cd33314d0b | ||
|
|
4ba50267f1 | ||
|
|
d46e603366 | ||
|
|
f8878d73c3 | ||
|
|
9c0dac044c | ||
|
|
2f8e330b73 | ||
|
|
e6e55aa827 | ||
|
|
07bff8cb37 | ||
|
|
d857144653 | ||
|
|
17fdd840bb | ||
|
|
da6711c93c | ||
|
|
36b307eb49 | ||
|
|
2d8ec2fd78 | ||
|
|
11ba96103c | ||
|
|
843d2a224d | ||
|
|
0faf01c194 | ||
|
|
e868cf6270 | ||
|
|
255b3d0da7 | ||
|
|
5fad8d0680 | ||
|
|
d3dcdcb92e | ||
|
|
44ae5edc56 | ||
|
|
f0542923d7 | ||
|
|
29d0c07164 | ||
|
|
152abb086a | ||
|
|
d5c36ddc89 | ||
|
|
226a4ea2f3 | ||
|
|
ac0eafe6ab | ||
|
|
87bc9a5431 | ||
|
|
ef62892f60 | ||
|
|
7ae24488d9 | ||
|
|
6482670fdf | ||
|
|
317cf6d00f | ||
|
|
a5eeac6e5a | ||
|
|
e2cf9255ac | ||
|
|
25941cdbdd | ||
|
|
1764633bc5 | ||
|
|
f6eb90bd2d | ||
|
|
5f46c820e6 | ||
|
|
c20b279362 | ||
|
|
24ceb8801e | ||
|
|
8d5ca66db7 | ||
|
|
f0d250ff15 | ||
|
|
074cafbab1 | ||
|
|
79cd52833c | ||
|
|
6b75286885 | ||
|
|
b884ae039c | ||
|
|
3ecfdd027e | ||
|
|
55ca9927b0 | ||
|
|
b67377c41e | ||
|
|
15f24eb26b | ||
|
|
0283bb0e91 | ||
|
|
d39c0995b6 | ||
|
|
db4c088ce9 | ||
|
|
a4898e24b7 | ||
|
|
086db0d52f | ||
|
|
7b3523fb24 | ||
|
|
74ca91d611 | ||
|
|
e94d4b28b0 | ||
|
|
a85aa2da52 | ||
|
|
dded8504e6 | ||
|
|
a2bad425f6 | ||
|
|
a4ec8ec05d | ||
|
|
1b4bf7918b | ||
|
|
1f48e44cc6 | ||
|
|
83613f02a0 | ||
|
|
9d8743e765 | ||
|
|
84b9b11ef5 | ||
|
|
2327a0012e | ||
|
|
4cdd796a3d | ||
|
|
e819265e7e | ||
|
|
070a96c506 | ||
|
|
e1be54823c | ||
|
|
d7dd9393ff | ||
|
|
64c36a1562 | ||
|
|
13a54565ba | ||
|
|
12eb17cc41 | ||
|
|
9715dfa126 | ||
|
|
a6e3689e97 | ||
|
|
2e5cf81231 | ||
|
|
4eeca12d6a | ||
|
|
ad577d22d0 | ||
|
|
5e68094e87 | ||
|
|
10c26fe642 | ||
|
|
49ecd252ed | ||
|
|
2ee2a5dc46 | ||
|
|
430fdf4fcf | ||
|
|
abca81ea38 | ||
|
|
a0a76f738b | ||
|
|
7d440c2430 | ||
|
|
94274fd092 | ||
|
|
266edd80f3 | ||
|
|
d1b1be32ca | ||
|
|
fbdd923d43 | ||
|
|
2c30414f88 | ||
|
|
d8e469a33a | ||
|
|
a6b89b3ea3 | ||
|
|
0f80901bce | ||
|
|
8fc45e5590 | ||
|
|
c829304e31 | ||
|
|
d3f94ff2fb | ||
|
|
85d9a9dc27 | ||
|
|
04db6d3838 | ||
|
|
e5a28ac64e | ||
|
|
2397c6c0ea | ||
|
|
c3e6c3d1da | ||
|
|
22da75cb72 | ||
|
|
22a8ca37b6 | ||
|
|
692afba3d9 | ||
|
|
9c621d5816 | ||
|
|
feedbcf581 | ||
|
|
a57f3be58d | ||
|
|
897dffc426 | ||
|
|
6f0b61e6d8 | ||
|
|
674ee9ac9e | ||
|
|
5eab013cf6 | ||
|
|
079ef8d800 | ||
|
|
1a9be279c6 | ||
|
|
f5d2eba0c4 | ||
|
|
e7d19c9084 | ||
|
|
638bf38b25 | ||
|
|
c4dc49ac5e | ||
|
|
926600e29d | ||
|
|
5e58f1e273 | ||
|
|
c79ad65d37 | ||
|
|
95a97ad776 | ||
|
|
6c28cedf23 | ||
|
|
a3599b53bb | ||
|
|
438c501fae | ||
|
|
25cbd55841 | ||
|
|
5ef53f251d | ||
|
|
92819b6c9e | ||
|
|
d22674f362 | ||
|
|
0fc83e9255 | ||
|
|
f171c5d59e | ||
|
|
78b8299130 | ||
|
|
2c7fe0c92d | ||
|
|
6ff30f2a0e | ||
|
|
7707c5e558 | ||
|
|
9b7a920e0d | ||
|
|
2e51380be5 | ||
|
|
445f508ea5 | ||
|
|
b82c89c582 | ||
|
|
85a47a8049 | ||
|
|
f8e9798038 | ||
|
|
581d6747ad | ||
|
|
00a1aa7628 | ||
|
|
79a9048db5 | ||
|
|
2bfba2e399 | ||
|
|
484419e526 | ||
|
|
8c18830c97 | ||
|
|
653d370a85 | ||
|
|
316e91cfcf | ||
|
|
f31632c8c8 | ||
|
|
bb52c13f9a | ||
|
|
738d43fd83 | ||
|
|
1c6c8588d9 | ||
|
|
7a97cb2e02 | ||
|
|
5e0d2992c9 | ||
|
|
210859a5ef | ||
|
|
25f6d229dd | ||
|
|
60405abf58 | ||
|
|
8070b11c27 | ||
|
|
7d76ce77c9 | ||
|
|
3d48cbc111 | ||
|
|
36f34da227 | ||
|
|
d06e07542e | ||
|
|
a0c8646923 | ||
|
|
6c924de209 | ||
|
|
500503c069 | ||
|
|
5dd8cd66dd | ||
|
|
41e84e55f1 | ||
|
|
7f8888d7dd | ||
|
|
216928f904 | ||
|
|
345bab3a99 | ||
|
|
064a7a113c |
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Information**
|
||||
<!-- Do not bother opening an issue for version 0.9.2 or older, it is extremely outdated. Please update to the latest one and see if your issue persists. -->
|
||||
- Solaar version (`git describe --tags` if cloned from Solaar repository):
|
||||
- Distribution:
|
||||
- Kernel version (ex. `uname -srmo`): `KERNEL VERSION HERE`
|
||||
- Output of `solaar show`:
|
||||
```
|
||||
OUTPUT HERE
|
||||
```
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
26
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Information**
|
||||
- Output of `solaar show` for the target device (if applicable):
|
||||
```
|
||||
OUTPUT HERE
|
||||
```
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
15
.gitignore
vendored
@@ -1,4 +1,17 @@
|
||||
*.so
|
||||
*.pyc
|
||||
*.pyo
|
||||
__pycache__/
|
||||
*.log
|
||||
*.mo
|
||||
|
||||
/lib/Solaar.egg-info/
|
||||
/lib/solaar.egg-info/
|
||||
/build/
|
||||
/sdist/
|
||||
/dist/
|
||||
/deb_dist/
|
||||
/MANIFEST
|
||||
|
||||
/docs/captures/
|
||||
/share/logitech_icons/
|
||||
/share/locale/
|
||||
|
||||
41
COPYING
@@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@@ -225,7 +225,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
14
COPYRIGHT
Normal file
@@ -0,0 +1,14 @@
|
||||
Copyright 2012, 2013
|
||||
Daniel Pavel <daniel.pavel@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 2.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
144
ChangeLog
Normal file
@@ -0,0 +1,144 @@
|
||||
1.0.2:
|
||||
* Add usage document
|
||||
|
||||
1.0.2rc3:
|
||||
* Don't produce error dialog for inaccessible receivers with access control lists.
|
||||
* Add option --battery-icons=symbolic to use symbolic icons if available.
|
||||
* Update French translation
|
||||
* Update installation documentation
|
||||
|
||||
1.0.2rc2:
|
||||
* Remove packaging directory tree as it is not maintained
|
||||
* Pip installs udev rule and solaar autostart when doing install without --user flag
|
||||
* Use Solaar icon instead of a missing battery icon
|
||||
* Use only standard icons for battery levels. Symbolic icons do not change to white in dark themes because of problems external to Solaar.
|
||||
* Better reporting of battery levels when charging for some devices.
|
||||
* Add information on K600 TV, M350 WIPD 4080, and MX Keys
|
||||
* Remove assertion requiring receivers to still be in window when they are updated.
|
||||
* Augment long description of Solaar showing up in repositories.
|
||||
* Update installation directions.
|
||||
* Install udev rule as well as autostart file when doing system install.
|
||||
* Add support for Ayatana AppIndicator.
|
||||
* Use setuptools icon directory on system installs when not using pip.
|
||||
* Add receiver C517 and several older devices.
|
||||
* Improved translations for polish.
|
||||
* Bypass bug in appindicator when solaar is file in current directory.
|
||||
* Don't check that device kind matches feature kind.
|
||||
* Better determination of icons for battery levels.
|
||||
* Use Ayatana AppIndicator if available.
|
||||
* Improve error reporting when required system packages are not install.
|
||||
* Better tooltip description
|
||||
* Add release script to help when creating releases
|
||||
|
||||
1.0.2-rc1:
|
||||
* Look up tray icon filenames to get around a bug in libappindicator.
|
||||
* Make the default behavior be to show the main window at startup.
|
||||
* Support c537 nano receiver
|
||||
* Add logind signals for suspend/resume.
|
||||
* Remove solaar-gnome3 package
|
||||
* Ignore features for devices that don't follow feature specification
|
||||
* Add probe command to command-line interface to dump receiver registers
|
||||
* Don't terminate on malformed or unknown messages
|
||||
* Create fewer internal notifications for messages from devices
|
||||
* Add a button to the main window to terminate (quit) Solaar
|
||||
* Set up nano receivers as receivers with no unpairing and with re-pairing
|
||||
* Set up c534 as receiver with max 2 pairings, no unpairing, re-pairing
|
||||
* Better support receivers that do not unpair or when pairing replace existing pairings
|
||||
* Add information about receiver pairing to receiver data structure
|
||||
* Better support devices that only allow a limited number of total re-pairings
|
||||
* Add --window option to control main window visibility and tray usage
|
||||
* Ignore receiver if USB id is not retrieved
|
||||
* Fix bug with double deleting when devices are disconnected
|
||||
* Determine some receiver information from data structure for USB ids
|
||||
* Treat battery level of 0 as unknown
|
||||
* Fix bug on devices with no serial number
|
||||
* Drop support for python2, and use python3 throughout
|
||||
* Fix bug in remembering features discovered from device reports
|
||||
* Show icons in main window device list
|
||||
* Count offline devices when determining whether pairing is possible
|
||||
* Update French, Dutch, German, and Croation translations
|
||||
* Better icons for battery levels
|
||||
* Support DPI, Backlight 2, Battery Voltage features
|
||||
* Support M585, M590, M330, MX Master 2s and 3, new M310, new K800, craft keyboard
|
||||
* Documentation improvements
|
||||
* Clean up directory structure and remove unused files
|
||||
|
||||
1.0.1:
|
||||
* Updated the repo url.
|
||||
* Fixed typo which was crashing the application.
|
||||
* Improved the HID write routine which was causing issues on some devices.
|
||||
* Fix non-unifying receivers in Linux 5.2.
|
||||
* Add new Lightspeed receiver (used in the G305)
|
||||
|
||||
1.0.0:
|
||||
* Too many to track...
|
||||
|
||||
0.9.3:
|
||||
* Merged solaar-cli functionality into main solaar.
|
||||
* Scrolling over the systray icon switches between multiple peripherals.
|
||||
* Swedish translation courtesy of Daniel Zippert and Emelie Snecker
|
||||
* French translation courtesy of Papoteur, David Geiger and Damien Lallement.
|
||||
* Fixed some untranslated strings.
|
||||
|
||||
0.9.2:
|
||||
* Added support for hand detection on the K800.
|
||||
* Added support for V550 and V450 Nano.
|
||||
* Fixed side-scrolling with the M705 Marathon.
|
||||
* Fixed identification of the T650 Touchpad.
|
||||
* Added internationalization support and romanian translation.
|
||||
* Polish translation courtesy of Adrian Piotrowicz.
|
||||
|
||||
0.9.1:
|
||||
* When devices report a battery alert, only show the alert once.
|
||||
* Make sure devices in the window tree are sorted by registration index.
|
||||
* Added an autostart .desktop file.
|
||||
* Replaced single-instance code with GtkApplication.
|
||||
* Fixed identification of the M505 mouse.
|
||||
* Fixed an occasional windowing layout bug with the C52F Nano Receiver.
|
||||
|
||||
0.9.0:
|
||||
* New single-window UI.
|
||||
* Performance MX leds show the current battery charge.
|
||||
* Support the VX Nano mouse.
|
||||
* Faster and more accurate detection of devices.
|
||||
* If upower is accessible through DBus, handle suspend/resume.
|
||||
* Replaced Solaar icons with SVGs.
|
||||
* Running solaar-cli in parallel with solaar is now less likely to cause issues.
|
||||
* Bugfixes to saving and applying device settings.
|
||||
* Properly handle ^C when running in console.
|
||||
|
||||
0.8.9:
|
||||
|
||||
* Improved support for gnome-shell/Unity.
|
||||
* Persist devices settings between runs.
|
||||
* Fixed reading of MK700 keyboard battery status.
|
||||
* Use battery icons from the current theme instead of custom ones.
|
||||
* Debian/Ubuntu packages now depend on an icon theme, to make sure
|
||||
no missing icons appear in the application window.
|
||||
* Fixed missing icons under Kubuntu.
|
||||
* Many more bug-fixes and reliability improvements.
|
||||
|
||||
0.8.8:
|
||||
|
||||
* Partial support for some Nano receivers.
|
||||
* Improved support for some devices: M510, K800, Performance MX.
|
||||
* Improved battery support for some HID++ 1.0 devices.
|
||||
* Properly handle device loss on computer sleep/wake.
|
||||
* Better handling of receiver adding and removal at runtime.
|
||||
* Removed a few more unhelpful notifications.
|
||||
* Incipient support for multiple connected receivers.
|
||||
* More Python 3 fixes.
|
||||
|
||||
0.8.7:
|
||||
|
||||
* Don't show the "device disconnected" notification, it can be annoying and
|
||||
not very useful.
|
||||
* More robust detection of systray icon visibility.
|
||||
|
||||
0.8.6:
|
||||
|
||||
* Ensure the Gtk application is single-instance.
|
||||
* Fix identifying available dpi values.
|
||||
* Fixed locating application icons when installed in a custom prefix.
|
||||
* Fixed some icon names for the oxygen theme.
|
||||
* Python 3 fixes.
|
||||
3
MANIFEST.in
Normal file
@@ -0,0 +1,3 @@
|
||||
include COPYRIGHT COPYING README.md ChangeLog
|
||||
recursive-include rules.d *
|
||||
recursive-include share/locale *
|
||||
106
README.md
@@ -1,106 +0,0 @@
|
||||
**Solaar** is a Linux device manager for Logitech's
|
||||
[Unifying Receiver](http://www.logitech.com/en-us/66/6079) peripherals. It is
|
||||
able to pair/unpair devices to the receiver, and for some devices read battery
|
||||
status.
|
||||
|
||||
It comes in two flavors, command-line and GUI. Both are able to list the
|
||||
devices paired to a Unifying Receiver, show detailed info for each device, and
|
||||
also pair/unpair supported devices with the receiver.
|
||||
|
||||
## Supported Devices
|
||||
|
||||
**Solaar** will detect all devices paired with your Unifying Receiver, and at
|
||||
the very least display some basic information about them. Depending on the
|
||||
device, it may be able to read its battery status.
|
||||
|
||||
A few devices also have extended support, mostly because I was able to directly
|
||||
test on them:
|
||||
|
||||
* The [K750 Solar Keyboard](http://www.logitech.com/keyboards/keyboard/devices/7454)
|
||||
is also queried for its solar charge status. Pressing the Solar key on the
|
||||
keyboard will pop-up the application window and display the current lighting
|
||||
value (Lux) as reported by the keyboard, similar to Logitech's *Solar.app* for
|
||||
Windows.
|
||||
|
||||
Also, you can change the way the function keys (`F1`..`F12`) work, i.e.
|
||||
whether holding `FN` while pressing the function keys will generate the
|
||||
standard keycodes or the special function (yellow icons) keycodes.
|
||||
|
||||
* The [M705 Marathon Mouse](http://www.logitech.com/product/marathon-mouse-m705)
|
||||
supports turning on/off Smooth Scrolling (higher sensitivity on vertical
|
||||
scrolling with the wheel).
|
||||
|
||||
Extended support for other devices may be added in the future, depending on the
|
||||
documentation available, but the K750 keyboard and M705 mouse are the only
|
||||
devices I have and can test on right now.
|
||||
|
||||
## Requirements
|
||||
|
||||
You should have a reasonably new kernel (3.2+), with the `logitech-djreceiver`
|
||||
driver enabled and loaded; also, the `udev` package must be installed and the
|
||||
daemon running. If you have a modern Linux distribution (2011+), you're most
|
||||
likely good to go.
|
||||
|
||||
The command-line application (`bin/solaar-cli`) requires Python 2.7.3 or 3.2+
|
||||
(either version should work), and the `python-pyudev`/`python3-pyudev` package.
|
||||
|
||||
The GUI application (`bin/solaar`) also requires Gtk3, and its GObject
|
||||
Introspection bindings. The Debian/Ubuntu package names are
|
||||
`python-gi`/`python3-gi` and `gir1.2-gtk-3.0`; if you're using another
|
||||
distribution the required packages are most likely named something similar.
|
||||
If the desktop notifications bindings are also installed (`gir1.2-notify-0.7`),
|
||||
you will also get desktop notifications when devices come online/go offline.
|
||||
|
||||
## Installation
|
||||
|
||||
Normally USB devices are not accessible for r/w by regular users, so you will
|
||||
need to do a one-time udev rule installation to allow access to the Logitech
|
||||
Unifying Receiver.
|
||||
|
||||
You can run the `rules.d/install.sh` script from Solaar to do this installation
|
||||
automatically (it will switch to root when necessary), or you can do all the
|
||||
required steps by hand, as the root user:
|
||||
|
||||
1. copy `rules.d/99-logitech-unfiying-receiver.rules` from Solaar to
|
||||
`/etc/udev/rules.d/`
|
||||
|
||||
By default, the rule makes the Unifying Receiver device available for r/w by
|
||||
all users belonging to the `plugdev` system group (standard Debian/Ubuntu
|
||||
group for pluggable devices). It may need changes, specific to your
|
||||
particular system's configuration. If in doubt, replacing `GROUP="plugdev"`
|
||||
with `GROUP="<your username>"` should just work.
|
||||
|
||||
2. run `udevadm control --reload-rules` to let the udev daemon know about the new
|
||||
rule
|
||||
3. physically remove the Unifying Receiver, wait 10 seconds and re-insert it
|
||||
|
||||
## Known Issues
|
||||
|
||||
- When running under Ubuntu's Unity, the tray icon will probably not appear, nor
|
||||
will the application window. Either run the application with the '-S' option,
|
||||
or whitelist "Solaar" into the systray. For details, see
|
||||
[How do I access and enable more icons to be in the system tray?](http://askubuntu.com/questions/30742/how-do-i-access-and-enable-more-icons-to-be-in-the-system-tray).
|
||||
|
||||
Support for Unity's indicators is a planned feature.
|
||||
|
||||
- Running the command-line application (`bin/solaar-cli`) while the GUI
|
||||
application is also running *may* occasionally cause either of them to become
|
||||
confused about the state of the devices. I haven't encountered this often
|
||||
enough to be able to be able to diagnose it properly yet.
|
||||
|
||||
## Thanks
|
||||
|
||||
This project began as a third-hand clone of [Noah K. Tilton](https://github.com/noah)'s
|
||||
logitech-solar-k750 project on GitHub (no longer available). It was developed
|
||||
further thanks to the diggings in Logitech's HID++ protocol done by many other
|
||||
people:
|
||||
|
||||
- [Julien Danjou](http://julien.danjou.info/blog/2012/logitech-k750-linux-support),
|
||||
who also provided some internal
|
||||
[Logitech documentation](http://julien.danjou.info/blog/2012/logitech-unifying-upower)
|
||||
- [Lars-Dominik Braun](http://6xq.net/git/lars/lshidpp.git)
|
||||
- [Alexander Hofbauer](http://derhofbauer.at/blog/blog/2012/08/28/logitech-performance-mx)
|
||||
- [Clach04](http://bitbucket.org/clach04/logitech-unifying-receiver-tools)
|
||||
|
||||
Also thanks to Douglas Wagner and Julien Gascard for helping with application
|
||||
testing and supporting new devices.
|
||||
15
RELEASE.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Solaar releases
|
||||
|
||||
###### Please read before making a release.
|
||||
|
||||
We support two type of releases: normal releases (ex. `1.0.0`) and release
|
||||
candidates (ex. `1.0.0rc1`). Release candidates must have a `rcX` suffix.
|
||||
|
||||
Release routine:
|
||||
- Update ChangeLog, setup.py, lib/solaar/__init__.py, and docs/_config.yml to the new version
|
||||
- Create a commit that starts with `release VERSION`
|
||||
- Push commit to Solaar repository
|
||||
- Invoke `./release.sh`
|
||||
- Git tags are signed so you must have GPG set up
|
||||
- You are required to have a have a github token with `public_repo` access
|
||||
in `~/.github-token`
|
||||
157
app/listener.py
@@ -1,157 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from logging import getLogger, DEBUG as _DEBUG
|
||||
_log = getLogger('listener')
|
||||
del getLogger
|
||||
|
||||
from logitech.unifying_receiver import (Receiver,
|
||||
listener as _listener,
|
||||
status as _status)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from collections import namedtuple
|
||||
_GHOST_DEVICE = namedtuple('_GHOST_DEVICE', ['number', 'name', 'kind', 'status', 'max_devices'])
|
||||
_GHOST_DEVICE.__bool__ = lambda self: False
|
||||
_GHOST_DEVICE.__nonzero__ = _GHOST_DEVICE.__bool__
|
||||
del namedtuple
|
||||
|
||||
def _ghost(device):
|
||||
return _GHOST_DEVICE(number=device.number, name=device.name, kind=device.kind, status=None, max_devices=None)
|
||||
|
||||
DUMMY = _GHOST_DEVICE(Receiver.number, Receiver.name, None, 'Receiver not found.', Receiver.max_devices)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
# how often to poll devices that haven't updated their statuses on their own
|
||||
# (through notifications)
|
||||
_POLL_TICK = 60 # seconds
|
||||
|
||||
|
||||
class ReceiverListener(_listener.EventsListener):
|
||||
"""Keeps the status of a Unifying Receiver.
|
||||
"""
|
||||
def __init__(self, receiver, status_changed_callback=None):
|
||||
super(ReceiverListener, self).__init__(receiver, self._notifications_handler)
|
||||
self.tick_period = _POLL_TICK
|
||||
self._last_tick = 0
|
||||
|
||||
self.status_changed_callback = status_changed_callback
|
||||
|
||||
# make it a bit similar with the regular devices
|
||||
receiver.kind = None
|
||||
# replace the
|
||||
receiver.handle = _listener.ThreadedHandle(receiver.handle, receiver.path)
|
||||
receiver.status = _status.ReceiverStatus(receiver, self._status_changed)
|
||||
|
||||
def has_started(self):
|
||||
_log.info("notifications listener has started")
|
||||
self.receiver.enable_notifications()
|
||||
self.receiver.notify_devices()
|
||||
self._status_changed(self.receiver, _status.ALERT.LOW)
|
||||
|
||||
def has_stopped(self):
|
||||
_log.info("notifications listener has stopped")
|
||||
if self.receiver:
|
||||
self.receiver.enable_notifications(False)
|
||||
self.receiver.close()
|
||||
self.receiver = None
|
||||
self._status_changed(None, _status.ALERT.LOW)
|
||||
|
||||
def tick(self, timestamp):
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("polling status: %s %s", self.receiver, list(iter(self.receiver)))
|
||||
|
||||
if self._last_tick > 0 and timestamp - self._last_tick > _POLL_TICK * 3:
|
||||
# if we missed a couple of polls, most likely the computer went into
|
||||
# sleep, and we have to reinitialize the receiver again
|
||||
_log.warn("possible sleep detected, closing this listener")
|
||||
self.stop()
|
||||
return
|
||||
|
||||
self._last_tick = timestamp
|
||||
|
||||
# read these in case they haven't been read already
|
||||
self.receiver.serial, self.receiver.firmware
|
||||
if self.receiver.status.lock_open:
|
||||
# don't mess with stuff while pairing
|
||||
return
|
||||
|
||||
for dev in self.receiver:
|
||||
if dev.status is not None:
|
||||
dev.status.poll(timestamp)
|
||||
|
||||
def _status_changed(self, device, alert=_status.ALERT.NONE, reason=None):
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("status_changed %s: %s %s (%X) %s", device,
|
||||
None if device is None else 'active' if device.status else 'inactive',
|
||||
None if device is None else device.status,
|
||||
alert, reason or '')
|
||||
if self.status_changed_callback:
|
||||
r = self.receiver or DUMMY
|
||||
if device is None or device.kind is None:
|
||||
# the status of the receiver changed
|
||||
self.status_changed_callback(r, None, alert, reason)
|
||||
else:
|
||||
if device.status is None:
|
||||
# device was unpaired, and since the object is weakref'ed
|
||||
# it won't be valid for much longer
|
||||
device = _ghost(device)
|
||||
|
||||
self.status_changed_callback(r, device, alert, reason)
|
||||
|
||||
if device.status is None:
|
||||
# the receiver changed status as well
|
||||
self.status_changed_callback(r)
|
||||
|
||||
def _notifications_handler(self, n):
|
||||
assert self.receiver
|
||||
if n.devnumber == 0xFF:
|
||||
# a receiver notification
|
||||
if self.receiver.status is not None:
|
||||
self.receiver.status.process_notification(n)
|
||||
else:
|
||||
# a device notification
|
||||
assert n.devnumber > 0 and n.devnumber <= self.receiver.max_devices
|
||||
already_known = n.devnumber in self.receiver
|
||||
dev = self.receiver[n.devnumber]
|
||||
|
||||
if not dev:
|
||||
_log.warn("received %s for invalid device %d: %s", n, n.devnumber, repr(dev))
|
||||
return
|
||||
|
||||
if not already_known:
|
||||
# read these as soon as possible, they will be used everywhere
|
||||
dev.protocol, dev.codename
|
||||
dev.status = _status.DeviceStatus(dev, self._status_changed)
|
||||
# the receiver changed status as well
|
||||
self._status_changed(self.receiver)
|
||||
|
||||
# status may be None if the device has just been unpaired
|
||||
if dev.status is not None:
|
||||
dev.status.process_notification(n)
|
||||
if self.receiver.status.lock_open and not already_known:
|
||||
# this should be the first notification after a device was paired
|
||||
assert n.sub_id == 0x41 and n.address == 0x04
|
||||
_log.info("pairing detected new device")
|
||||
self.receiver.status.new_device = dev
|
||||
|
||||
def __str__(self):
|
||||
return '<ReceiverListener(%s,%s)>' % (self.receiver.path, self.receiver.handle)
|
||||
__unicode__ = __str__
|
||||
|
||||
@classmethod
|
||||
def open(self, status_changed_callback=None):
|
||||
receiver = Receiver.open()
|
||||
if receiver:
|
||||
rl = ReceiverListener(receiver, status_changed_callback)
|
||||
rl.start()
|
||||
return rl
|
||||
128
app/solaar.py
@@ -1,128 +0,0 @@
|
||||
#!/usr/bin/env python -u
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
NAME = 'Solaar'
|
||||
VERSION = '0.8.4'
|
||||
__author__ = "Daniel Pavel <daniel.pavel@gmail.com>"
|
||||
__version__ = VERSION
|
||||
__license__ = "GPL"
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _require(module, os_package):
|
||||
try:
|
||||
__import__(module)
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.exit("%s: missing required package '%s'" % (NAME, os_package))
|
||||
|
||||
|
||||
def _parse_arguments():
|
||||
import argparse
|
||||
arg_parser = argparse.ArgumentParser(prog=NAME.lower())
|
||||
arg_parser.add_argument('-S', '--no-systray', action='store_false', dest='systray',
|
||||
help='do not place an icon in the desktop\'s systray')
|
||||
arg_parser.add_argument('-N', '--no-notifications', action='store_false', dest='notifications',
|
||||
help='disable desktop notifications (shown only when in systray)')
|
||||
arg_parser.add_argument('-d', '--debug', action='count', default=0,
|
||||
help='print logging messages, for debugging purposes (may be repeated for extra verbosity)')
|
||||
arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__)
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
import logging
|
||||
if args.debug > 0:
|
||||
log_level = logging.WARNING - 10 * args.debug
|
||||
log_format='%(asctime)s %(levelname)8s [%(threadName)s] %(name)s: %(message)s'
|
||||
logging.basicConfig(level=max(log_level, logging.DEBUG), format=log_format)
|
||||
else:
|
||||
logging.root.addHandler(logging.NullHandler())
|
||||
logging.root.setLevel(logging.CRITICAL)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def _run(args):
|
||||
import ui
|
||||
|
||||
# even if --no-notifications is given on the command line, still have to
|
||||
# check they are available, and decide whether to put the option in the
|
||||
# systray icon
|
||||
args.notifications &= args.systray
|
||||
if args.systray and ui.notify.init(NAME):
|
||||
ui.action.toggle_notifications.set_active(args.notifications)
|
||||
if not args.notifications:
|
||||
ui.notify.uninit()
|
||||
else:
|
||||
ui.action.toggle_notifications = None
|
||||
|
||||
from listener import DUMMY
|
||||
window = ui.main_window.create(NAME, DUMMY.name, DUMMY.max_devices, args.systray)
|
||||
if args.systray:
|
||||
menu_actions = (ui.action.toggle_notifications,
|
||||
ui.action.about)
|
||||
icon = ui.status_icon.create(window, menu_actions)
|
||||
else:
|
||||
icon = None
|
||||
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
# initializes the receiver listener
|
||||
def check_for_listener(notify=False):
|
||||
# print ("check_for_listener", notify)
|
||||
global listener
|
||||
listener = None
|
||||
|
||||
from listener import ReceiverListener
|
||||
try:
|
||||
listener = ReceiverListener.open(status_changed)
|
||||
except OSError:
|
||||
ui.error(window, 'Permissions error',
|
||||
'Found a possible Unifying Receiver device,\n'
|
||||
'but did not have permission to open it.')
|
||||
|
||||
if listener is None:
|
||||
if notify:
|
||||
status_changed(DUMMY)
|
||||
else:
|
||||
return True
|
||||
|
||||
from logitech.unifying_receiver import status
|
||||
|
||||
# callback delivering status notifications from the receiver/devices to the UI
|
||||
def status_changed(receiver, device=None, alert=status.ALERT.NONE, reason=None):
|
||||
if alert & status.ALERT.MED:
|
||||
GObject.idle_add(window.present)
|
||||
if window:
|
||||
GObject.idle_add(ui.main_window.update, window, receiver, device)
|
||||
if icon:
|
||||
GObject.idle_add(ui.status_icon.update, icon, receiver, device)
|
||||
|
||||
if ui.notify.available:
|
||||
# always notify on receiver updates
|
||||
if device is None or alert & status.ALERT.LOW:
|
||||
GObject.idle_add(ui.notify.show, device or receiver, reason)
|
||||
|
||||
if receiver is DUMMY:
|
||||
GObject.timeout_add(3000, check_for_listener)
|
||||
|
||||
GObject.timeout_add(10, check_for_listener, True)
|
||||
Gtk.main()
|
||||
|
||||
if listener:
|
||||
listener.stop()
|
||||
listener.join()
|
||||
|
||||
ui.notify.uninit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
_require('pyudev', 'python-pyudev')
|
||||
_require('gi.repository', 'python-gi')
|
||||
_require('gi.repository.Gtk', 'gir1.2-gtk-3.0')
|
||||
|
||||
args = _parse_arguments()
|
||||
listener = None
|
||||
_run(args)
|
||||
@@ -1,384 +0,0 @@
|
||||
#!/usr/bin/env python -u
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import sys
|
||||
|
||||
import solaar
|
||||
NAME = 'solaar-cli'
|
||||
__author__ = solaar.__author__
|
||||
__version__ = solaar.__version__
|
||||
__license__ = solaar.__license__
|
||||
del solaar
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _fail(text):
|
||||
sys.exit("%s: error: %s" % (NAME, text))
|
||||
|
||||
|
||||
def _require(module, os_package):
|
||||
try:
|
||||
__import__(module)
|
||||
except ImportError:
|
||||
_fail("missing required package '%s'" % os_package)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _receiver():
|
||||
from logitech.unifying_receiver import Receiver
|
||||
try:
|
||||
r = Receiver.open()
|
||||
except Exception as e:
|
||||
_fail(str(e))
|
||||
if r is None:
|
||||
_fail("Logitech Unifying Receiver not found")
|
||||
return r
|
||||
|
||||
|
||||
def _find_device(receiver, name, may_be_receiver=False):
|
||||
if len(name) == 1:
|
||||
try:
|
||||
number = int(name)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
if number in range(1, 1 + receiver.max_devices):
|
||||
dev = receiver[number]
|
||||
if dev is None:
|
||||
_fail("no paired device with number", number)
|
||||
return dev
|
||||
|
||||
if len(name) < 3:
|
||||
_fail("need at least 3 characters to match a device")
|
||||
|
||||
name = name.lower()
|
||||
if may_be_receiver and ('receiver'.startswith(name) or name == receiver.serial.lower()):
|
||||
return receiver
|
||||
|
||||
for dev in receiver:
|
||||
if (name == dev.serial.lower() or
|
||||
name == dev.codename.lower() or
|
||||
name == str(dev.kind).lower() or
|
||||
name in dev.name.lower()):
|
||||
return dev
|
||||
|
||||
_fail("no device found matching '%s'" % name)
|
||||
|
||||
|
||||
def _print_receiver(receiver, verbose=False):
|
||||
paired_count = receiver.count()
|
||||
if not verbose:
|
||||
print ("-: Unifying Receiver [%s:%s] with %d devices" % (receiver.path, receiver.serial, paired_count))
|
||||
return
|
||||
|
||||
print ("-: Unifying Receiver")
|
||||
print (" Device path :", receiver.path)
|
||||
print (" Serial :", receiver.serial)
|
||||
for f in receiver.firmware:
|
||||
print (" %-11s: %s" % (f.kind, f.version))
|
||||
|
||||
print (" Has", paired_count, "paired device(s).")
|
||||
|
||||
notification_flags = receiver.request(0x8100)
|
||||
if notification_flags:
|
||||
notification_flags = ord(notification_flags[0:1]) << 16 | ord(notification_flags[1:2]) << 8
|
||||
if notification_flags:
|
||||
from logitech.unifying_receiver import hidpp10
|
||||
notification_names = hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags)
|
||||
print (" Enabled notifications: 0x%06X = %s." % (notification_flags, ', '.join(notification_names)))
|
||||
else:
|
||||
print (" All notifications disabled.")
|
||||
|
||||
if paired_count > 0:
|
||||
activity = receiver.request(0x83B3)
|
||||
if activity:
|
||||
activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)]
|
||||
print (" Device activity counters:", ', '.join(('%d=%d' % (d, a)) for d, a in activity if a > 0))
|
||||
|
||||
|
||||
def _print_device(dev, verbose=False):
|
||||
p = dev.protocol
|
||||
state = '' if p > 0 else 'inactive'
|
||||
|
||||
if not verbose:
|
||||
print ("%d: %s [%s:%s]" % (dev.number, dev.name, dev.codename, dev.serial), state)
|
||||
return
|
||||
|
||||
print ("%d: %s" % (dev.number, dev.name))
|
||||
print (" Codename :", dev.codename)
|
||||
print (" Kind :", dev.kind)
|
||||
if p == 0:
|
||||
print (" Protocol : unknown (device is inactive)")
|
||||
else:
|
||||
print (" Protocol : HID++ %1.1f" % p)
|
||||
print (" Polling rate :", dev.polling_rate, "ms")
|
||||
print (" Wireless PID :", dev.wpid)
|
||||
print (" Serial number:", dev.serial)
|
||||
for fw in dev.firmware:
|
||||
print (" %-11s:" % fw.kind, (fw.name + ' ' + fw.version).strip())
|
||||
|
||||
if dev.power_switch_location:
|
||||
print (" The power switch is located on the", dev.power_switch_location)
|
||||
|
||||
from logitech.unifying_receiver import hidpp10, hidpp20
|
||||
if p > 0:
|
||||
if dev.features:
|
||||
print (" Supports %d HID++ 2.0 features:" % len(dev.features))
|
||||
for index, feature in enumerate(dev.features):
|
||||
feature = dev.features[index]
|
||||
flags = dev.request(0x0000, feature.bytes(2))
|
||||
flags = 0 if flags is None else ord(flags[1:2])
|
||||
flags = hidpp20.FEATURE_FLAG.flag_names(flags)
|
||||
print (" %2d: %-20s {%04X} %s" % (index, feature, feature, ', '.join(flags)))
|
||||
|
||||
if dev.keys:
|
||||
print (" Has %d reprogrammable keys:" % len(dev.keys))
|
||||
for k in dev.keys:
|
||||
flags = hidpp20.KEY_FLAG.flag_names(k.flags)
|
||||
print (" %2d: %-20s => %-20s %s" % (k.index, k.key, k.task, ', '.join(flags)))
|
||||
|
||||
if p > 0:
|
||||
battery = hidpp20.get_battery(dev)
|
||||
if battery is None:
|
||||
battery = hidpp10.get_battery(dev)
|
||||
if battery:
|
||||
charge, status = battery
|
||||
print (" Battery is %d%% charged," % charge, status)
|
||||
else:
|
||||
print (" Battery status unavailable.")
|
||||
else:
|
||||
print (" Battery status is unknown (device is inactive).")
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def show_devices(receiver, args):
|
||||
if args.device == 'all':
|
||||
_print_receiver(receiver, args.verbose)
|
||||
for dev in receiver:
|
||||
if args.verbose:
|
||||
print ("")
|
||||
_print_device(dev, args.verbose)
|
||||
else:
|
||||
dev = _find_device(receiver, args.device, True)
|
||||
if dev is receiver:
|
||||
_print_receiver(receiver, args.verbose)
|
||||
else:
|
||||
_print_device(dev, args.verbose)
|
||||
|
||||
|
||||
def pair_device(receiver, args):
|
||||
# get all current devices
|
||||
known_devices = [dev.number for dev in receiver]
|
||||
|
||||
from logitech.unifying_receiver import status
|
||||
r_status = status.ReceiverStatus(receiver, lambda *args, **kwargs: None)
|
||||
|
||||
done = [False]
|
||||
|
||||
def _notification_handler(n):
|
||||
if n.devnumber == 0xFF:
|
||||
r_status.process_notification(n)
|
||||
if not r_status.lock_open:
|
||||
done[0] = True
|
||||
elif n.sub_id == 0x41 and n.address == 0x04:
|
||||
if n.devnumber not in known_devices:
|
||||
r_status.new_device = receiver[n.devnumber]
|
||||
|
||||
from logitech.unifying_receiver import base
|
||||
base.notifications_hook = _notification_handler
|
||||
|
||||
# check if it's necessary to set the notification flags
|
||||
notification_flags = receiver.request(0x8100)
|
||||
if notification_flags:
|
||||
# just to see if any bits are set
|
||||
notification_flags = ord(notification_flags[:1]) + ord(notification_flags[1:2]) + ord(notification_flags[2:3])
|
||||
if not notification_flags:
|
||||
# if there are any notifications set, just assume the one we need is already set
|
||||
receiver.enable_notifications()
|
||||
receiver.set_lock(False, timeout=20)
|
||||
print ("Pairing: turn your new device on (timing out in 20 seconds).")
|
||||
|
||||
while not done[0]:
|
||||
n = base.read(receiver.handle, 2000)
|
||||
if n:
|
||||
n = base.make_notification(*n)
|
||||
if n:
|
||||
_notification_handler(n)
|
||||
|
||||
if not notification_flags:
|
||||
# only clear the flags if they weren't set before, otherwise a
|
||||
# concurrently running Solaar app will stop working properly
|
||||
receiver.enable_notifications(False)
|
||||
base.notifications_hook = None
|
||||
|
||||
if r_status.new_device:
|
||||
dev = r_status.new_device
|
||||
print ("Paired device %d: %s [%s:%s]" % (dev.number, dev.name, dev.codename, dev.serial))
|
||||
else:
|
||||
_fail(r_status[status.ERROR])
|
||||
|
||||
|
||||
def unpair_device(receiver, args):
|
||||
dev = _find_device(receiver, args.device)
|
||||
|
||||
# query these now, it's last chance to get them
|
||||
number, name, codename, serial = dev.number, dev.name, dev.codename, dev.serial
|
||||
try:
|
||||
del receiver[number]
|
||||
print ("Unpaired %d: %s [%s:%s]" % (number, name, codename, serial))
|
||||
except Exception as e:
|
||||
_fail("failed to unpair device %s: %s" % (dev.name, e))
|
||||
|
||||
|
||||
def config_device(receiver, args):
|
||||
dev = _find_device(receiver, args.device)
|
||||
# if dev is receiver:
|
||||
# _fail("no settings for the receiver")
|
||||
|
||||
if not dev.settings:
|
||||
_fail("no settings for %s" % dev.name)
|
||||
|
||||
if not args.setting:
|
||||
print ("[%s:%s]" % (dev.serial, dev.kind))
|
||||
print ("#", dev.name)
|
||||
for s in dev.settings:
|
||||
print ("")
|
||||
print ("# %s" % s.label)
|
||||
if s.choices:
|
||||
print ("# possible values: one of [", ', '.join(str(v) for v in s.choices), "], or higher/lower/highest/max/lowest/min")
|
||||
else:
|
||||
print ("# possible values: true/t/yes/y/1 or false/f/no/n/0")
|
||||
value = s.read()
|
||||
if value is None:
|
||||
print ("# %s = ? (failed to read from device)" % s.name)
|
||||
else:
|
||||
print (s.name, "=", value)
|
||||
return
|
||||
|
||||
setting = None
|
||||
for s in dev.settings:
|
||||
if args.setting.lower() == s.name.lower():
|
||||
setting = s
|
||||
break
|
||||
if setting is None:
|
||||
_fail("no setting '%s' for %s" % (args.setting, dev.name))
|
||||
|
||||
if args.value is None:
|
||||
result = setting.read()
|
||||
if result is None:
|
||||
_fail("failed to read '%s'" % setting.name)
|
||||
print ("%s = %s" % (setting.name, setting.read()))
|
||||
return
|
||||
|
||||
from logitech.unifying_receiver import settings as _settings
|
||||
|
||||
if setting.kind == _settings.KIND.toggle:
|
||||
value = args.value
|
||||
try:
|
||||
value = bool(int(value))
|
||||
except:
|
||||
if value.lower() in ['1', 'true', 'yes', 't', 'y']:
|
||||
value = True
|
||||
elif value.lower() in ['0', 'false', 'no', 'f', 'n']:
|
||||
value = False
|
||||
else:
|
||||
_fail("don't know how to interpret '%s' as boolean" % value)
|
||||
|
||||
elif setting.choices:
|
||||
value = args.value.lower()
|
||||
|
||||
if value in ['higher', 'lower']:
|
||||
old_value = setting.read()
|
||||
if old_value is None:
|
||||
_fail("could not read current value of '%s'" % setting.name)
|
||||
|
||||
if value == 'lower':
|
||||
lower_values = setting.choices[:old_value]
|
||||
value = lower_values[-1] if lower_values else setting.choices[:][0]
|
||||
elif value == 'higher':
|
||||
higher_values = setting.choices[old_value + 1:]
|
||||
value = higher_values[0] if higher_values else setting.choices[:][-1]
|
||||
elif value in ('highest', 'max'):
|
||||
value = setting.choices[:][-1]
|
||||
elif value in ('lowest', 'min'):
|
||||
value = setting.choices[:][0]
|
||||
elif value not in setting.choices:
|
||||
_fail("possible values for '%s' are: [%s]" % (setting.name, ', '.join(str(v) for v in setting.choices)))
|
||||
value = setting.choices[value]
|
||||
|
||||
else:
|
||||
raise NotImplemented
|
||||
|
||||
result = setting.write(value)
|
||||
if result is None:
|
||||
_fail("failed to set '%s' = '%s' [%s]" % (setting.name, value, repr(value)))
|
||||
print ("%s = %s" % (setting.name, result))
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _parse_arguments():
|
||||
import argparse
|
||||
arg_parser = argparse.ArgumentParser(prog=NAME.lower())
|
||||
arg_parser.add_argument('-d', '--debug', action='count', default=0,
|
||||
help='print logging messages, for debugging purposes (may be repeated for extra verbosity)')
|
||||
arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__)
|
||||
|
||||
subparsers = arg_parser.add_subparsers(title='commands')
|
||||
|
||||
sp = subparsers.add_parser('show', help='show information about paired devices')
|
||||
sp.add_argument('device', nargs='?', default='all',
|
||||
help='device to show information about; may be a device number (1..6), a device serial, '
|
||||
'at least 3 characters of a device\'s name, "receiver", or "all" (the default)')
|
||||
sp.add_argument('-v', '--verbose', action='store_true',
|
||||
help='print all available information about the inspected device(s)')
|
||||
sp.set_defaults(cmd=show_devices)
|
||||
|
||||
sp = subparsers.add_parser('config', help='read/write device-specific settings',
|
||||
epilog='Please note that configuration only works on active devices.')
|
||||
sp.add_argument('device',
|
||||
help='device to configure; may be a device number (1..6), a device serial, '
|
||||
'or at least 3 characters of a device\'s name')
|
||||
sp.add_argument('setting', nargs='?',
|
||||
help='device-specific setting; leave empty to list available settings')
|
||||
sp.add_argument('value', nargs='?',
|
||||
help='new value for the setting')
|
||||
sp.set_defaults(cmd=config_device)
|
||||
|
||||
sp = subparsers.add_parser('pair', help='pair a new device',
|
||||
epilog='The Logitech Unifying Receiver supports up to 6 paired devices at the same time.')
|
||||
sp.set_defaults(cmd=pair_device)
|
||||
|
||||
sp = subparsers.add_parser('unpair', help='unpair a device')
|
||||
sp.add_argument('device',
|
||||
help='device to unpair; may be a device number (1..6), a device serial, '
|
||||
'or at least 3 characters of a device\'s name.')
|
||||
sp.set_defaults(cmd=unpair_device)
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
import logging
|
||||
if args.debug > 0:
|
||||
log_level = logging.WARNING - 10 * args.debug
|
||||
log_format='%(asctime)s %(levelname)8s %(name)s: %(message)s'
|
||||
logging.basicConfig(level=max(log_level, logging.DEBUG), format=log_format)
|
||||
else:
|
||||
logging.root.addHandler(logging.NullHandler())
|
||||
logging.root.setLevel(logging.CRITICAL)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
_require('pyudev', 'python-pyudev')
|
||||
args = _parse_arguments()
|
||||
receiver = _receiver()
|
||||
args.cmd(receiver, args)
|
||||
@@ -1,111 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from gi.repository import GObject, Gtk
|
||||
GObject.threads_init()
|
||||
|
||||
_LARGE_SIZE = 64
|
||||
Gtk.IconSize.LARGE = Gtk.icon_size_register('large', _LARGE_SIZE, _LARGE_SIZE)
|
||||
# Gtk.IconSize.XLARGE = Gtk.icon_size_register('x-large', _LARGE_SIZE * 2, _LARGE_SIZE * 2)
|
||||
|
||||
from . import notify, status_icon, main_window, pair_window, action
|
||||
|
||||
from solaar import NAME
|
||||
APP_ICON = { 1: NAME, 2: NAME + '-mask', 0: NAME + '-init', -1: NAME + '-fail' }
|
||||
|
||||
|
||||
def get_battery_icon(level):
|
||||
if level < 0:
|
||||
return 'battery_unknown'
|
||||
return 'battery_%03d' % (10 * ((level + 5) // 10))
|
||||
|
||||
|
||||
_ICON_SETS = {}
|
||||
|
||||
def device_icon_set(name, kind=None):
|
||||
icon_set = _ICON_SETS.get(name)
|
||||
if icon_set is None:
|
||||
icon_set = Gtk.IconSet.new()
|
||||
_ICON_SETS[name] = icon_set
|
||||
|
||||
names = ['preferences-desktop-peripherals']
|
||||
if kind:
|
||||
if str(kind) == 'numpad':
|
||||
names += ('input-dialpad',)
|
||||
elif str(kind) == 'touchpad':
|
||||
names += ('input-tablet',)
|
||||
elif str(kind) == 'trackball':
|
||||
names += ('input-mouse',)
|
||||
names += ('input-' + str(kind),)
|
||||
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
if theme.has_icon(name):
|
||||
names += (name,)
|
||||
|
||||
source = Gtk.IconSource.new()
|
||||
for n in names:
|
||||
source.set_icon_name(n)
|
||||
icon_set.add_source(source)
|
||||
icon_set.names = names
|
||||
|
||||
return icon_set
|
||||
|
||||
|
||||
def device_icon_file(name, kind=None):
|
||||
icon_set = device_icon_set(name, kind)
|
||||
assert icon_set
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
for n in reversed(icon_set.names):
|
||||
if theme.has_icon(n):
|
||||
return theme.lookup_icon(n, _LARGE_SIZE, 0).get_filename()
|
||||
|
||||
|
||||
def icon_file(name, size=_LARGE_SIZE):
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
if theme.has_icon(name):
|
||||
return theme.lookup_icon(name, size, 0).get_filename()
|
||||
|
||||
|
||||
def error(window, title, text):
|
||||
m = Gtk.MessageDialog(window, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, text)
|
||||
m.set_title(title)
|
||||
m.run()
|
||||
m.destroy()
|
||||
|
||||
|
||||
def remove_children(container):
|
||||
container.foreach(lambda x, _: container.remove(x), None)
|
||||
|
||||
|
||||
# def find_children(container, *child_names):
|
||||
# assert container is not None
|
||||
# assert isinstance(container, Gtk.Container)
|
||||
#
|
||||
# def _iterate_children(widget, names, result, count):
|
||||
# assert isinstance(widget, Gtk.Widget)
|
||||
# wname = widget.get_name()
|
||||
# if wname in names:
|
||||
# index = names.index(wname)
|
||||
# names[index] = None
|
||||
# result[index] = widget
|
||||
# count -= 1
|
||||
#
|
||||
# if count > 0 and isinstance(widget, Gtk.Container):
|
||||
# for w in widget:
|
||||
# # assert isinstance(w, Gtk.Widget):
|
||||
# count = _iterate_children(w, names, result, count)
|
||||
# if count == 0:
|
||||
# break
|
||||
#
|
||||
# return count
|
||||
#
|
||||
# names = list(child_names)
|
||||
# count = len(names)
|
||||
# result = [None] * count
|
||||
# if _iterate_children(container, names, result, count) > 0:
|
||||
# # some children could not be found
|
||||
# pass
|
||||
# return tuple(result) if count > 1 else result[0]
|
||||
113
app/ui/action.py
@@ -1,113 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
# from sys import version as PYTHON_VERSION
|
||||
from gi.repository import Gtk, Gdk
|
||||
|
||||
import ui
|
||||
from solaar import NAME as _NAME
|
||||
from solaar import VERSION as _VERSION
|
||||
|
||||
|
||||
def _action(name, label, function, *args):
|
||||
action = Gtk.Action(name, label, label, None)
|
||||
action.set_icon_name(name)
|
||||
if function:
|
||||
action.connect('activate', function, *args)
|
||||
return action
|
||||
|
||||
|
||||
def _toggle_action(name, label, function, *args):
|
||||
action = Gtk.ToggleAction(name, label, label, None)
|
||||
action.set_icon_name(name)
|
||||
action.connect('activate', function, *args)
|
||||
return action
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _toggle_notifications(action):
|
||||
if action.get_active():
|
||||
ui.notify.init(_NAME)
|
||||
else:
|
||||
ui.notify.uninit()
|
||||
action.set_sensitive(ui.notify.available)
|
||||
toggle_notifications = _toggle_action('notifications', 'Notifications', _toggle_notifications)
|
||||
|
||||
|
||||
def _show_about_window(action):
|
||||
about = Gtk.AboutDialog()
|
||||
|
||||
about.set_icon_name(_NAME)
|
||||
about.set_program_name(_NAME)
|
||||
about.set_logo_icon_name(_NAME)
|
||||
about.set_version(_VERSION)
|
||||
about.set_comments('Shows status of devices connected\nto a Logitech Unifying Receiver.')
|
||||
|
||||
about.set_copyright(b'\xC2\xA9'.decode('utf-8') + ' 2012 Daniel Pavel')
|
||||
about.set_license_type(Gtk.License.GPL_2_0)
|
||||
|
||||
about.set_authors(('Daniel Pavel http://github.com/pwr',))
|
||||
try:
|
||||
about.add_credit_section('Testing', ('Douglas Wagner', 'Julien Gascard'))
|
||||
about.add_credit_section('Technical specifications\nprovided by',
|
||||
('Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower',))
|
||||
except TypeError:
|
||||
# gtk3 < 3.6 has incorrect gi bindings
|
||||
pass
|
||||
except:
|
||||
# is the Gtk3 version too old?
|
||||
pass
|
||||
|
||||
about.set_website('http://pwr.github.com/Solaar/')
|
||||
about.set_website_label('Solaar')
|
||||
|
||||
about.run()
|
||||
about.destroy()
|
||||
about = _action('help-about', 'About ' + _NAME, _show_about_window)
|
||||
|
||||
quit = _action('exit', 'Quit', Gtk.main_quit)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _pair_device(action, frame):
|
||||
window = frame.get_toplevel()
|
||||
|
||||
pair_dialog = ui.pair_window.create(action, frame._device)
|
||||
pair_dialog.set_transient_for(window)
|
||||
pair_dialog.set_destroy_with_parent(True)
|
||||
pair_dialog.set_modal(True)
|
||||
pair_dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
||||
pair_dialog.set_position(Gtk.WindowPosition.CENTER)
|
||||
pair_dialog.present()
|
||||
|
||||
def pair(frame):
|
||||
return _action('add', 'Pair new device', _pair_device, frame)
|
||||
|
||||
|
||||
def _unpair_device(action, frame):
|
||||
window = frame.get_toplevel()
|
||||
# window.present()
|
||||
device = frame._device
|
||||
qdialog = Gtk.MessageDialog(window, 0,
|
||||
Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
|
||||
"Unpair device\n%s ?" % device.name)
|
||||
qdialog.set_icon_name('remove')
|
||||
qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
||||
qdialog.add_button('Unpair', Gtk.ResponseType.ACCEPT)
|
||||
choice = qdialog.run()
|
||||
qdialog.destroy()
|
||||
if choice == Gtk.ResponseType.ACCEPT:
|
||||
try:
|
||||
del device.receiver[device.number]
|
||||
except:
|
||||
ui.error(window, 'Unpairing failed', 'Failed to unpair device\n%s .' % device.name)
|
||||
|
||||
def unpair(frame):
|
||||
return _action('edit-delete', 'Unpair', _unpair_device, frame)
|
||||
@@ -1,198 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
import ui
|
||||
from logitech.unifying_receiver import settings as _settings
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
try:
|
||||
from Queue import Queue as _Queue
|
||||
except ImportError:
|
||||
from queue import Queue as _Queue
|
||||
_apply_queue = _Queue(4)
|
||||
|
||||
def _process_apply_queue():
|
||||
def _write_start(sbox):
|
||||
_, failed, spinner, control = sbox.get_children()
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(False)
|
||||
spinner.set_visible(True)
|
||||
spinner.start()
|
||||
|
||||
while True:
|
||||
task = _apply_queue.get()
|
||||
assert isinstance(task, tuple)
|
||||
# print ("task", *task)
|
||||
if task[0] == 'write':
|
||||
_, setting, value, sbox = task
|
||||
GObject.idle_add(_write_start, sbox, priority=0)
|
||||
value = setting.write(value)
|
||||
elif task[0] == 'read':
|
||||
_, setting, force_read, sbox = task
|
||||
value = setting.read(not force_read)
|
||||
GObject.idle_add(_update_setting_item, sbox, value, priority=99)
|
||||
|
||||
from threading import Thread as _Thread
|
||||
_queue_processor = _Thread(name='SettingsProcessor', target=_process_apply_queue)
|
||||
_queue_processor.daemon = True
|
||||
_queue_processor.start()
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _switch_notify(switch, _, setting, spinner):
|
||||
# print ("switch notify", switch, switch.get_active(), setting)
|
||||
if switch.get_sensitive():
|
||||
# value = setting.write(switch.get_active() == True)
|
||||
# _update_setting_item(switch.get_parent(), value)
|
||||
_apply_queue.put(('write', setting, switch.get_active() == True, switch.get_parent()))
|
||||
|
||||
|
||||
def _combo_notify(cbbox, setting, spinner):
|
||||
# print ("combo notify", cbbox, cbbox.get_active_id(), setting)
|
||||
if cbbox.get_sensitive():
|
||||
_apply_queue.put(('write', setting, cbbox.get_active_id(), cbbox.get_parent()))
|
||||
|
||||
|
||||
# def _scale_notify(scale, setting, spinner):
|
||||
# _apply_queue.put(('write', setting, scale.get_value(), scale.get_parent()))
|
||||
|
||||
|
||||
# def _snap_to_markers(scale, scroll, value, setting):
|
||||
# value = int(value)
|
||||
# candidate = None
|
||||
# delta = 0xFFFFFFFF
|
||||
# for c in setting.choices:
|
||||
# d = abs(value - int(c))
|
||||
# if d < delta:
|
||||
# candidate = c
|
||||
# delta = d
|
||||
|
||||
# assert candidate is not None
|
||||
# scale.set_value(int(candidate))
|
||||
# return True
|
||||
|
||||
|
||||
def _add_settings(box, device):
|
||||
for s in device.settings:
|
||||
sbox = Gtk.HBox(homogeneous=False, spacing=8)
|
||||
sbox.pack_start(Gtk.Label(s.label), False, False, 0)
|
||||
|
||||
spinner = Gtk.Spinner()
|
||||
spinner.set_tooltip_text('Working...')
|
||||
|
||||
failed = Gtk.Image.new_from_icon_name('dialog-warning', Gtk.IconSize.SMALL_TOOLBAR)
|
||||
failed.set_tooltip_text('Failed to read value from the device.')
|
||||
|
||||
if s.kind == _settings.KIND.toggle:
|
||||
control = Gtk.Switch()
|
||||
control.connect('notify::active', _switch_notify, s, spinner)
|
||||
elif s.kind == _settings.KIND.choice:
|
||||
control = Gtk.ComboBoxText()
|
||||
for entry in s.choices:
|
||||
control.append(str(entry), str(entry))
|
||||
control.connect('changed', _combo_notify, s, spinner)
|
||||
# elif s.kind == _settings.KIND.range:
|
||||
# first, second = s.choices[:2]
|
||||
# last = s.choices[-1:][0]
|
||||
# control = Gtk.HScale.new_with_range(first, last, second - first)
|
||||
# control.set_draw_value(False)
|
||||
# control.set_has_origin(False)
|
||||
# for entry in s.choices:
|
||||
# control.add_mark(int(entry), Gtk.PositionType.TOP, str(entry))
|
||||
# control.connect('change-value', _snap_to_markers, s)
|
||||
# control.connect('value-changed', _scale_notify, s, spinner)
|
||||
else:
|
||||
raise NotImplemented
|
||||
|
||||
control.set_sensitive(False) # the first read will enable it
|
||||
sbox.pack_end(control, False, False, 0)
|
||||
sbox.pack_end(spinner, False, False, 0)
|
||||
sbox.pack_end(failed, False, False, 0)
|
||||
|
||||
if s.description:
|
||||
sbox.set_tooltip_text(s.description)
|
||||
|
||||
sbox.show_all()
|
||||
spinner.start() # the first read will stop it
|
||||
failed.set_visible(False)
|
||||
box.pack_start(sbox, False, False, 0)
|
||||
yield sbox
|
||||
|
||||
|
||||
def _update_setting_item(sbox, value):
|
||||
_, failed, spinner, control = sbox.get_children()
|
||||
spinner.set_visible(False)
|
||||
spinner.stop()
|
||||
|
||||
# print ("update", control, "with new value", value)
|
||||
if value is None:
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(True)
|
||||
return
|
||||
|
||||
failed.set_visible(False)
|
||||
if isinstance(control, Gtk.Switch):
|
||||
control.set_active(value)
|
||||
elif isinstance(control, Gtk.ComboBoxText):
|
||||
control.set_active_id(str(value))
|
||||
# elif isinstance(control, Gtk.Scale):
|
||||
# control.set_value(int(value))
|
||||
else:
|
||||
raise NotImplemented
|
||||
control.set_sensitive(True)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def create():
|
||||
b = Gtk.VBox(homogeneous=False, spacing=4)
|
||||
b.set_property('margin', 8)
|
||||
return b
|
||||
|
||||
|
||||
def update(frame):
|
||||
box = frame._config_box
|
||||
assert box
|
||||
device = frame._device
|
||||
|
||||
if device is None:
|
||||
# remove all settings widgets
|
||||
# if another device gets paired here, it will add its own widgets
|
||||
ui.remove_children(box)
|
||||
return
|
||||
|
||||
if not box.get_visible():
|
||||
# no point in doing this right now, is there?
|
||||
return
|
||||
|
||||
if not device.settings:
|
||||
# nothing to do here
|
||||
return
|
||||
|
||||
force_read = False
|
||||
items = box.get_children()
|
||||
if len(device.settings) != len(items):
|
||||
ui.remove_children(box)
|
||||
if device.status:
|
||||
items = list(_add_settings(box, device))
|
||||
assert len(device.settings) == len(items)
|
||||
force_read = True
|
||||
|
||||
device_active = bool(device.status)
|
||||
# if the device just became active, re-read the settings
|
||||
force_read |= device_active and not box.get_sensitive()
|
||||
box.set_sensitive(device_active)
|
||||
if device_active:
|
||||
for sbox, s in zip(items, device.settings):
|
||||
_apply_queue.put(('read', s, force_read, sbox))
|
||||
@@ -1,434 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from gi.repository import Gtk, Gdk, GObject
|
||||
|
||||
import ui
|
||||
from logitech.unifying_receiver import status as _status
|
||||
from . import config_panel as _config_panel
|
||||
|
||||
|
||||
_RECEIVER_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
||||
_DEVICE_ICON_SIZE = Gtk.IconSize.DIALOG
|
||||
_STATUS_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
||||
_TOOLBAR_ICON_SIZE = Gtk.IconSize.MENU
|
||||
_PLACEHOLDER = '~'
|
||||
_FALLBACK_ICON = 'preferences-desktop-peripherals'
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _make_receiver_box(name):
|
||||
frame = Gtk.Frame()
|
||||
frame._device = None
|
||||
frame.set_name(name)
|
||||
|
||||
icon_set = ui.device_icon_set(name)
|
||||
icon = Gtk.Image.new_from_icon_set(icon_set, _RECEIVER_ICON_SIZE)
|
||||
icon.set_padding(2, 2)
|
||||
frame._icon = icon
|
||||
|
||||
label = Gtk.Label('Scanning...')
|
||||
label.set_alignment(0, 0.5)
|
||||
frame._label = label
|
||||
|
||||
pairing_icon = Gtk.Image.new_from_icon_name('network-wireless', _RECEIVER_ICON_SIZE)
|
||||
pairing_icon.set_tooltip_text('The pairing lock is open.')
|
||||
pairing_icon._tick = 0
|
||||
frame._pairing_icon = pairing_icon
|
||||
|
||||
toolbar = Gtk.Toolbar()
|
||||
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
||||
toolbar.set_icon_size(_TOOLBAR_ICON_SIZE)
|
||||
toolbar.set_show_arrow(False)
|
||||
frame._toolbar = toolbar
|
||||
|
||||
hbox = Gtk.HBox(homogeneous=False, spacing=8)
|
||||
hbox.pack_start(icon, False, False, 0)
|
||||
hbox.pack_start(label, True, True, 0)
|
||||
hbox.pack_start(pairing_icon, False, False, 0)
|
||||
hbox.pack_start(toolbar, False, False, 0)
|
||||
|
||||
info_label = Gtk.Label()
|
||||
info_label.set_markup('<small>reading ...</small>')
|
||||
info_label.set_property('margin-left', 36)
|
||||
info_label.set_alignment(0, 0)
|
||||
info_label.set_selectable(True)
|
||||
frame._info_label = info_label
|
||||
|
||||
def _update_info_label(f):
|
||||
device = f._device
|
||||
if f._info_label.get_visible() and '\n' not in f._info_label.get_text():
|
||||
items = [('Path', device.path), ('Serial', device.serial)] + \
|
||||
[(fw.kind, fw.version) for fw in device.firmware]
|
||||
f._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-13s: %s' % item for item in items))
|
||||
|
||||
def _toggle_info_label(action, f):
|
||||
active = action.get_active()
|
||||
vb = f.get_child()
|
||||
for c in vb.get_children()[1:]:
|
||||
c.set_visible(active)
|
||||
|
||||
if active:
|
||||
GObject.timeout_add(50, _update_info_label, f)
|
||||
|
||||
toggle_info_action = ui.action._toggle_action('info', 'Details', _toggle_info_label, frame)
|
||||
toolbar.insert(toggle_info_action.create_tool_item(), 0)
|
||||
toolbar.insert(ui.action.pair(frame).create_tool_item(), -1)
|
||||
# toolbar.insert(ui.action.about.create_tool_item(), -1)
|
||||
|
||||
vbox = Gtk.VBox(homogeneous=False, spacing=2)
|
||||
vbox.set_border_width(2)
|
||||
vbox.pack_start(hbox, True, True, 0)
|
||||
vbox.pack_start(Gtk.HSeparator(), False, False, 0)
|
||||
vbox.pack_start(info_label, True, True, 0)
|
||||
|
||||
frame.add(vbox)
|
||||
frame.show_all()
|
||||
|
||||
pairing_icon.set_visible(False)
|
||||
_toggle_info_label(toggle_info_action, frame)
|
||||
return frame
|
||||
|
||||
|
||||
def _make_device_box(index):
|
||||
frame = Gtk.Frame()
|
||||
frame._device = None
|
||||
frame.set_name(_PLACEHOLDER)
|
||||
|
||||
icon = Gtk.Image.new_from_icon_name(_FALLBACK_ICON, _DEVICE_ICON_SIZE)
|
||||
icon.set_alignment(0.5, 0)
|
||||
frame._icon = icon
|
||||
|
||||
label = Gtk.Label('Initializing...')
|
||||
label.set_alignment(0, 0.5)
|
||||
label.set_padding(4, 0)
|
||||
frame._label = label
|
||||
|
||||
battery_icon = Gtk.Image.new_from_icon_name(ui.get_battery_icon(-1), _STATUS_ICON_SIZE)
|
||||
|
||||
battery_label = Gtk.Label()
|
||||
battery_label.set_width_chars(6)
|
||||
battery_label.set_alignment(0, 0.5)
|
||||
|
||||
light_icon = Gtk.Image.new_from_icon_name('light_unknown', _STATUS_ICON_SIZE)
|
||||
|
||||
light_label = Gtk.Label()
|
||||
light_label.set_alignment(0, 0.5)
|
||||
light_label.set_width_chars(8)
|
||||
|
||||
not_encrypted_icon = Gtk.Image.new_from_icon_name('security-low', _STATUS_ICON_SIZE)
|
||||
not_encrypted_icon.set_name('not-encrypted')
|
||||
not_encrypted_icon.set_tooltip_text('The wireless link between this device and the Unifying Receiver is not encrypted.\n'
|
||||
'\n'
|
||||
'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n'
|
||||
'\n'
|
||||
'It is, however, a major security issue for text-input devices (keyboards, numpads),\n'
|
||||
'because typed text can be sniffed inconspicuously by 3rd parties within range.')
|
||||
|
||||
toolbar = Gtk.Toolbar()
|
||||
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
||||
toolbar.set_icon_size(_TOOLBAR_ICON_SIZE)
|
||||
toolbar.set_show_arrow(False)
|
||||
frame._toolbar = toolbar
|
||||
|
||||
status_box = Gtk.HBox(homogeneous=False, spacing=2)
|
||||
status_box.pack_start(battery_icon, False, True, 0)
|
||||
status_box.pack_start(battery_label, False, True, 0)
|
||||
status_box.pack_start(light_icon, False, True, 0)
|
||||
status_box.pack_start(light_label, False, True, 0)
|
||||
status_box.pack_end(toolbar, False, False, 0)
|
||||
status_box.pack_end(not_encrypted_icon, False, False, 0)
|
||||
frame._status_icons = status_box
|
||||
|
||||
status_vbox = Gtk.VBox(homogeneous=False, spacing=4)
|
||||
status_vbox.pack_start(label, True, True, 0)
|
||||
status_vbox.pack_start(status_box, True, True, 0)
|
||||
|
||||
device_box = Gtk.HBox(homogeneous=False, spacing=4)
|
||||
# device_box.set_border_width(4)
|
||||
device_box.pack_start(icon, False, False, 0)
|
||||
device_box.pack_start(status_vbox, True, True, 0)
|
||||
device_box.show_all()
|
||||
|
||||
info_label = Gtk.Label()
|
||||
info_label.set_markup('<small>reading ...</small>')
|
||||
info_label.set_property('margin-left', 54)
|
||||
info_label.set_selectable(True)
|
||||
info_label.set_alignment(0, 0)
|
||||
frame._info_label = info_label
|
||||
|
||||
def _update_info_label(f):
|
||||
if frame._info_label.get_text().count('\n') < 4:
|
||||
device = f._device
|
||||
assert device
|
||||
|
||||
items = [None, None, None, None, None, None, None, None]
|
||||
hid = device.protocol
|
||||
items[0] = ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown')
|
||||
items[1] = ('Polling rate', '%d ms' % device.polling_rate)
|
||||
items[2] = ('Wireless PID', device.wpid)
|
||||
items[3] = ('Serial', device.serial)
|
||||
firmware = device.firmware
|
||||
if firmware:
|
||||
items[4:] = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware]
|
||||
|
||||
frame._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-13s: %s' % i for i in items if i))
|
||||
|
||||
def _toggle_info_label(action, f):
|
||||
active = action.get_active()
|
||||
if active:
|
||||
# set config toggle button as inactive
|
||||
f._toolbar.get_children()[-1].set_active(False)
|
||||
|
||||
vb = f.get_child()
|
||||
children = vb.get_children()
|
||||
children[1].set_visible(active) # separator
|
||||
children[2].set_visible(active) # info label
|
||||
|
||||
if active:
|
||||
GObject.timeout_add(30, _update_info_label, f)
|
||||
|
||||
def _toggle_config(action, f):
|
||||
active = action.get_active()
|
||||
if active:
|
||||
# set info toggle button as inactive
|
||||
f._toolbar.get_children()[0].set_active(False)
|
||||
|
||||
vb = f.get_child()
|
||||
children = vb.get_children()
|
||||
children[1].set_visible(active) # separator
|
||||
children[3].set_visible(active) # config box
|
||||
children[4].set_visible(active) # unpair button
|
||||
|
||||
if active:
|
||||
GObject.timeout_add(30, _config_panel.update, f)
|
||||
|
||||
toggle_info_action = ui.action._toggle_action('info', 'Details', _toggle_info_label, frame)
|
||||
toolbar.insert(toggle_info_action.create_tool_item(), 0)
|
||||
toggle_config_action = ui.action._toggle_action('preferences-system', 'Configuration', _toggle_config, frame)
|
||||
toolbar.insert(toggle_config_action.create_tool_item(), -1)
|
||||
|
||||
vbox = Gtk.VBox(homogeneous=False, spacing=2)
|
||||
vbox.set_border_width(2)
|
||||
vbox.pack_start(device_box, True, True, 0)
|
||||
vbox.pack_start(Gtk.HSeparator(), False, False, 0)
|
||||
vbox.pack_start(info_label, False, False, 0)
|
||||
|
||||
frame._config_box = _config_panel.create()
|
||||
vbox.pack_start(frame._config_box, False, False, 0)
|
||||
|
||||
unpair = Gtk.Button('Unpair')
|
||||
unpair.set_image(Gtk.Image.new_from_icon_name('edit-delete', Gtk.IconSize.BUTTON))
|
||||
unpair.connect('clicked', ui.action._unpair_device, frame)
|
||||
unpair.set_relief(Gtk.ReliefStyle.NONE)
|
||||
unpair.set_property('margin-left', 106)
|
||||
unpair.set_property('margin-right', 106)
|
||||
unpair.set_property('can-focus', False) # exclude from tab-navigation
|
||||
vbox.pack_end(unpair, False, False, 0)
|
||||
|
||||
vbox.show_all()
|
||||
frame.add(vbox)
|
||||
|
||||
_toggle_info_label(toggle_info_action, frame)
|
||||
_toggle_config(toggle_config_action, frame)
|
||||
return frame
|
||||
|
||||
|
||||
def create(title, name, max_devices, systray=False):
|
||||
window = Gtk.Window()
|
||||
window.set_title(title)
|
||||
window.set_icon_name(ui.APP_ICON[0])
|
||||
window.set_role('status-window')
|
||||
|
||||
vbox = Gtk.VBox(homogeneous=False, spacing=12)
|
||||
vbox.set_border_width(4)
|
||||
|
||||
rbox = _make_receiver_box(name)
|
||||
vbox.add(rbox)
|
||||
for i in range(1, 1 + max_devices):
|
||||
dbox = _make_device_box(i)
|
||||
vbox.add(dbox)
|
||||
vbox.set_visible(True)
|
||||
|
||||
window.add(vbox)
|
||||
|
||||
geometry = Gdk.Geometry()
|
||||
geometry.min_width = 320
|
||||
geometry.min_height = 32
|
||||
window.set_geometry_hints(vbox, geometry, Gdk.WindowHints.MIN_SIZE)
|
||||
window.set_resizable(False)
|
||||
|
||||
def _toggle_visible(w, trigger):
|
||||
if w.get_visible():
|
||||
# hiding moves the window to 0,0
|
||||
position = w.get_position()
|
||||
w.hide()
|
||||
w.move(*position)
|
||||
else:
|
||||
if isinstance(trigger, Gtk.StatusIcon):
|
||||
x, y = w.get_position()
|
||||
if x == 0 and y == 0:
|
||||
# if the window hasn't been shown yet, position it next to the status icon
|
||||
x, y, _ = Gtk.StatusIcon.position_menu(Gtk.Menu(), trigger)
|
||||
w.move(x, y)
|
||||
w.present()
|
||||
return True
|
||||
|
||||
def _set_has_systray(w, systray):
|
||||
if systray != w._has_systray:
|
||||
w._has_systray = systray
|
||||
if systray:
|
||||
if w._delete_event_connection is None or not w.get_skip_taskbar_hint():
|
||||
w.set_skip_taskbar_hint(True)
|
||||
w.set_skip_pager_hint(True)
|
||||
if w._delete_event_connection:
|
||||
w.disconnect(w._delete_event_connection)
|
||||
w._delete_event_connection = w.connect('delete-event', _toggle_visible)
|
||||
else:
|
||||
if w._delete_event_connection is None or w.get_skip_taskbar_hint():
|
||||
w.set_skip_taskbar_hint(False)
|
||||
w.set_skip_pager_hint(False)
|
||||
if w._delete_event_connection:
|
||||
w.disconnect(w._delete_event_connection)
|
||||
w._delete_event_connection = w.connect('delete-event', Gtk.main_quit)
|
||||
w.present()
|
||||
|
||||
from types import MethodType
|
||||
window.toggle_visible = MethodType(_toggle_visible, window)
|
||||
window.set_has_systray = MethodType(_set_has_systray, window)
|
||||
del MethodType
|
||||
|
||||
window.set_keep_above(True)
|
||||
window._delete_event_connection = None
|
||||
window._has_systray = None
|
||||
window.set_has_systray(systray)
|
||||
|
||||
return window
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _update_receiver_box(frame, receiver):
|
||||
frame._label.set_text(str(receiver.status))
|
||||
if receiver:
|
||||
frame._device = receiver
|
||||
frame._icon.set_sensitive(True)
|
||||
if receiver.status.lock_open:
|
||||
if frame._pairing_icon._tick == 0:
|
||||
def _pairing_tick(i, s):
|
||||
if s and s.lock_open:
|
||||
i.set_sensitive(bool(i._tick % 2))
|
||||
i._tick += 1
|
||||
return True
|
||||
i.set_visible(False)
|
||||
i.set_sensitive(True)
|
||||
i._tick = 0
|
||||
frame._pairing_icon.set_visible(True)
|
||||
GObject.timeout_add(1000, _pairing_tick, frame._pairing_icon, receiver.status)
|
||||
else:
|
||||
frame._pairing_icon.set_visible(False)
|
||||
frame._pairing_icon.set_sensitive(True)
|
||||
frame._pairing_icon._tick = 0
|
||||
frame._toolbar.set_sensitive(True)
|
||||
else:
|
||||
frame._device = None
|
||||
frame._icon.set_sensitive(False)
|
||||
frame._pairing_icon.set_visible(False)
|
||||
frame._toolbar.set_sensitive(False)
|
||||
frame._toolbar.get_children()[0].set_active(False)
|
||||
frame._info_label.set_text('')
|
||||
|
||||
|
||||
def _update_device_box(frame, dev):
|
||||
if dev is None:
|
||||
frame.set_visible(False)
|
||||
frame.set_name(_PLACEHOLDER)
|
||||
frame._device = None
|
||||
_config_panel.update(frame)
|
||||
return
|
||||
|
||||
first_run = frame.get_name() != dev.name
|
||||
if first_run:
|
||||
frame._device = dev
|
||||
frame.set_name(dev.name)
|
||||
icon_set = ui.device_icon_set(dev.name, dev.kind)
|
||||
frame._icon.set_from_icon_set(icon_set, _DEVICE_ICON_SIZE)
|
||||
frame._label.set_markup('<b>%s</b>' % dev.name)
|
||||
for i in frame._toolbar.get_children():
|
||||
i.set_active(False)
|
||||
|
||||
battery_icon, battery_label, light_icon, light_label, not_encrypted_icon, _ = frame._status_icons
|
||||
battery_level = dev.status.get(_status.BATTERY_LEVEL)
|
||||
|
||||
if dev.status:
|
||||
frame._label.set_sensitive(True)
|
||||
|
||||
if battery_level is None:
|
||||
battery_icon.set_sensitive(False)
|
||||
battery_icon.set_from_icon_name(ui.get_battery_icon(-1), _STATUS_ICON_SIZE)
|
||||
battery_label.set_markup('<small>no status</small>')
|
||||
battery_label.set_sensitive(True)
|
||||
else:
|
||||
battery_icon.set_from_icon_name(ui.get_battery_icon(battery_level), _STATUS_ICON_SIZE)
|
||||
battery_icon.set_sensitive(True)
|
||||
battery_label.set_text('%d%%' % battery_level)
|
||||
battery_label.set_sensitive(True)
|
||||
|
||||
battery_status = dev.status.get(_status.BATTERY_STATUS)
|
||||
battery_icon.set_tooltip_text(battery_status or '')
|
||||
|
||||
light_level = dev.status.get(_status.LIGHT_LEVEL)
|
||||
if light_level is None:
|
||||
light_icon.set_visible(False)
|
||||
light_label.set_visible(False)
|
||||
else:
|
||||
icon_name = 'light_%03d' % (20 * ((light_level + 50) // 100))
|
||||
light_icon.set_from_icon_name(icon_name, _STATUS_ICON_SIZE)
|
||||
light_icon.set_visible(True)
|
||||
light_label.set_text('%d lux' % light_level)
|
||||
light_label.set_visible(True)
|
||||
|
||||
not_encrypted_icon.set_visible(dev.status.get(_status.ENCRYPTED) == False)
|
||||
|
||||
else:
|
||||
frame._label.set_sensitive(False)
|
||||
|
||||
battery_icon.set_sensitive(False)
|
||||
battery_label.set_sensitive(False)
|
||||
if battery_level is None:
|
||||
battery_label.set_markup('<small>inactive</small>')
|
||||
else:
|
||||
battery_label.set_markup('%d%%' % battery_level)
|
||||
|
||||
light_icon.set_visible(False)
|
||||
light_label.set_visible(False)
|
||||
not_encrypted_icon.set_visible(False)
|
||||
|
||||
frame._toolbar.get_children()[-1].set_active(False)
|
||||
|
||||
frame.set_visible(True)
|
||||
_config_panel.update(frame)
|
||||
|
||||
|
||||
def update(window, receiver, device=None):
|
||||
assert receiver is not None
|
||||
# print ("update", receiver, receiver.status, len(receiver), device)
|
||||
window.set_icon_name(ui.APP_ICON[1 if receiver else -1])
|
||||
|
||||
vbox = window.get_child()
|
||||
frames = list(vbox.get_children())
|
||||
assert len(frames) == 1 + receiver.max_devices, frames
|
||||
|
||||
if device is None:
|
||||
_update_receiver_box(frames[0], receiver)
|
||||
if not receiver:
|
||||
for frame in frames[1:]:
|
||||
_update_device_box(frame, None)
|
||||
else:
|
||||
_update_device_box(frames[device.number], None if device.status is None else device)
|
||||
@@ -1,70 +0,0 @@
|
||||
#
|
||||
# Optional desktop notifications.
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
|
||||
try:
|
||||
# this import is allowed to fail, in which case the entire feature is unavailable
|
||||
from gi.repository import Notify
|
||||
import logging
|
||||
|
||||
import ui
|
||||
|
||||
# assumed to be working since the import succeeded
|
||||
available = True
|
||||
_notifications = {}
|
||||
|
||||
|
||||
def init(app_title):
|
||||
"""Init the notifications system."""
|
||||
global available
|
||||
if available:
|
||||
if not Notify.is_initted():
|
||||
logging.info("starting desktop notifications")
|
||||
try:
|
||||
return Notify.init(app_title)
|
||||
except:
|
||||
logging.exception("initializing desktop notifications")
|
||||
available = False
|
||||
return available and Notify.is_initted()
|
||||
|
||||
|
||||
def uninit():
|
||||
if available and Notify.is_initted():
|
||||
logging.info("stopping desktop notifications")
|
||||
_notifications.clear()
|
||||
Notify.uninit()
|
||||
|
||||
|
||||
def show(dev, reason=None):
|
||||
"""Show a notification with title and text."""
|
||||
if available and Notify.is_initted():
|
||||
summary = dev.name
|
||||
|
||||
# if a notification with same name is already visible, reuse it to avoid spamming
|
||||
n = _notifications.get(summary)
|
||||
if n is None:
|
||||
n = _notifications[summary] = Notify.Notification()
|
||||
|
||||
message = reason or ('unpaired' if dev.status is None else
|
||||
(str(dev.status) or ('connected' if dev.status else 'inactive')))
|
||||
|
||||
# we need to use the filename here because the notifications daemon
|
||||
# is an external application that does not know about our icon sets
|
||||
n.update(summary, message, ui.device_icon_file(dev.name, dev.kind))
|
||||
urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL
|
||||
n.set_urgency(urgency)
|
||||
|
||||
try:
|
||||
# logging.debug("showing %s", n)
|
||||
n.show()
|
||||
except Exception:
|
||||
logging.exception("showing %s", n)
|
||||
|
||||
except ImportError:
|
||||
available = False
|
||||
init = lambda app_title: False
|
||||
uninit = lambda: None
|
||||
show = lambda dev, reason: None
|
||||
@@ -1,126 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from gi.repository import Gtk, GObject, GdkPixbuf
|
||||
|
||||
import ui
|
||||
from logitech.unifying_receiver import status as _status
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
_NO_DEVICES = [None] * 6
|
||||
|
||||
def create(window, menu_actions=None):
|
||||
name = window.get_title()
|
||||
icon = Gtk.StatusIcon()
|
||||
icon.set_title(name)
|
||||
icon.set_name(name)
|
||||
icon.set_from_icon_name(ui.APP_ICON[0])
|
||||
icon._devices = list(_NO_DEVICES)
|
||||
|
||||
icon.set_tooltip_text(name)
|
||||
icon.connect('activate', window.toggle_visible)
|
||||
|
||||
menu = Gtk.Menu()
|
||||
for action in menu_actions or ():
|
||||
if action:
|
||||
menu.append(action.create_menu_item())
|
||||
|
||||
menu.append(ui.action.quit.create_menu_item())
|
||||
menu.show_all()
|
||||
|
||||
icon.connect('popup_menu',
|
||||
lambda icon, button, time, menu:
|
||||
menu.popup(None, None, icon.position_menu, icon, button, time),
|
||||
menu)
|
||||
|
||||
# use size-changed to detect if the systray is available or not
|
||||
def _size_changed(i, size, w):
|
||||
def _check_systray(i2, w2):
|
||||
w2.set_has_systray(i2.is_embedded() and i2.get_visible())
|
||||
# first guess
|
||||
GObject.timeout_add(250, _check_systray, i, w)
|
||||
# just to make sure...
|
||||
GObject.timeout_add(1000, _check_systray, i, w)
|
||||
icon.connect('size-changed', _size_changed, window)
|
||||
|
||||
return icon
|
||||
|
||||
|
||||
_PIXMAPS = {}
|
||||
def _icon_with_battery(level, active):
|
||||
battery_icon = ui.get_battery_icon(level)
|
||||
name = '%s-%s' % (battery_icon, active)
|
||||
if name not in _PIXMAPS:
|
||||
mask = ui.icon_file(ui.APP_ICON[2], 128)
|
||||
assert mask
|
||||
mask = GdkPixbuf.Pixbuf.new_from_file(mask)
|
||||
assert mask.get_width() == 128 and mask.get_height() == 128
|
||||
mask.saturate_and_pixelate(mask, 0.7, False)
|
||||
|
||||
battery = ui.icon_file(battery_icon, 128)
|
||||
assert battery
|
||||
battery = GdkPixbuf.Pixbuf.new_from_file(battery)
|
||||
assert battery.get_width() == 128 and battery.get_height() == 128
|
||||
if not active:
|
||||
battery.saturate_and_pixelate(battery, 0, True)
|
||||
|
||||
# TODO can the masking be done at runtime?
|
||||
battery.composite(mask, 0, 7, 80, 121, -32, 7, 1, 1, GdkPixbuf.InterpType.NEAREST, 255)
|
||||
_PIXMAPS[name] = mask
|
||||
|
||||
return _PIXMAPS[name]
|
||||
|
||||
def update(icon, receiver, device=None):
|
||||
# print ("icon update", receiver, receiver.status, len(receiver), device)
|
||||
if device is not None:
|
||||
icon._devices[device.number] = None if device.status is None else device
|
||||
if not receiver:
|
||||
icon._devices[:] = _NO_DEVICES
|
||||
if not icon.is_embedded():
|
||||
return
|
||||
|
||||
def _lines(r, devices):
|
||||
yield '<b>%s</b>: %s' % (ui.NAME, r.status)
|
||||
yield ''
|
||||
|
||||
for dev in devices:
|
||||
if dev is None:
|
||||
continue
|
||||
|
||||
yield '<b>%s</b>' % dev.name
|
||||
|
||||
assert hasattr(dev, 'status') and dev.status is not None
|
||||
p = str(dev.status)
|
||||
if p: # does it have any properties to print?
|
||||
if dev.status:
|
||||
yield '\t%s' % p
|
||||
else:
|
||||
yield '\t%s <small>(inactive)</small>' % p
|
||||
else:
|
||||
if dev.status:
|
||||
yield '\t<small>no status</small>'
|
||||
else:
|
||||
yield '\t<small>(inactive)</small>'
|
||||
yield ''
|
||||
|
||||
icon.set_tooltip_markup('\n'.join(_lines(receiver, icon._devices)).rstrip('\n'))
|
||||
|
||||
battery_status = None
|
||||
battery_level = 1000
|
||||
for dev in icon._devices:
|
||||
if dev is not None:
|
||||
level = dev.status.get(_status.BATTERY_LEVEL)
|
||||
if level is not None and level < battery_level:
|
||||
battery_status = dev.status
|
||||
battery_level = level
|
||||
|
||||
if battery_status is None:
|
||||
icon.set_from_icon_name(ui.APP_ICON[1 if receiver else -1])
|
||||
else:
|
||||
icon.set_from_pixbuf(_icon_with_battery(battery_level, bool(battery_status)))
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
Z=`dirname "$0"`
|
||||
Z=`readlink -f "$Z/.."`
|
||||
|
||||
export PYTHONPATH="$Z/lib"
|
||||
export PYTHONUNBUFFERED=yes
|
||||
|
||||
NICE=`which nice 2>/dev/null`
|
||||
PYTHON=${PYTHON:-`which python python2 python3 | head -n 1`}
|
||||
exec $NICE $PYTHON -m hidapi.hidconsole "$@"
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if test -z "$1"; then
|
||||
echo "Use: $0 <device number 1..6>"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
Z=`readlink -f "$0"`
|
||||
HC=`dirname "$Z"`/hidconsole
|
||||
|
||||
z='0 1 2 3 4 5 6 7 8 9 a b c d e f'
|
||||
|
||||
for x in $z; do
|
||||
for y in $z; do
|
||||
echo "10 0${1} 81${x}${y} 000000"
|
||||
done
|
||||
done | "$HC" --hidpp | grep -v ' 8F.. ..0[12]' | grep -B 1 '^>> '
|
||||
|
||||
for x in $z; do
|
||||
for y in $z; do
|
||||
echo "10 0${1} 83${x}${y} 000000"
|
||||
done
|
||||
done | "$HC" --hidpp | grep -v ' 8F.. ..0[12]' | grep -B 1 '^>> '
|
||||
62
bin/solaar
@@ -1,11 +1,57 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env python3
|
||||
# -*- python-mode -*-
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
Z=`dirname "$0"`
|
||||
Z=`readlink -f "$Z/.."`
|
||||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
export PYTHONPATH="$Z/app:$Z/lib"
|
||||
export XDG_DATA_DIRS="$Z/share_override:$Z/share:$XDG_DATA_DIRS"
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
NICE=`which nice 2>/dev/null`
|
||||
PYTHON=${PYTHON:-`which python python2 python3 | head -n 1`}
|
||||
exec $NICE $PYTHON -m solaar "$@"
|
||||
|
||||
def init_paths():
|
||||
"""Make the app work in the source tree."""
|
||||
import sys
|
||||
import os.path as _path
|
||||
|
||||
# Python 2 need conversion from utf-8 filenames
|
||||
# Python 3 might have problems converting back to UTF-8 in case of Unicode surrogates
|
||||
try:
|
||||
if sys.version_info < (3,):
|
||||
decoded_path = sys.path[0].decode(sys.getfilesystemencoding())
|
||||
else:
|
||||
decoded_path = sys.path[0]
|
||||
sys.path[0].encode(sys.getfilesystemencoding())
|
||||
|
||||
except UnicodeError:
|
||||
sys.stderr.write('ERROR: Solaar cannot recognize encoding of filesystem path, this may happen because non UTF-8 characters in the pathname.\n')
|
||||
sys.exit(1)
|
||||
|
||||
prefix = _path.normpath(_path.join(_path.realpath(decoded_path), '..'))
|
||||
src_lib = _path.join(prefix, 'lib')
|
||||
share_lib = _path.join(prefix, 'share', 'solaar', 'lib')
|
||||
for location in src_lib, share_lib:
|
||||
init_py = _path.join(location, 'solaar', '__init__.py')
|
||||
# print ("sys.path[0]: checking", init_py)
|
||||
if _path.exists(init_py):
|
||||
# print ("sys.path[0]: found", location, "replacing", sys.path[0])
|
||||
sys.path[0] = location
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_paths()
|
||||
import solaar.gtk
|
||||
solaar.gtk.main()
|
||||
|
||||
@@ -1,10 +1,43 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env python3
|
||||
# -*- python-mode -*-
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
Z=`dirname "$0"`
|
||||
Z=`readlink -f "$Z/.."`
|
||||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
export PYTHONPATH="$Z/app:$Z/lib"
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
NICE=`which nice 2>/dev/null`
|
||||
PYTHON=${PYTHON:-`which python python2 python3 | head -n 1`}
|
||||
exec $NICE $PYTHON -m solaar_cli "$@"
|
||||
|
||||
def init_paths():
|
||||
"""Make the app work in the source tree."""
|
||||
import sys
|
||||
import os.path as _path
|
||||
|
||||
prefix = _path.normpath(_path.join(_path.realpath(sys.path[0]), '..'))
|
||||
src_lib = _path.join(prefix, 'lib')
|
||||
share_lib = _path.join(prefix, 'share', 'solaar', 'lib')
|
||||
for location in src_lib, share_lib:
|
||||
init_py = _path.join(location, 'solaar', '__init__.py')
|
||||
if _path.exists(init_py):
|
||||
sys.path[0] = location
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print ('WARNING: solaar-cli is deprecated; use solaar with the usual arguments')
|
||||
init_paths()
|
||||
import solaar.cli
|
||||
solaar.cli.run()
|
||||
|
||||
6
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# ignore documentation-specific files
|
||||
|
||||
.jekyll-metadata
|
||||
Gemfile
|
||||
Gemfile.lock
|
||||
_site/
|
||||
4
docs/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Documentation Readme
|
||||
|
||||
This project's documentation is hosted using GitHub pages, which serves static pages using Jeykll.
|
||||
[Please refer to the official documentation for instructions for how to build the site locally.](https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/)
|
||||
BIN
docs/Solaar-main-window-keyboard.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/Solaar-main-window-mouse.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
docs/Solaar-main-window-offline.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
docs/Solaar-main-window-receiver.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
docs/Solaar-menu.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
10
docs/_config.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
title: Solaar
|
||||
description: Linux Device Manager for Logitech Unifying Receivers and Paired Devices.
|
||||
tagline: Linux Device Manager for Logitech Unifying Receivers and Paired Devices.
|
||||
owner: pwr-Solaar
|
||||
owner_url: https://github.com/pwr-Solaar
|
||||
repository: pwr-Solaar/Solaar
|
||||
version: 1.0.2
|
||||
show_downloads: false
|
||||
encoding: utf-8
|
||||
theme: jekyll-theme-slate
|
||||
53
docs/_layouts/default.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.lang | default: "en-US" }}">
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,maximum-scale=2">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
|
||||
<link rel="icon" type="image/png" href="{{ site.baseurl }}/assets/favicon.png" />
|
||||
|
||||
{% seo %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div id="header_wrap" class="outer">
|
||||
<header class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<a id="forkme_banner" href="{{ site.github.repository_url }}">View on GitHub</a>
|
||||
{% endif %}
|
||||
<h1 id="project_title">
|
||||
<img src="{{ site.baseurl }}/assets/solaar.svg" style="margin-bottom: -10px; width: 48px; height: 48px; border: 0; box-shadow: none;" />
|
||||
{{ site.title | default: site.github.repository_name }}</h1>
|
||||
<h2 id="project_tagline">{{ site.description | default: site.github.project_tagline }}</h2>
|
||||
|
||||
{% if site.show_downloads %}
|
||||
<section id="downloads">
|
||||
<a class="zip_download_link" href="{{ site.github.zip_url }}">Download this project as a .zip file</a>
|
||||
<a class="tar_download_link" href="{{ site.github.tar_url }}">Download this project as a tar.gz file</a>
|
||||
</section>
|
||||
{% endif %}
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<div id="main_content_wrap" class="outer">
|
||||
<section id="main_content" class="inner">
|
||||
{{ content }}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div id="footer_wrap" class="outer">
|
||||
<footer class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<p class="copyright">{{ site.title | default: site.github.repository_name }} maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
|
||||
{% endif %}
|
||||
<p>Published with <a href="https://pages.github.com">GitHub Pages</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
47
docs/_layouts/page.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.lang | default: "en-US" }}">
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,maximum-scale=2">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
|
||||
<link rel="icon" type="image/png" href="{{ site.baseurl }}/assets/favicon.png" />
|
||||
{% seo %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div id="header_wrap" class="outer">
|
||||
<header class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<a id="forkme_banner" href="{{ site.github.repository_url }}">View on GitHub</a>
|
||||
{% endif %}
|
||||
|
||||
<h1 id="project_title">
|
||||
<a href="{{ site.baseurl }}" style="color: #fff;">
|
||||
<img src="{{ site.baseurl }}/assets/solaar.svg" style="margin-bottom: -10px; width: 48px; height: 48px; border: 0; box-shadow: none;" />
|
||||
{{ site.title | default: site.github.repository_name }}</h1>
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<div id="main_content_wrap" class="outer">
|
||||
<section id="main_content" class="inner">
|
||||
{{ content }}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div id="footer_wrap" class="outer">
|
||||
<footer class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<p class="copyright">{{ site.title | default: site.github.repository_name }} maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
|
||||
{% endif %}
|
||||
<p>Published with <a href="https://pages.github.com">GitHub Pages</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
docs/assets/favicon.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
100
docs/assets/solaar.svg
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="solaar.svg"
|
||||
inkscape:export-filename="/home/chris/Git/Solaar/_includes/favicon.png"
|
||||
inkscape:export-xdpi="66.666672"
|
||||
inkscape:export-ydpi="66.666672"
|
||||
width="43.200001"
|
||||
height="43.200001">
|
||||
<metadata
|
||||
id="metadata21">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1807"
|
||||
inkscape:window-height="797"
|
||||
id="namedview19"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="2.36"
|
||||
inkscape:cx="1.4135593"
|
||||
inkscape:cy="-4.4"
|
||||
inkscape:window-x="3000"
|
||||
inkscape:window-y="840"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="gradient_blue">
|
||||
<stop
|
||||
style="stop-color:#009099;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop7" />
|
||||
<stop
|
||||
style="stop-color:#00a899;stop-opacity:0.9"
|
||||
offset="1"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="5"
|
||||
y1="50"
|
||||
x2="95"
|
||||
y2="50"
|
||||
id="gradient_rect"
|
||||
xlink:href="#gradient_blue"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
x1="37"
|
||||
y1="50"
|
||||
x2="63"
|
||||
y2="50"
|
||||
id="gradient_dot"
|
||||
xlink:href="#gradient_blue"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<g
|
||||
transform="matrix(0.48,0,0,0.48,-2.4,-2.4)"
|
||||
id="g13">
|
||||
<path
|
||||
d="m 21.5,5.5 c -8.864,0 -16,7.136 -16,16 l 0,57 c 0,8.864 7.136,16 16,16 l 57,0 c 8.864,0 16,-7.136 16,-16 l 0,-57 c 0,-8.864 -7.136,-16 -16,-16 l -57,0 z m 16.1875,11.1875 9.03125,15.625 C 47.784179,32.115965 48.877705,32 50,32 c 1.122295,0 2.215821,0.115965 3.28125,0.3125 l 9.03125,-15.625 10.375,6 -9.03125,15.625 C 65.078123,39.972287 66.191785,41.898777 66.9375,44 L 85,44 85,56 66.9375,56 c -0.745715,2.101223 -1.859377,4.027713 -3.28125,5.6875 l 9.03125,15.625 -10.375,6 -9.03125,-15.625 C 52.215821,67.884035 51.122295,68 50,68 48.877705,68 47.784179,67.884035 46.71875,67.6875 l -9.03125,15.625 -10.375,-6 9.03125,-15.625 C 34.921877,60.027713 33.808215,58.101223 33.0625,56 L 15,56 15,44 33.0625,44 c 0.745715,-2.101223 1.859377,-4.027713 3.28125,-5.6875 l -9.03125,-15.625 10.375,-6 z"
|
||||
style="fill:url(#gradient_rect);fill-opacity:1;fill-rule:nonzero;stroke:#16161d;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
|
||||
id="path15"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 62,50 a 12,12 0 1 1 -24,0 12,12 0 1 1 24,0 z"
|
||||
style="fill:url(#gradient_dot);fill-opacity:1;fill-rule:nonzero;stroke:#16161d;stroke-opacity:1"
|
||||
id="path17"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
115
docs/capabilities.md
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
title: Solaar Capabilities
|
||||
layout: page
|
||||
---
|
||||
# Solaar capabilities
|
||||
|
||||
[**Solaar**][solaar] reports on and controls [Logitech][logitech] devices
|
||||
(keyboards, mice, and trackballs) that connect to your computer via a
|
||||
Logitech USB receiver (a very small piece of hardware that plugs into one of
|
||||
your USB ports) and communicate with the receiver using Logitech's HID++
|
||||
protocol. Solaar is designed to detect all devices paired with your
|
||||
receivers, and at the very least display some basic information about them.
|
||||
|
||||
At this moment, all [Unifying][unifying] receivers are supported (devices
|
||||
with USB ID `046d:c52b` or `046d:c532`) as are several Lightspeed Receivers
|
||||
and a dozen Nano receivers.
|
||||
|
||||
## HID++
|
||||
|
||||
HID++ is a Logitech-proprietary protocol that extends the standard HID
|
||||
protocol for interfacing with keyboards, mice, and so on. HID++ allows
|
||||
Logitech receivers to communicate with multiple devices and modify some
|
||||
features of the device on the device itself. As the HID++ protocol is
|
||||
proprietary many aspects of it are unknown. Some information about HID++
|
||||
has been obtained from Logitech but even that is subject to change and
|
||||
extension.
|
||||
|
||||
There are several versions of the HID++ and many different Logitech
|
||||
receivers and devices that utilize it. Different receivers and devices
|
||||
implement different portions of HID++ so even if two devices appear to be
|
||||
the same in both physical appearance and behavior they may working
|
||||
completely differently underneath. (For example, there are versions of the
|
||||
M510 mouse that use different versions of the HID++ protocol.)
|
||||
Contrariwise, two different devices may appear different physically but
|
||||
actually look the same to software. (For example, some M185 mice look the
|
||||
same to software as some M310 mice.)
|
||||
|
||||
The software identity of a receiver can be determined by its USB id
|
||||
(reported by Solaar and also viewable in Linux using `lsusb`). The software
|
||||
identity of a device can be determined by its Wireless PID as reported by
|
||||
Solaar.
|
||||
|
||||
Even something as fundamental as pairing works differently for different
|
||||
receivers. For Unifying receivers, pairing adds a new paired device, but
|
||||
only if there is an open slot on the receiver. So these receivers need to
|
||||
be able to unpair devices that they have been paired with or else they will
|
||||
not have any open slots for pairing. Some other receivers, like the
|
||||
Nano receiver with USB ID `046d:c534`, can only pair with particular kinds of
|
||||
devices and pairing a new device replaces whatever device of that kind was
|
||||
previously paired to the receiver. These receivers cannot unpair. Further,
|
||||
some receivers can pair an unlimited number of times but others can only
|
||||
pair a limited number of times.
|
||||
|
||||
Only some connections between receivers and devices are possible. In should
|
||||
be possible to connect any device with a Unifying logo on it to any receiver
|
||||
with a Unifying logo on it. Receivers without the Unifying logo probably
|
||||
can connect only to the kind of devices they were bought with and devices
|
||||
without the Unifying logo can probably only connect to the kind of receiver
|
||||
that they were bought with.
|
||||
|
||||
|
||||
## Supported features
|
||||
|
||||
Solaar uses the HID++ protocol to pair devices to receivers and unpair
|
||||
devices from receivers. Solaar also uses the HID++ protocol to display
|
||||
features of receivers and devices. Solaar can modify some of the features
|
||||
of devices. Solaar currently only displays some features and can modify
|
||||
even fewer.
|
||||
|
||||
Solaar does not do anything beyond using the HID++ protocol to change the
|
||||
behavior of receivers and devices. In particular, Solaar cannot change how
|
||||
the operating system turns the keycodes that a keyboard produces into
|
||||
characters that are sent to programs. That is the province of HID device
|
||||
drivers and other software (such as X11).
|
||||
|
||||
Logitech receivers and devices have firmware in them. Some of the firmware
|
||||
can be updated using Logitech software in Windows. For example, there are
|
||||
security issues with some Logitech receivers and devices and Logitech has
|
||||
firmware updates to alleviate these problems. Some Logitech firmware can
|
||||
also be updated in Linux using `fwupdmgr`.
|
||||
WARNING: Updating firmware can cause a piece of hardware to to become
|
||||
permanently non-functional if something goes wrong with the update or the
|
||||
update installs the wrong firmware.
|
||||
|
||||
Solaar does keep track of some of the changeable state of a device between
|
||||
invocations. When Solaar starts it restores on-line devices to their
|
||||
previously-known state. Also, while running Solaar restores devices to
|
||||
their previously-known state when the device comes on line.
|
||||
|
||||
Querying a device for its current state can require quite a few HID++
|
||||
interactions. These interactions can temporarily slow down the device, so
|
||||
Solaar tries to internally cache information about devices. If the device
|
||||
state is changed by some other means, even sometimes by another invocation
|
||||
of Solaar, this cached information may become incorrect. Currently there is
|
||||
no way to force an update of the cached information besides terminating
|
||||
Solaar and starting it again.
|
||||
|
||||
|
||||
## Battery Icons
|
||||
|
||||
For many devices, Solaar shows the approximate battery level via icons that
|
||||
show up in both main Solaar window and the system tray. Solaar used to use
|
||||
several heuristics to determine which icon names to use for this purpose,
|
||||
but as more and more battery icon schemes have been developed this has
|
||||
become impossible to do well. Solaar now uses the eleven standard
|
||||
battery icon names `battery-{full,good,low,critical,empty}[-charging]` and
|
||||
`battery-missing`.
|
||||
|
||||
Solaar will use the symbolic versions of these icons if started with the
|
||||
option `--battery-icons=symbolic`. Because of bugs external to Solaar
|
||||
these symbolic icons may be nearly invisible in dark themes.
|
||||
|
||||
[solaar]: https://github.com/pwr-Solaar/Solaar
|
||||
[logitech]: https://www.logitech.com
|
||||
[unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver
|
||||
8
docs/debian.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Debian Repository
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Debian repository
|
||||
|
||||
Solaar is now part of the [official debian repository](https://packages.debian.org/solaar), to install it on your debian machine, use the following command: `sudo apt install solaar`
|
||||
217
docs/devices.md
Normal file
@@ -0,0 +1,217 @@
|
||||
---
|
||||
title: Supported Devices
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Supported devices and receivers
|
||||
|
||||
These tables list Logitech receivers and devices and to what degree their
|
||||
features are supported by Solaar. The information in these tables is
|
||||
incomplete, based on what devices users have been able to test Solaar with.
|
||||
|
||||
The HID++ column specifies the device's HID++ version. Some devices report
|
||||
version 4.5, but that is the same as version 2.0 as listed here.
|
||||
For devices what support HID++ 2.0 or greater, Solaar is able to discover
|
||||
the features the device supports.
|
||||
|
||||
The Battery column specifies if Solaar is able to read the device's battery
|
||||
level.
|
||||
|
||||
For mice, the DPI column specifies if the mouse's sensitivity is fixed (`-`),
|
||||
can only be read (`R`), or can be read and changed by Solaar (`R/W`).
|
||||
|
||||
The reprog(rammable) keys feature is currently not fully supported by Solaar.
|
||||
You are able to read this feature using command-line interface of Solaar,
|
||||
but it is not possible to assign different keys.
|
||||
|
||||
|
||||
If your device works with Solaar, but its supported features are not
|
||||
correctly specified here, please open an issue on the [Solaar github
|
||||
repository][solaar] with the pleasant news.
|
||||
|
||||
|
||||
### Receivers:
|
||||
|
||||
| USB ID | Kind | Max Paired Devices |
|
||||
------------|------------|--------------------|
|
||||
| 046d:c517 | Nano | 1 |
|
||||
| 046d:c518 | Nano | 1 |
|
||||
| 046d:c51a | Nano | 1 |
|
||||
| 046d:c51b | Nano | 1 |
|
||||
| 046d:c521 | Nano | 1 |
|
||||
| 046d:c525 | Nano | 1 |
|
||||
| 046d:c526 | Nano | 1 |
|
||||
| 046d:c52b | Unifying | 6 |
|
||||
| 046d:c52e | Nano | 1 |
|
||||
| 046d:c52f | Nano | 1 |
|
||||
| 046d:c531 | Nano | 1 |
|
||||
| 046d:c532 | Unifying | 6 |
|
||||
| 064d:c534 | Nano | 2 |
|
||||
| 064d:c539 | Lightspeed | 1 |
|
||||
| 064d:c53a | Lightspeed | 1 |
|
||||
| 064d:c53f | Lightspeed | 1 |
|
||||
| 17ef:6042 | Nano | 1 |
|
||||
|
||||
|
||||
|
||||
### Keyboards (Unifying):
|
||||
|
||||
| Device | WPID | HID++ | Battery | Other supported features |
|
||||
|------------------|------|-------|---------|-----------------------------------------|
|
||||
| K230 | 400D | 2.0 | yes | |
|
||||
| K270 | 4003 | 2.0 | yes | |
|
||||
| K340 | 2007 | 1.0 | yes | |
|
||||
| K350 | 200A | 1.0 | yes | |
|
||||
| K360 | 4004 | 2.0 | yes | FN swap, reprog keys |
|
||||
| K375s | 4071 | | | FN swap |
|
||||
| K400 Touch | 400E | 2.0 | yes | FN swap |
|
||||
| K400 Touch | 4024 | 2.0 | yes | FN swap |
|
||||
| K400 Plus | 404D | 2.0 | | FN swap |
|
||||
| K520 | 2011 | 1.0 | yes | FN swap |
|
||||
| K600 TV | 4078 | 2.0 | yes | FN swap |
|
||||
| K750 Solar | 4002 | 2.0 | yes | FN swap, Lux reading, light button |
|
||||
| K780 | 405B | 2.0 | yes | FN swap |
|
||||
| K800 Illuminated | 2010 | 1.0 | yes | FN swap, reprog keys, LEDs |
|
||||
| K800 (new ver) | 406E | 2.0 | yes | FN swap |
|
||||
| K830 Illuminated | 4032 | 2.0 | yes | FN swap |
|
||||
| MX Keys | 408A | 2.0 | yes | |
|
||||
| N545 | 2006 | | yes | |
|
||||
| TK820 | | 2.0 | yes | FN swap |
|
||||
| Craft | 4066 | 2.0 | | |
|
||||
|
||||
* The [K750 Solar Keyboard][K750] can be queried for its solar charge status.
|
||||
Pressing the `Light-Check` button on the keyboard will pop-up the application
|
||||
window and display the current lighting value (Lux) as reported by the
|
||||
keyboard, similar to Logitech's *Solar.app* for Windows.
|
||||
|
||||
* FN swap changes the way the function keys (`F1`..`F12`) work, i.e., whether holding `FN` while pressing the function keys will generate the standard `Fx` keycodes or the special function (yellow icons) keycodes.
|
||||
|
||||
|
||||
### Mice (Unifying):
|
||||
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|------|-------|---------|-------|---------------------------------|
|
||||
| M150 | 4022 | 2.0 | | | |
|
||||
| M185 | 4055 | 2.0 | | R/W | smooth scrolling |
|
||||
| M310 | 4031 | 2.0 | yes | | |
|
||||
| M310 | 4055 | 2.0 | | R/W | smooth scrolling |
|
||||
| M317 | | | | | |
|
||||
| M325 | 400A | 2.0 | yes | 1000 | smooth scrolling |
|
||||
| M330 | | 2.0 | yes | 1000 | smooth scrolling |
|
||||
| M345 | 4017 | 2.0 | yes | - | smooth scrolling |
|
||||
| M350 | 101C | 1.0 | yes | | |
|
||||
| M350 | 4080 | 2.0 | | | |
|
||||
| M505 | 101D | 1.0 | yes | | smooth scrolling, side scrolling|
|
||||
| M510 | 1025 | 1.0 | yes | | smooth scrolling, side scrolling|
|
||||
| M510 | 4051 | 2.0 | yes | | smooth scrolling |
|
||||
| M515 Couch | 4007 | 2.0 | yes | - | smooth scrolling |
|
||||
| M525 | 4013 | 2.0 | yes | - | smooth scrolling |
|
||||
| M560 | | 2.0 | yes | - | smooth scrolling |
|
||||
| M585 | 406B | 2.0 | yes | R/W | smooth scrolling |
|
||||
| M590 | 406B | 2.0 | yes | R/W | smooth scrolling |
|
||||
| M600 Touch | 401A | 2.0 | yes | | |
|
||||
| M705 Marathon | 101B | 1.0 | yes | - | smooth scrolling, side scrolling|
|
||||
| M705 Marathon | 406D | 2.0 | yes | R/W | smooth scrolling |
|
||||
| T400 Zone Touch | | 2.0 | yes | | smooth scrolling |
|
||||
| T620 Touch | | 2.0 | yes | | |
|
||||
| Performance MX | 101A | 1.0 | yes | R/W | smooth scrolling, side scrolling|
|
||||
| Anywhere MX | 1017 | 1.0 | yes | R/W | smooth scrolling, side scrolling|
|
||||
| Anywhere MX 2 | 404A | 2.0 | yes | R/W | smooth scrolling |
|
||||
| MX Master | 4041 | 2.0 | yes | R/W | smooth scrolling, smart shift |
|
||||
| MX Master 2S | 4069 | 2.0 | yes | R/W | smooth scrolling, smart shift |
|
||||
| Cube | | 2.0 | yes | | |
|
||||
|
||||
|
||||
### Mice (Nano):
|
||||
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|------|-------|---------|-------|---------------------------------|
|
||||
| G7 | 1002 | 1.0 | yes | - | |
|
||||
| G700 | 1023 | 1.0 | yes | - | smooth scrolling, side scrolling|
|
||||
| G700s | 102A | 1.0 | yes | - | smooth scrolling, side scrolling|
|
||||
| V450 Nano | 1011 | 1.0 | yes | - | smooth scrolling |
|
||||
| V550 Nano | 1013 | 1.0 | yes | - | smooth scrolling, side scrolling|
|
||||
| VX Nano | 100B | 1.0 | yes | - | smooth scrolling, side scrolling|
|
||||
| VX Nano | 100F | 1.0 | yes | - | smooth scrolling, side scrolling|
|
||||
| M175 | 4008 | | yes | | |
|
||||
| M185 (old) | 4038 | 2.0 | yes | R/W | smooth scrolling (note) |
|
||||
| M185 (new) | 4054 | 2.0 | no | R/W | smooth scrolling (note) |
|
||||
| M187 | 4019 | 2.0 | yes | | |
|
||||
| M215 | 1020 | 1.0 | yes | | |
|
||||
| M235 | 4055 | 2.0 | yes | R/W | smooth scrolling |
|
||||
| M305 | 101F | 1.0 | yes | | side scrolling |
|
||||
| M310 | 1024 | 1.0 | yes | | |
|
||||
| M315 | | | yes | | |
|
||||
| M330 | | ?.? | yes | ? | smooth scrolling |
|
||||
| MX 1100 | 1014 | 1.0 | yes | - | smooth scrolling, side scrolling|
|
||||
|
||||
(old): M185 with P/N: 810-003496
|
||||
|
||||
(new): M185 with P/N: 810-005238 or 810-005232
|
||||
|
||||
(note): Currently, smooth scrolling events are not processed in xfce and this
|
||||
setting is useful only to disable smooth scrolling.
|
||||
|
||||
|
||||
### Mice (Mini):
|
||||
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|------|-------|---------|-------|---------------------------------|
|
||||
| MX610 | 1001 | 1.0 | yes | | |
|
||||
| MX610 lefthanded | 1004 | 1.0 | yes | | |
|
||||
| MX620 | 100A | 1.0 | yes | | |
|
||||
| MX620 | 1016 | 1.0 | yes | | |
|
||||
| V400 | 1003 | 1.0 | yes | | |
|
||||
| V450 | 1005 | 1.0 | yes | | |
|
||||
| VX Revolution | 1006 | 1.0 | yes | | |
|
||||
| VX Revolution | 100D | 1.0 | yes | | |
|
||||
| MX Air | 1007 | 1.0 | yes | | |
|
||||
| MX Air | 100E | 1.0 | yes | | |
|
||||
| MX Revolution | 1008 | 1.0 | yes | | |
|
||||
| MX Revolution | 100C | 1.0 | yes | | |
|
||||
|
||||
|
||||
### Trackballs (Unifying):
|
||||
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|-------------------|------|-------|---------|-------|---------------------------------|
|
||||
| M570 Trackball | | 1.0 | yes | - | |
|
||||
| MX Ergo Trackball | | 2.0 | yes | - | |
|
||||
|
||||
### Touchpads (Unifying):
|
||||
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|------|-------|---------|-------|---------------------------------|
|
||||
| Wireless Touch | 4011 | 2.0 | yes | | |
|
||||
| T650 Touchpad | 4101 | 2.0 | yes | | smooth scrolling |
|
||||
|
||||
|
||||
### Mice and Keyboards sold as combos:
|
||||
|
||||
| Device | WPID | HID++ | Battery | Other supported features |
|
||||
|------------------|------|-------|---------|-----------------------------------------|
|
||||
| MK220 | | 2.0 | yes | |
|
||||
| MK270 | 4023 | 2.0 | yes | reprog keys |
|
||||
| MK320 | 200F | | | |
|
||||
| MK330 | | | | |
|
||||
| MK520 | | M2/K1 | yes | FN swap, reprog keys |
|
||||
| MK550 | | | | |
|
||||
| MK700 | 2008 | 1.0 | yes | FN swap, reprog keys |
|
||||
| MK710 | | 1.0 | yes | FN swap, reprog keys |
|
||||
|
||||
|
||||
[solaar]: https://github.com/pwr-Solaar/Solaar
|
||||
[logitech]: https://www.logitech.com
|
||||
[unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver
|
||||
[G700s]: https://gaming.logitech.com/en-us/product/g700s-rechargeable-wireless-gaming-mouse
|
||||
[K360]: http://logitech.com/product/keyboard-k360
|
||||
[K700]: http://logitech.com/product/wireless-desktop-mk710
|
||||
[K750]: http://logitech.com/product/k750-keyboard
|
||||
[K800]: http://logitech.com/product/wireless-illuminated-keyboard-k800
|
||||
[K830]: http://logitech.com/product/living-room-keyboard-k830
|
||||
[M510]: http://logitech.com/product/wireless-mouse-m510
|
||||
[M705]: http://logitech.com/product/marathon-mouse-m705
|
||||
[P_MX]: http://logitech.com/product/performance-mouse-mx
|
||||
[A_MX]: http://logitech.com/product/anywhere-mouse-mx
|
||||
[M325]: http://logitech.com/product/wireless-mouse-m325
|
||||
[M330]: https://www.logitech.com/en-us/product/m330-silent-plus
|
||||
48
docs/devices/M510_HIDppV4.5.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
1: Wireless Mouse M510
|
||||
Codename : M510v2
|
||||
Kind : mouse
|
||||
Wireless PID : 4051
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 8989B04C
|
||||
Firmware: RQM 62.00.B0013
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1802 {1802} internal, hidden
|
||||
7: unknown:1810 {1810} internal, hidden
|
||||
8: unknown:1830 {1830} internal, hidden
|
||||
9: unknown:1862 {1862} internal, hidden
|
||||
10: unknown:1890 {1890} internal, hidden
|
||||
11: unknown:18A0 {18A0} internal, hidden
|
||||
12: unknown:18B1 {18B1} internal, hidden
|
||||
13: REPROG CONTROLS V4 {1B04}
|
||||
14: WIRELESS DEVICE STATUS {1D4B}
|
||||
15: unknown:1DF0 {1DF0} hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1EB0 {1EB0} internal, hidden
|
||||
19: unknown:1F03 {1F03} internal, hidden
|
||||
20: LOWRES WHEEL {2130}
|
||||
21: POINTER SPEED {2205}
|
||||
Has 7 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
3: LEFT SCROLL AS AC PAN , default: HorzScrollLeftSet => LEFT SCROLL AS AC PAN
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
4: RIGHT SCROLL AS AC PAN , default: HorzScrollRightSet => RIGHT SCROLL AS AC PAN
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
5: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
6: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery: 70%, discharging.
|
||||
51
docs/devices/anywhere-mx-2.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : A7F5923B
|
||||
Firmware : 24.01.B0023
|
||||
Bootloader : 01.08
|
||||
Other : AA.AD
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=11
|
||||
|
||||
1: Wireless Mouse MX Anywhere 2
|
||||
Codename : MX Anywhere 2
|
||||
Kind : mouse
|
||||
Wireless PID : 404A
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: F3B81C5B
|
||||
Bootloader: BOT 23.00.B0007
|
||||
Firmware: MPM 02.00.B0007
|
||||
Firmware: MPM 02.00.B0007
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 26 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: CHANGE HOST {1814}
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
9: ADJUSTABLE DPI {2201}
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HIRES WHEEL {2121}
|
||||
12: DFUCONTROL 2 {00C1}
|
||||
13: unknown:1813 {1813} internal, hidden
|
||||
14: unknown:1830 {1830} internal, hidden
|
||||
15: unknown:1890 {1890} internal, hidden
|
||||
16: unknown:1891 {1891} internal, hidden
|
||||
17: unknown:18A1 {18A1} internal, hidden
|
||||
18: unknown:18C0 {18C0} internal, hidden
|
||||
19: unknown:1DF3 {1DF3} internal, hidden
|
||||
20: unknown:1E00 {1E00} hidden
|
||||
21: unknown:1EB0 {1EB0} internal, hidden
|
||||
22: unknown:1803 {1803} internal, hidden
|
||||
23: unknown:1861 {1861} internal, hidden
|
||||
24: unknown:9000 {9000} internal, hidden
|
||||
25: unknown:1805 {1805} internal, hidden
|
||||
Battery: 0%, recharging.
|
||||
88
docs/devices/anywhere-mx.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
Receiver
|
||||
LZ301AR-DJ
|
||||
M/N:C-U0007
|
||||
(ltunify)
|
||||
Serial number: D1759614
|
||||
Firmware version: 012.001.00019
|
||||
Bootloader version: BL.002.014
|
||||
|
||||
Supported notification flags: 00 09 00
|
||||
- 01: Wireless Notifications
|
||||
- 08: Software Present
|
||||
|
||||
Mouse
|
||||
(ltunify)
|
||||
HID++ version: 1.0
|
||||
Device index 1
|
||||
Mouse
|
||||
Name: Anywhere MX
|
||||
Wireless Product ID: 1017
|
||||
Serial number: 13865F99
|
||||
Firmware version: 016.001.00040
|
||||
Bootloader version: BL.002.010
|
||||
(solaar)
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : D1759614
|
||||
Firmware : 12.01.B0019
|
||||
Bootloader : 02.14
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
Device activity counters: 1=19
|
||||
|
||||
1: Anywhere Mouse MX
|
||||
Codename : Anywhere MX
|
||||
Kind : mouse
|
||||
Wireless PID : 1017
|
||||
Protocol : HID++ 1.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 13865F99
|
||||
Firmware: 16.01.B0040
|
||||
Bootloader: 02.10
|
||||
Other: 00.06
|
||||
The power switch is located on the base.
|
||||
Notifications: (none).
|
||||
Battery: 100%, discharging.
|
||||
|
||||
(scan-registers)
|
||||
# Old notification flags: 000000
|
||||
# 00 - Enabled Notifications, supported flags: Battery Status (10)
|
||||
>> ( 0.792) [10 01 8100 100000] b'\x10\x01\x81\x00\x10\x00\x00'
|
||||
|
||||
# 01 - scrolling settings?
|
||||
# Flags:
|
||||
# 0x40 - Enable Smooth Scrolling
|
||||
# 0x02 - "confuse KDE", see https://github.com/pwr/Solaar/issues/115
|
||||
<< ( 0.011) [10 01 8101 000000] b'\x10\x01\x81\x01\x00\x00\x00'
|
||||
>> ( 1.710) [10 01 8101 020000] b'\x10\x01\x81\x01\x02\x00\x00'
|
||||
|
||||
# 0D - battery level. first byte is remaining charge in percent; second is
|
||||
# (guessed) maximum?; third is charge status (30=discharging)
|
||||
# "10 ix 0D 64 64 30 00" is a battery notification (when enabled)
|
||||
<< ( 9.789) [10 01 810D 000000] b'\x10\x01\x81\r\x00\x00\x00'
|
||||
>> ( 9.816) [10 01 810D 646430] b'\x10\x01\x81\rdd0'
|
||||
|
||||
# 63 - DPI (range 0x80-0x8e (inclusive))
|
||||
<< ( 75.521) [10 01 8163 000000] b'\x10\x01\x81c\x00\x00\x00'
|
||||
>> ( 75.550) [10 01 8163 890000] b'\x10\x01\x81c\x89\x00\x00'
|
||||
|
||||
# D0 - ?
|
||||
<< ( 163.118) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00'
|
||||
>> ( 163.148) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00'
|
||||
|
||||
# D4 - ?
|
||||
<< ( 166.034) [10 01 81D4 000000] b'\x10\x01\x81\xd4\x00\x00\x00'
|
||||
>> ( 166.063) [10 01 81D4 000008] b'\x10\x01\x81\xd4\x00\x00\x08'
|
||||
|
||||
# F1 - firmware/bootloader version
|
||||
<< ( 187.172) [10 01 81F1 000000] b'\x10\x01\x81\xf1\x00\x00\x00'
|
||||
>> ( 187.199) [10 01 8F81 F10300] b'\x10\x01\x8f\x81\xf1\x03\x00'
|
||||
|
||||
# F3 - ?
|
||||
<< ( 188.629) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00'
|
||||
>> ( 188.661) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00'
|
||||
|
||||
# FD - ?
|
||||
<< ( 195.715) [10 01 83FD 000000] b'\x10\x01\x83\xfd\x00\x00\x00'
|
||||
>> ( 195.746) [11 01 83FD 00000000000000000000000000000000] b'\x11\x01\x83\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
114
docs/devices/craft.txt
Normal file
@@ -0,0 +1,114 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : E21FAD57
|
||||
Firmware : 24.06.B0030
|
||||
Bootloader : 01.08
|
||||
Other : AA.AC
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=243, 2=173
|
||||
|
||||
1: Craft Advanced Keyboard
|
||||
Codename : Craft
|
||||
Kind : keyboard
|
||||
Wireless PID : 4066
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 428C2F81
|
||||
Bootloader: BOT 41.00.B0014
|
||||
Firmware: MPK 07.01.B0015
|
||||
Other:
|
||||
Other:
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 39 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0007 {0007}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: CHANGE HOST {1814}
|
||||
9: unknown:1815 {1815}
|
||||
10: unknown:1982 {1982}
|
||||
11: REPROG CONTROLS V4 {1B04}
|
||||
12: unknown:1C00 {1C00}
|
||||
13: K375S FN INVERSION {40A3}
|
||||
14: ENCRYPTION {4100}
|
||||
15: LOCK KEY STATE {4220}
|
||||
16: KEYBOARD DISABLE {4521}
|
||||
17: unknown:4531 {4531}
|
||||
18: unknown:4600 {4600}
|
||||
19: unknown:00C2 {00C2}
|
||||
20: unknown:1803 {1803} internal, hidden
|
||||
21: unknown:1806 {1806} internal, hidden
|
||||
22: unknown:1813 {1813} internal, hidden
|
||||
23: unknown:1805 {1805} internal, hidden
|
||||
24: unknown:1830 {1830} internal, hidden
|
||||
25: unknown:1890 {1890} internal, hidden
|
||||
26: unknown:1891 {1891} internal, hidden
|
||||
27: unknown:1801 {1801} internal, hidden
|
||||
28: unknown:18A1 {18A1} internal, hidden
|
||||
29: unknown:9280 {9280} internal, hidden
|
||||
30: unknown:1A20 {1A20} internal, hidden
|
||||
31: unknown:1DF3 {1DF3} internal, hidden
|
||||
32: unknown:1E00 {1E00} hidden
|
||||
33: unknown:1EB0 {1EB0} internal, hidden
|
||||
34: unknown:1861 {1861} internal, hidden
|
||||
35: unknown:18B0 {18B0} internal, hidden
|
||||
36: unknown:92C0 {92C0} internal, hidden
|
||||
37: unknown:9203 {9203} internal, hidden
|
||||
38: unknown:3615 {3615} internal, hidden
|
||||
Has 24 reprogrammable keys:
|
||||
0: unknown:00D1 , default: unknown:00AE => unknown:00D1
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
1: unknown:00D2 , default: unknown:00AF => unknown:00D2
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
2: unknown:00D3 , default: unknown:00B0 => unknown:00D3
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
3: unknown:00C7 , default: unknown:00A3 => unknown:00C7
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:1, group:0, gmask:0
|
||||
4: unknown:00C8 , default: unknown:00A4 => unknown:00C8
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:2, group:0, gmask:0
|
||||
5: unknown:00E0 , default: unknown:00BF => unknown:00E0
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:3, group:0, gmask:0
|
||||
6: unknown:00E1 , default: unknown:00C0 => unknown:00E1
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:4, group:0, gmask:0
|
||||
7: SHOW DESKTOP , default: ShowDesktop => SHOW DESKTOP
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:5, group:0, gmask:0
|
||||
8: unknown:00E2 , default: unknown:00C1 => unknown:00E2
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:6, group:0, gmask:0
|
||||
9: unknown:00E3 , default: unknown:00C2 => unknown:00E3
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:7, group:0, gmask:0
|
||||
10: unknown:00E4 , default: Previous => unknown:00E4
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:8, group:0, gmask:0
|
||||
11: unknown:00E5 , default: Play/Pause => unknown:00E5
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:9, group:0, gmask:0
|
||||
12: unknown:00E6 , default: Next => unknown:00E6
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:10, group:0, gmask:0
|
||||
13: unknown:00E7 , default: Mute => unknown:00E7
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:11, group:0, gmask:0
|
||||
14: unknown:00E8 , default: Volume Down => unknown:00E8
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:12, group:0, gmask:0
|
||||
15: unknown:00E9 , default: Volume Up => unknown:00E9
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
16: Calculator , default: Calculator => Calculator
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
17: unknown:00BF , default: unknown:009B => unknown:00BF
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
18: unknown:00EA , default: unknown:00C3 => unknown:00EA
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
19: Lock PC , default: WindowsLock => Lock PC
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
20: unknown:00EC , default: unknown:00B8 => unknown:00EC
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
21: unknown:00EB , default: unknown:00B6 => unknown:00EB
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
22: unknown:00DE , default: Do Nothing One => unknown:00DE
|
||||
is FN, pos:0, group:0, gmask:0
|
||||
23: unknown:0034 , default: Do Nothing One => unknown:0034
|
||||
nonstandard, pos:0, group:0, gmask:0
|
||||
Battery: 0%, full.
|
||||
|
||||
43
docs/devices/k270-unifying.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : C03E9E2E
|
||||
Firmware : 12.01.B0019
|
||||
Bootloader : 02.14
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 2=118
|
||||
|
||||
2: Wireless Keyboard K270(unifying)
|
||||
Codename : K270(unifying)
|
||||
Kind : keyboard
|
||||
Wireless PID : 4003
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: FE8C8542
|
||||
Firmware: RQK 35.00.B0017
|
||||
The power switch is located on the top case.
|
||||
Supports 12 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1820 {1820} hidden
|
||||
6: REPROG CONTROLS {1B00}
|
||||
7: WIRELESS DEVICE STATUS {1D4B}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1DF3 {1DF3} hidden
|
||||
10: ENCRYPTION {4100}
|
||||
11: KEYBOARD LAYOUT {4520}
|
||||
Has 8 reprogrammable keys:
|
||||
0: Play/Pause => Play/Pause nonstandard
|
||||
1: Mute => Mute nonstandard
|
||||
2: Volume Down => Volume Down nonstandard
|
||||
3: Volume Up => Volume Up nonstandard
|
||||
4: MY HOME => HomePage nonstandard, reprogrammable
|
||||
5: Mail => Email nonstandard, reprogrammable
|
||||
6: SLEEP => Sleep nonstandard, reprogrammable
|
||||
7: Calculator => Calculator nonstandard, reprogrammable
|
||||
Battery: 90%, discharging.
|
||||
|
||||
93
docs/devices/k360.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Receiver
|
||||
LZ22175-DJ
|
||||
LZ30965-DJ (another receiver)
|
||||
M/N:C-U0007
|
||||
(ltunify)
|
||||
Serial number: 53B19204
|
||||
Serial number: 82C3964B (another receiver)
|
||||
Firmware version: 012.001.00019
|
||||
Bootloader version: BL.002.014
|
||||
(solaar-cli)
|
||||
-: Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
Serial : 53B19204
|
||||
Serial : 82C3964B (another receiver)
|
||||
Firmware : 12.01.B0019
|
||||
Bootloader : 02.14
|
||||
Has 1 paired device(s) out of a maximum of 6
|
||||
Enabled notifications: 0x000900 = wireless, software present.
|
||||
|
||||
Keyboard
|
||||
K360
|
||||
P/N: 820-003472
|
||||
S/N: 1223CE0521E8
|
||||
S/N: 1311CE0097D8 (another keyboard)
|
||||
M/N: Y-R0017
|
||||
(ltunify)
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Keyboard
|
||||
Name: K360
|
||||
Wireless Product ID: 4004
|
||||
Serial number: 60BA944E
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 12
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0003] IFirmwareInfo
|
||||
3: [0005] GetDeviceNameType
|
||||
4: [1000] batteryLevelStatus
|
||||
5: [1820] H unknown
|
||||
6: [1B00] SpecialKeysMSEButtons
|
||||
7: [1D4B] WirelessDeviceStatus
|
||||
8: [1DF0] H unknown
|
||||
9: [1DF3] H unknown
|
||||
10: [40A0] FnInversion
|
||||
11: [4100] Encryption
|
||||
12: [4520] KeyboardLayout
|
||||
(O = obsolete feature; H = SW hidden feature)
|
||||
(solaar-cli)
|
||||
1: Wireless Keyboard K360
|
||||
Codename : K360
|
||||
Kind : keyboard
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms
|
||||
Wireless PID : 4004
|
||||
Serial number: 60BA944E
|
||||
Serial number: 0D2694C9 (another keyboard)
|
||||
Firmware : RQK 36.00.B0007
|
||||
The power switch is located on the top case
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1820 {1820} hidden
|
||||
6: REPROG CONTROLS {1B00}
|
||||
7: WIRELESS DEVICE STATUS {1D4B}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1DF3 {1DF3} hidden
|
||||
10: FN INVERSION {40A0}
|
||||
11: ENCRYPTION {4100}
|
||||
12: KEYBOARD LAYOUT {4520}
|
||||
Has 18 reprogrammable keys:
|
||||
0: MY HOME => HomePage FN sensitive, is FN, reprogrammable
|
||||
1: Mail => Mail FN sensitive, is FN, reprogrammable
|
||||
2: SEARCH => Search FN sensitive, is FN, reprogrammable
|
||||
3: MEDIA PLAYER => Music FN sensitive, is FN, reprogrammable
|
||||
4: Application Switcher => Application Switcher FN sensitive, is FN, reprogrammable
|
||||
5: SHOW DESKTOP => ShowDesktop FN sensitive, is FN, reprogrammable
|
||||
6: MINIMIZE AS WIN M => WindowsMinimize FN sensitive, is FN, reprogrammable
|
||||
7: MAXIMIZE AS WIN SHIFT M => WindowsRestore FN sensitive, is FN, reprogrammable
|
||||
8: MY COMPUTER AS WIN E => My Computer FN sensitive, is FN, reprogrammable
|
||||
9: Lock PC => WindowsLock FN sensitive, is FN, reprogrammable
|
||||
10: SLEEP => Sleep FN sensitive, is FN, reprogrammable
|
||||
11: Calculator => Calculator FN sensitive, is FN, reprogrammable
|
||||
12: Previous => Previous nonstandard
|
||||
13: Play/Pause => Play/Pause nonstandard
|
||||
14: Next => Next nonstandard
|
||||
15: Mute => Mute nonstandard
|
||||
16: Volume Down => Volume Down nonstandard
|
||||
17: Volume Up => Volume Up nonstandard
|
||||
Battery is 90% charged, discharging
|
||||
39
docs/devices/k750.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
(from Julien Danjou)
|
||||
|
||||
(solaar)
|
||||
2: Wireless Solar Keyboard K750
|
||||
Codename : K750
|
||||
Kind : keyboard
|
||||
Wireless PID : 4002
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 5692B2EC
|
||||
Firmware: RQK 33.00.B0015
|
||||
Bootloader: DFU 00.02.B0003
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 11 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: REPROG CONTROLS {1B00}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: SOLAR DASHBOARD {4301}
|
||||
10: KEYBOARD LAYOUT {4520}
|
||||
Has 12 reprogrammable keys:
|
||||
0: MY HOME => HomePage FN sensitive, is FN, reprogrammable
|
||||
1: Mail => Email FN sensitive, is FN, reprogrammable
|
||||
2: SEARCH => Search FN sensitive, is FN, reprogrammable
|
||||
3: Calculator => Calculator FN sensitive, is FN, reprogrammable
|
||||
4: MEDIA PLAYER => Music FN sensitive, is FN, reprogrammable
|
||||
5: Previous => Previous FN sensitive, is FN
|
||||
6: Play/Pause => Play/Pause FN sensitive, is FN
|
||||
7: Next => Next FN sensitive, is FN
|
||||
8: Mute => Mute FN sensitive, is FN
|
||||
9: Volume Down => Volume Down FN sensitive, is FN
|
||||
10: Volume Up => Volume Up FN sensitive, is FN
|
||||
11: SLEEP => Sleep FN sensitive, is FN, reprogrammable
|
||||
Battery status unavailable.
|
||||
44
docs/devices/k780.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
2: K780 Multi-Device Keyboard
|
||||
Codename : K780
|
||||
Kind : keyboard
|
||||
Wireless PID : 405B
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 4D71FEE1
|
||||
Bootloader: BOT 25.00.B0005
|
||||
Firmware: MPK 01.00.B0018
|
||||
Other:
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 31 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0007 {0007}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: CHANGE HOST {1814}
|
||||
9: unknown:1815 {1815}
|
||||
10: REPROG CONTROLS V4 {1B04}
|
||||
11: unknown:1C00 {1C00}
|
||||
12: NEW FN INVERSION {40A2}
|
||||
13: ENCRYPTION {4100}
|
||||
14: KEYBOARD DISABLE {4521}
|
||||
15: unknown:4531 {4531}
|
||||
16: LOCK KEY STATE {4220}
|
||||
17: unknown:00C2 {00C2}
|
||||
18: unknown:1803 {1803} internal, hidden
|
||||
19: unknown:1806 {1806} internal, hidden
|
||||
20: unknown:1805 {1805} internal, hidden
|
||||
21: unknown:1813 {1813} internal, hidden
|
||||
22: unknown:1830 {1830} internal, hidden
|
||||
23: unknown:1861 {1861} internal, hidden
|
||||
24: unknown:1890 {1890} internal, hidden
|
||||
25: unknown:1891 {1891} internal, hidden
|
||||
26: unknown:18A1 {18A1} internal, hidden
|
||||
27: unknown:1DF3 {1DF3} internal, hidden
|
||||
28: unknown:1E00 {1E00} hidden
|
||||
29: unknown:1EB0 {1EB0} internal, hidden
|
||||
30: unknown:18B0 {18B0} internal, hidden
|
||||
Battery: 90%, discharging.
|
||||
557
docs/devices/k800-new.txt
Normal file
@@ -0,0 +1,557 @@
|
||||
[kamil@kamil-pc Solaar]$ bin/solaar -dd show
|
||||
16:01:46,533 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 030000]
|
||||
16:01:46,539 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 03DC279AB20A06090000000000000000]
|
||||
16:01:46,539 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 80B2 000000]
|
||||
16:01:46,550 DEBUG [MainThread] logitech_receiver.base: (3) => r[20 01 4101 71401E0000000400000000]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) => r[20 02 4101 6E401A4000000400000000]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) => r[20 00 4102 0000000000000000000000]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8F80 B20300]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) device 0xFF error on request {80B2}: 3 = invalid value
|
||||
16:01:46,551 DEBUG [MainThread] solaar.cli: [/dev/hidraw0] => <UnifyingReceiver(/dev/hidraw0,3)>
|
||||
16:01:46,646 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 8102 000000]
|
||||
16:01:46,651 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8102 000200]
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : DC279AB2
|
||||
16:01:46,652 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 010000]
|
||||
16:01:46,657 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 012411]
|
||||
16:01:46,658 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 020000]
|
||||
16:01:46,664 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 020036]
|
||||
16:01:46,664 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 040000]
|
||||
16:01:46,669 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 040209]
|
||||
16:01:46,670 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 030000]
|
||||
16:01:46,675 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 03AAAC]
|
||||
Firmware : 24.11.B0036
|
||||
Bootloader : 02.09
|
||||
Other : AA.AC
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
16:01:46,676 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 8100 000000]
|
||||
16:01:46,681 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8100 000900]
|
||||
Notifications: wireless, software present (0x000900)
|
||||
16:01:46,682 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B3 000000]
|
||||
16:01:46,687 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B3 51060000000000000000000000000000]
|
||||
Device activity counters: 1=81, 2=6
|
||||
16:01:46,688 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 8102 000000]
|
||||
16:01:46,693 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8102 000200]
|
||||
16:01:46,694 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 200000]
|
||||
16:01:46,699 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 20090840710402020700000000000000]
|
||||
16:01:46,700 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 400000]
|
||||
16:01:46,705 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 40094D58204D61737465720000000000]
|
||||
16:01:46,706 INFO [MainThread] logitech_receiver.receiver: <UnifyingReceiver(/dev/hidraw0,3)>: found new device 1 (4071)
|
||||
|
||||
16:01:46,706 DEBUG [MainThread] logitech_receiver.base: (3) pinging device 1
|
||||
16:01:46,706 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 001E 000016]
|
||||
16:01:47,033 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 00000000000000000000000000000000]
|
||||
16:01:47,049 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 001E 04051600000000000000000000000000]
|
||||
1: Wireless Mouse MX Master
|
||||
Codename : MX Master
|
||||
Kind : mouse
|
||||
Wireless PID : 4071
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
16:01:47,049 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 300000]
|
||||
16:01:47,055 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 30A6A400641E00000001000000000000]
|
||||
Serial number: A6A40064
|
||||
16:01:47,055 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 000100]
|
||||
16:01:47,061 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,077 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 01000100000000000000000000000000]
|
||||
16:01:47,077 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0108 000000]
|
||||
16:01:47,093 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000D 00000000000000000000000000000000]
|
||||
16:01:47,101 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0108 1F000000000000000000000000000000]
|
||||
16:01:47,101 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 000300]
|
||||
16:01:47,109 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,117 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 02000200000000000000000000000000]
|
||||
16:01:47,117 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 020D 000000]
|
||||
16:01:47,125 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,133 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 020D 040382B4290006B01E40710000030000]
|
||||
16:01:47,134 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021E 000000]
|
||||
16:01:47,141 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,149 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021E 01424F54561000050040717622661101]
|
||||
16:01:47,150 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021C 010000]
|
||||
16:01:47,171 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021C 004D504D1210000500B01E7622661101]
|
||||
16:01:47,171 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021E 020000]
|
||||
16:01:47,191 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021E 004D504D121000050140717622661101]
|
||||
16:01:47,191 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021F 030000]
|
||||
16:01:47,213 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021F 050000000000005A0000000000000000]
|
||||
Bootloader: BOT 56.10.B0005
|
||||
Firmware: MPM 12.10.B0005
|
||||
Firmware: MPM 12.10.B0005
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 32 HID++ 2.0 features:
|
||||
16:01:47,214 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000B 000000]
|
||||
16:01:47,311 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000100000000000000000000000000]
|
||||
0: ROOT {0000}
|
||||
16:01:47,312 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 000100]
|
||||
16:01:47,333 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 01000100000000000000000000000000]
|
||||
1: FEATURE SET {0001}
|
||||
16:01:47,334 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 000300]
|
||||
16:01:47,431 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 02000200000000000000000000000000]
|
||||
2: DEVICE FW VERSION {0003}
|
||||
16:01:47,431 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 030000]
|
||||
16:01:47,453 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 00050000000000000000000000000000]
|
||||
16:01:47,454 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 000500]
|
||||
16:01:47,475 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 03000000000000000000000000000000]
|
||||
3: DEVICE NAME {0005}
|
||||
16:01:47,475 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 040000]
|
||||
16:01:47,575 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 1D4B0000000000000000000000000000]
|
||||
16:01:47,575 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 1D4B00]
|
||||
16:01:47,593 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 04000000000000000000000000000000]
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
16:01:47,593 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 050000]
|
||||
16:01:47,617 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 00200000000000000000000000000000]
|
||||
16:01:47,617 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 002000]
|
||||
16:01:47,715 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 05000000000000000000000000000000]
|
||||
5: RESET {0020}
|
||||
16:01:47,715 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 060000]
|
||||
16:01:47,735 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 00210000000000000000000000000000]
|
||||
16:01:47,735 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000B 002100]
|
||||
16:01:47,755 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 06000000000000000000000000000000]
|
||||
6: unknown:0021 {0021}
|
||||
16:01:47,755 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 070000]
|
||||
16:01:47,775 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 10000000000000000000000000000000]
|
||||
16:01:47,775 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 100000]
|
||||
16:01:47,795 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 07000000000000000000000000000000]
|
||||
7: BATTERY STATUS {1000}
|
||||
16:01:47,795 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 080000]
|
||||
16:01:47,815 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 18066000000000000000000000000000]
|
||||
16:01:47,815 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 180600]
|
||||
16:01:47,835 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 08600000000000000000000000000000]
|
||||
8: unknown:1806 {1806} internal, hidden
|
||||
16:01:47,835 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 090000]
|
||||
16:01:47,855 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 18140001000000000000000000000000]
|
||||
16:01:47,855 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 181400]
|
||||
16:01:47,875 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 09000100000000000000000000000000]
|
||||
9: CHANGE HOST {1814}
|
||||
16:01:47,875 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 0A0000]
|
||||
16:01:47,895 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 1B040003000000000000000000000000]
|
||||
16:01:47,895 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 1B0400]
|
||||
16:01:47,915 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 0A000300000000000000000000000000]
|
||||
10: REPROG CONTROLS V4 {1B04}
|
||||
16:01:47,915 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 0B0000]
|
||||
16:01:47,935 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 22010001000000000000000000000000]
|
||||
16:01:47,935 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000E 220100]
|
||||
16:01:47,957 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000E 0B000100000000000000000000000000]
|
||||
11: ADJUSTABLE DPI {2201}
|
||||
16:01:47,957 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 0C0000]
|
||||
16:01:47,977 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 21000000000000000000000000000000]
|
||||
16:01:47,977 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 210000]
|
||||
16:01:47,997 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 0C000000000000000000000000000000]
|
||||
12: VERTICAL SCROLLING {2100}
|
||||
16:01:47,998 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0C0C 000000]
|
||||
16:01:48,017 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0C0C 03180000000000000000000000000000]
|
||||
Roller type: 3G
|
||||
Ratchet per turn: 24
|
||||
Scroll lines: 0
|
||||
16:01:48,017 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 0D0000]
|
||||
16:01:48,037 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 21100000000000000000000000000000]
|
||||
16:01:48,037 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 211000]
|
||||
16:01:48,057 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 0D000000000000000000000000000000]
|
||||
13: SMART SHIFT {2110}
|
||||
16:01:48,057 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 0E0000]
|
||||
16:01:48,077 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 21210000000000000000000000000000]
|
||||
16:01:48,077 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 212100]
|
||||
16:01:48,097 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 0E000000000000000000000000000000]
|
||||
14: HIRES WHEEL {2121}
|
||||
16:01:48,097 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0E09 000000]
|
||||
16:01:48,117 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0E09 080C0000000000000000000000000000]
|
||||
16:01:48,117 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0E1D 000000]
|
||||
16:01:48,137 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0E1D 02000000000000000000000000000000]
|
||||
16:01:48,137 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0E38 000000]
|
||||
16:01:48,159 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0E38 01000000000000000000000000000000]
|
||||
Multiplier: 8
|
||||
Has invert
|
||||
Normal wheel motion
|
||||
Has ratchet switch
|
||||
Normal wheel mode
|
||||
High resolution mode
|
||||
HID notification
|
||||
16:01:48,159 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 0F0000]
|
||||
16:01:48,179 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 65010000000000000000000000000000]
|
||||
16:01:48,179 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 650100]
|
||||
16:01:48,199 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 0F000000000000000000000000000000]
|
||||
15: GESTURE 2 {6501}
|
||||
16:01:48,199 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 100000]
|
||||
16:01:48,219 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 00C20000000000000000000000000000]
|
||||
16:01:48,219 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 00C200]
|
||||
16:01:48,239 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 10000000000000000000000000000000]
|
||||
16: unknown:00C2 {00C2}
|
||||
16:01:48,240 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011B 110000]
|
||||
16:01:48,259 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011B 18136000000000000000000000000000]
|
||||
16:01:48,259 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 181300]
|
||||
16:01:48,279 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 11600000000000000000000000000000]
|
||||
17: unknown:1813 {1813} internal, hidden
|
||||
16:01:48,279 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 120000]
|
||||
16:01:48,299 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 18306000000000000000000000000000]
|
||||
16:01:48,299 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 183000]
|
||||
16:01:48,319 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 12600000000000000000000000000000]
|
||||
18: unknown:1830 {1830} internal, hidden
|
||||
16:01:48,319 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011B 130000]
|
||||
16:01:48,339 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011B 18906000000000000000000000000000]
|
||||
16:01:48,339 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 189000]
|
||||
16:01:48,359 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 13600000000000000000000000000000]
|
||||
19: unknown:1890 {1890} internal, hidden
|
||||
16:01:48,359 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011B 140000]
|
||||
16:01:48,381 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011B 18916000000000000000000000000000]
|
||||
16:01:48,381 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 189100]
|
||||
16:01:48,401 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 14600000000000000000000000000000]
|
||||
20: unknown:1891 {1891} internal, hidden
|
||||
16:01:48,401 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 150000]
|
||||
16:01:48,421 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 18A16000000000000000000000000000]
|
||||
16:01:48,421 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 18A100]
|
||||
16:01:48,441 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 15600000000000000000000000000000]
|
||||
21: unknown:18A1 {18A1} internal, hidden
|
||||
16:01:48,441 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 160000]
|
||||
16:01:48,461 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 18C06000000000000000000000000000]
|
||||
16:01:48,461 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 18C000]
|
||||
16:01:48,481 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 16600000000000000000000000000000]
|
||||
22: unknown:18C0 {18C0} internal, hidden
|
||||
16:01:48,481 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 170000]
|
||||
16:01:48,501 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 1DF36000000000000000000000000000]
|
||||
16:01:48,501 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 1DF300]
|
||||
16:01:48,521 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 17600000000000000000000000000000]
|
||||
23: unknown:1DF3 {1DF3} internal, hidden
|
||||
16:01:48,521 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 180000]
|
||||
16:01:48,541 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 1E004000000000000000000000000000]
|
||||
16:01:48,541 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 1E0000]
|
||||
16:01:48,561 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 18400000000000000000000000000000]
|
||||
24: unknown:1E00 {1E00} hidden
|
||||
16:01:48,561 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 190000]
|
||||
16:01:48,581 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 1EB06000000000000000000000000000]
|
||||
16:01:48,581 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 1EB000]
|
||||
16:01:48,603 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 19600000000000000000000000000000]
|
||||
25: unknown:1EB0 {1EB0} internal, hidden
|
||||
16:01:48,603 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 1A0000]
|
||||
16:01:48,623 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 18036000000000000000000000000000]
|
||||
16:01:48,623 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 180300]
|
||||
16:01:48,643 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 1A600000000000000000000000000000]
|
||||
26: unknown:1803 {1803} internal, hidden
|
||||
16:01:48,643 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0118 1B0000]
|
||||
16:01:48,663 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0118 18616000000000000000000000000000]
|
||||
16:01:48,663 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 186100]
|
||||
16:01:48,683 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 1B600000000000000000000000000000]
|
||||
27: unknown:1861 {1861} internal, hidden
|
||||
16:01:48,683 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0118 1C0000]
|
||||
16:01:48,703 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0118 90016000000000000000000000000000]
|
||||
16:01:48,703 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 900100]
|
||||
16:01:48,723 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 1C600000000000000000000000000000]
|
||||
28: unknown:9001 {9001} internal, hidden
|
||||
16:01:48,723 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 1D0000]
|
||||
16:01:48,743 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 92006000000000000000000000000000]
|
||||
16:01:48,743 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000D 920000]
|
||||
16:01:48,763 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000D 1D600000000000000000000000000000]
|
||||
29: unknown:9200 {9200} internal, hidden
|
||||
16:01:48,763 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 1E0000]
|
||||
16:01:48,783 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 92026000000000000000000000000000]
|
||||
16:01:48,783 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 920200]
|
||||
16:01:48,805 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 1E600000000000000000000000000000]
|
||||
30: unknown:9202 {9202} internal, hidden
|
||||
16:01:48,805 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 1F0000]
|
||||
16:01:48,825 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 18056000000000000000000000000000]
|
||||
16:01:48,825 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000B 180500]
|
||||
16:01:48,845 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 1F600000000000000000000000000000]
|
||||
31: unknown:1805 {1805} internal, hidden
|
||||
16:01:48,845 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A0A 000000]
|
||||
16:01:48,865 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A0A 08000000000000000000000000000000]
|
||||
Has 8 reprogrammable keys:
|
||||
16:01:48,865 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1C 000000]
|
||||
16:01:48,885 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1C 00500038010001010000000000000000]
|
||||
16:01:48,885 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2B 005000]
|
||||
16:01:48,905 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2B 00500000000000000000000000000000]
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
16:01:48,905 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1D 010000]
|
||||
16:01:48,925 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1D 00510039010001010000000000000000]
|
||||
16:01:48,925 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A28 005100]
|
||||
16:01:48,945 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A28 00510000000000000000000000000000]
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
16:01:48,945 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1B 020000]
|
||||
16:01:48,965 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1B 0052003A310003070100000000000000]
|
||||
16:01:48,965 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2B 005200]
|
||||
16:01:48,985 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2B 00520000000000000000000000000000]
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
16:01:48,985 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1E 030000]
|
||||
16:01:49,005 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1E 0053003C310002030100000000000000]
|
||||
16:01:49,006 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A28 005300]
|
||||
16:01:49,027 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A28 00530000000000000000000000000000]
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
16:01:49,027 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1B 040000]
|
||||
16:01:49,047 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1B 0056003E310002030100000000000000]
|
||||
16:01:49,047 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2F 005600]
|
||||
16:01:49,067 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2F 00560000000000000000000000000000]
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
16:01:49,067 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1C 050000]
|
||||
16:01:49,087 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1C 00C300A9310003070100000000000000]
|
||||
16:01:49,087 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2A 00C300]
|
||||
16:01:49,107 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2A 00C30000000000000000000000000000]
|
||||
5: unknown:00C3 , default: unknown:00A9 => unknown:00C3
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
16:01:49,107 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1A 060000]
|
||||
16:01:49,127 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1A 00C4009D310003070100000000000000]
|
||||
16:01:49,127 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2C 00C400]
|
||||
16:01:49,147 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2C 00C40000000000000000000000000000]
|
||||
6: unknown:00C4 , default: unknown:009D => unknown:00C4
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
16:01:49,147 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1C 070000]
|
||||
16:01:49,167 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1C 00D700B4A00004000300000000000000]
|
||||
16:01:49,167 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2C 00D700]
|
||||
16:01:49,187 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2C 00D70000000000000000000000000000]
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:4, gmask:0
|
||||
16:01:49,187 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 070B 000000]
|
||||
16:01:49,207 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 070B 32140000000000000000000000000000]
|
||||
16:01:49,207 DEBUG [MainThread] logitech_receiver.hidpp20: device 1 battery 50% charged, next level 20% charge, status 0 = discharging
|
||||
Battery: 50%, discharging.
|
||||
16:01:49,207 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 210000]
|
||||
16:01:49,213 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 210814406E0402010700000000000000]
|
||||
16:01:49,213 INFO [MainThread] logitech_receiver.receiver: <UnifyingReceiver(/dev/hidraw0,3)>: found new device 2 (406E)
|
||||
|
||||
16:01:49,213 DEBUG [MainThread] logitech_receiver.base: (3) pinging device 2
|
||||
16:01:49,213 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 001F 0000F4]
|
||||
16:01:49,245 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 001F 0405F400000000000000000000000000]
|
||||
2: Wireless Illuminated Keyboard K800 new
|
||||
Codename : new
|
||||
Kind : keyboard
|
||||
Wireless PID : 406E
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 20 ms (50Hz)
|
||||
16:01:49,245 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 310000]
|
||||
16:01:49,251 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 31636E14131A40000007000000000000]
|
||||
Serial number: 636E1413
|
||||
16:01:49,251 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000A 000100]
|
||||
16:01:49,285 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000A 01000100000000000000000000000000]
|
||||
16:01:49,285 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 010B 000000]
|
||||
16:01:49,325 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 010B 18000000000000000000000000000000]
|
||||
16:01:49,325 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0009 000300]
|
||||
16:01:49,365 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0009 02000200000000000000000000000000]
|
||||
16:01:49,365 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 020C 000000]
|
||||
16:01:49,405 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 020C 0254FC62F90004406E00000000010000]
|
||||
16:01:49,405 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 021E 000000]
|
||||
16:01:49,446 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 021E 01424F546000000200406ED4E7454501]
|
||||
16:01:49,446 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 021C 010000]
|
||||
16:01:49,487 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 021C 0052514B6801000901406ED4E7454501]
|
||||
Bootloader: BOT 60.00.B0002
|
||||
Firmware: RQK 68.01.B0009
|
||||
The power switch is located on the top right corner.
|
||||
Supports 25 HID++ 2.0 features:
|
||||
16:01:49,487 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 000000]
|
||||
16:01:49,527 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 00000100000000000000000000000000]
|
||||
0: ROOT {0000}
|
||||
16:01:49,527 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 000100]
|
||||
16:01:49,567 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 01000100000000000000000000000000]
|
||||
1: FEATURE SET {0001}
|
||||
16:01:49,568 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000C 000300]
|
||||
16:01:49,607 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000C 02000200000000000000000000000000]
|
||||
2: DEVICE FW VERSION {0003}
|
||||
16:01:49,607 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011A 030000]
|
||||
16:01:49,647 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011A 00050000000000000000000000000000]
|
||||
16:01:49,647 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000D 000500]
|
||||
16:01:49,687 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000D 03000000000000000000000000000000]
|
||||
3: DEVICE NAME {0005}
|
||||
16:01:49,687 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011C 040000]
|
||||
16:01:49,729 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011C 1D4B0000000000000000000000000000]
|
||||
16:01:49,729 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000D 1D4B00]
|
||||
16:01:49,770 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000D 04000000000000000000000000000000]
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
16:01:49,770 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0119 050000]
|
||||
16:01:49,809 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0119 00200000000000000000000000000000]
|
||||
16:01:49,809 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000D 002000]
|
||||
16:01:49,849 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000D 05000000000000000000000000000000]
|
||||
5: RESET {0020}
|
||||
16:01:49,849 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 060000]
|
||||
16:01:49,889 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 10000000000000000000000000000000]
|
||||
16:01:49,889 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0009 100000]
|
||||
16:01:49,929 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0009 06000000000000000000000000000000]
|
||||
6: BATTERY STATUS {1000}
|
||||
16:01:49,929 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 070000]
|
||||
16:01:49,969 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 19830000000000000000000000000000]
|
||||
16:01:49,969 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000B 198300]
|
||||
16:01:50,009 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000B 07000000000000000000000000000000]
|
||||
7: unknown:1983 {1983}
|
||||
16:01:50,010 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011F 080000]
|
||||
16:01:50,051 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011F 1B040003000000000000000000000000]
|
||||
16:01:50,051 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 1B0400]
|
||||
16:01:50,091 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 08000300000000000000000000000000]
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
16:01:50,091 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0119 090000]
|
||||
16:01:50,131 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0119 40A00000000000000000000000000000]
|
||||
16:01:50,131 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0009 40A000]
|
||||
16:01:50,171 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0009 09000000000000000000000000000000]
|
||||
9: FN INVERSION {40A0}
|
||||
16:01:50,171 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011C 0A0000]
|
||||
16:01:50,211 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011C 41000000000000000000000000000000]
|
||||
16:01:50,211 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000A 410000]
|
||||
16:01:50,251 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000A 0A000000000000000000000000000000]
|
||||
10: ENCRYPTION {4100}
|
||||
16:01:50,251 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 0B0000]
|
||||
16:01:50,293 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 45210000000000000000000000000000]
|
||||
16:01:50,293 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 452100]
|
||||
16:01:50,333 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 0B000000000000000000000000000000]
|
||||
11: KEYBOARD DISABLE {4521}
|
||||
16:01:50,334 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011E 0C0000]
|
||||
16:01:50,373 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011E 00C20000000000000000000000000000]
|
||||
16:01:50,373 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 00C200]
|
||||
16:01:50,413 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 0C000000000000000000000000000000]
|
||||
12: unknown:00C2 {00C2}
|
||||
16:01:50,414 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 0D0000]
|
||||
16:01:50,453 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 18036000000000000000000000000000]
|
||||
16:01:50,454 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0008 180300]
|
||||
16:01:50,493 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0008 0D600000000000000000000000000000]
|
||||
13: unknown:1803 {1803} internal, hidden
|
||||
16:01:50,494 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 0E0000]
|
||||
16:01:50,533 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 18066001000000000000000000000000]
|
||||
16:01:50,533 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000C 180600]
|
||||
16:01:50,575 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000C 0E600100000000000000000000000000]
|
||||
14: unknown:1806 {1806} internal, hidden
|
||||
16:01:50,576 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011A 0F0000]
|
||||
16:01:50,615 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011A 18116000000000000000000000000000]
|
||||
16:01:50,615 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0008 181100]
|
||||
16:01:50,655 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0008 0F600000000000000000000000000000]
|
||||
15: unknown:1811 {1811} internal, hidden
|
||||
16:01:50,656 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011B 100000]
|
||||
16:01:50,695 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011B 18306000000000000000000000000000]
|
||||
16:01:50,696 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 183000]
|
||||
16:01:50,735 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 10600000000000000000000000000000]
|
||||
16: unknown:1830 {1830} internal, hidden
|
||||
16:01:50,736 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0119 110000]
|
||||
16:01:50,775 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0119 18906000000000000000000000000000]
|
||||
16:01:50,776 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000A 189000]
|
||||
16:01:50,815 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000A 11600000000000000000000000000000]
|
||||
17: unknown:1890 {1890} internal, hidden
|
||||
16:01:50,816 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 120000]
|
||||
16:01:50,858 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 18A16000000000000000000000000000]
|
||||
16:01:50,858 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 18A100]
|
||||
16:01:50,897 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 12600000000000000000000000000000]
|
||||
18: unknown:18A1 {18A1} internal, hidden
|
||||
16:01:50,898 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 130000]
|
||||
16:01:50,937 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 1DF36000000000000000000000000000]
|
||||
16:01:50,938 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000B 1DF300]
|
||||
16:01:50,977 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000B 13600000000000000000000000000000]
|
||||
19: unknown:1DF3 {1DF3} internal, hidden
|
||||
16:01:50,978 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011B 140000]
|
||||
16:01:51,017 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011B 1E004000000000000000000000000000]
|
||||
16:01:51,018 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 1E0000]
|
||||
16:01:51,057 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 14400000000000000000000000000000]
|
||||
20: unknown:1E00 {1E00} hidden
|
||||
16:01:51,058 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011B 150000]
|
||||
16:01:51,097 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011B 1EB06000000000000000000000000000]
|
||||
16:01:51,098 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 1EB000]
|
||||
16:01:51,137 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 15600000000000000000000000000000]
|
||||
21: unknown:1EB0 {1EB0} internal, hidden
|
||||
16:01:51,138 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 160000]
|
||||
16:01:51,179 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 18616000000000000000000000000000]
|
||||
16:01:51,180 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 186100]
|
||||
16:01:51,220 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 16600000000000000000000000000000]
|
||||
22: unknown:1861 {1861} internal, hidden
|
||||
16:01:51,220 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 170000]
|
||||
16:01:51,260 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 1A206000000000000000000000000000]
|
||||
16:01:51,260 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0008 1A2000]
|
||||
16:01:51,300 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0008 17600000000000000000000000000000]
|
||||
23: unknown:1A20 {1A20} internal, hidden
|
||||
16:01:51,300 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 180000]
|
||||
16:01:51,339 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 18B06000000000000000000000000000]
|
||||
16:01:51,340 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 18B000]
|
||||
16:01:51,379 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 18600000000000000000000000000000]
|
||||
24: unknown:18B0 {18B0} internal, hidden
|
||||
16:01:51,380 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 080E 000000]
|
||||
16:01:51,419 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 080E 0F000000000000000000000000000000]
|
||||
Has 15 reprogrammable keys:
|
||||
16:01:51,420 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081C 000000]
|
||||
16:01:51,461 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081C 0022001A3A0100000000000000000000]
|
||||
16:01:51,462 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082F 002200]
|
||||
16:01:51,501 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082F 00220000000000000000000000000000]
|
||||
0: MY HOME , default: HomePage => MY HOME
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:1, group:0, gmask:0
|
||||
16:01:51,502 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0819 010000]
|
||||
16:01:51,541 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0819 000E000E3A0200000000000000000000]
|
||||
16:01:51,542 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0829 000E00]
|
||||
16:01:51,581 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0829 000E0000000000000000000000000000]
|
||||
1: Mail , default: Email => Mail
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:2, group:0, gmask:0
|
||||
16:01:51,582 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0818 020000]
|
||||
16:01:51,621 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0818 003E002D3A0300000000000000000000]
|
||||
16:01:51,622 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0828 003E00]
|
||||
16:01:51,661 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0828 003E0000000000000000000000000000]
|
||||
2: SEARCH , default: SearchForFiles => SEARCH
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:3, group:0, gmask:0
|
||||
16:01:51,662 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081B 030000]
|
||||
16:01:51,701 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081B 000800083A0400000000000000000000]
|
||||
16:01:51,702 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082E 000800]
|
||||
16:01:51,743 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082E 00080000000000000000000000000000]
|
||||
3: Application Switcher , default: Application Switcher => Application Switcher
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, gmask:0
|
||||
16:01:51,744 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081D 040000]
|
||||
16:01:51,784 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081D 00E200C12A0500000000000000000000]
|
||||
16:01:51,784 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082E 00E200]
|
||||
16:01:51,824 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082E 00E20000000000000000000000000000]
|
||||
4: unknown:00E2 , default: unknown:00C1 => unknown:00E2
|
||||
is FN, FN sensitive, divertable, pos:5, group:0, gmask:0
|
||||
16:01:51,824 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081A 050000]
|
||||
16:01:51,864 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081A 00E300C22A0600000000000000000000]
|
||||
16:01:51,864 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082B 00E300]
|
||||
16:01:51,904 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082B 00E30000000000000000000000000000]
|
||||
5: unknown:00E3 , default: unknown:00C2 => unknown:00E3
|
||||
is FN, FN sensitive, divertable, pos:6, group:0, gmask:0
|
||||
16:01:51,904 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0818 060000]
|
||||
16:01:51,943 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0818 0040002F3A0800000000000000000000]
|
||||
16:01:51,944 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082E 004000]
|
||||
16:01:51,984 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082E 00400000000000000000000000000000]
|
||||
6: SLEEP , default: Sleep => SLEEP
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:8, group:0, gmask:0
|
||||
16:01:51,984 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081D 070000]
|
||||
16:01:52,025 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081D 0028001D3A0900000000000000000000]
|
||||
16:01:52,026 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0828 002800]
|
||||
16:01:52,065 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0828 00280000000000000000000000000000]
|
||||
7: MEDIA PLAYER , default: Music => MEDIA PLAYER
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:9, group:0, gmask:0
|
||||
16:01:52,066 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081F 080000]
|
||||
16:01:52,106 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081F 000600062A0A00000000000000000000]
|
||||
16:01:52,106 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0828 000600]
|
||||
16:01:52,145 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0828 00060000000000000000000000000000]
|
||||
8: Previous , default: Previous => Previous
|
||||
is FN, FN sensitive, divertable, pos:10, group:0, gmask:0
|
||||
16:01:52,146 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081F 090000]
|
||||
16:01:52,186 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081F 000400042A0B00000000000000000000]
|
||||
16:01:52,186 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082B 000400]
|
||||
16:01:52,225 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082B 00040000000000000000000000000000]
|
||||
9: Play/Pause , default: Play/Pause => Play/Pause
|
||||
is FN, FN sensitive, divertable, pos:11, group:0, gmask:0
|
||||
16:01:52,226 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081B 0A0000]
|
||||
16:01:52,265 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081B 000500052A0C00000000000000000000]
|
||||
16:01:52,266 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0829 000500]
|
||||
16:01:52,308 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0829 00050000000000000000000000000000]
|
||||
10: Next , default: Next => Next
|
||||
is FN, FN sensitive, divertable, pos:12, group:0, gmask:0
|
||||
16:01:52,308 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0818 0B0000]
|
||||
16:01:52,348 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0818 00010001240000000000000000000000]
|
||||
16:01:52,348 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082C 000100]
|
||||
16:01:52,387 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082C 00010000000000000000000000000000]
|
||||
11: Volume Up , default: Volume Up => Volume Up
|
||||
nonstandard, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,388 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081C 0C0000]
|
||||
16:01:52,427 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081C 00020002240000000000000000000000]
|
||||
16:01:52,428 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082C 000200]
|
||||
16:01:52,468 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082C 00020000000000000000000000000000]
|
||||
12: Volume Down , default: Volume Down => Volume Down
|
||||
nonstandard, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,468 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0819 0D0000]
|
||||
16:01:52,507 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0819 00030003240000000000000000000000]
|
||||
16:01:52,508 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082D 000300]
|
||||
16:01:52,547 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082D 00030000000000000000000000000000]
|
||||
13: Mute , default: Mute => Mute
|
||||
nonstandard, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,548 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081B 0E0000]
|
||||
16:01:52,589 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081B 000A000A340000000000000000000000]
|
||||
16:01:52,590 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082F 000A00]
|
||||
16:01:52,630 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082F 000A0000000000000000000000000000]
|
||||
14: Calculator , default: Calculator => Calculator
|
||||
nonstandard, reprogrammable, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,630 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 060C 000000]
|
||||
16:01:52,670 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 060C 32140000000000000000000000000000]
|
||||
16:01:52,670 DEBUG [MainThread] logitech_receiver.hidpp20: device 2 battery 50% charged, next level 20% charge, status 0 = discharging
|
||||
Battery: 50%, discharging.
|
||||
52
docs/devices/k800.txt
Normal file
@@ -0,0 +1,52 @@
|
||||
# 0x00 - Enabled Notifications. rw (see HID++ 1.0 spec)
|
||||
<< ( 0.055) [10 02 8100 000000] '\x10\x02\x81\x00\x00\x00\x00'
|
||||
>> ( 0.084) [10 02 8100 000000] '\x10\x02\x81\x00\x00\x00\x00'
|
||||
|
||||
# 0x01 - Keyboard hand detection. rw, last param is 00 when hand detection is
|
||||
# enabled, 30 when disabled. (when enabled, keyboard will light up if not
|
||||
# already when hovering over the front)
|
||||
<< ( 1.085) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00'
|
||||
>> ( 1.114) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00'
|
||||
|
||||
# 0x07 - Battery status (3 = one bar; 1 = red/critical; 5=two bars; 7=three
|
||||
# bars/full. Second returned param is 25 when keyboard is charging )
|
||||
<< ( 7.327) [10 02 8107 000000] '\x10\x02\x81\x07\x00\x00\x00'
|
||||
>> ( 7.368) [10 02 8107 030000] '\x10\x02\x81\x07\x03\x00\x00'
|
||||
|
||||
# 0x09 - F key function. rw (read: status, set/get: 00 01 00 means swap
|
||||
# functions, 00 00 00 means do not swap functions)
|
||||
<< ( 9.411) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00'
|
||||
>> ( 9.440) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00'
|
||||
|
||||
# 0x17 - Illumination info r/w. Last param: 02 to disable backlight, 01 to
|
||||
# enable backlight
|
||||
<< ( 24.965) [10 02 8117 000000] '\x10\x02\x81\x17\x00\x00\x00'
|
||||
>> ( 24.988) [10 02 8117 3C0001] '\x10\x02\x81\x17<\x00\x01'
|
||||
|
||||
# 0x51 - ?
|
||||
<< ( 99.294) [10 02 8151 000000] '\x10\x02\x81Q\x00\x00\x00'
|
||||
>> ( 99.543) [10 02 8151 000000] '\x10\x02\x81Q\x00\x00\x00'
|
||||
|
||||
# 0x54 - ?
|
||||
<< ( 103.046) [10 02 8154 000000] '\x10\x02\x81T\x00\x00\x00'
|
||||
>> ( 103.295) [10 02 8154 FF0000] '\x10\x02\x81T\xff\x00\x00'
|
||||
|
||||
# 0xD0 - ?
|
||||
<< ( 253.860) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00'
|
||||
>> ( 253.883) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00'
|
||||
|
||||
# 0xF1 - Version info (params 0n 00 00 where n is 1..4)
|
||||
<< ( 289.991) [10 02 81F1 000000] '\x10\x02\x81\xf1\x00\x00\x00'
|
||||
>> ( 290.032) [10 02 8F81 F10300] '\x10\x02\x8f\x81\xf1\x03\x00'
|
||||
|
||||
# 0xF3 - ?
|
||||
<< ( 292.075) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
>> ( 292.116) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
|
||||
# 0x0F - This changes, the last commented line was observed in an earlier run
|
||||
<< ( 17.728) [10 02 830F 000000] '\x10\x02\x83\x0f\x00\x00\x00'
|
||||
>> ( 17.976) [11 02 830F FFFB00000240025C000000000FF90080] '\x11\x02\x83\x0f\xff\xfb\x00\x00\x02@\x02\\\x00\x00\x00\x00\x0f\xf9\x00\x80'
|
||||
#>> ( 17.999) [11 02 830F FFFC007F0243025D000000000FF60080] '\x11\x02\x83\x0f\xff\xfc\x00\x7f\x02C\x02]\x00\x00\x00\x00\x0f\xf6\x00\x80'
|
||||
|
||||
# See also https://git.lekensteyn.nl/ltunify/tree/registers.txt for a verbose
|
||||
# meaning of registers and params.
|
||||
41
docs/devices/k830.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
# Provided by Mikkel Munch Mortensen
|
||||
|
||||
(solaar)
|
||||
3: Illuminated Living-Room Keyboard K830
|
||||
Codename : K830
|
||||
Kind : keyboard
|
||||
Wireless PID : 4032
|
||||
Protocol : HID++ 4.1
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 9F7C6FD7
|
||||
Firmware: RQK 56.00.B0020
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 27 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: unknown:0020 {0020}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: BACKLIGHT {1981}
|
||||
8: unknown:1B04 {1B04}
|
||||
9: unknown:2005 {2005}
|
||||
10: NEW FN INVERSION {40A2}
|
||||
11: ENCRYPTION {4100}
|
||||
12: unknown:4521 {4521}
|
||||
13: TOUCHPAD RAW XY {6100} hidden
|
||||
14: unknown:6501 {6501}
|
||||
15: unknown:00C1 {00C1}
|
||||
16: unknown:1811 {1811} internal, hidden
|
||||
17: unknown:1830 {1830} internal, hidden
|
||||
18: unknown:1890 {1890} internal, hidden
|
||||
19: unknown:18A0 {18A0} internal, hidden
|
||||
20: unknown:1DF3 {1DF3} internal, hidden
|
||||
21: unknown:1E00 {1E00} hidden
|
||||
22: unknown:1EB0 {1EB0} internal, hidden
|
||||
23: unknown:1861 {1861} internal, hidden
|
||||
24: unknown:1A20 {1A20} internal, hidden
|
||||
25: unknown:18B0 {18B0} internal, hidden
|
||||
26: unknown:1F07 {1F07} internal, hidden
|
||||
Battery: 50%, discharging.
|
||||
49
docs/devices/m185_new.txt
Normal file
@@ -0,0 +1,49 @@
|
||||
P/N: 810-005238
|
||||
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c534
|
||||
Serial : 0
|
||||
Firmware : 29.01.B0016
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
|
||||
2: Wireless Mouse M185
|
||||
Codename : M185
|
||||
Kind : mouse
|
||||
Wireless PID : 4054
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 1377ED51
|
||||
Firmware: RQM 64.00.B0008
|
||||
The power switch is located on the base.
|
||||
Supports 20 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: REPROG CONTROLS V4 {1B04}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: LOWRES WHEEL {2130}
|
||||
8: POINTER SPEED {2205}
|
||||
9: unknown:1802 {1802} internal, hidden
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1850 {1850} internal, hidden
|
||||
13: unknown:1869 {1869} internal, hidden
|
||||
14: unknown:1890 {1890} internal, hidden
|
||||
15: unknown:18B1 {18B1} internal, hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: unknown:1E80 {1E80} internal, hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery status unavailable.
|
||||
|
||||
51
docs/devices/m185_old.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
P/N: 810-003496
|
||||
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c52f
|
||||
Serial : 6D0342C5
|
||||
Firmware : 30.00.B0009
|
||||
Has 1 paired device(s) out of a maximum of 1.
|
||||
Notifications: (none)
|
||||
|
||||
1: Wireless Mouse
|
||||
Codename : Wireless Mouse
|
||||
Kind : mouse
|
||||
Wireless PID : 4055
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 6D0342C5
|
||||
Firmware: RQM 65.00.B0003
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1810 {1810} internal, hidden
|
||||
7: unknown:1830 {1830} internal, hidden
|
||||
8: unknown:1802 {1802} internal, hidden
|
||||
9: unknown:1862 {1862} internal, hidden
|
||||
10: unknown:1890 {1890} internal, hidden
|
||||
11: unknown:18A0 {18A0} internal, hidden
|
||||
12: unknown:18B1 {18B1} internal, hidden
|
||||
13: REPROG CONTROLS V4 {1B04}
|
||||
14: WIRELESS DEVICE STATUS {1D4B}
|
||||
15: unknown:1DF0 {1DF0} hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1EB0 {1EB0} internal, hidden
|
||||
19: unknown:1F03 {1F03} internal, hidden
|
||||
20: LOWRES WHEEL {2130}
|
||||
21: POINTER SPEED {2205}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery: 5%, discharging.
|
||||
|
||||
40
docs/devices/m235.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
Wireless Mouse M235
|
||||
Codename : M235
|
||||
Kind : mouse
|
||||
Wireless PID : 4055
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 65.00.B0003
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1810 {1810} internal, hidden
|
||||
7: unknown:1830 {1830} internal, hidden
|
||||
8: unknown:1802 {1802} internal, hidden
|
||||
9: unknown:1862 {1862} internal, hidden
|
||||
10: unknown:1890 {1890} internal, hidden
|
||||
11: unknown:18A0 {18A0} internal, hidden
|
||||
12: unknown:18B1 {18B1} internal, hidden
|
||||
13: REPROG CONTROLS V4 {1B04}
|
||||
14: WIRELESS DEVICE STATUS {1D4B}
|
||||
15: unknown:1DF0 {1DF0} hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1EB0 {1EB0} internal, hidden
|
||||
19: unknown:1F03 {1F03} internal, hidden
|
||||
20: LOWRES WHEEL {2130}
|
||||
21: POINTER SPEED {2205}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery: 70%, discharging.
|
||||
29
docs/devices/m325.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
Wireless Mouse M325
|
||||
Codename : M325
|
||||
Kind : mouse
|
||||
Wireless PID : 400A
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: ABB05E01
|
||||
Firmware: RQM 27.02.B0028
|
||||
The power switch is located on the base.
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1F03 {1F03} hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
Has 5 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
3: BACK AS BUTTON 4 => Back mse, reprogrammable
|
||||
4: FORWARD AS BUTTON 5 => BrowserForward mse, reprogrammable
|
||||
56
docs/devices/m345.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
Receiver
|
||||
LZ141AX-DJ
|
||||
M/N: C-U0008
|
||||
(ltunify)
|
||||
Serial number: 574197D3
|
||||
Firmware version: 024.000.00018
|
||||
Bootloader version: BL.000.006
|
||||
|
||||
Mouse
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Mouse
|
||||
Name: M345
|
||||
Wireless Product ID: 4017
|
||||
Serial number: 920DC223
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 12
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0003] IFirmwareInfo
|
||||
3: [0005] GetDeviceNameType
|
||||
4: [1000] batteryLevelStatus
|
||||
5: [1D4B] WirelessDeviceStatus
|
||||
6: [1DF3] H unknown
|
||||
7: [1B00] SpecialKeysMSEButtons
|
||||
8: [1DF0] H unknown
|
||||
9: [1F03] H unknown
|
||||
10: [2100] VerticalScrolling
|
||||
11: [2120] HiResScrolling
|
||||
12: [2200] MousePointer
|
||||
(O = obsolete feature; H = SW hidden feature)
|
||||
(solaar-cli)
|
||||
1: Wireless Mouse M345
|
||||
Codename : M345
|
||||
Kind : mouse
|
||||
Wireless PID : 4017
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms
|
||||
Serial number: 920DC223
|
||||
Firmware: RQM 27.02.B0028
|
||||
The power switch is located on the base.
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1F03 {1F03} hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
Battery: 90%, discharging,
|
||||
@@ -1,8 +1,8 @@
|
||||
# ?
|
||||
# notification flags
|
||||
<< ( 0.001) [10 01 8100 000000] '\x10\x01\x81\x00\x00\x00\x00'
|
||||
>> ( 0.062) [10 01 8100 000000] '\x10\x01\x81\x00\x00\x00\x00'
|
||||
|
||||
# ?
|
||||
# smooth scroll
|
||||
<< ( 1.063) [10 01 8101 000000] '\x10\x01\x81\x01\x00\x00\x00'
|
||||
>> ( 1.078) [10 01 8101 820000] '\x10\x01\x81\x01\x82\x00\x00'
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<< ( 2.079) [10 01 8102 000000] '\x10\x01\x81\x02\x00\x00\x00'
|
||||
>> ( 2.094) [10 01 8102 000080] '\x10\x01\x81\x02\x00\x00\x80'
|
||||
|
||||
# ?
|
||||
# battery status
|
||||
<< ( 7.263) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00'
|
||||
>> ( 7.278) [10 01 8107 050000] '\x10\x01\x81\x07\x05\x00\x00'
|
||||
|
||||
|
||||
34
docs/devices/m515.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
1: Couch Mouse M515
|
||||
Codename : M515
|
||||
Kind : mouse
|
||||
Wireless PID : 4007
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms
|
||||
Serial number: BED587E9
|
||||
Firmware: RQM 24.00.B0023
|
||||
Bootloader: DFU 00.02.B0010
|
||||
The power switch is located on the base.
|
||||
Supports 16 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: DFUCONTROL {00C0}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1A30 {1A30} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: WIRELESS DEVICE STATUS {1D4B}
|
||||
9: unknown:1DF3 {1DF3} hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
13: unknown:1F02 {1F02} hidden
|
||||
14: unknown:1F03 {1F03} hidden
|
||||
15: unknown:1E80 {1E80} hidden
|
||||
Has 5 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
3: BACK AS BUTTON 4 => BackEx mse, reprogrammable
|
||||
4: FORWARD AS BUTTON 5 => BrowserForwardEx mse, reprogrammable
|
||||
Battery: 65%, discharging,
|
||||
56
docs/devices/m525.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
Mouse
|
||||
(ltunify)
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Mouse
|
||||
Name: M525
|
||||
Wireless Product ID: 4013
|
||||
Serial number: DAFA335E
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 12
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0003] IFirmwareInfo
|
||||
3: [0005] GetDeviceNameType
|
||||
4: [1000] batteryLevelStatus
|
||||
5: [1D4B] WirelessDeviceStatus
|
||||
6: [1DF3] H unknown
|
||||
7: [1B00] SpecialKeysMSEButtons
|
||||
8: [1DF0] H unknown
|
||||
9: [1F03] H unknown
|
||||
10: [2100] VerticalScrolling
|
||||
11: [2120] HiResScrolling
|
||||
12: [2200] MousePointer
|
||||
(O = obsolete feature; H = SW hidden feature;
|
||||
I = reserved for internal use)
|
||||
(solaar)
|
||||
1: Wireless Mouse M525
|
||||
Codename : M525
|
||||
Kind : mouse
|
||||
Wireless PID : 4013
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: DAFA335E
|
||||
Firmware: RQM 27.02.B0028
|
||||
The power switch is located on the base.
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1F03 {1F03} hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
Has 5 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
3: BACK AS BUTTON 4 => Back mse, reprogrammable
|
||||
4: FORWARD AS BUTTON 5 => BrowserForward mse, reprogrammable
|
||||
Battery: 90%, discharging.
|
||||
41
docs/devices/m560.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
1: Wireless Mouse M560
|
||||
Codename : M560
|
||||
Kind : mouse
|
||||
Wireless PID : 402D
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 16E6E42A
|
||||
Firmware: RQM 48.00.B0015
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1830 {1830} internal, hidden
|
||||
6: unknown:1850 {1850} internal, hidden
|
||||
7: unknown:1860 {1860} internal, hidden
|
||||
8: unknown:1890 {1890} internal, hidden
|
||||
9: unknown:18A0 {18A0} internal, hidden
|
||||
10: REPROG CONTROLS V3 {1B03}
|
||||
11: WIRELESS DEVICE STATUS {1D4B}
|
||||
12: unknown:1DF3 {1DF3} internal, hidden
|
||||
13: REPROG CONTROLS {1B00}
|
||||
14: unknown:1DF0 {1DF0} hidden
|
||||
15: unknown:1E00 {1E00} hidden
|
||||
16: unknown:18B0 {18B0} internal, hidden
|
||||
17: unknown:1E90 {1E90} internal, hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: VERTICAL SCROLLING {2100}
|
||||
20: HI RES SCROLLING {2120}
|
||||
21: MOUSE POINTER {2200}
|
||||
Has 7 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: BACK AS BUTTON 4 => Win8BackHorzLeft mse, reprogrammable
|
||||
3: FORWARD AS BUTTON 5 => Win8ForwardHorzRight mse, reprogrammable
|
||||
4: unknown:00B0 => Win8MetroWin7Forward mse, reprogrammable
|
||||
5: SHOW DESKTOP HPP => Win8ShowDesktopWin7Back mse, reprogrammable
|
||||
6: unknown:00AF => Win8Charm Appswitch GifAnimation mse, reprogrammable, unknown:000020
|
||||
Battery: 70%, discharging.
|
||||
25
docs/devices/m570.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : BAFF9007
|
||||
Firmware : 12.03.B0025
|
||||
Bootloader : 02.15
|
||||
Other : AA.AA
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=221
|
||||
|
||||
1: Wireless Trackball M570
|
||||
Codename : M570
|
||||
Kind : mouse
|
||||
Wireless PID : 1028
|
||||
Protocol : HID++ 1.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 891DC05A
|
||||
Firmware: 26.00.B0003
|
||||
Bootloader: 02.06
|
||||
Other: 00.01
|
||||
The power switch is located on the base.
|
||||
Notifications: battery status (0x100000).
|
||||
Battery: 100%, discharging.
|
||||
|
||||
83
docs/devices/m585.txt
Normal file
@@ -0,0 +1,83 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : E21FAD57
|
||||
Firmware : 24.06.B0030
|
||||
Bootloader : 01.08
|
||||
Other : AA.AC
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=190, 2=66
|
||||
|
||||
|
||||
2: Multi Device Silent Mouse M585/M590
|
||||
Codename : M585/M590
|
||||
Kind : mouse
|
||||
Wireless PID : 406B
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 22AEB299
|
||||
Bootloader: BOT 48.01.B0002
|
||||
Firmware: MPM 05.10.B0011
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 35 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0021 {0021}
|
||||
7: unknown:0007 {0007}
|
||||
8: BATTERY STATUS {1000}
|
||||
9: CHANGE HOST {1814}
|
||||
10: unknown:1815 {1815}
|
||||
11: REPROG CONTROLS V4 {1B04}
|
||||
12: unknown:1C00 {1C00}
|
||||
13: POINTER SPEED {2205}
|
||||
Pointer Speed: 1.31640625
|
||||
14: VERTICAL SCROLLING {2100}
|
||||
Roller type: standard
|
||||
Ratchet per turn: 18
|
||||
Scroll lines: 0
|
||||
15: unknown:00C2 {00C2}
|
||||
16: unknown:1802 {1802} internal, hidden
|
||||
17: unknown:1803 {1803} internal, hidden
|
||||
18: unknown:1806 {1806} internal, hidden
|
||||
19: unknown:1805 {1805} internal, hidden
|
||||
20: unknown:1813 {1813} internal, hidden
|
||||
21: unknown:1830 {1830} internal, hidden
|
||||
22: unknown:1861 {1861} internal, hidden
|
||||
23: unknown:1890 {1890} internal, hidden
|
||||
24: unknown:1891 {1891} internal, hidden
|
||||
25: unknown:18A1 {18A1} internal, hidden
|
||||
26: unknown:1DF3 {1DF3} internal, hidden
|
||||
27: unknown:1E00 {1E00} hidden
|
||||
28: unknown:1EB0 {1EB0} internal, hidden
|
||||
29: unknown:18B1 {18B1} internal, hidden
|
||||
30: unknown:1850 {1850} internal, hidden
|
||||
31: unknown:1E22 {1E22}
|
||||
32: unknown:1F03 {1F03} internal, hidden
|
||||
33: unknown:18C0 {18C0} internal, hidden
|
||||
34: LOWRES WHEEL {2130}
|
||||
Wheel Reports: HID
|
||||
Has 8 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
5: LEFT SCROLL AS AC PAN , default: HorzScrollLeftSet => LEFT SCROLL AS AC PAN
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
6: RIGHT SCROLL AS AC PAN , default: HorzScrollRightSet => RIGHT SCROLL AS AC PAN
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:3, gmask:0
|
||||
Battery: 50%, discharging.
|
||||
|
||||
@@ -36,7 +36,7 @@ registers:
|
||||
<< ( 259.270) [10 02 81F1 000000] '\x10\x02\x81\xf1\x00\x00\x00'
|
||||
>> ( 259.283) [10 02 8F81 F10300] '\x10\x02\x8f\x81\xf1\x03\x00'
|
||||
|
||||
# writing 01 here will trigger an avalance of events, most likely
|
||||
# writing 01 here will trigger an avalanche of events, most likely
|
||||
# raw input from the mouse; disable by writing 00
|
||||
<< ( 261.300) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
>> ( 261.315) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
|
||||
82
docs/devices/mk220-new.txt
Normal file
@@ -0,0 +1,82 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw1
|
||||
USB id : 046d:c534
|
||||
Serial : 0
|
||||
Firmware : 29.00.B0015
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
|
||||
1: Wireless Keyboard MK270
|
||||
Codename : MK270
|
||||
Kind : keyboard
|
||||
Wireless PID : 4023
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQK 49.00.B0029
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: KEYBOARD LAYOUT {4520}
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18B0 {18B0} internal, hidden
|
||||
15: unknown:1DF3 {1DF3} internal, hidden
|
||||
16: unknown:1E00 {1E00} hidden
|
||||
17: unknown:1868 {1868} internal, hidden
|
||||
Has 11 reprogrammable keys:
|
||||
0: MY HOME => HomePage is FN, FN sensitive, reprogrammable
|
||||
1: Mail => Email is FN, FN sensitive, reprogrammable
|
||||
2: SEARCH => Search is FN, FN sensitive, reprogrammable
|
||||
3: Calculator => Calculator is FN, FN sensitive, reprogrammable
|
||||
4: MEDIA PLAYER => Music is FN, FN sensitive, reprogrammable
|
||||
5: Previous => Previous is FN, FN sensitive
|
||||
6: Play/Pause => Play/Pause is FN, FN sensitive
|
||||
7: Next => Next is FN, FN sensitive
|
||||
8: Mute => Mute is FN, FN sensitive
|
||||
9: Volume Down => Volume Down is FN, FN sensitive
|
||||
10: Volume Up => Volume Up is FN, FN sensitive
|
||||
Battery: 30%, discharging.
|
||||
|
||||
2: Wireless Mouse
|
||||
Codename :
|
||||
Kind : mouse
|
||||
Wireless PID : 4022
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 38.00.B0044
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: VERTICAL SCROLLING {2100}
|
||||
8: MOUSE POINTER {2200}
|
||||
9: unknown:1810 {1810} internal, hidden
|
||||
10: unknown:1830 {1830} internal, hidden
|
||||
11: unknown:1850 {1850} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18B0 {18B0} internal, hidden
|
||||
14: unknown:1DF3 {1DF3} internal, hidden
|
||||
15: unknown:1868 {1868} internal, hidden
|
||||
16: unknown:1869 {1869} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 30%, discharging.
|
||||
|
||||
78
docs/devices/mk220.txt
Normal file
@@ -0,0 +1,78 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw1
|
||||
USB id : 046d:c52e
|
||||
Serial : 758596BF
|
||||
Firmware : 23.01.B0006
|
||||
Has 2 paired device(s) out of a maximum of 2.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
|
||||
1: Wireless Mouse M150
|
||||
Codename : M150
|
||||
Kind : mouse
|
||||
Wireless PID : 400C
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 93850883
|
||||
Firmware: RQM 29.00.B0010
|
||||
The power switch is located on the base.
|
||||
Supports 16 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: unknown:1850 {1850} hidden
|
||||
5: unknown:1860 {1860} hidden
|
||||
6: BATTERY STATUS {1000}
|
||||
7: WIRELESS DEVICE STATUS {1D4B}
|
||||
8: unknown:1DF3 {1DF3} hidden
|
||||
9: REPROG CONTROLS {1B00}
|
||||
10: unknown:1DF0 {1DF0} hidden
|
||||
11: unknown:1E00 {1E00} hidden
|
||||
12: unknown:1E80 {1E80} hidden
|
||||
13: unknown:1F03 {1F03} hidden
|
||||
14: VERTICAL SCROLLING {2100}
|
||||
15: MOUSE POINTER {2200}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 90%, discharging.
|
||||
|
||||
2: Wireless Keyboard K220
|
||||
Codename : K220
|
||||
Kind : keyboard
|
||||
Wireless PID : 4005
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 5BB1D72E
|
||||
Firmware: RQK 37.00.B0011
|
||||
Supports 14 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: FEATURE INFO {0002}
|
||||
3: DEVICE FW VERSION {0003}
|
||||
4: DEVICE NAME {0005}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1820 {1820} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: REPROG CONTROLS V2 {1B01}
|
||||
9: WIRELESS DEVICE STATUS {1D4B}
|
||||
10: unknown:1DF0 {1DF0} hidden
|
||||
11: unknown:1DF3 {1DF3} hidden
|
||||
12: ENCRYPTION {4100}
|
||||
13: KEYBOARD LAYOUT {4520}
|
||||
Has 12 reprogrammable keys:
|
||||
0: FN F1 => Do Nothing One is FN, reprogrammable
|
||||
1: FN F2 => Do Nothing One is FN, reprogrammable
|
||||
2: FN F3 => Do Nothing One is FN, reprogrammable
|
||||
3: FN F4 => Do Nothing One is FN, reprogrammable
|
||||
4: FN F5 => Do Nothing One is FN, reprogrammable
|
||||
5: FN F6 => Do Nothing One is FN, reprogrammable
|
||||
6: FN F7 => Do Nothing One is FN, reprogrammable
|
||||
7: FN F8 => Do Nothing One is FN, reprogrammable
|
||||
8: FN F9 => Do Nothing One is FN, reprogrammable
|
||||
9: Mute => Mute is FN
|
||||
10: Volume Down => Volume Down is FN
|
||||
11: Volume Up => Volume Up is FN
|
||||
Battery: 90%, discharging.
|
||||
|
||||
129
docs/devices/mk240-nano.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Logitech MK240 NANO Device Information
|
||||
## `solaar show all` Dump
|
||||
```
|
||||
Unifying Receiver (NOTE: NOT claimed to be supporting Unifying from the package box, the only advanced feature that may be related is 128-bit AES encryption) (M/N: C-U0010)
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c534
|
||||
Serial : 0
|
||||
Firmware : 29.01.B0016
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
|
||||
1: Wireless Keyboard MK270 (NOTE: The Product is actually "MK240 NANO Wireless Keyboard and Mouse Combo" with the "K240"(M/N: Y-R0036) keyboard model)
|
||||
Codename : MK270
|
||||
Kind : keyboard
|
||||
Wireless PID : 4023
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 4BBBBA4A
|
||||
Firmware: RQK 49.00.B0029
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: KEYBOARD LAYOUT {4520}
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18B0 {18B0} internal, hidden
|
||||
15: unknown:1DF3 {1DF3} internal, hidden
|
||||
16: unknown:1E00 {1E00} hidden
|
||||
17: unknown:1868 {1868} internal, hidden
|
||||
Has 11 reprogrammable keys:
|
||||
0: MY HOME => HomePage is FN, FN sensitive, reprogrammable
|
||||
1: Mail => Email is FN, FN sensitive, reprogrammable
|
||||
2: SEARCH => Search is FN, FN sensitive, reprogrammable
|
||||
3: Calculator => Calculator is FN, FN sensitive, reprogrammable
|
||||
4: MEDIA PLAYER => Music is FN, FN sensitive, reprogrammable
|
||||
5: Previous => Previous is FN, FN sensitive
|
||||
6: Play/Pause => Play/Pause is FN, FN sensitive
|
||||
7: Next => Next is FN, FN sensitive
|
||||
8: Mute => Mute is FN, FN sensitive
|
||||
9: Volume Down => Volume Down is FN, FN sensitive
|
||||
10: Volume Up => Volume Up is FN, FN sensitive
|
||||
Battery: 30%, discharging. (NOTE: Capacity readings appears to be faked, or in extremely low sensitivity)
|
||||
|
||||
2: Wireless Mouse M150 (NOTE: The Product is actually "MK240 NANO Wireless Keyboard and Mouse Combo" with the "M212"(M/N: M-R0041) mouse model)
|
||||
Codename : M150
|
||||
Kind : mouse
|
||||
Wireless PID : 4022
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 38.00.B0044
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: VERTICAL SCROLLING {2100}
|
||||
8: MOUSE POINTER {2200}
|
||||
9: unknown:1810 {1810} internal, hidden
|
||||
10: unknown:1830 {1830} internal, hidden
|
||||
11: unknown:1850 {1850} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18B0 {18B0} internal, hidden
|
||||
14: unknown:1DF3 {1DF3} internal, hidden
|
||||
15: unknown:1868 {1868} internal, hidden
|
||||
16: unknown:1869 {1869} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 30%, discharging. (NOTE: Capacity readings appears to be faked, or in extremely low sensitivity, in the Logitech SetPoint utility battery level is displayed as "HIGH")
|
||||
```
|
||||
|
||||
## Connect Utility Report
|
||||
```
|
||||
Re-Connect Software Version : 2.00.3
|
||||
Dj Api Version : 2, 50, 25
|
||||
|
||||
接收器(Receiver)
|
||||
Name : 無線接收器(Wireless Receiver)
|
||||
ModelId : 0x46dc534
|
||||
Serial Number :
|
||||
Handle : 0xff000001
|
||||
Wireless Status : 0x3
|
||||
Firmware version : 029.001.00016
|
||||
Bootloader version :
|
||||
Dfu Status : 0x1
|
||||
Is Dfu Cancellable : Yes
|
||||
Max Device Capacity : 6
|
||||
|
||||
滑鼠(Mouse)
|
||||
Name :
|
||||
ModelId : 0x0
|
||||
Serial Number : 4022-00-00-00-00
|
||||
Handle : 0x2000003
|
||||
Wireless Status : 0x0
|
||||
Firmware version : 038.000.00044
|
||||
Bootloader version :
|
||||
Dfu Status : 0x1
|
||||
Is Dfu Cancellable : No
|
||||
Battery Status : 0x2
|
||||
Parent Handle : 0xff000001
|
||||
|
||||
鍵盤(Keyboard)
|
||||
Name :
|
||||
ModelId : 0x0
|
||||
Serial Number : 4023-4B-BB-BA-4A
|
||||
Handle : 0x1000002
|
||||
Wireless Status : 0x0
|
||||
Firmware version : 049.000.00029
|
||||
Bootloader version :
|
||||
Dfu Status : 0x1
|
||||
Is Dfu Cancellable : No
|
||||
Battery Status : 0x2
|
||||
Parent Handle : 0xff000001
|
||||
```
|
||||
86
docs/devices/mk270.txt
Normal file
@@ -0,0 +1,86 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw9
|
||||
USB id : 046d:c534
|
||||
Serial : 00000000
|
||||
Firmware : 29.00.B0015
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
|
||||
1: Wireless Keyboard
|
||||
Codename : MK270
|
||||
Kind : keyboard
|
||||
Wireless PID : 4023
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQK 49.00.B0029
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: KEYBOARD LAYOUT {4520}
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18B0 {18B0} internal, hidden
|
||||
15: unknown:1DF3 {1DF3} internal, hidden
|
||||
16: unknown:1E00 {1E00} hidden
|
||||
17: unknown:1868 {1868} internal, hidden
|
||||
Has 11 reprogrammable keys:
|
||||
0: MY HOME => HomePage FN sensitive, is FN, reprogrammable
|
||||
1: Mail => Email FN sensitive, is FN, reprogrammable
|
||||
2: SEARCH => Search FN sensitive, is FN, reprogrammable
|
||||
3: Calculator => Calculator FN sensitive, is FN, reprogrammable
|
||||
4: MEDIA PLAYER => Music FN sensitive, is FN, reprogrammable
|
||||
5: Previous => Previous FN sensitive, is FN
|
||||
6: Play/Pause => Play/Pause FN sensitive, is FN
|
||||
7: Next => Next FN sensitive, is FN
|
||||
8: Mute => Mute FN sensitive, is FN
|
||||
9: Volume Down => Volume Down FN sensitive, is FN
|
||||
10: Volume Up => Volume Up FN sensitive, is FN
|
||||
Battery: 30%, discharging.
|
||||
|
||||
2: Wireless Mouse
|
||||
Codename : M185
|
||||
Kind : mouse
|
||||
Wireless PID : 4038
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 54.00.B0004
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1830 {1830} internal, hidden
|
||||
6: unknown:1850 {1850} internal, hidden
|
||||
7: unknown:1860 {1860} internal, hidden
|
||||
8: unknown:1890 {1890} internal, hidden
|
||||
9: unknown:18A0 {18A0} internal, hidden
|
||||
10: unknown:18C0 {18C0} internal, hidden
|
||||
11: WIRELESS DEVICE STATUS {1D4B}
|
||||
12: unknown:1DF3 {1DF3} internal, hidden
|
||||
13: REPROG CONTROLS {1B00}
|
||||
14: unknown:1DF0 {1DF0} hidden
|
||||
15: unknown:1E00 {1E00} hidden
|
||||
16: unknown:1E80 {1E80} internal, hidden
|
||||
17: unknown:1E90 {1E90} internal, hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: VERTICAL SCROLLING {2100}
|
||||
20: MOUSE POINTER {2200}
|
||||
21: unknown:18B0 {18B0} internal, hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 70%, discharging.
|
||||
|
||||
61
docs/devices/mk520.txt
Normal file
@@ -0,0 +1,61 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c52b
|
||||
Serial : AF9C98A0
|
||||
Firmware : 12.03.B0025
|
||||
Bootloader : 02.15
|
||||
Other : AA.AA
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=22, 2=167
|
||||
|
||||
1: Wireless Mouse M310/M310t
|
||||
Codename : M310/M310t
|
||||
Kind : mouse
|
||||
Wireless PID : 4031
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 03E8C0B4
|
||||
Firmware: RQM 40.01.B0017
|
||||
The power switch is located on the base.
|
||||
Supports 21 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1830 {1830} internal, hidden
|
||||
6: unknown:1850 {1850} internal, hidden
|
||||
7: unknown:1860 {1860} internal, hidden
|
||||
8: unknown:1890 {1890} internal, hidden
|
||||
9: unknown:18A0 {18A0} internal, hidden
|
||||
10: unknown:18C0 {18C0} internal, hidden
|
||||
11: WIRELESS DEVICE STATUS {1D4B}
|
||||
12: unknown:1DF3 {1DF3} internal, hidden
|
||||
13: REPROG CONTROLS {1B00}
|
||||
14: unknown:1DF0 {1DF0} hidden
|
||||
15: unknown:1E00 {1E00} hidden
|
||||
16: unknown:1E80 {1E80} internal, hidden
|
||||
17: unknown:1E90 {1E90} internal, hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: VERTICAL SCROLLING {2100}
|
||||
20: MOUSE POINTER {2200}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 70%, discharging.
|
||||
|
||||
2: Wireless Keyboard K520
|
||||
Codename : K520
|
||||
Kind : keyboard
|
||||
Wireless PID : 2011
|
||||
Protocol : HID++ 1.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: ACDE97EF
|
||||
Firmware: 26.00.B0012
|
||||
Other: 00.07
|
||||
The power switch is located on the top case.
|
||||
Notifications: (none).
|
||||
Battery: full, discharging.
|
||||
|
||||
26
docs/devices/mk700.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
# Enabled Notifications
|
||||
# 10 - battery status
|
||||
# 02 + 01 - remap FN keys (multimedia + power buttons)
|
||||
>> ( 1.412) [10 02 8100 130000] '\x10\x02\x81\x00\x13\x00\x00'
|
||||
<< ( 0.011) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00'
|
||||
>> ( 0.276) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00'
|
||||
|
||||
# Battery status
|
||||
<< ( 6.033) [10 02 8107 000000] '\x10\x02\x81\x07\x00\x00\x00'
|
||||
>> ( 6.344) [10 02 8107 070000] '\x10\x02\x81\x07\x07\x00\x00'
|
||||
|
||||
# FN status
|
||||
<< ( 8.055) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00'
|
||||
>> ( 8.144) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00'
|
||||
|
||||
# ?
|
||||
<< ( 208.316) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00'
|
||||
>> ( 208.353) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00'
|
||||
|
||||
# version info
|
||||
<< ( 237.436) [10 02 81F1 000000] '\x10\x02\x81\xf1\x00\x00\x00'
|
||||
>> ( 237.744) [10 02 8F81 F10300] '\x10\x02\x8f\x81\xf1\x03\x00'
|
||||
|
||||
# ?
|
||||
<< ( 239.459) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
>> ( 239.766) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
84
docs/devices/mx-master-2s.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw7
|
||||
USB id : 046d:c52b
|
||||
Serial : BB7D9447
|
||||
Firmware : 24.01.B0023
|
||||
Bootloader : 01.08
|
||||
Other : AA.AD
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 2=156
|
||||
|
||||
1: Wireless Mouse MX Master 2S
|
||||
Codename : MX Master 2S
|
||||
Kind : mouse
|
||||
Wireless PID : 4069
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: EAB71F76
|
||||
Bootloader: BOT 56.01.B0006
|
||||
Firmware: MPM 12.01.B0006
|
||||
Firmware: MPM 12.01.B0006
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 32 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0021 {0021}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: unknown:1806 {1806} internal, hidden
|
||||
9: CHANGE HOST {1814}
|
||||
10: REPROG CONTROLS V4 {1B04}
|
||||
11: ADJUSTABLE DPI {2201}
|
||||
12: VERTICAL SCROLLING {2100}
|
||||
Roller type: 3G
|
||||
Ratchet per turn: 24
|
||||
Scroll lines: 0
|
||||
13: SMART SHIFT {2110}
|
||||
14: HIRES WHEEL {2121}
|
||||
Multiplier: 8
|
||||
Has invert
|
||||
Normal wheel motion
|
||||
Has ratchet switch
|
||||
Normal wheel mode
|
||||
High resolution mode
|
||||
HID notification
|
||||
15: GESTURE 2 {6501}
|
||||
16: unknown:00C2 {00C2}
|
||||
17: unknown:1813 {1813} internal, hidden
|
||||
18: unknown:1830 {1830} internal, hidden
|
||||
19: unknown:1890 {1890} internal, hidden
|
||||
20: unknown:1891 {1891} internal, hidden
|
||||
21: unknown:18A1 {18A1} internal, hidden
|
||||
22: unknown:18C0 {18C0} internal, hidden
|
||||
23: unknown:1DF3 {1DF3} internal, hidden
|
||||
24: unknown:1E00 {1E00} hidden
|
||||
25: unknown:1EB0 {1EB0} internal, hidden
|
||||
26: unknown:1803 {1803} internal, hidden
|
||||
27: unknown:1861 {1861} internal, hidden
|
||||
28: unknown:9001 {9001} internal, hidden
|
||||
29: unknown:9200 {9200} internal, hidden
|
||||
30: unknown:9202 {9202} internal, hidden
|
||||
31: unknown:1805 {1805} internal, hidden
|
||||
Has 8 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
5: unknown:00C3 , default: unknown:00A9 => unknown:00C3
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
6: unknown:00C4 , default: unknown:009D => unknown:00C4
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:4, gmask:0
|
||||
Battery: 50%, discharging.
|
||||
83
docs/devices/mx-master-3.txt
Normal file
@@ -0,0 +1,83 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : 32A29DF4
|
||||
Firmware : 12.09.B0030
|
||||
Bootloader : 04.16
|
||||
Other : AA.AA
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless (0x000100)
|
||||
Device activity counters: 1=18
|
||||
|
||||
1: Wireless Mouse MX Master 3
|
||||
Codename : MX Master 3
|
||||
Kind : mouse
|
||||
Wireless PID : 4082
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: B0C9EC6E
|
||||
Bootloader: BOT 95.00.B0013
|
||||
Firmware: MPM 19.00.B0013
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 34 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0021 {0021}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
9: CHANGE HOST {1814}
|
||||
10: unknown:2250 {2250}
|
||||
11: ADJUSTABLE DPI {2201}
|
||||
12: SMART SHIFT {2110}
|
||||
13: HIRES WHEEL {2121}
|
||||
Multiplier: 15
|
||||
Has invert
|
||||
Normal wheel motion
|
||||
Has ratchet switch
|
||||
Normal wheel mode
|
||||
Low resolution mode
|
||||
HID notification
|
||||
14: unknown:2150 {2150}
|
||||
15: unknown:2251 {2251}
|
||||
16: unknown:00C2 {00C2}
|
||||
17: unknown:1802 {1802} internal, hidden
|
||||
18: unknown:1803 {1803} internal, hidden
|
||||
19: unknown:1806 {1806} internal, hidden
|
||||
20: unknown:1813 {1813} internal, hidden
|
||||
21: unknown:1805 {1805} internal, hidden
|
||||
22: unknown:1830 {1830} internal, hidden
|
||||
23: unknown:1890 {1890} internal, hidden
|
||||
24: unknown:1891 {1891} internal, hidden
|
||||
25: unknown:18A1 {18A1} internal, hidden
|
||||
26: unknown:1DF3 {1DF3} internal, hidden
|
||||
27: unknown:1E00 {1E00} hidden
|
||||
28: unknown:1EB0 {1EB0} internal, hidden
|
||||
29: unknown:1861 {1861} internal, hidden
|
||||
30: unknown:9001 {9001} internal, hidden
|
||||
31: unknown:9203 {9203} internal, hidden
|
||||
32: unknown:9205 {9205} internal, hidden
|
||||
33: unknown:9300 {9300} internal, hidden
|
||||
Has 8 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:3, gmask:7
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
5: unknown:00C3 , default: unknown:00A9 => unknown:00C3
|
||||
divertable, mse, reprogrammable, pos:0, group:3, gmask:7
|
||||
6: unknown:00C4 , default: unknown:009D => unknown:00C4
|
||||
divertable, mse, reprogrammable, pos:0, group:3, gmask:7
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:4, gmask:0
|
||||
Battery: 100%, discharging.
|
||||
|
||||
54
docs/devices/mx-master.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c52b
|
||||
Serial : 0E039B8F
|
||||
Firmware : 24.01.B0023
|
||||
Bootloader : 01.08
|
||||
Other : AA.AC
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=134
|
||||
|
||||
1: Wireless Mouse MX Master
|
||||
Codename : MX Master
|
||||
Kind : mouse
|
||||
Wireless PID : 4041
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: A975E230
|
||||
Bootloader: BOT 18.00.B0012
|
||||
Firmware: MPM 11.00.B0012
|
||||
Firmware: MPM 11.00.B0012
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 29 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: CHANGE HOST {1814}
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
9: ADJUSTABLE DPI {2201}
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: SMART SHIFT {2110}
|
||||
12: HIRES WHEEL {2121}
|
||||
13: GESTURE 2 {6501}
|
||||
14: DFUCONTROL 2 {00C1}
|
||||
15: unknown:1813 {1813} internal, hidden
|
||||
16: unknown:1830 {1830} internal, hidden
|
||||
17: unknown:1890 {1890} internal, hidden
|
||||
18: unknown:18A1 {18A1} internal, hidden
|
||||
19: unknown:18C0 {18C0} internal, hidden
|
||||
20: unknown:1DF3 {1DF3} internal, hidden
|
||||
21: unknown:1E00 {1E00} hidden
|
||||
22: unknown:1EB0 {1EB0} internal, hidden
|
||||
23: unknown:1803 {1803} internal, hidden
|
||||
24: unknown:1861 {1861} internal, hidden
|
||||
25: unknown:9000 {9000} internal, hidden
|
||||
26: unknown:9200 {9200} internal, hidden
|
||||
27: unknown:9240 {9240} internal, hidden
|
||||
28: unknown:1805 {1805} internal, hidden
|
||||
Battery: 50%, discharging.
|
||||
@@ -1,12 +1,40 @@
|
||||
# Notifications (r1_bit0 = battery status?)
|
||||
<< ( 0.113) [10 01 8100 000000] '\x10\x01\x81\x00\x00\x00\x00'
|
||||
>> ( 0.157) [10 01 8100 100000] '\x10\x01\x81\x00\x10\x00\x00'
|
||||
|
||||
short register 0x63: values 0x81 .. 0x8F
|
||||
set DPI as 100 .. 1500
|
||||
# ?
|
||||
<< ( 1.050) [10 01 8101 000000] '\x10\x01\x81\x01\x00\x00\x00'
|
||||
>> ( 1.097) [10 01 8101 020000] '\x10\x01\x81\x01\x02\x00\x00'
|
||||
|
||||
# battery (07 means full)
|
||||
<< ( 7.335) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00'
|
||||
>> ( 7.382) [10 01 8107 070000] '\x10\x01\x81\x07\x07\x00\x00'
|
||||
|
||||
short register 0x51: set leds
|
||||
value: ab cd 00
|
||||
where a/b/c/d values are 1=off, 2=on, 3=flash
|
||||
a = lower led
|
||||
b = red led
|
||||
c = upper led
|
||||
d = middle led
|
||||
# Set LEDS - ab cd 00, where a/b/c/d values are 1=off, 2=on, 3=flash
|
||||
# a = lower led
|
||||
# b = red led
|
||||
# c = upper led
|
||||
# d = middle led
|
||||
# below: all leds are off
|
||||
<< ( 86.592) [10 01 8151 000000] '\x10\x01\x81Q\x00\x00\x00'
|
||||
>> ( 86.639) [10 01 8151 111100] '\x10\x01\x81Q\x11\x11\x00'
|
||||
|
||||
# DPI (values in range 0x81..0x8F; logical value: 100..1500)
|
||||
<< ( 108.430) [10 01 8163 000000] '\x10\x01\x81c\x00\x00\x00'
|
||||
>> ( 108.477) [10 01 8163 890000] '\x10\x01\x81c\x89\x00\x00'
|
||||
|
||||
# ?
|
||||
<< ( 240.505) [10 01 81D0 000000] '\x10\x01\x81\xd0\x00\x00\x00'
|
||||
>> ( 240.550) [10 01 81D0 000000] '\x10\x01\x81\xd0\x00\x00\x00'
|
||||
|
||||
# ?
|
||||
<< ( 245.690) [10 01 81D4 000000] '\x10\x01\x81\xd4\x00\x00\x00'
|
||||
>> ( 245.737) [10 01 81D4 000012] '\x10\x01\x81\xd4\x00\x00\x12'
|
||||
|
||||
# Firmware/bootloader version
|
||||
<< ( 281.016) [10 01 81F1 000000] '\x10\x01\x81\xf1\x00\x00\x00'
|
||||
>> ( 282.177) [10 01 8F81 F10300] '\x10\x01\x8f\x81\xf1\x03\x00'
|
||||
|
||||
# ?
|
||||
<< ( 284.106) [10 01 81F3 000000] '\x10\x01\x81\xf3\x00\x00\x00'
|
||||
>> ( 284.153) [10 01 81F3 000000] '\x10\x01\x81\xf3\x00\x00\x00'
|
||||
|
||||
109
docs/devices/t400.txt
Normal file
@@ -0,0 +1,109 @@
|
||||
Receiver
|
||||
LZ2388S-DJ
|
||||
M/N:C-U0007
|
||||
(ltunify)
|
||||
Serial number: E6B794F8
|
||||
Firmware version: 012.001.00019
|
||||
Bootloader version: BL.002.014
|
||||
|
||||
Mouse
|
||||
(ltunify)
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Mouse
|
||||
Name: T400
|
||||
Wireless Product ID: 4026
|
||||
Serial number: 131A3093
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 27
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0002] unknown
|
||||
3: [0003] IFirmwareInfo
|
||||
4: [0005] GetDeviceNameType
|
||||
5: [00C0] DFUControl
|
||||
6: [1000] batteryLevelStatus
|
||||
7: [1802] HI unknown
|
||||
8: [1810] HI unknown
|
||||
9: [1830] HI unknown
|
||||
10: [1850] HI unknown
|
||||
11: [1860] HI unknown
|
||||
12: [1890] HI unknown
|
||||
13: [18A0] HI unknown
|
||||
14: [18E3] HI unknown
|
||||
15: [1B00] SpecialKeysMSEButtons
|
||||
16: [1D4B] WirelessDeviceStatus
|
||||
17: [1DF3] HI unknown
|
||||
18: [1E00] H unknown
|
||||
19: [1E80] HI unknown
|
||||
20: [1F03] HI unknown
|
||||
21: [1F04] HI unknown
|
||||
22: [2100] VerticalScrolling
|
||||
23: [2101] H unknown
|
||||
24: [2120] HiResScrolling
|
||||
25: [2200] MousePointer
|
||||
26: [6110] H TouchmouseRawPoints
|
||||
27: [1B03] ReprogControlsV3
|
||||
(O = obsolete feature; H = SW hidden feature;
|
||||
I = reserved for internal use)
|
||||
(solaar)
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : E6B794F8
|
||||
Firmware : 12.01.B0019
|
||||
Bootloader : 02.14
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
Device activity counters: 1=134
|
||||
|
||||
1: Zone Touch Mouse T400
|
||||
Codename : T400
|
||||
Kind : mouse
|
||||
Wireless PID : 4026
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 131A3093
|
||||
Firmware: RQM 39.00.B0029
|
||||
Bootloader: BL 03.00
|
||||
Hardware: 72
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 28 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: FEATURE INFO {0002}
|
||||
3: DEVICE FW VERSION {0003}
|
||||
4: DEVICE NAME {0005}
|
||||
5: DFUCONTROL {00C0}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: unknown:1802 {1802} internal, hidden
|
||||
8: unknown:1810 {1810} internal, hidden
|
||||
9: unknown:1830 {1830} internal, hidden
|
||||
10: unknown:1850 {1850} internal, hidden
|
||||
11: unknown:1860 {1860} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18E3 {18E3} internal, hidden
|
||||
15: REPROG CONTROLS {1B00}
|
||||
16: WIRELESS DEVICE STATUS {1D4B}
|
||||
17: unknown:1DF3 {1DF3} internal, hidden
|
||||
18: unknown:1E00 {1E00} hidden
|
||||
19: unknown:1E80 {1E80} internal, hidden
|
||||
20: unknown:1F03 {1F03} internal, hidden
|
||||
21: unknown:1F04 {1F04} internal, hidden
|
||||
22: VERTICAL SCROLLING {2100}
|
||||
23: unknown:2101 {2101} hidden
|
||||
24: HI RES SCROLLING {2120}
|
||||
25: MOUSE POINTER {2200}
|
||||
26: TOUCHMOUSE RAW POINTS {6110} hidden
|
||||
27: REPROG CONTROLS V3 {1B03}
|
||||
Has 7 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
3: METRO START SCREEN => MetroStartScreen mse, reprogrammable
|
||||
4: ZOOMIN => Do Nothing mse, reprogrammable
|
||||
5: ZOOMOUT => Do Nothing mse, reprogrammable
|
||||
6: BACK HSCROLL => TouchBackForwardHorzScroll
|
||||
Battery: 100%, discharging.
|
||||
94
docs/devices/t650.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Receiver
|
||||
LZ2458D-DJ
|
||||
M/N:C-U0008
|
||||
(ltunify)
|
||||
Serial number: 28E69A3E
|
||||
Firmware version: 024.000.00018
|
||||
Bootloader version: BL.000.006
|
||||
|
||||
Touchpad
|
||||
(ltunify)
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Touchpad
|
||||
Name: T650
|
||||
Wireless Product ID: 4101
|
||||
Serial number: 22205A4D
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 22
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0002] unknown
|
||||
3: [0003] IFirmwareInfo
|
||||
4: [0005] GetDeviceNameType
|
||||
5: [1000] batteryLevelStatus
|
||||
6: [1D4B] WirelessDeviceStatus
|
||||
7: [1DF3] HI unknown
|
||||
8: [1B00] SpecialKeysMSEButtons
|
||||
9: [1F03] HI unknown
|
||||
10: [2100] VerticalScrolling
|
||||
11: [2120] HiResScrolling
|
||||
12: [2200] MousePointer
|
||||
13: [00C0] DFUControl
|
||||
14: [1E80] HI unknown
|
||||
15: [6100] TouchpadRawXy
|
||||
16: [1860] HI unknown
|
||||
17: [1E00] H unknown
|
||||
18: [1B01] ReprogControlsV2
|
||||
19: [1890] HI unknown
|
||||
20: [18E5] HI unknown
|
||||
21: [18A0] HI unknown
|
||||
22: [1830] HI unknown
|
||||
(O = obsolete feature; H = SW hidden feature;
|
||||
I = reserved for internal use)
|
||||
(solaar)
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : 28E69A3E
|
||||
Firmware : 24.00.B0018
|
||||
Bootloader : 00.06
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
Device activity counters: 1=221
|
||||
|
||||
1: Wireless Rechargeable Touchpad T650
|
||||
Codename : T650
|
||||
Kind : touchpad
|
||||
Wireless PID : 4101
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 22205A4D
|
||||
Firmware: RQM 41.01.B0037
|
||||
Bootloader: BL 03.00
|
||||
Hardware: 72
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 23 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: FEATURE INFO {0002}
|
||||
3: DEVICE FW VERSION {0003}
|
||||
4: DEVICE NAME {0005}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: unknown:1DF3 {1DF3} internal, hidden
|
||||
8: REPROG CONTROLS {1B00}
|
||||
9: unknown:1F03 {1F03} internal, hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
13: DFUCONTROL {00C0}
|
||||
14: unknown:1E80 {1E80} internal, hidden
|
||||
15: TOUCHPAD RAW XY {6100}
|
||||
16: unknown:1860 {1860} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: REPROG CONTROLS V2 {1B01}
|
||||
19: unknown:1890 {1890} internal, hidden
|
||||
20: unknown:18E5 {18E5} internal, hidden
|
||||
21: unknown:18A0 {18A0} internal, hidden
|
||||
22: unknown:1830 {1830} internal, hidden
|
||||
Has 2 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
Battery: 50%, discharging.
|
||||
112
docs/features.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: List of HID++ 2.0 features
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Feature status
|
||||
See functions in hidpp20.py and settings_templates.py
|
||||
|
||||
Feature | ID | Status | Notes
|
||||
---------------------------------------|----------|:------------------:|------
|
||||
`ROOT` | `0x0000` | :heavy_check_mark: | System
|
||||
`FEATURE_SET` | `0x0001` | :heavy_check_mark: | System
|
||||
`FEATURE_INFO` | `0x0002` | :heavy_check_mark: | System
|
||||
`DEVICE_FW_VERSION` | `0x0003` | :heavy_check_mark: | `get_firmware()`
|
||||
`DEVICE_UNIT_ID` | `0x0004` | :x: |
|
||||
`DEVICE_NAME` | `0x0005` | :heavy_check_mark: | `get_kind()`, `get_name()`
|
||||
`DEVICE_GROUPS` | `0x0006` | :x: |
|
||||
`DEVICE_FRIENDLY_NAME` | `0x0007` | :x: |
|
||||
`KEEP_ALIVE` | `0x0008` | :x: |
|
||||
`RESET` | `0x0020` | :x: | aka "Config Change"
|
||||
`CRYPTO_ID` | `0x0021` | :x: |
|
||||
`TARGET_SOFTWARE` | `0x0030` | :x: |
|
||||
`WIRELESS_SIGNAL_STRENGTH` | `0x0080` | :x: |
|
||||
`DFUCONTROL_LEGACY` | `0x00C0` | :x: |
|
||||
`DFUCONTROL_UNSIGNED` | `0x00C1` | :x: |
|
||||
`DFUCONTROL_SIGNED` | `0x00C2` | :x: |
|
||||
`DFU` | `0x00D0` | :x: |
|
||||
`BATTERY_STATUS` | `0x1000` | :heavy_check_mark: | `get_battery()`
|
||||
`BATTERY_VOLTAGE` | `0x1001` | :x: |
|
||||
`CHARGING_CONTROL` | `0x1010` | :x: |
|
||||
`LED_CONTROL` | `0x1300` | :x: |
|
||||
`GENERIC_TEST` | `0x1800` | :x: |
|
||||
`DEVICE_RESET` | `0x1802` | :x: |
|
||||
`OOBSTATE` | `0x1805` | :x: |
|
||||
`CONFIG_DEVICE_PROPS` | `0x1806` | :x: |
|
||||
`CHANGE_HOST` | `0x1814` | :x: |
|
||||
`HOSTS_INFO` | `0x1815` | :x: |
|
||||
`BACKLIGHT` | `0x1981` | :x: |
|
||||
`BACKLIGHT2` | `0x1982` | :x: |
|
||||
`BACKLIGHT3` | `0x1983` | :x: |
|
||||
`PRESENTER_CONTROL` | `0x1A00` | :x: |
|
||||
`SENSOR_3D` | `0x1A01` | :x: |
|
||||
`REPROG_CONTROLS` | `0x1B00` | :heavy_plus_sign: | Partially, only listing. `get_keys()`
|
||||
`REPROG_CONTROLS_V2` | `0x1B01` | :x: |
|
||||
`REPROG_CONTROLS_V2_2` | `0x1B02` | :x: |
|
||||
`REPROG_CONTROLS_V3` | `0x1B03` | :x: |
|
||||
`REPROG_CONTROLS_V4` | `0x1B04` | :heavy_plus_sign: | Partially, only listing. `get_keys()`
|
||||
`REPORT_HID_USAGE` | `0x1BC0` | :x: |
|
||||
`PERSISTENT_REMAPPABLE_ACTION` | `0x1C00` | :x: |
|
||||
`WIRELESS_DEVICE_STATUS` | `0x1D4B` | :x: |
|
||||
`REMAINING_PAIRING` | `0x1DF0` | :x: |
|
||||
`FIRMWARE_PROPERTIES` | `0x1F1F` | :x: |
|
||||
`ADC_MEASUREMENT` | `0x1F20` | :x: |
|
||||
`LEFT_RIGHT_SWAP` | `0x2001` | :x: |
|
||||
`SWAP_BUTTON_CANCEL` | `0x2005` | :x: |
|
||||
`POINTER_AXIS_ORIENTATION` | `0x2006` | :x: |
|
||||
`VERTICAL_SCROLLING` | `0x2100` | :heavy_check_mark: | `get_vertical_scrolling_info()`
|
||||
`SMART_SHIFT` | `0x2110` | :heavy_check_mark: | `_feature_smart_shift()`
|
||||
`HI_RES_SCROLLING` | `0x2120` | :heavy_check_mark: | `get_hi_res_scrolling_info()`, `_feature_hi_res_scroll()`
|
||||
`HIRES_WHEEL` | `0x2121` | :heavy_check_mark: | `get_hires_wheel()`, `_feature_hires_smooth_invert()`, `_feature_hires_smooth_resolution()`
|
||||
`LOWRES_WHEEL` | `0x2130` | :heavy_check_mark: | `get_lowres_wheel_status()`, `_feature_lowres_smooth_scroll()`
|
||||
`THUMB_WHEEL` | `0x2150` | :x: |
|
||||
`MOUSE_POINTER` | `0x2200` | :heavy_check_mark: | `get_mouse_pointer_info()`
|
||||
`ADJUSTABLE_DPI` | `0x2201` | :heavy_check_mark: | `_feature_adjustable_dpi()`
|
||||
`POINTER_SPEED` | `0x2205` | :heavy_check_mark: | `get_pointer_speed_info()`, `_feature_pointer_speed()`
|
||||
`ANGLE_SNAPPING` | `0x2230` | :x: |
|
||||
`SURFACE_TUNING` | `0x2240` | :x: |
|
||||
`HYBRID_TRACKING` | `0x2400` | :x: |
|
||||
`FN_INVERSION` | `0x40A0` | :heavy_check_mark: | `_feature_fn_swap()`
|
||||
`NEW_FN_INVERSION` | `0x40A2` | :heavy_check_mark: | `_feature_new_fn_swap()`
|
||||
`K375S_FN_INVERSION` | `0x40A3` | :heavy_check_mark: | `_feature_k375s_fn_swap()`
|
||||
`ENCRYPTION` | `0x4100` | :x: |
|
||||
`LOCK_KEY_STATE` | `0x4220` | :x: |
|
||||
`SOLAR_DASHBOARD` | `0x4301` | :x: |
|
||||
`KEYBOARD_LAYOUT` | `0x4520` | :x: |
|
||||
`KEYBOARD_DISABLE` | `0x4521` | :x: |
|
||||
`KEYBOARD_DISABLE_BY_USAGE` | `0x4522` | :x: |
|
||||
`DUALPLATFORM` | `0x4530` | :x: |
|
||||
`MULTIPLATFORM` | `0x4531` | :x: |
|
||||
`KEYBOARD_LAYOUT_2` | `0x4540` | :x: |
|
||||
`CROWN` | `0x4600` | :x: |
|
||||
`TOUCHPAD_FW_ITEMS` | `0x6010` | :x: |
|
||||
`TOUCHPAD_SW_ITEMS` | `0x6011` | :x: |
|
||||
`TOUCHPAD_WIN8_FW_ITEMS` | `0x6012` | :x: |
|
||||
`TAP_ENABLE` | `0x6020` | :x: |
|
||||
`TAP_ENABLE_EXTENDED` | `0x6021` | :x: |
|
||||
`CURSOR_BALLISTIC` | `0x6030` | :x: |
|
||||
`TOUCHPAD_RESOLUTION` | `0x6040` | :x: |
|
||||
`TOUCHPAD_RAW_XY` | `0x6100` | :x: |
|
||||
`TOUCHMOUSE_RAW_POINTS` | `0x6110` | :x: |
|
||||
`TOUCHMOUSE_6120` | `0x6120` | :x: |
|
||||
`GESTURE` | `0x6500` | :x: |
|
||||
`GESTURE_2` | `0x6501` | :x: |
|
||||
`GKEY` | `0x8010` | :x: |
|
||||
`MKEYS` | `0x8020` | :x: |
|
||||
`MR` | `0x8030` | :x: |
|
||||
`BRIGHTNESS_CONTROL` | `0x8040` | :x: |
|
||||
`REPORT_RATE` | `0x8060` | :x: |
|
||||
`COLOR_LED_EFFECTS` | `0x8070` | :x: |
|
||||
`RGB_EFFECTS` | `0X8071` | :x: |
|
||||
`PER_KEY_LIGHTING` | `0x8080` | :x: |
|
||||
`PER_KEY_LIGHTING_V2` | `0x8081` | :x: |
|
||||
`MODE_STATUS` | `0x8090` | :x: |
|
||||
`ONBOARD_PROFILES` | `0x8100` | :x: |
|
||||
`MOUSE_BUTTON_SPY` | `0x8110` | :x: |
|
||||
`LATENCY_MONITORING` | `0x8111` | :x: |
|
||||
`GAMING_ATTACHMENTS` | `0x8120` | :x: |
|
||||
`FORCE_FEEDBACK` | `0x8123` | :x: |
|
||||
`SIDETONE` | `0x8300` | :x: |
|
||||
`EQUALIZER` | `0x8310` | :x: |
|
||||
`HEADSET_OUT` | `0x8320` | :x: |
|
||||
|
||||
58
docs/i18n.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Translating Solaar
|
||||
|
||||
First, make sure you have installed the `gettext` package.
|
||||
|
||||
Here are the steps to add/update a translation (you should run all scripts from
|
||||
the source root):
|
||||
|
||||
1. Get an up-to-date copy of the source files. Preferably, make a clone on
|
||||
GitHub and clone it locally on your machine; this way you can later make a
|
||||
pull request to the main project.
|
||||
|
||||
2. Run `./tools/po-update.sh <language>`; it will create/update the file
|
||||
`./po/<language>.po`.
|
||||
|
||||
3. Edit `./po/<language>.po` with your favourite editor (just make sure it saves
|
||||
the file with the UTF-8 encoding). For each string in english (msgid), edit
|
||||
the translation (msgstr); if you leave msgstr empty, the string will remain
|
||||
untranslated.
|
||||
|
||||
Alternatively, you can use the excellent `poedit`.
|
||||
|
||||
4. Run `./tools/po-compile.sh`. It will bring up-to-date all the compiled
|
||||
language files, necessary at runtime.
|
||||
|
||||
5. Start Solaar (`./bin/solaar`). By default it will pick up the system language
|
||||
from your environment; to start it in another language, run
|
||||
`LANGUAGE=<language> ./bin/solaar`.
|
||||
|
||||
You can edit the translation iteratively, just repeat from step 3.
|
||||
If the upstream changes, do a `git pull` and then repeat from step 2.
|
||||
|
||||
Before opening a pull request, please run `./tools/po-update.sh` again. It will
|
||||
format and sort the translation file, and ensure a minimal diff when updating
|
||||
a translation.
|
||||
|
||||
|
||||
# Supported languages
|
||||
|
||||
Currently Solaar has been translated in the following languages:
|
||||
|
||||
- Français: [Papoteur][papoteur], [David Geiger][david-geiger],
|
||||
[Damien Lallement][damsweb]
|
||||
- Italiano: [Michele Olivo][micheleolivo]
|
||||
- Polski: [Adrian Piotrowicz][nexces]
|
||||
- Portuguese-BR: [Drovetto][drovetto], [Josenivaldo Benito Jr.][jrbenito]
|
||||
- Română: Daniel Pavel
|
||||
- Russian: [Dimitriy Ryazantcev][DJm00n]
|
||||
- Svensk: [Daniel Zippert][zipperten], Emelie Snecker
|
||||
|
||||
[papoteur]: http://github.com/papoteur
|
||||
[david-geiger]: http://github.com/david-geiger
|
||||
[damsweb]: http://github.com/damsweb
|
||||
[DJm00n]: https://github.com/DJm00n
|
||||
[nexces]: http://github.com/nexces
|
||||
[zipperten]: http://github.com/zipperten
|
||||
[micheleolivo]: http://github.com/micheleolivo
|
||||
[drovetto]: https://github.com/drovetto
|
||||
[jrbenito]: https://github.com/jrbenito/
|
||||
34
docs/icons_names.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
# battery icon names across various icon themes
|
||||
|
||||
B = 'battery'
|
||||
CG = 'charging'
|
||||
GB = 'gpm-battery'
|
||||
|
||||
theme (unknown) 0 0-CG 20 20-CG 40 40-CG 60 60-CG 80 80-CG 100 100-CG 100-full
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
gnome B-missing B-empty - B-caution B-caution-CG B-low B-low-CG - - B-good B-good-CG B-full B-full-CG B-full-charged
|
||||
|
||||
Humanity GB-missing GB-000 GB-000-CG GB-020 GB-020-CG GB-040 GB-040-CG GB-060 GB-060-CG GB-080 GB-080-CG GB-100 GB-100-CG GB-charged
|
||||
(gnome) - B_empty - B-caution - B-low - B_two_thirds - B_third_fourth - B_full B_plugged B_charged
|
||||
|
||||
elementary B-missing B-000 B-000-CG B-020 B-020-CG B-040 B-040-CG B-060 B-060-CG B-080 B-080-CG B-100 B-100-CG B-charged
|
||||
(gnome) - B-empty - B-caution B-caution-CG B-low B-low-CG - - B-good B-good-CG B-full B-full-CG B-full-charged
|
||||
- B_empty - - - - - B_two_thirds - B_third_fourth - B_full B_plugged B_charged
|
||||
|
||||
faenza - GB-000 GB-000-CG GB-020 GB-020-CG GB-040 GB-040-CG GB-060 GB-060-CG GB-080 GB-080-CG GB-100 GB-100-CG GB-charged
|
||||
(gnome) - B_empty - B_caution - B_low - B_two_thirds - B_third_fourth - B_full B_plugged B_charged
|
||||
|
||||
ubuntu-mono GB-missing GB-000 GB-000-CG GB-020 GB-020-CG GB-040 GB-040-CG GB-060 GB-060-CG GB-080 GB-080-CG GB-100 GB-100-CG GB-charged
|
||||
(Humanity) B-000 B-000-CG B-020 B-020-CG B-040 B-040-CG B-060 B-060-CG B-080 B-080-CG B-100 B-100-CG B-charged
|
||||
B_empty - B-caution - B-low - - - - - B_full - B_charged
|
||||
|
||||
oxygen B-missing B-low B-CG-low B-caution B-CG-caution B-040 B-CG-040 B-060 B-CG-060 B-080 B-CG-080 B-100 B-CG -
|
||||
|
||||
moblin - - - B-low - B-caution - - - B-good - B-full B-charging -
|
||||
|
||||
nuvola B-missing B-low - - - - - - - - - - B-charging -
|
||||
|
||||
|
||||
|
||||
# weather icons (for lux)
|
||||
136
docs/index.md
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: Solaar
|
||||
layout: default
|
||||
---
|
||||
|
||||
**Solaar** is a Linux manager for Logitech's devices that connect via a USB
|
||||
[Unifying][unifying], Lightspeed, or Nano receiver.
|
||||
Solaar does not work with Logitech peripherals that
|
||||
use Bluetooth or peripherals from other companies.
|
||||
|
||||
Solaar can be used as a GUI application or via its command-line interface.
|
||||
Both interfaces are able to list the devices paired to a receiver and
|
||||
show information about each device, including battery status for devices that support this feature.
|
||||
Solaar's GUI normally uses an icon in the system tray and starts with its main window hidden.
|
||||
If Solaar is invoked with the `--window=show` option (the default) Solaar starts with its main window visible.
|
||||
If Solaar is invoked with the `--window=hide` option Solaar starts with its main window hidden.
|
||||
If solaar is invoked with the `--window=only` option Solaar does not set up an icon in the
|
||||
system tray and also starts with its main window showing.
|
||||
For more information on Solaar's command-line interface use the help option,
|
||||
as in `solaar --help`.
|
||||
|
||||
Solaar is able to pair and unpair devices with
|
||||
receivers as supported by the receiver. Solaar can also control
|
||||
some of the changeable features of devices, such as smooth scrolling or
|
||||
function key behavior.
|
||||
For more information on how to use Solaar see
|
||||
[docs/usage.md](https://pwr-solaar.github.io/Solaar/usage).
|
||||
For more information on the capabilities of Solaar see
|
||||
[docs/capabilities.md](https://pwr-solaar.github.io/Solaar/capabilities).
|
||||
|
||||
Solaar does not process normal input from the devices. Solaar is thus unable
|
||||
to fix problems that arise from incorrect handling of mouse movements or keycodes
|
||||
by Linux drivers or other software.
|
||||
|
||||
Solaar has progressed past version 1.0. Problems with earlier versions should
|
||||
not be reported as bugs. Instead upgrade to a recent version or manually install
|
||||
the current version from [GitHub](https://github.com/pwr-Solaar/Solaar).
|
||||
Some of the capabilities of Solaar have been developed by observing the behavior of
|
||||
Logitech receivers and devices and generalizing from these observations.
|
||||
If your Logitech receiver or device behaves in a strange way this may be caused by
|
||||
an incorrect behavior generalization.
|
||||
Please report such experiences by creating an issue in
|
||||
[the Solaar repository](https://github.com/pwr-Solaar/Solaar/issues).
|
||||
|
||||
[unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver
|
||||
|
||||
## Supported Devices
|
||||
|
||||
**Solaar** will detect all devices paired with your Unifying, Lightspeed, or Nano
|
||||
receiver, and at the very least display some basic information about them.
|
||||
Solaar can pair and unpair a Logitech device showing the Unifying logo (Solaar's version of the [logo][logo])
|
||||
with any Unifying receiver and can pair and unpair devices with Lightspeed receivers.
|
||||
Solaar can pair some Logitech
|
||||
devices with Logitech Nano receivers but not all Logitech devices can be
|
||||
paired with Nano receivers. Logitech devices without a Unifying logo
|
||||
generally cannot be paired with Unifying receivers.
|
||||
|
||||
For some devices, extra settings (usually not available through the standard
|
||||
Linux system configuration) are supported. For a list of supported devices
|
||||
and their features, see [docs/devices.md](https://pwr-solaar.github.io/Solaar/devices).
|
||||
|
||||
[logo]: https://pwr-solaar.github.io/Solaar/assets/solaar.svg
|
||||
|
||||
## Pre-built packages
|
||||
|
||||
Pre-built packages are available for a few Linux distros.
|
||||
|
||||
Solaar has progressed beyond version 1.0 but some distros or repositories
|
||||
version 0.9.2, which is very old. Installing the current version will
|
||||
provide significant improvements. If a recent version of Solaar is not
|
||||
available from the standard repositories for your distribution you can try
|
||||
one of these packages.
|
||||
|
||||
* Arch solaar package in the [community repository][arch]
|
||||
* Debian 7 (Wheezy) or higher: packages in this [repository](https://pwr-solaar.github.io/Solaar/debian)
|
||||
* Ubuntu/Kubuntu 16.04+: use the solaar package from [universe repository][universe repository]
|
||||
* Ubuntu/Kubuntu stable packages: use the [Solaar stable ppa][ppa2], courtesy of [gogo][ppa4]
|
||||
* Ubuntu/Kubuntu git build packages: use the [Solaar git ppa][ppa1], courtesy of [gogo][ppa4]
|
||||
* a [Fedora package][fedora], courtesy of Eric Smith
|
||||
* a [Gentoo package][gentoo], courtesy of Carlos Silva and Tim Harder
|
||||
* a [Mageia package][mageia], courtesy of David Geiger
|
||||
* an [OpenSUSE rpm][opensuse], courtesy of Mathias Homann
|
||||
|
||||
Solaar uses a standard system tray implementation; solaar-gnome3 is no longer required for gnome or unity integration.
|
||||
|
||||
[ppa4]: https://launchpad.net/~trebelnik-stefina
|
||||
[ppa2]: https://launchpad.net/~solaar-unifying/+archive/ubuntu/stable
|
||||
[ppa1]: https://launchpad.net/~solaar-unifying/+archive/ubuntu/ppa
|
||||
[ppa]: http://launchpad.net/~daniel.pavel/+archive/solaar
|
||||
[arch]: https://www.archlinux.org/packages/community/any/solaar/
|
||||
[fedora]: https://apps.fedoraproject.org/packages/solaar
|
||||
[gentoo]: https://packages.gentoo.org/packages/app-misc/solaar
|
||||
[mageia]: http://mageia.madb.org/package/show/release/cauldron/application/0/name/solaar
|
||||
[opensuse]: http://software.opensuse.org/package/Solaar
|
||||
[universe repository]: http://packages.ubuntu.com/search?keywords=solaar&searchon=names&suite=all§ion=all
|
||||
|
||||
## Manual installation
|
||||
|
||||
See [docs/installation.md](https://pwr-solaar.github.io/Solaar/installation) for the step-by-step procedure for manual installation.
|
||||
|
||||
|
||||
## Known Issues
|
||||
|
||||
- KDE/Kubuntu: if some icons appear broken in the application, make sure you've
|
||||
properly configured the Gtk theme and icon theme in KDE's control panel.
|
||||
|
||||
- Running the command-line application while the GUI
|
||||
application is also running *may* occasionally cause either of them to become
|
||||
confused about the state of the devices.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This software is distributed under the terms of the
|
||||
[GNU Public License, v2](COPYING).
|
||||
|
||||
|
||||
## Thanks
|
||||
|
||||
This project began as a third-hand clone of [Noah K. Tilton](https://github.com/noah)'s
|
||||
logitech-solar-k750 project on GitHub (no longer available). It was developed
|
||||
further thanks to the diggings in Logitech's HID++ protocol done by many other
|
||||
people:
|
||||
|
||||
- [Julien Danjou](http://julien.danjou.info/blog/2012/logitech-k750-linux-support),
|
||||
who also provided some internal
|
||||
[Logitech documentation](http://julien.danjou.info/blog/2012/logitech-unifying-upower)
|
||||
- [Lars-Dominik Braun](http://6xq.net/git/lars/lshidpp.git)
|
||||
- [Alexander Hofbauer](http://derhofbauer.at/blog/blog/2012/08/28/logitech-performance-mx)
|
||||
- [Clach04](http://bitbucket.org/clach04/logitech-unifying-receiver-tools)
|
||||
- [Peter Wu](https://lekensteyn.nl/logitech-unifying.html)
|
||||
- [Nestor Lopez Casado](http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28)
|
||||
provided some more Logitech specifications for the HID++ protocol
|
||||
|
||||
Also thanks to Douglas Wagner, Julien Gascard and Peter Wu for helping with
|
||||
application testing and supporting new devices.
|
||||
106
docs/installation.md
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Manual Installation
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Manual installation
|
||||
|
||||
|
||||
## Downloading
|
||||
|
||||
Clone solaar from GitHub via `git clone https://github.com/pwr-Solaar/Solaar.git`
|
||||
|
||||
|
||||
## Requirements for running Solaar
|
||||
|
||||
Installing Solaar from a repository should have set up all these requirements
|
||||
so in this situation you should be able to skip this section.
|
||||
|
||||
Solaar needs a reasonably new kernel (5.0+ should work fine), with kernel modules `hid-logitech-dj`
|
||||
and `hid-logitech-hidpp` loaded. Also, the `udev` package must be installed
|
||||
and its daemon running. If you have a recent Linux distribution, you are
|
||||
most likely good to go.
|
||||
|
||||
Solaar requires Python 3.2+
|
||||
and the `python3-pyudev` package.
|
||||
To run the GUI, solaar also requires Gtk3, and its GObject
|
||||
introspection bindings.
|
||||
The Debian/Ubuntu packages that need to be installed are
|
||||
`python3-gi` and `gir1.2-gtk-3.0`;
|
||||
in Fedora you need `gtk3` and `python3-gobject`;
|
||||
if you're using another
|
||||
distribution the required packages are most likely named something similar.
|
||||
|
||||
If the desktop notifications bindings are also installed
|
||||
(`gir1.2-notify-0.7` for Debian/Ubuntu),
|
||||
you will also get desktop notifications when devices come online/go offline.
|
||||
For GNOME Shell/Budgie Desktop/KDE/XFCE support, you also need to have
|
||||
`gir1.2-ayatanaappindicator3-0.1` installed in Debian/Ubuntu. Although it is
|
||||
recommended to install and use `gir1.2-ayatanaappindicator3-0.1` if it is
|
||||
available, you can also use `gir1.2-appindicator3-0.1` if necessary (e.g.,
|
||||
for Unity in Ubuntu).
|
||||
|
||||
|
||||
### Installing Solaar's udev Rule
|
||||
|
||||
Solaar needs to write to the receiver's HID device.
|
||||
To be able to do this without running as root requires udev rule
|
||||
that gives seated users write access to the HID devices for Logitech receivers.
|
||||
|
||||
You can install this rule by copying, as root,
|
||||
`rules.d/42-logitech-unify-permissions.rules` from Solaar to
|
||||
`/etc/udev/rules.d`.
|
||||
The udev daemon will automatically pick up this file using inotify.
|
||||
|
||||
For this rule to set up the correct permissions for your receiver
|
||||
you will then need to either physically remove the receiver and
|
||||
re-insert it or reboot your computer.
|
||||
|
||||
|
||||
## Running from the Download Directories
|
||||
|
||||
If Solaar's udev rule is installed,
|
||||
you can just go to the solaar directory and run `bin/solaar` for the GUI
|
||||
or `bin/solaar <command> <arguments>` for the CLI.
|
||||
|
||||
Otherwise you will need to run Solaar as root via
|
||||
`sudo bin/solaar` for the GUI
|
||||
or `sudo bin/solaar <command> <arguments>` for the CLI.
|
||||
|
||||
|
||||
## Installing Solaar
|
||||
|
||||
Python programs are usually installed using [pip][pip].
|
||||
The pip instructions for solaar are in `setup.py`, the standard place to put such instructions.
|
||||
|
||||
To install solaar for yourself only run `pip install --user .` from the solaar directory.
|
||||
This tells pip to install into your `.local` directory, but does not install Solaar's udev rule.
|
||||
(See above for installing the udev rule.)
|
||||
You can then run solaar as `sudo ~/.local/bin/solaar` (or just `~/.local/bin/solaar`
|
||||
if the udev rule has been installed).
|
||||
|
||||
Installing python programs to system directories using pip is generally frowned on both
|
||||
because this runs arbitrary code as root and because this can override existing python libraries
|
||||
that other users or even the system depend on. If you want to install solaar to /usr/local run
|
||||
`sudo bash -c 'umask 022 ; pip install .'` in the solaar directory.
|
||||
(The umask is needed so that the created files and directories can be read and executed by everyone.)
|
||||
This will also install the udev rule and the Solaar autostart desktop file.
|
||||
Then solaar can be run as /usr/local/bin/solaar.
|
||||
|
||||
[pip]: https://en.wikipedia.org/wiki/Pip_(package_manager)
|
||||
|
||||
|
||||
## Running Solaar at Startup
|
||||
|
||||
Solaar is run automatically at user login via the desktop file
|
||||
`/etc/xdg/autostart/solaar.desktop`.
|
||||
|
||||
If you install Solaar yourself you may need to create or modify this file.
|
||||
|
||||
|
||||
## Using PyPI
|
||||
|
||||
As an alternative to downloading and installing you can install the most recent release
|
||||
(but not the current github version) of Solaar from PyPI.
|
||||
Just run `pip install --user solaar` or `sudo pip install solaar`.
|
||||
The `--user` flag will not install the Solaar udev rule or the Solaar autostart file.
|
||||
BIN
docs/logitech/2200_mousepointer.pdf
Normal file
BIN
docs/logitech/6100_touchpadraw.pdf
Normal file
26
docs/logitech/battery-level.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
The battery/charging level and status is reported only if the related
|
||||
reporting flag in register 0x00 is enabled by the host. The
|
||||
"Battery/Charging Level" byte indicates the battery level if the
|
||||
"Charging State" indicates 0x00 ("Not Charging"). If "Charging State"
|
||||
indicates 0x21 to 0x23 ("Charging"), the "Battery/Charging Level" byte
|
||||
indicates the level of charging.
|
||||
|
||||
10 ix 07 r0 r1 r2 00
|
||||
r0 -> Battery/Charging Level
|
||||
0x00 = Reserved/Unknown
|
||||
0x01 = Critical
|
||||
0x02 = Critical (legacy value, don't use)
|
||||
0x03 = Low
|
||||
0x04 = Low (legacy value, don't use)
|
||||
0x05 = Good
|
||||
0x06 = Good (legacy value, don't use)
|
||||
0x07 = Full
|
||||
0x08..0xFF = Reserved
|
||||
r1 -> Charging state
|
||||
0x00 = Not charging
|
||||
0x01..0x1F = Reserved (not charging)
|
||||
0x20 = Unknown charging state
|
||||
0x21 = Charging
|
||||
0x22 = Charging complete
|
||||
0x23 = Charging error
|
||||
0x24..0xFF = Reserved
|
||||
BIN
docs/logitech/performance_mx_led_registers.pdf
Normal file
88
docs/usage.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Solaar Usage
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Solaar usage
|
||||
|
||||
Under normal usage Solaar creates an icon in your system tray. This icon is
|
||||
usually a battery icon showing the approximate battery level for your device
|
||||
with the lowest known battery level. If there is no battery information to
|
||||
show the icon is one of the Solaar icons.
|
||||
|
||||
Solaar also has a main window. To show the main window if it is not visible
|
||||
click on the icon in the system tray to bring up the Solaar menu and then
|
||||
click on a receiver or device in the menu.
|
||||
|
||||
Here is an example of the Solaar icon (in the system tray) and the Solaar menu:
|
||||
|
||||

|
||||
|
||||
Clicking on "Quit" in the Solaar menu terminates Solaar.
|
||||
Clicking on "About Solaar" pops up a window with information on Solaar.
|
||||
|
||||
## Solaar options
|
||||
|
||||
There are several options that affect how Solaar behaves:
|
||||
|
||||
* `--help` shows a help message and then quits
|
||||
* `---window=show` starts Solaar with the main window showing
|
||||
* `---window=hide` starts Solaar with the main window not showing
|
||||
* `---window=only` starts Solaar with no system tray icon and the main window showing
|
||||
* `--battery-icons=symbolic` uses symbolic icons for battery levels
|
||||
|
||||
## Solaar main window
|
||||
|
||||
The Solaar main window shows your Logitech receivers and devices that Solaar
|
||||
knows about and can be used to pair new devices, unpair paired devices, and
|
||||
view and change some settings of the selected receiver or device.
|
||||
To select a receiver or device click on it in the left side of the window.
|
||||
|
||||
Closing the main window does not terminate Solaar (unless Solaar is not using the system tray).
|
||||
Clicking on "Quit Solaar" terminates Solaar.
|
||||
Clicking on "About Solaar" pops up a window with information on Solaar.
|
||||
Clicking on the lightbulb (or similar icon) displays detailed information
|
||||
about the selected receiver or device (mostly only useful for debugging).
|
||||
|
||||
When a receiver is selected in the main window you can pair a new device by
|
||||
clicking on the "Pair new device" button.
|
||||
Then turn on the device and it should pair with the receiver if that is possible.
|
||||
|
||||

|
||||
|
||||
Some receivers can only pair with certain kinds of devices. Some receivers
|
||||
pair a new device by replacing an existing paired device of the same kind.
|
||||
Some receivers can only pair a limited number of times.
|
||||
A receiver with the Unifying logo should be able to pair with any device
|
||||
with the Unifying logo. If there are no open pairing slots, however, you may
|
||||
not be able to pair a new device. In this case to pair a new device you
|
||||
first need to unpair a device.
|
||||
|
||||
When a device is selected you can unpair the device if your receiver supports
|
||||
unpairing. To unpair the device, just click on the "Unpair" button and
|
||||
confirm in the window that pops up.
|
||||
|
||||
When a device is selected you can see the approximate battery level of the
|
||||
device, if that is reported by the device, and the status of the link
|
||||
between the device and its receiver.
|
||||
You can also see some settings of the device and change
|
||||
some of these. Changing settings is performed by clicking on buttons or
|
||||
moving sliders.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
If the selected device is off or otherwise disconnected its settings cannot be changed
|
||||
but it still can be unpaired if its receiver allows unpairing.
|
||||
|
||||

|
||||
|
||||
|
||||
## Solaar command line interface
|
||||
|
||||
Solaar also has a command line interface that can do most of what can be
|
||||
done using the Solaar main window. For more information on the Solaar
|
||||
command line interface, run `solaar --help` to see the Solaar commands and
|
||||
then `solaar <command> --help` to see the arguments to any of the commands.
|
||||
|
||||
14
docs/usb-ids.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Unifying receiver:
|
||||
046d:c52b interface: 2 driver: logitech-djreceiver
|
||||
046d:c532 interface: 2 driver: logitech-djreceiver
|
||||
|
||||
|
||||
Nano receiver, Advanced/Unifying ready:
|
||||
046d:c52f interface: 1 driver: hid-generic
|
||||
|
||||
|
||||
Nano receiver:
|
||||
046d:c51a interface: 1 driver: hid-generic
|
||||
046d:c526 interface: 1 driver: hid-generic
|
||||
046d:c52e interface: 1 driver: hid-generic
|
||||
046d:c531 interface: 1 driver: hid-generic
|
||||
429
docs/usb.ids.txt
Normal file
@@ -0,0 +1,429 @@
|
||||
#
|
||||
# List of USB ID's
|
||||
#
|
||||
# Maintained by Stephen J. Gowdy <linux.usb.ids@gmail.com>
|
||||
# If you have any new entries, please submit them via
|
||||
# http://www.linux-usb.org/usb-ids.html
|
||||
# or send entries as patches (diff -u old new) in the
|
||||
# body of your email (a bot will attempt to deal with it).
|
||||
# The latest version can be obtained from
|
||||
# http://www.linux-usb.org/usb.ids
|
||||
#
|
||||
# Version: 2019.11.05
|
||||
# Date: 2019-11-05 20:34:06
|
||||
#
|
||||
|
||||
# Vendors, devices and interfaces. Please keep sorted.
|
||||
|
||||
# Syntax:
|
||||
# vendor vendor_name
|
||||
# device device_name <-- single tab
|
||||
# interface interface_name <-- two tabs
|
||||
|
||||
046d Logitech, Inc.
|
||||
0082 Acer Aspire 5672 Webcam
|
||||
0200 WingMan Extreme Joystick
|
||||
0203 M2452 Keyboard
|
||||
0242 Chillstream for Xbox 360
|
||||
0301 M4848 Mouse
|
||||
0401 HP PageScan
|
||||
0402 NEC PageScan
|
||||
040f Logitech/Storm PageScan
|
||||
0430 Mic (Cordless)
|
||||
0801 QuickCam Home
|
||||
0802 Webcam C200
|
||||
0804 Webcam C250
|
||||
0805 Webcam C300
|
||||
0807 Webcam B500
|
||||
0808 Webcam C600
|
||||
0809 Webcam Pro 9000
|
||||
080a Portable Webcam C905
|
||||
080f Webcam C120
|
||||
0810 QuickCam Pro
|
||||
0819 Webcam C210
|
||||
081a Webcam C260
|
||||
081b Webcam C310
|
||||
081d HD Webcam C510
|
||||
0820 QuickCam VC
|
||||
0821 HD Webcam C910
|
||||
0823 HD Webcam B910
|
||||
0825 Webcam C270
|
||||
0826 HD Webcam C525
|
||||
0828 HD Webcam B990
|
||||
082b Webcam C170
|
||||
082c HD Webcam C615
|
||||
082d HD Pro Webcam C920
|
||||
0830 QuickClip
|
||||
0836 B525 HD Webcam
|
||||
0837 BCC950 ConferenceCam
|
||||
0840 QuickCam Express
|
||||
0843 Webcam C930e
|
||||
0850 QuickCam Web
|
||||
085c C922 Pro Stream Webcam
|
||||
0870 QuickCam Express
|
||||
0890 QuickCam Traveler
|
||||
0892 OrbiCam
|
||||
0894 CrystalCam
|
||||
0895 QuickCam for Dell Notebooks
|
||||
0896 OrbiCam
|
||||
0897 QuickCam for Dell Notebooks
|
||||
0899 QuickCam for Dell Notebooks
|
||||
089d QuickCam E2500 series
|
||||
08a0 QuickCam IM
|
||||
08a1 QuickCam IM with sound
|
||||
08a2 Labtec Webcam Pro
|
||||
08a3 QuickCam QuickCam Chat
|
||||
08a6 QuickCam IM
|
||||
08a7 QuickCam Image
|
||||
08a9 Notebook Deluxe
|
||||
08aa Labtec Notebooks
|
||||
08ac QuickCam Cool
|
||||
08ad QuickCam Communicate STX
|
||||
08ae QuickCam for Notebooks
|
||||
08af QuickCam Easy/Cool
|
||||
08b0 QuickCam 3000 Pro [pwc]
|
||||
08b1 QuickCam Notebook Pro
|
||||
08b2 QuickCam Pro 4000
|
||||
08b3 QuickCam Zoom
|
||||
08b4 QuickCam Zoom
|
||||
08b5 QuickCam Sphere
|
||||
08b9 QuickCam IM
|
||||
08bd Microphone (Pro 4000)
|
||||
08c0 QuickCam Pro 3000
|
||||
08c1 QuickCam Fusion
|
||||
08c2 QuickCam PTZ
|
||||
08c3 Camera (Notebooks Pro)
|
||||
08c5 QuickCam Pro 5000
|
||||
08c6 QuickCam for DELL Notebooks
|
||||
08c7 QuickCam OEM Cisco VT Camera II
|
||||
08c9 QuickCam Ultra Vision
|
||||
08ca Mic (Fusion)
|
||||
08cb Mic (Notebooks Pro)
|
||||
08cc Mic (PTZ)
|
||||
08ce QuickCam Pro 5000
|
||||
08cf QuickCam UpdateMe
|
||||
08d0 QuickCam Express
|
||||
08d7 QuickCam Communicate STX
|
||||
08d8 QuickCam for Notebook Deluxe
|
||||
08d9 QuickCam IM/Connect
|
||||
08da QuickCam Messenger
|
||||
08dd QuickCam for Notebooks
|
||||
08e0 QuickCam Express
|
||||
08e1 Labtec Webcam
|
||||
08f0 QuickCam Messenger
|
||||
08f1 QuickCam Express
|
||||
08f2 Microphone (Messenger)
|
||||
08f3 QuickCam Express
|
||||
08f4 Labtec Webcam
|
||||
08f5 QuickCam Messenger Communicate
|
||||
08f6 QuickCam Messenger Plus
|
||||
0900 ClickSmart 310
|
||||
0901 ClickSmart 510
|
||||
0903 ClickSmart 820
|
||||
0905 ClickSmart 820
|
||||
0910 QuickCam Cordless
|
||||
0920 QuickCam Express
|
||||
0921 Labtec Webcam
|
||||
0922 QuickCam Live
|
||||
0928 QuickCam Express
|
||||
0929 Labtec Webcam Pro
|
||||
092a QuickCam for Notebooks
|
||||
092b Labtec Webcam Plus
|
||||
092c QuickCam Chat
|
||||
092d QuickCam Express / Go
|
||||
092e QuickCam Chat
|
||||
092f QuickCam Express Plus
|
||||
0950 Pocket Camera
|
||||
0960 ClickSmart 420
|
||||
0970 Pocket750
|
||||
0990 QuickCam Pro 9000
|
||||
0991 QuickCam Pro for Notebooks
|
||||
0992 QuickCam Communicate Deluxe
|
||||
0994 QuickCam Orbit/Sphere AF
|
||||
09a1 QuickCam Communicate MP/S5500
|
||||
09a2 QuickCam Communicate Deluxe/S7500
|
||||
09a4 QuickCam E 3500
|
||||
09a5 Quickcam 3000 For Business
|
||||
09a6 QuickCam Vision Pro
|
||||
09b0 Acer OrbiCam
|
||||
09b2 Fujitsu Webcam
|
||||
09c0 QuickCam for Dell Notebooks Mic
|
||||
09c1 QuickCam Deluxe for Notebooks
|
||||
0a01 USB Headset
|
||||
0a02 Premium Stereo USB Headset 350
|
||||
0a03 Logitech USB Microphone
|
||||
0a04 V20 portable speakers (USB powered)
|
||||
0a07 Z-10 Speakers
|
||||
0a0b ClearChat Pro USB
|
||||
0a0c Clear Chat Comfort USB Headset
|
||||
0a13 Z-5 Speakers
|
||||
0a14 USB Headset
|
||||
0a15 G35 Headset
|
||||
0a17 G330 Headset
|
||||
0a1f G930
|
||||
0a29 H600 [Wireless Headset]
|
||||
0a37 USB Headset H540
|
||||
0a38 Headset H340
|
||||
0a44 Headset H390
|
||||
0a45 960 Headset
|
||||
0a4d G430 Surround Sound Gaming Headset
|
||||
0a5b G933 Wireless Headset Dongle
|
||||
0a66 [G533 Wireless Headset Dongle]
|
||||
0b02 C-UV35 [Bluetooth Mini-Receiver] (HID proxy mode)
|
||||
8801 Video Camera
|
||||
b014 Bluetooth Mouse M336/M337/M535
|
||||
b305 BT Mini-Receiver
|
||||
bfe4 Premium Optical Wheel Mouse
|
||||
c000 N43 [Pilot Mouse]
|
||||
c001 N48/M-BB48/M-UK96A [FirstMouse Plus]
|
||||
c002 M-BA47 [MouseMan Plus]
|
||||
c003 MouseMan
|
||||
c004 WingMan Gaming Mouse
|
||||
c005 WingMan Gaming Wheel Mouse
|
||||
c00b MouseMan Wheel
|
||||
c00c Optical Wheel Mouse
|
||||
c00d MouseMan Wheel+
|
||||
c00e M-BJ58/M-BJ69 Optical Wheel Mouse
|
||||
c00f MouseMan Traveler/Mobile
|
||||
c011 Optical MouseMan
|
||||
c012 Mouseman Dual Optical
|
||||
c014 Corded Workstation Mouse
|
||||
c015 Corded Workstation Mouse
|
||||
c016 Optical Wheel Mouse
|
||||
c018 Optical Wheel Mouse
|
||||
c019 Optical Tilt Wheel Mouse
|
||||
c01a M-BQ85 Optical Wheel Mouse
|
||||
c01b MX310 Optical Mouse
|
||||
c01c Optical Mouse
|
||||
c01d MX510 Optical Mouse
|
||||
c01e MX518 Optical Mouse
|
||||
c024 MX300 Optical Mouse
|
||||
c025 MX500 Optical Mouse
|
||||
c030 iFeel Mouse
|
||||
c031 iFeel Mouse+
|
||||
c032 MouseMan iFeel
|
||||
c033 iFeel MouseMan+
|
||||
c034 MouseMan Optical
|
||||
c035 Mouse
|
||||
c036 Mouse
|
||||
c037 Mouse
|
||||
c038 Mouse
|
||||
c03d M-BT96a Pilot Optical Mouse
|
||||
c03e Premium Optical Wheel Mouse (M-BT58)
|
||||
c03f M-BT85 [UltraX Optical Mouse]
|
||||
c040 Corded Tilt-Wheel Mouse
|
||||
c041 G5 Laser Mouse
|
||||
c042 G3 Laser Mouse
|
||||
c043 MX320/MX400 Laser Mouse
|
||||
c044 LX3 Optical Mouse
|
||||
c045 Optical Mouse
|
||||
c046 RX1000 Laser Mouse
|
||||
c047 Laser Mouse M-UAL120
|
||||
c048 G9 Laser Mouse
|
||||
c049 G5 Laser Mouse
|
||||
c050 RX 250 Optical Mouse
|
||||
c051 G3 (MX518) Optical Mouse
|
||||
c053 Laser Mouse
|
||||
c054 Bluetooth mini-receiver
|
||||
c058 M115 Mouse
|
||||
c05a M90/M100 Optical Mouse
|
||||
c05b M-U0004 810-001317 [B110 Optical USB Mouse]
|
||||
c05d Optical Mouse
|
||||
c05f M115 Optical Mouse
|
||||
c061 RX1500 Laser Mouse
|
||||
c062 M-UAS144 [LS1 Laser Mouse]
|
||||
c063 DELL Laser Mouse
|
||||
c064 M110 corded optical mouse (M-B0001)
|
||||
c066 G9x Laser Mouse
|
||||
c068 G500 Laser Mouse
|
||||
c069 M-U0007 [Corded Mouse M500]
|
||||
c06a USB Optical Mouse
|
||||
c06b G700 Wireless Gaming Mouse
|
||||
c06c Optical Mouse
|
||||
c077 M105 Optical Mouse
|
||||
c07c M-R0017 [G700s Rechargeable Gaming Mouse]
|
||||
c07d G502 Mouse
|
||||
c07e G402 Gaming Mouse
|
||||
c080 G303 Gaming Mouse
|
||||
c083 G403 Prodigy Gaming Mouse
|
||||
c084 G203 Gaming Mouse
|
||||
c101 UltraX Media Remote
|
||||
c110 Harmony 785/880/885 Remote
|
||||
c111 Harmony 525 Remote
|
||||
c112 Harmony 890 Remote
|
||||
c11f Harmony 900/1100 Remote
|
||||
c121 Harmony One Remote
|
||||
c122 Harmony 650/700 Remote
|
||||
c124 Harmony 300/350 Remote
|
||||
c125 Harmony 200 Remote
|
||||
c126 Harmony Link
|
||||
c129 Harmony Hub
|
||||
c12b Harmony Touch/Ultimate Remote
|
||||
c201 WingMan Extreme Joystick with Throttle
|
||||
c202 WingMan Formula
|
||||
c207 WingMan Extreme Digital 3D
|
||||
c208 WingMan Gamepad Extreme
|
||||
c209 WingMan Gamepad
|
||||
c20a WingMan RumblePad
|
||||
c20b WingMan Action Pad
|
||||
c20c WingMan Precision
|
||||
c20d WingMan Attack 2
|
||||
c20e WingMan Formula GP
|
||||
c211 iTouch Cordless Receiver
|
||||
c212 WingMan Extreme Digital 3D
|
||||
c213 J-UH16 (Freedom 2.4 Cordless Joystick)
|
||||
c214 ATK3 (Attack III Joystick)
|
||||
c215 Extreme 3D Pro
|
||||
c216 F310 Gamepad [DirectInput Mode]
|
||||
c218 F510 Gamepad [DirectInput Mode]
|
||||
c219 F710 Gamepad [DirectInput Mode]
|
||||
c21a Precision Gamepad
|
||||
c21c G13 Advanced Gameboard
|
||||
c21d F310 Gamepad [XInput Mode]
|
||||
c21e F510 Gamepad [XInput Mode]
|
||||
c21f F710 Wireless Gamepad [XInput Mode]
|
||||
c221 G11/G15 Keyboard / Keyboard
|
||||
c222 G15 Keyboard / LCD
|
||||
c223 G11/G15 Keyboard / USB Hub
|
||||
c225 G11/G15 Keyboard / G keys
|
||||
c226 G15 Refresh Keyboard
|
||||
c227 G15 Refresh Keyboard
|
||||
c228 G19 Gaming Keyboard
|
||||
c229 G19 Gaming Keyboard Macro Interface
|
||||
c22a Gaming Keyboard G110
|
||||
c22b Gaming Keyboard G110 G-keys
|
||||
c22d G510 Gaming Keyboard
|
||||
c22e G510 Gaming Keyboard onboard audio
|
||||
c231 G13 Virtual Mouse
|
||||
c245 G400 Optical Mouse
|
||||
c246 Gaming Mouse G300
|
||||
c248 G105 Gaming Keyboard
|
||||
c24a G600 Gaming Mouse
|
||||
c24c G400s Optical Mouse
|
||||
c24d G710 Gaming Keyboard
|
||||
c24e G500s Laser Gaming Mouse
|
||||
c281 WingMan Force
|
||||
c283 WingMan Force 3D
|
||||
c285 WingMan Strike Force 3D
|
||||
c286 Force 3D Pro
|
||||
c287 Flight System G940
|
||||
c291 WingMan Formula Force
|
||||
c293 WingMan Formula Force GP
|
||||
c294 Driving Force
|
||||
c295 Momo Force Steering Wheel
|
||||
c298 Driving Force Pro
|
||||
c299 G25 Racing Wheel
|
||||
c29b G27 Racing Wheel
|
||||
c29c Speed Force Wireless Wheel for Wii
|
||||
c2a0 Wingman Force Feedback Mouse
|
||||
c2a1 WingMan Force Feedback Mouse
|
||||
c2ab G13 Joystick
|
||||
c301 iTouch Keyboard
|
||||
c302 iTouch Pro Keyboard
|
||||
c303 iTouch Keyboard
|
||||
c305 Internet Keyboard
|
||||
c307 Internet Keyboard
|
||||
c308 Internet Navigator Keyboard
|
||||
c309 Y-BF37 [Internet Navigator Keyboard]
|
||||
c30a iTouch Composite
|
||||
c30b NetPlay Keyboard
|
||||
c30c Internet Keys (X)
|
||||
c30d Internet Keys
|
||||
c30e UltraX Keyboard (Y-BL49)
|
||||
c30f Logicool HID-Compliant Keyboard (106 key)
|
||||
c311 Y-UF49 [Internet Pro Keyboard]
|
||||
c312 DeLuxe 250 Keyboard
|
||||
c313 Internet 350 Keyboard
|
||||
c315 Classic Keyboard 200
|
||||
c316 HID-Compliant Keyboard
|
||||
c317 Wave Corded Keyboard
|
||||
c318 Illuminated Keyboard
|
||||
c31a Comfort Wave 450
|
||||
c31b Compact Keyboard K300
|
||||
c31c Keyboard K120
|
||||
c31d Media Keyboard K200
|
||||
c31f Comfort Keyboard K290
|
||||
c326 Washable Keyboard K310
|
||||
c328 Corded Keyboard K280e
|
||||
c332 G502 Proteus Spectrum Optical Mouse
|
||||
c335 G910 Orion Spectrum Mechanical Keyboard
|
||||
c33a G413 Gaming Keyboard
|
||||
c401 TrackMan Marble Wheel
|
||||
c402 Marble Mouse (2-button)
|
||||
c403 Turbo TrackMan Marble FX
|
||||
c404 TrackMan Wheel
|
||||
c408 Marble Mouse (4-button)
|
||||
c501 Cordless Mouse Receiver
|
||||
c502 Cordless Mouse & iTouch Keys
|
||||
c503 Cordless Mouse+Keyboard Receiver
|
||||
c504 Cordless Mouse+Keyboard Receiver
|
||||
c505 Cordless Mouse+Keyboard Receiver
|
||||
c506 MX700 Cordless Mouse Receiver
|
||||
c508 Cordless Trackball
|
||||
c509 Cordless Keyboard & Mouse
|
||||
c50a Cordless Mouse
|
||||
c50b Cordless Desktop Optical
|
||||
c50c Cordless Desktop S510
|
||||
c50d Cordless Mouse
|
||||
c50e Cordless Mouse Receiver
|
||||
c510 Cordless Mouse
|
||||
c512 LX-700 Cordless Desktop Receiver
|
||||
c513 MX3000 Cordless Desktop Receiver
|
||||
c514 Cordless Mouse
|
||||
c515 Cordless 2.4 GHz Presenter Presentation remote control
|
||||
c517 LX710 Cordless Desktop Laser
|
||||
c518 MX610 Laser Cordless Mouse
|
||||
c51a MX Revolution/G7 Cordless Mouse
|
||||
c51b V220 Cordless Optical Mouse for Notebooks
|
||||
c521 Cordless Mouse Receiver
|
||||
c525 MX Revolution Cordless Mouse
|
||||
c526 Nano Receiver
|
||||
c529 Logitech Keyboard + Mice
|
||||
c52b Unifying Receiver
|
||||
c52d R700 Remote Presenter receiver
|
||||
c52e MK260 Wireless Combo Receiver
|
||||
c52f Unifying Receiver
|
||||
c531 C-U0007 [Unifying Receiver]
|
||||
c532 Unifying Receiver
|
||||
c534 Unifying Receiver
|
||||
c603 3Dconnexion Spacemouse Plus XT
|
||||
c605 3Dconnexion CADman
|
||||
c606 3Dconnexion Spacemouse Classic
|
||||
c621 3Dconnexion Spaceball 5000
|
||||
c623 3Dconnexion Space Traveller 3D Mouse
|
||||
c625 3Dconnexion Space Pilot 3D Mouse
|
||||
c626 3Dconnexion Space Navigator 3D Mouse
|
||||
c627 3Dconnexion Space Explorer 3D Mouse
|
||||
c628 3Dconnexion Space Navigator for Notebooks
|
||||
c629 3Dconnexion SpacePilot Pro 3D Mouse
|
||||
c62b 3Dconnexion Space Mouse Pro
|
||||
c640 NuLOOQ navigator
|
||||
c702 Cordless Presenter
|
||||
c703 Elite Keyboard Y-RP20 + Mouse MX900 (Bluetooth)
|
||||
c704 diNovo Wireless Desktop
|
||||
c705 MX900 Bluetooth Wireless Hub (C-UJ16A)
|
||||
c707 Bluetooth wireless hub
|
||||
c708 Bluetooth wireless hub
|
||||
c709 BT Mini-Receiver (HCI mode)
|
||||
c70a MX5000 Cordless Desktop
|
||||
c70b BT Mini-Receiver (HID proxy mode)
|
||||
c70c BT Mini-Receiver (HID proxy mode)
|
||||
c70d Bluetooth wireless hub
|
||||
c70e MX1000 Bluetooth Laser Mouse
|
||||
c70f Bluetooth wireless hub
|
||||
c712 Bluetooth wireless hub
|
||||
c714 diNovo Edge Keyboard
|
||||
c715 Bluetooth wireless hub
|
||||
c71a Bluetooth wireless hub
|
||||
c71d Bluetooth wireless hub
|
||||
c71f diNovo Mini Wireless Keyboard
|
||||
c720 Bluetooth wireless hub
|
||||
ca03 MOMO Racing
|
||||
ca04 Formula Vibration Feedback Wheel
|
||||
ca84 Cordless Controller for Xbox
|
||||
ca88 Thunderpad for Xbox
|
||||
ca8a Precision Vibration Feedback Wheel for Xbox
|
||||
caa3 DriveFX Racing Wheel
|
||||
cab1 Cordless Keyboard for Wii HID Receiver
|
||||
d001 QuickCam Pro
|
||||
f301 Controller
|
||||
BIN
jekyll/images/bg_hr.png
Normal file
|
After Width: | Height: | Size: 96 B |
BIN
jekyll/images/blacktocat.png
Normal file
|
After Width: | Height: | Size: 432 B |
BIN
jekyll/images/icon_download.png
Normal file
|
After Width: | Height: | Size: 230 B |
BIN
jekyll/images/sprite_download.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
@@ -1,9 +1,37 @@
|
||||
# -*- python-mode -*-
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""Generic Human Interface Device API."""
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
__author__ = "Daniel Pavel"
|
||||
__license__ = "GPL"
|
||||
__version__ = "0.5"
|
||||
__version__ = '0.9'
|
||||
|
||||
from hidapi.udev import *
|
||||
from hidapi.udev import (
|
||||
enumerate,
|
||||
open,
|
||||
close,
|
||||
open_path,
|
||||
monitor_glib,
|
||||
read,
|
||||
write,
|
||||
get_manufacturer,
|
||||
get_product,
|
||||
get_serial,
|
||||
)
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
#!/usr/bin/env python -u
|
||||
# -*- python-mode -*-
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
@@ -7,20 +24,36 @@ import sys
|
||||
from select import select as _select
|
||||
import time
|
||||
from binascii import hexlify, unhexlify
|
||||
import hidapi as _hid
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
try:
|
||||
read_packet = raw_input
|
||||
except NameError:
|
||||
# Python 3 equivalent of raw_input
|
||||
read_packet = input
|
||||
|
||||
interactive = os.isatty(0)
|
||||
start_time = 0
|
||||
try: # python3 support
|
||||
read_packet = raw_input
|
||||
except:
|
||||
read_packet = input
|
||||
prompt = '?? Input: ' if interactive else ''
|
||||
start_time = time.time()
|
||||
|
||||
strhex = lambda d: hexlify(d).decode('ascii').upper()
|
||||
try:
|
||||
unicode
|
||||
# this is certanly Python 2
|
||||
is_string = lambda d: isinstance(d, unicode)
|
||||
# no easy way to distinguish between b'' and '' :(
|
||||
# or (isinstance(d, str) \
|
||||
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
||||
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
||||
# )
|
||||
except:
|
||||
# this is certanly Python 3
|
||||
# In Py3, unicode and str are equal (the unicode object does not exist)
|
||||
is_string = lambda d: isinstance(d, str)
|
||||
|
||||
#
|
||||
#
|
||||
@@ -28,48 +61,54 @@ strhex = lambda d: hexlify(d).decode('ascii').upper()
|
||||
|
||||
from threading import Lock
|
||||
print_lock = Lock()
|
||||
del Lock
|
||||
|
||||
def _print(marker, data, scroll=False):
|
||||
t = time.time() - start_time
|
||||
if type(data) == unicode:
|
||||
if is_string(data):
|
||||
s = marker + ' ' + data
|
||||
else:
|
||||
hexs = strhex(data)
|
||||
s = '%s (% 8.3f) [%s %s %s %s] %s' % (marker, t, hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:], repr(data))
|
||||
|
||||
print_lock.acquire()
|
||||
with print_lock:
|
||||
# allow only one thread at a time to write to the console, otherwise
|
||||
# the output gets garbled, especially with ANSI codes.
|
||||
|
||||
if interactive and scroll:
|
||||
# scroll the entire screen above the current line up by 1 line
|
||||
sys.stdout.write('\033[s' # save cursor position
|
||||
'\033[S' # scroll up
|
||||
'\033[A' # cursor up
|
||||
'\033[L' # insert 1 line
|
||||
'\033[G') # move cursor to column 1
|
||||
sys.stdout.write(s)
|
||||
if interactive and scroll:
|
||||
# restore cursor position
|
||||
sys.stdout.write('\033[u')
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
if interactive and scroll:
|
||||
# scroll the entire screen above the current line up by 1 line
|
||||
sys.stdout.write('\033[s' # save cursor position
|
||||
'\033[S' # scroll up
|
||||
'\033[A' # cursor up
|
||||
'\033[L' # insert 1 line
|
||||
'\033[G') # move cursor to column 1
|
||||
sys.stdout.write(s)
|
||||
if interactive and scroll:
|
||||
# restore cursor position
|
||||
sys.stdout.write('\033[u')
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
|
||||
print_lock.release()
|
||||
# flush stdout manually...
|
||||
# because trying to open stdin/out unbuffered programmatically
|
||||
# works much too differently in Python 2/3
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def _error(text, scroll=False):
|
||||
_print("!!", text, scroll)
|
||||
_print('!!', text, scroll)
|
||||
|
||||
|
||||
def _continuous_read(handle, timeout=2000):
|
||||
while True:
|
||||
try:
|
||||
reply = hidapi.read(handle, 128, timeout)
|
||||
reply = _hid.read(handle, 128, timeout)
|
||||
except OSError as e:
|
||||
_error("Read failed, aborting: " + str(e), True)
|
||||
break
|
||||
assert reply is not None
|
||||
if reply:
|
||||
_print(">>", reply, True)
|
||||
_print('>>', reply, True)
|
||||
|
||||
|
||||
def _validate_input(line, hidpp=False):
|
||||
@@ -104,9 +143,11 @@ def _validate_input(line, hidpp=False):
|
||||
|
||||
return data
|
||||
|
||||
def _open(device, hidpp):
|
||||
if hidpp and not device:
|
||||
for d in hidapi.enumerate(vendor_id=0x046d):
|
||||
|
||||
def _open(args):
|
||||
device = args.device
|
||||
if args.hidpp and not device:
|
||||
for d in _hid.enumerate(vendor_id=0x046d):
|
||||
if d.driver == 'logitech-djreceiver':
|
||||
device = d.path
|
||||
break
|
||||
@@ -116,19 +157,24 @@ def _open(device, hidpp):
|
||||
sys.exit("!! Device path required.")
|
||||
|
||||
print (".. Opening device", device)
|
||||
handle = hidapi.open_path(device)
|
||||
handle = _hid.open_path(device)
|
||||
if not handle:
|
||||
sys.exit("!! Failed to open %s, aborting." % device)
|
||||
|
||||
print (".. Opened handle %s, vendor %s product %s serial %s." % (
|
||||
repr(handle),
|
||||
repr(hidapi.get_manufacturer(handle)),
|
||||
repr(hidapi.get_product(handle)),
|
||||
repr(hidapi.get_serial(handle))))
|
||||
print (".. Opened handle %r, vendor %r product %r serial %r." % (
|
||||
handle,
|
||||
_hid.get_manufacturer(handle),
|
||||
_hid.get_product(handle),
|
||||
_hid.get_serial(handle)))
|
||||
if args.hidpp:
|
||||
if hidapi.get_manufacturer(handle) != 'Logitech':
|
||||
if _hid.get_manufacturer(handle) != b'Logitech':
|
||||
sys.exit("!! Only Logitech devices support the HID++ protocol.")
|
||||
print (".. HID++ validation enabled.")
|
||||
else:
|
||||
if (_hid.get_manufacturer(handle) == b'Logitech' and
|
||||
b'Receiver' in _hid.get_product(handle)):
|
||||
args.hidpp = True
|
||||
print (".. Logitech receiver detected, HID++ validation enabled.")
|
||||
|
||||
return handle
|
||||
|
||||
@@ -136,17 +182,19 @@ def _open(device, hidpp):
|
||||
#
|
||||
#
|
||||
|
||||
if __name__ == '__main__':
|
||||
def _parse_arguments():
|
||||
import argparse
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('--history', help='history file (default ~/.hidconsole-history)')
|
||||
arg_parser.add_argument('--hidpp', action='store_true', help='ensure input data is a valid HID++ request')
|
||||
arg_parser.add_argument('device', nargs='?', help='linux device to connect to (/dev/hidrawX); '
|
||||
'may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver')
|
||||
args = arg_parser.parse_args()
|
||||
arg_parser.add_argument('--history', help="history file (default ~/.hidconsole-history)")
|
||||
arg_parser.add_argument('--hidpp', action='store_true', help="ensure input data is a valid HID++ request")
|
||||
arg_parser.add_argument('device', nargs='?', help="linux device to connect to (/dev/hidrawX); "
|
||||
"may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver")
|
||||
return arg_parser.parse_args()
|
||||
|
||||
import hidapi
|
||||
handle = _open(args.device, args.hidpp)
|
||||
|
||||
def main():
|
||||
args = _parse_arguments()
|
||||
handle = _open(args)
|
||||
|
||||
if interactive:
|
||||
print (".. Press ^C/^D to exit, or type hex bytes to write to the device.")
|
||||
@@ -154,15 +202,13 @@ if __name__ == '__main__':
|
||||
import readline
|
||||
if args.history is None:
|
||||
import os.path
|
||||
args.history = os.path.join(os.path.expanduser("~"), ".hidconsole-history")
|
||||
args.history = os.path.join(os.path.expanduser('~'), '.hidconsole-history')
|
||||
try:
|
||||
readline.read_history_file(args.history)
|
||||
except:
|
||||
# file may not exist yet
|
||||
pass
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
from threading import Thread
|
||||
t = Thread(target=_continuous_read, args=(handle,))
|
||||
@@ -176,6 +222,7 @@ if __name__ == '__main__':
|
||||
while t.is_alive():
|
||||
line = read_packet(prompt)
|
||||
line = line.strip().replace(' ', '')
|
||||
# print ("line", line)
|
||||
if not line:
|
||||
continue
|
||||
|
||||
@@ -183,24 +230,29 @@ if __name__ == '__main__':
|
||||
if data is None:
|
||||
continue
|
||||
|
||||
_print("<<", data)
|
||||
hidapi.write(handle, data)
|
||||
_print('<<', data)
|
||||
_hid.write(handle, data)
|
||||
# wait for some kind of reply
|
||||
if args.hidpp and not interactive:
|
||||
rlist, wlist, xlist = _select([handle], [], [], 1)
|
||||
if data[1:2] == b'\xFF':
|
||||
# the receiver will reply very fast, in a few milliseconds
|
||||
time.sleep(0.010)
|
||||
else:
|
||||
# the devices might reply quite slow
|
||||
rlist, wlist, xlist = _select([handle], [], [], 1)
|
||||
time.sleep(1)
|
||||
time.sleep(0.700)
|
||||
except EOFError:
|
||||
if interactive:
|
||||
print ("")
|
||||
except Exception as e:
|
||||
print ('%s: %s' % (type(e).__name__, e))
|
||||
else:
|
||||
time.sleep(1)
|
||||
|
||||
print (".. Closing handle", repr(handle))
|
||||
hidapi.close(handle)
|
||||
if interactive:
|
||||
readline.write_history_file(args.history)
|
||||
finally:
|
||||
print (".. Closing handle %r" % handle)
|
||||
_hid.close(handle)
|
||||
if interactive:
|
||||
readline.write_history_file(args.history)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,384 +0,0 @@
|
||||
"""Generic Human Interface Device API.
|
||||
|
||||
It is little more than a thin ctypes layer over a native hidapi implementation.
|
||||
The docstrings are mostly copied from the hidapi API header, with changes where
|
||||
necessary.
|
||||
|
||||
The native HID API implemenation is available at
|
||||
https://github.com/signal11/hidapi.
|
||||
|
||||
The native implementation comes in two flavors, hidraw (``libhidapi-hidraw.so``)
|
||||
and libusb (``libhidapi-libusb.so``). For this API to work, at least one of them
|
||||
must be in ``LD_LIBRARY_PATH``; otherwise an ImportError will be raised.
|
||||
|
||||
Using the native hidraw implementation is recommended.
|
||||
Currently the native libusb implementation (temporarily) detaches the device's
|
||||
USB driver from the kernel, and it may cause the device to become unresponsive.
|
||||
"""
|
||||
|
||||
#
|
||||
# LEGACY, no longer supported
|
||||
#
|
||||
|
||||
__version__ = '0.3-hidapi-0.7.0'
|
||||
|
||||
|
||||
import ctypes as _C
|
||||
from struct import pack as _pack
|
||||
|
||||
|
||||
#
|
||||
# look for a native implementation in the same directory as this file
|
||||
#
|
||||
|
||||
# The CDLL native library object.
|
||||
_native = None
|
||||
|
||||
for native_implementation in ('hidraw', 'libusb'):
|
||||
try:
|
||||
_native = _C.cdll.LoadLibrary('libhidapi-' + native_implementation + '.so')
|
||||
break
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if _native is None:
|
||||
raise ImportError('hidapi: failed to load any HID API native implementation')
|
||||
|
||||
|
||||
#
|
||||
# Structures used by this API.
|
||||
#
|
||||
|
||||
|
||||
# used by the native implementation when enumerating, no need to expose it
|
||||
class _NativeDeviceInfo(_C.Structure):
|
||||
pass
|
||||
_NativeDeviceInfo._fields_ = [
|
||||
('path', _C.c_char_p),
|
||||
('vendor_id', _C.c_ushort),
|
||||
('product_id', _C.c_ushort),
|
||||
('serial', _C.c_wchar_p),
|
||||
('release', _C.c_ushort),
|
||||
('manufacturer', _C.c_wchar_p),
|
||||
('product', _C.c_wchar_p),
|
||||
('usage_page', _C.c_ushort),
|
||||
('usage', _C.c_ushort),
|
||||
('interface', _C.c_int),
|
||||
('next_device', _C.POINTER(_NativeDeviceInfo))
|
||||
]
|
||||
|
||||
|
||||
# the tuple object we'll expose when enumerating devices
|
||||
from collections import namedtuple
|
||||
DeviceInfo = namedtuple('DeviceInfo', [
|
||||
'path',
|
||||
'vendor_id',
|
||||
'product_id',
|
||||
'serial',
|
||||
'release',
|
||||
'manufacturer',
|
||||
'product',
|
||||
'interface',
|
||||
'driver',
|
||||
])
|
||||
del namedtuple
|
||||
|
||||
|
||||
# create a DeviceInfo tuple from a hid_device object
|
||||
def _makeDeviceInfo(native_device_info):
|
||||
return DeviceInfo(
|
||||
path=native_device_info.path.decode('ascii'),
|
||||
vendor_id=hex(native_device_info.vendor_id)[2:].zfill(4),
|
||||
product_id=hex(native_device_info.product_id)[2:].zfill(4),
|
||||
serial=native_device_info.serial if native_device_info.serial else None,
|
||||
release=hex(native_device_info.release)[2:],
|
||||
manufacturer=native_device_info.manufacturer,
|
||||
product=native_device_info.product,
|
||||
interface=native_device_info.interface,
|
||||
driver=None)
|
||||
|
||||
|
||||
#
|
||||
# set-up arguments and return types for each hidapi function
|
||||
#
|
||||
|
||||
_native.hid_init.argtypes = None
|
||||
_native.hid_init.restype = _C.c_int
|
||||
|
||||
_native.hid_exit.argtypes = None
|
||||
_native.hid_exit.restype = _C.c_int
|
||||
|
||||
_native.hid_enumerate.argtypes = [_C.c_ushort, _C.c_ushort]
|
||||
_native.hid_enumerate.restype = _C.POINTER(_NativeDeviceInfo)
|
||||
|
||||
_native.hid_free_enumeration.argtypes = [_C.POINTER(_NativeDeviceInfo)]
|
||||
_native.hid_free_enumeration.restype = None
|
||||
|
||||
_native.hid_open.argtypes = [_C.c_ushort, _C.c_ushort, _C.c_wchar_p]
|
||||
_native.hid_open.restype = _C.c_void_p
|
||||
|
||||
_native.hid_open_path.argtypes = [_C.c_char_p]
|
||||
_native.hid_open_path.restype = _C.c_void_p
|
||||
|
||||
_native.hid_close.argtypes = [_C.c_void_p]
|
||||
_native.hid_close.restype = None
|
||||
|
||||
_native.hid_write.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
|
||||
_native.hid_write.restype = _C.c_int
|
||||
|
||||
_native.hid_read.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
|
||||
_native.hid_read.restype = _C.c_int
|
||||
|
||||
_native.hid_read_timeout.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t, _C.c_int]
|
||||
_native.hid_read_timeout.restype = _C.c_int
|
||||
|
||||
_native.hid_set_nonblocking.argtypes = [_C.c_void_p, _C.c_int]
|
||||
_native.hid_set_nonblocking.restype = _C.c_int
|
||||
|
||||
_native.hid_send_feature_report.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
|
||||
_native.hid_send_feature_report.restype = _C.c_int
|
||||
|
||||
_native.hid_get_feature_report.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
|
||||
_native.hid_get_feature_report.restype = _C.c_int
|
||||
|
||||
_native.hid_get_manufacturer_string.argtypes = [_C.c_void_p, _C.c_wchar_p, _C.c_size_t]
|
||||
_native.hid_get_manufacturer_string.restype = _C.c_int
|
||||
|
||||
_native.hid_get_product_string.argtypes = [_C.c_void_p, _C.c_wchar_p, _C.c_size_t]
|
||||
_native.hid_get_product_string.restype = _C.c_int
|
||||
|
||||
_native.hid_get_serial_number_string.argtypes = [_C.c_void_p, _C.c_wchar_p, _C.c_size_t]
|
||||
_native.hid_get_serial_number_string.restype = _C.c_int
|
||||
|
||||
_native.hid_get_indexed_string.argtypes = [_C.c_void_p, _C.c_int, _C.c_wchar_p, _C.c_size_t]
|
||||
_native.hid_get_indexed_string.restype = _C.c_int
|
||||
|
||||
_native.hid_error.argtypes = [_C.c_void_p]
|
||||
_native.hid_error.restype = _C.c_wchar_p
|
||||
|
||||
|
||||
#
|
||||
# exposed API
|
||||
# docstrings mostly copied from hidapi.h
|
||||
#
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize the HIDAPI library.
|
||||
|
||||
This function initializes the HIDAPI library. Calling it is not strictly
|
||||
necessary, as it will be called automatically by enumerate() and any of the
|
||||
open_*() functions if it is needed. This function should be called at the
|
||||
beginning of execution however, if there is a chance of HIDAPI handles
|
||||
being opened by different threads simultaneously.
|
||||
|
||||
:returns: ``True`` if successful.
|
||||
"""
|
||||
return _native.hid_init() == 0
|
||||
|
||||
|
||||
def exit():
|
||||
"""Finalize the HIDAPI library.
|
||||
|
||||
This function frees all of the static data associated with HIDAPI. It should
|
||||
be called at the end of execution to avoid memory leaks.
|
||||
|
||||
:returns: ``True`` if successful.
|
||||
"""
|
||||
return _native.hid_exit() == 0
|
||||
|
||||
|
||||
def enumerate(vendor_id=None, product_id=None, interface_number=None):
|
||||
"""Enumerate the HID Devices.
|
||||
|
||||
List all the HID devices attached to the system, optionally filtering by
|
||||
vendor_id, product_id, and/or interface_number.
|
||||
|
||||
:returns: an iterable of matching ``DeviceInfo`` tuples.
|
||||
"""
|
||||
|
||||
devices = _native.hid_enumerate(vendor_id, product_id)
|
||||
d = devices
|
||||
while d:
|
||||
if interface_number is None or interface_number == d.contents.interface:
|
||||
yield _makeDeviceInfo(d.contents)
|
||||
d = d.contents.next_device
|
||||
|
||||
if devices:
|
||||
_native.hid_free_enumeration(devices)
|
||||
|
||||
|
||||
def open(vendor_id, product_id, serial=None):
|
||||
"""Open a HID device by its Vendor ID, Product ID and optional serial number.
|
||||
|
||||
If no serial is provided, the first device with the specified IDs is opened.
|
||||
|
||||
:returns: an opaque device handle, or ``None``.
|
||||
"""
|
||||
return _native.hid_open(vendor_id, product_id, serial) or None
|
||||
|
||||
|
||||
def open_path(device_path):
|
||||
"""Open a HID device by its path name.
|
||||
|
||||
:param device_path: the path of a ``DeviceInfo`` tuple returned by
|
||||
enumerate().
|
||||
|
||||
:returns: an opaque device handle, or ``None``.
|
||||
"""
|
||||
if type(device_path) == str:
|
||||
device_path = device_path.encode('ascii')
|
||||
return _native.hid_open_path(device_path) or None
|
||||
|
||||
|
||||
def close(device_handle):
|
||||
"""Close a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
_native.hid_close(device_handle)
|
||||
|
||||
|
||||
def write(device_handle, data):
|
||||
"""Write an Output report to a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param data: the data bytes to send including the report number as the
|
||||
first byte.
|
||||
|
||||
The first byte of data[] must contain the Report ID. For
|
||||
devices which only support a single report, this must be set
|
||||
to 0x0. The remaining bytes contain the report data. Since
|
||||
the Report ID is mandatory, calls to hid_write() will always
|
||||
contain one more byte than the report contains. For example,
|
||||
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_write(), the Report ID (or 0x0, for devices with a
|
||||
single report), followed by the report data (16 bytes). In
|
||||
this example, the length passed in would be 17.
|
||||
|
||||
write() will send the data on the first OUT endpoint, if
|
||||
one exists. If it does not, it will send the data through
|
||||
the Control Endpoint (Endpoint 0).
|
||||
|
||||
:returns: ``True`` if the write was successful.
|
||||
"""
|
||||
bytes_written = _native.hid_write(device_handle, _C.c_char_p(data), len(data))
|
||||
return bytes_written > -1
|
||||
|
||||
|
||||
def read(device_handle, bytes_count, timeout_ms=-1):
|
||||
"""Read an Input report from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param bytes_count: maximum number of bytes to read.
|
||||
:param timeout_ms: can be -1 (default) to wait for data indefinitely, 0 to
|
||||
read whatever is in the device's input buffer, or a positive integer to
|
||||
wait that many milliseconds.
|
||||
|
||||
Input reports are returned to the host through the INTERRUPT IN endpoint.
|
||||
The first byte will contain the Report number if the device uses numbered
|
||||
reports.
|
||||
|
||||
:returns: the data packet read, an empty bytes string if a timeout was
|
||||
reached, or None if there was an error while reading.
|
||||
"""
|
||||
out_buffer = _C.create_string_buffer(b'\x00' * (bytes_count + 1))
|
||||
bytes_read = _native.hid_read_timeout(device_handle, out_buffer, bytes_count, timeout_ms)
|
||||
if bytes_read == -1:
|
||||
return None
|
||||
if bytes_read == 0:
|
||||
return b''
|
||||
return out_buffer[:bytes_read]
|
||||
|
||||
|
||||
def send_feature_report(device_handle, data, report_number=None):
|
||||
"""Send a Feature report to the device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param data: the data bytes to send including the report number as the
|
||||
first byte.
|
||||
:param report_number: if set, it is sent as the first byte with the data.
|
||||
|
||||
Feature reports are sent over the Control endpoint as a
|
||||
Set_Report transfer. The first byte of data[] must
|
||||
contain the Report ID. For devices which only support a
|
||||
single report, this must be set to 0x0. The remaining bytes
|
||||
contain the report data. Since the Report ID is mandatory,
|
||||
calls to send_feature_report() will always contain one
|
||||
more byte than the report contains. For example, if a hid
|
||||
report is 16 bytes long, 17 bytes must be passed to
|
||||
send_feature_report(): the Report ID (or 0x0, for
|
||||
devices which do not use numbered reports), followed by the
|
||||
report data (16 bytes).
|
||||
|
||||
:returns: ``True`` if the report was successfully written to the device.
|
||||
"""
|
||||
if report_number is not None:
|
||||
data = _pack(b'!B', report_number) + data
|
||||
bytes_written = _native.hid_send_feature_report(device_handle, _C.c_char_p(data), len(data))
|
||||
return bytes_written > -1
|
||||
|
||||
|
||||
def get_feature_report(device_handle, bytes_count, report_number=None):
|
||||
"""Get a feature report from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param bytes_count: how many bytes to read.
|
||||
:param report_number: if set, it is sent as the report number.
|
||||
|
||||
:returns: the feature report data.
|
||||
"""
|
||||
out_buffer = _C.create_string_buffer('\x00' * (bytes_count + 2))
|
||||
if report_number is not None:
|
||||
out_buffer[0] = _pack(b'!B', report_number)
|
||||
bytes_read = _native.hid_get_feature_report(device_handle, out_buffer, bytes_count)
|
||||
if bytes_read > -1:
|
||||
return out_buffer[:bytes_read]
|
||||
|
||||
|
||||
def _read_wchar(func, device_handle, index=None):
|
||||
_BUFFER_SIZE = 64
|
||||
buf = _C.create_unicode_buffer('\x00' * _BUFFER_SIZE)
|
||||
if index is None:
|
||||
ok = func(device_handle, buf, _BUFFER_SIZE)
|
||||
else:
|
||||
ok = func(device_handle, index, buf, _BUFFER_SIZE)
|
||||
if ok == 0:
|
||||
return buf.value
|
||||
|
||||
|
||||
def get_manufacturer(device_handle):
|
||||
"""Get the Manufacturer String from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
return _read_wchar(_native.hid_get_manufacturer_string, device_handle)
|
||||
|
||||
|
||||
def get_product(device_handle):
|
||||
"""Get the Product String from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
return _read_wchar(_native.hid_get_product_string, device_handle)
|
||||
|
||||
|
||||
def get_serial(device_handle):
|
||||
"""Get the serial number from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
serial = _read_wchar(_native.hid_get_serial_number_string, device_handle)
|
||||
if serial is not None:
|
||||
return ''.join(hex(ord(c)) for c in serial)
|
||||
|
||||
|
||||
def get_indexed_string(device_handle, index):
|
||||
"""Get a string from a HID device, based on its string index.
|
||||
|
||||
Note: currently not working in the ``hidraw`` native implementation.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param index: the index of the string to get.
|
||||
"""
|
||||
return _read_wchar(_native.hid_get_indexed_string, device_handle, index)
|
||||
@@ -1,3 +1,22 @@
|
||||
# -*- python-mode -*-
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""Generic Human Interface Device API.
|
||||
|
||||
It is currently a partial pure-Python implementation of the native HID API
|
||||
@@ -11,8 +30,10 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
|
||||
import os as _os
|
||||
import errno as _errno
|
||||
from time import sleep
|
||||
from select import select as _select
|
||||
from pyudev import Context as _Context, Device as _Device
|
||||
from pyudev import Context as _Context, Monitor as _Monitor, Device as _Device
|
||||
from pyudev import DeviceNotFoundError
|
||||
|
||||
|
||||
native_implementation = 'udev'
|
||||
@@ -57,7 +78,126 @@ def exit():
|
||||
return True
|
||||
|
||||
|
||||
def enumerate(vendor_id=None, product_id=None, interface_number=None):
|
||||
# The filter is used to determine whether this is a device of interest to Solaar
|
||||
def _match(action, device, filter):
|
||||
vendor_id=filter.get('vendor_id')
|
||||
product_id=filter.get('product_id')
|
||||
interface_number=filter.get('usb_interface')
|
||||
hid_driver=filter.get('hid_driver')
|
||||
|
||||
usb_device = device.find_parent('usb', 'usb_device')
|
||||
# print ("* parent", action, device, "usb:", usb_device)
|
||||
if not usb_device:
|
||||
return
|
||||
|
||||
vid = usb_device.get('ID_VENDOR_ID')
|
||||
pid = usb_device.get('ID_MODEL_ID')
|
||||
if vid is None or pid is None:
|
||||
return # there are reports that sometimes the usb_device isn't set up right so be defensive
|
||||
if not ((vendor_id is None or vendor_id == int(vid, 16)) and
|
||||
(product_id is None or product_id == int(pid, 16))):
|
||||
return
|
||||
|
||||
if action == 'add':
|
||||
hid_device = device.find_parent('hid')
|
||||
if not hid_device:
|
||||
return
|
||||
hid_driver_name = hid_device.get('DRIVER')
|
||||
# print ("** found hid", action, device, "hid:", hid_device, hid_driver_name)
|
||||
if hid_driver:
|
||||
if isinstance(hid_driver, tuple):
|
||||
if hid_driver_name not in hid_driver:
|
||||
return
|
||||
elif hid_driver_name != hid_driver:
|
||||
return
|
||||
|
||||
intf_device = device.find_parent('usb', 'usb_interface')
|
||||
# print ("*** usb interface", action, device, "usb_interface:", intf_device)
|
||||
if interface_number is None:
|
||||
usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber')
|
||||
else:
|
||||
usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber')
|
||||
if usb_interface is None or interface_number != usb_interface:
|
||||
return
|
||||
|
||||
attrs = usb_device.attributes
|
||||
d_info = DeviceInfo(path=device.device_node,
|
||||
vendor_id=vid[-4:],
|
||||
product_id=pid[-4:],
|
||||
serial=hid_device.get('HID_UNIQ'),
|
||||
release=attrs.get('bcdDevice'),
|
||||
manufacturer=attrs.get('manufacturer'),
|
||||
product=attrs.get('product'),
|
||||
interface=usb_interface,
|
||||
driver=hid_driver_name)
|
||||
return d_info
|
||||
|
||||
elif action == 'remove':
|
||||
# print (dict(device), dict(usb_device))
|
||||
|
||||
d_info = DeviceInfo(path=device.device_node,
|
||||
vendor_id=vid[-4:],
|
||||
product_id=pid[-4:],
|
||||
serial=None,
|
||||
release=None,
|
||||
manufacturer=None,
|
||||
product=None,
|
||||
interface=None,
|
||||
driver=None)
|
||||
return d_info
|
||||
|
||||
|
||||
def monitor_glib(callback, *device_filters):
|
||||
from gi.repository import GLib
|
||||
|
||||
c = _Context()
|
||||
|
||||
# already existing devices
|
||||
# for device in c.list_devices(subsystem='hidraw'):
|
||||
# # print (device, dict(device), dict(device.attributes))
|
||||
# for filter in device_filters:
|
||||
# d_info = _match('add', device, *filter)
|
||||
# if d_info:
|
||||
# GLib.idle_add(callback, 'add', d_info)
|
||||
# break
|
||||
|
||||
m = _Monitor.from_netlink(c)
|
||||
m.filter_by(subsystem='hidraw')
|
||||
|
||||
def _process_udev_event(monitor, condition, cb, filters):
|
||||
if condition == GLib.IO_IN:
|
||||
event = monitor.receive_device()
|
||||
if event:
|
||||
action, device = event
|
||||
# print ("***", action, device)
|
||||
if action == 'add':
|
||||
for filter in filters:
|
||||
d_info = _match(action, device, filter)
|
||||
if d_info:
|
||||
GLib.idle_add(cb, action, d_info)
|
||||
break
|
||||
elif action == 'remove':
|
||||
# the GLib notification does _not_ match!
|
||||
pass
|
||||
return True
|
||||
|
||||
try:
|
||||
# io_add_watch_full may not be available...
|
||||
GLib.io_add_watch_full(m, GLib.PRIORITY_LOW, GLib.IO_IN, _process_udev_event, callback, device_filters)
|
||||
# print ("did io_add_watch_full")
|
||||
except AttributeError:
|
||||
try:
|
||||
# and the priority parameter appeared later in the API
|
||||
GLib.io_add_watch(m, GLib.PRIORITY_LOW, GLib.IO_IN, _process_udev_event, callback, device_filters)
|
||||
# print ("did io_add_watch with priority")
|
||||
except:
|
||||
GLib.io_add_watch(m, GLib.IO_IN, _process_udev_event, callback, device_filters)
|
||||
# print ("did io_add_watch")
|
||||
|
||||
m.start()
|
||||
|
||||
|
||||
def enumerate(usb_id):
|
||||
"""Enumerate the HID Devices.
|
||||
|
||||
List all the HID devices attached to the system, optionally filtering by
|
||||
@@ -65,46 +205,11 @@ def enumerate(vendor_id=None, product_id=None, interface_number=None):
|
||||
|
||||
:returns: a list of matching ``DeviceInfo`` tuples.
|
||||
"""
|
||||
|
||||
for dev in _Context().list_devices(subsystem='hidraw'):
|
||||
hid_dev = dev.find_parent('hid')
|
||||
if not hid_dev:
|
||||
continue
|
||||
|
||||
assert 'HID_ID' in hid_dev
|
||||
bus, vid, pid = hid_dev['HID_ID'].split(':')
|
||||
if vendor_id is not None and vendor_id != int(vid, 16):
|
||||
continue
|
||||
if product_id is not None and product_id != int(pid, 16):
|
||||
continue
|
||||
|
||||
if bus == '0003': # USB
|
||||
intf_dev = dev.find_parent('usb', 'usb_interface')
|
||||
if not intf_dev:
|
||||
continue
|
||||
|
||||
interface = intf_dev.attributes.asint('bInterfaceNumber')
|
||||
if interface_number is not None and interface_number != interface:
|
||||
continue
|
||||
|
||||
serial = hid_dev['HID_UNIQ'] if 'HID_UNIQ' in hid_dev else None
|
||||
|
||||
usb_dev = dev.find_parent('usb', 'usb_device')
|
||||
assert usb_dev
|
||||
attrs = usb_dev.attributes
|
||||
d_info = DeviceInfo(path=dev.device_node,
|
||||
vendor_id=vid[-4:],
|
||||
product_id=pid[-4:],
|
||||
serial=serial,
|
||||
release=attrs['bcdDevice'],
|
||||
manufacturer=attrs['manufacturer'],
|
||||
product=attrs['product'],
|
||||
interface=interface,
|
||||
driver=hid_dev['DRIVER'])
|
||||
yield d_info
|
||||
|
||||
elif bus == '0005': # BLUETOOTH
|
||||
# TODO
|
||||
pass
|
||||
dev_info = _match('add', dev, usb_id)
|
||||
if dev_info:
|
||||
yield dev_info
|
||||
|
||||
|
||||
def open(vendor_id, product_id, serial=None):
|
||||
@@ -163,9 +268,21 @@ def write(device_handle, data):
|
||||
the Control Endpoint (Endpoint 0).
|
||||
"""
|
||||
assert device_handle
|
||||
bytes_written = _os.write(device_handle, data)
|
||||
assert data
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
retrycount = 0
|
||||
bytes_written = 0
|
||||
while(retrycount < 3):
|
||||
try:
|
||||
bytes_written = _os.write(device_handle, data)
|
||||
retrycount += 1
|
||||
except IOError as e:
|
||||
if e.errno == _errno.EPIPE:
|
||||
sleep(0.1)
|
||||
else:
|
||||
break
|
||||
if bytes_written != len(data):
|
||||
raise OSError(errno=_errno.EIO, strerror='written %d bytes out of expected %d' % (bytes_written, len(data)))
|
||||
raise IOError(_errno.EIO, 'written %d bytes out of expected %d' % (bytes_written, len(data)))
|
||||
|
||||
|
||||
def read(device_handle, bytes_count, timeout_ms=-1):
|
||||
@@ -190,12 +307,13 @@ def read(device_handle, bytes_count, timeout_ms=-1):
|
||||
|
||||
if xlist:
|
||||
assert xlist == [device_handle]
|
||||
raise OSError(errno=_errno.EIO, strerror='exception on file descriptor %d' % device_handle)
|
||||
raise IOError(_errno.EIO, 'exception on file descriptor %d' % device_handle)
|
||||
|
||||
if rlist:
|
||||
assert rlist == [device_handle]
|
||||
data = _os.read(device_handle, bytes_count)
|
||||
assert data is not None
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
return data
|
||||
else:
|
||||
return b''
|
||||
@@ -241,27 +359,31 @@ def get_indexed_string(device_handle, index):
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param index: the index of the string to get.
|
||||
:returns: the value corresponding to index, or None if no value found
|
||||
:rtype: bytes or NoneType
|
||||
"""
|
||||
if index not in _DEVICE_STRINGS:
|
||||
return None
|
||||
try:
|
||||
key = _DEVICE_STRINGS[index]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
assert device_handle
|
||||
stat = _os.fstat(device_handle)
|
||||
dev = _Device.from_device_number(_Context(), 'char', stat.st_rdev)
|
||||
if dev:
|
||||
hid_dev = dev.find_parent('hid')
|
||||
if hid_dev:
|
||||
assert 'HID_ID' in hid_dev
|
||||
bus, _, _ = hid_dev['HID_ID'].split(':')
|
||||
try:
|
||||
dev = _Device.from_device_number(_Context(), 'char', stat.st_rdev)
|
||||
except (DeviceNotFoundError, ValueError):
|
||||
return None
|
||||
|
||||
if bus == '0003': # USB
|
||||
usb_dev = dev.find_parent('usb', 'usb_device')
|
||||
assert usb_dev
|
||||
key = _DEVICE_STRINGS[index]
|
||||
attrs = usb_dev.attributes
|
||||
if key in attrs:
|
||||
return attrs[key]
|
||||
hid_dev = dev.find_parent('hid')
|
||||
if hid_dev:
|
||||
assert 'HID_ID' in hid_dev
|
||||
bus, _ignore, _ignore = hid_dev['HID_ID'].split(':')
|
||||
|
||||
elif bus == '0005': # BLUETOOTH
|
||||
# TODO
|
||||
pass
|
||||
if bus == '0003': # USB
|
||||
usb_dev = dev.find_parent('usb', 'usb_device')
|
||||
assert usb_dev
|
||||
return usb_dev.attributes.get(key)
|
||||
|
||||
elif bus == '0005': # BLUETOOTH
|
||||
# TODO
|
||||
pass
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
__author__ = "Daniel Pavel"
|
||||
__license__ = "GPL"
|
||||
__version__ = "0.8"
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Low-level interface for devices connected through a Logitech Universal
|
||||
Receiver (UR).
|
||||
|
||||
Uses the HID api exposed through hidapi.py, a Python thin layer over a native
|
||||
implementation.
|
||||
|
||||
Incomplete. Based on a bit of documentation, trial-and-error, and guesswork.
|
||||
|
||||
References:
|
||||
http://julien.danjou.info/blog/2012/logitech-k750-linux-support
|
||||
http://6xq.net/git/lars/lshidpp.git/plain/doc/
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
_DEBUG = logging.DEBUG
|
||||
_log = logging.getLogger('LUR')
|
||||
_log.setLevel(logging.root.level)
|
||||
# if logging.root.level > logging.DEBUG:
|
||||
# _log.addHandler(logging.NullHandler())
|
||||
# _log.propagate = 0
|
||||
|
||||
del logging
|
||||
|
||||
|
||||
from .common import strhex
|
||||
from .base import NoReceiver, NoSuchDevice, DeviceUnreachable
|
||||
from .receiver import Receiver, PairedDevice, MAX_PAIRED_DEVICES
|
||||
from .hidpp20 import FeatureNotSupported, FeatureCallError
|
||||
|
||||
from . import listener
|
||||
from . import status
|
||||
@@ -1,99 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from .common import NamedInts as _NamedInts
|
||||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
from . import settings as _settings
|
||||
|
||||
#
|
||||
# common strings for settings
|
||||
#
|
||||
|
||||
_SMOOTH_SCROLL = ('smooth-scroll', 'Smooth Scrolling', 'High-sensitivity mode for vertical scroll with the wheel.')
|
||||
_DPI = ('dpi', 'Sensitivity (DPI)', None)
|
||||
_FN_SWAP = ('fn-swap', 'Swap Fx function', ('When set, the F1..F12 keys will activate their special function,\n'
|
||||
'and you must hold the FN key to activate their standard function.\n'
|
||||
'\n'
|
||||
'When unset, the F1..F12 keys will activate their standard function,\n'
|
||||
'and you must hold the FN key to activate their special function.'))
|
||||
|
||||
|
||||
def _register_smooth_scroll(register, true_value, mask):
|
||||
return _settings.register_toggle(_SMOOTH_SCROLL[0], register, true_value=true_value, mask=mask,
|
||||
label=_SMOOTH_SCROLL[1], description=_SMOOTH_SCROLL[2])
|
||||
|
||||
|
||||
def _register_dpi(register, choices):
|
||||
return _settings.register_choices(_DPI[0], register, choices,
|
||||
label=_DPI[1], description=_DPI[2])
|
||||
|
||||
|
||||
def check_features(device, already_known):
|
||||
if _hidpp20.FEATURE.FN_STATUS in device.features and not any(s.name == 'fn-swap' for s in already_known):
|
||||
tfn = _settings.feature_toggle(_FN_SWAP[0], _hidpp20.FEATURE.FN_STATUS, write_returns_value=True,
|
||||
label=_FN_SWAP[1], description=_FN_SWAP[2])
|
||||
already_known.append(tfn(device))
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
|
||||
['name', 'kind', 'codename', 'registers', 'settings'])
|
||||
|
||||
DEVICES = {}
|
||||
|
||||
def _D(name, codename=None, kind=None, registers=None, settings=None):
|
||||
if kind is None:
|
||||
kind = (_hidpp10.DEVICE_KIND.mouse if 'Mouse' in name
|
||||
else _hidpp10.DEVICE_KIND.keyboard if 'Keyboard' in name
|
||||
else _hidpp10.DEVICE_KIND.touchpad if 'Touchpad' in name
|
||||
else _hidpp10.DEVICE_KIND.trackball if 'Trackball' in name
|
||||
else None)
|
||||
assert kind is not None
|
||||
|
||||
if codename is None:
|
||||
codename = name.split(' ')[-1]
|
||||
assert codename is not None
|
||||
|
||||
DEVICES[codename] = _DeviceDescriptor(name, kind, codename, registers, settings)
|
||||
|
||||
|
||||
_D('Wireless Mouse M315')
|
||||
_D('Wireless Mouse M325')
|
||||
_D('Wireless Mouse M505')
|
||||
_D('Wireless Mouse M510')
|
||||
_D('Couch Mouse M515')
|
||||
_D('Wireless Mouse M525')
|
||||
_D('Wireless Trackball M570')
|
||||
_D('Touch Mouse M600')
|
||||
_D('Marathon Mouse M705',
|
||||
settings=[
|
||||
_register_smooth_scroll(0x01, true_value=0x40, mask=0x40),
|
||||
# _register_dpi(0x63, _NamedInts.range(9, 11, lambda x: '_' + str(x * 100))),
|
||||
],
|
||||
)
|
||||
_D('Wireless Keyboard K230')
|
||||
_D('Wireless Keyboard K270')
|
||||
_D('Wireless Keyboard K350')
|
||||
_D('Wireless Keyboard K360')
|
||||
_D('Wireless Touch Keyboard K400')
|
||||
_D('Wireless Solar Keyboard K750')
|
||||
_D('Wireless Illuminated Keyboard K800')
|
||||
_D('Zone Touch Mouse T400')
|
||||
_D('Wireless Rechargeable Touchpad T650')
|
||||
_D('Logitech Cube', kind='mouse')
|
||||
_D('Anywhere Mouse MX', codename='Anywhere MX')
|
||||
_D('Performance Mouse MX', codename='Performance MX',
|
||||
settings=[
|
||||
_register_dpi(0x63, _NamedInts.range(0x81, 0x8F, lambda x: '_' + str((x - 0x80) * 100))),
|
||||
],
|
||||
)
|
||||
|
||||
del namedtuple
|
||||
@@ -1,129 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from logging import getLogger # , DEBUG as _DEBUG
|
||||
_log = getLogger('LUR').getChild('hidpp10')
|
||||
del getLogger
|
||||
|
||||
from .common import (strhex as _strhex,
|
||||
NamedInts as _NamedInts,
|
||||
FirmwareInfo as _FirmwareInfo)
|
||||
from .hidpp20 import FIRMWARE_KIND
|
||||
|
||||
#
|
||||
# constants
|
||||
#
|
||||
|
||||
DEVICE_KIND = _NamedInts(
|
||||
keyboard=0x01,
|
||||
mouse=0x02,
|
||||
numpad=0x03,
|
||||
presenter=0x04,
|
||||
trackball=0x08,
|
||||
touchpad=0x09)
|
||||
|
||||
POWER_SWITCH_LOCATION = _NamedInts(
|
||||
base=0x01,
|
||||
top_case=0x02,
|
||||
edge_of_top_right_corner=0x03,
|
||||
top_left_corner=0x05,
|
||||
bottom_left_corner=0x06,
|
||||
top_right_corner=0x07,
|
||||
bottom_right_corner=0x08,
|
||||
top_edge=0x09,
|
||||
right_edge=0x0A,
|
||||
left_edge=0x0B,
|
||||
bottom_edge=0x0C)
|
||||
|
||||
NOTIFICATION_FLAG = _NamedInts(
|
||||
battery_status=0x100000,
|
||||
wireless=0x000100,
|
||||
software_present=0x0000800)
|
||||
|
||||
ERROR = _NamedInts(
|
||||
invalid_SubID__command=0x01,
|
||||
invalid_address=0x02,
|
||||
invalid_value=0x03,
|
||||
connection_request_failed=0x04,
|
||||
too_many_devices=0x05,
|
||||
already_exists=0x06,
|
||||
busy=0x07,
|
||||
unknown_device=0x08,
|
||||
resource_error=0x09,
|
||||
request_unavailable=0x0A,
|
||||
unsupported_parameter_value=0x0B,
|
||||
wrong_pin_code=0x0C)
|
||||
|
||||
PAIRING_ERRORS = _NamedInts(
|
||||
device_timeout=0x01,
|
||||
device_not_supported=0x02,
|
||||
too_many_devices=0x03,
|
||||
sequence_timeout=0x06)
|
||||
|
||||
#
|
||||
# functions
|
||||
#
|
||||
|
||||
def get_register(device, name, default_number=-1):
|
||||
known_register = device.registers[name]
|
||||
register = known_register or default_number
|
||||
if register > 0:
|
||||
reply = device.request(0x8100 + (register & 0xFF))
|
||||
if reply:
|
||||
return reply
|
||||
|
||||
if not known_register and device.ping():
|
||||
_log.warn("%s: failed to read '%s' from default register 0x%02X, blacklisting", device, name, default_number)
|
||||
device.registers[-default_number] = name
|
||||
|
||||
|
||||
def get_battery(device):
|
||||
"""Reads a device's battery level, if provided by the HID++ 1.0 protocol."""
|
||||
reply = get_register(device, 'battery', 0x0D)
|
||||
if reply:
|
||||
charge = ord(reply[:1])
|
||||
status = ord(reply[2:3]) & 0xF0
|
||||
status = ('discharging' if status == 0x30
|
||||
else 'charging' if status == 0x50
|
||||
else 'fully charged' if status == 0x90
|
||||
else None)
|
||||
return charge, status
|
||||
|
||||
|
||||
def get_serial(device):
|
||||
if device.kind is None:
|
||||
dev_id = 0x03
|
||||
receiver = device
|
||||
else:
|
||||
dev_id = 0x30 + device.number - 1
|
||||
receiver = device.receiver
|
||||
|
||||
serial = receiver.request(0x83B5, dev_id)
|
||||
if serial:
|
||||
return _strhex(serial[1:5])
|
||||
|
||||
|
||||
def get_firmware(device):
|
||||
firmware = []
|
||||
|
||||
reply = device.request(0x81F1, 0x01)
|
||||
if reply:
|
||||
fw_version = _strhex(reply[1:3])
|
||||
fw_version = '%s.%s' % (fw_version[0:2], fw_version[2:4])
|
||||
reply = device.request(0x81F1, 0x02)
|
||||
if reply:
|
||||
fw_version += '.B' + _strhex(reply[1:3])
|
||||
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
|
||||
firmware.append(fw)
|
||||
|
||||
reply = device.request(0x81F1, 0x04)
|
||||
if reply:
|
||||
bl_version = _strhex(reply[1:3])
|
||||
bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4])
|
||||
bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, '', bl_version, None)
|
||||
firmware.append(bl)
|
||||
|
||||
return tuple(firmware)
|
||||
@@ -1,397 +0,0 @@
|
||||
#
|
||||
# Logitech Unifying Receiver API.
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from struct import pack as _pack, unpack as _unpack
|
||||
from weakref import proxy as _proxy
|
||||
|
||||
from logging import getLogger, DEBUG as _DEBUG
|
||||
_log = getLogger('LUR').getChild('hidpp20')
|
||||
del getLogger
|
||||
|
||||
from .common import (FirmwareInfo as _FirmwareInfo,
|
||||
ReprogrammableKeyInfo as _ReprogrammableKeyInfo,
|
||||
KwException as _KwException,
|
||||
NamedInts as _NamedInts)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
"""Possible features available on a Logitech device.
|
||||
|
||||
A particular device might not support all these features, and may support other
|
||||
unknown features as well.
|
||||
"""
|
||||
FEATURE = _NamedInts(
|
||||
ROOT=0x0000,
|
||||
FEATURE_SET=0x0001,
|
||||
FIRMWARE=0x0003,
|
||||
NAME=0x0005,
|
||||
BATTERY=0x1000,
|
||||
REPROGRAMMABLE_KEYS=0x1B00,
|
||||
WIRELESS=0x1D4B,
|
||||
FN_STATUS=0x40A0,
|
||||
SOLAR_CHARGE=0x4301,
|
||||
TOUCH_MOUSE=0x6110)
|
||||
FEATURE._fallback = lambda x: 'unknown:%04X' % x
|
||||
|
||||
FEATURE_FLAG = _NamedInts(
|
||||
internal=0x20,
|
||||
hidden=0x40,
|
||||
obsolete=0x80)
|
||||
|
||||
DEVICE_KIND = _NamedInts(
|
||||
keyboard=0x00,
|
||||
remote_control=0x01,
|
||||
numpad=0x02,
|
||||
mouse=0x03,
|
||||
touchpad=0x04,
|
||||
trackball=0x05,
|
||||
presenter=0x06,
|
||||
receiver=0x07)
|
||||
|
||||
FIRMWARE_KIND = _NamedInts(
|
||||
Firmware=0x00,
|
||||
Bootloader=0x01,
|
||||
Hardware=0x02,
|
||||
Other=0x03)
|
||||
|
||||
BATTERY_OK = lambda status: status < 5
|
||||
|
||||
BATTERY_STATUS = _NamedInts(
|
||||
discharging=0x00,
|
||||
recharging=0x01,
|
||||
almost_full=0x02,
|
||||
full=0x03,
|
||||
slow_recharge=0x04,
|
||||
invalid_battery=0x05,
|
||||
thermal_error=0x06)
|
||||
|
||||
KEY = _NamedInts(
|
||||
Volume_Up=0x0001,
|
||||
Volume_Down=0x0002,
|
||||
Mute=0x0003,
|
||||
Play__Pause=0x0004,
|
||||
Next=0x0005,
|
||||
Previous=0x0006,
|
||||
Stop=0x0007,
|
||||
Application_Switcher=0x0008,
|
||||
Calculator=0x000A,
|
||||
Mail=0x000E,
|
||||
Home=0x001A,
|
||||
Music=0x001D,
|
||||
Search=0x0029,
|
||||
Sleep=0x002F)
|
||||
KEY._fallback = lambda x: 'unknown:%04X' % x
|
||||
|
||||
KEY_FLAG = _NamedInts(
|
||||
reprogrammable=0x10,
|
||||
FN_sensitive=0x08,
|
||||
nonstandard=0x04,
|
||||
is_FN=0x02,
|
||||
mse=0x01)
|
||||
|
||||
ERROR = _NamedInts(
|
||||
unknown=0x01,
|
||||
invalid_argument=0x02,
|
||||
out_of_range=0x03,
|
||||
hardware_error=0x04,
|
||||
logitech_internal=0x05,
|
||||
invalid_feature_index=0x06,
|
||||
invalid_function=0x07,
|
||||
busy=0x08,
|
||||
unsupported=0x09)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
class FeatureNotSupported(_KwException):
|
||||
"""Raised when trying to request a feature not supported by the device."""
|
||||
pass
|
||||
|
||||
class FeatureCallError(_KwException):
|
||||
"""Raised if the device replied to a feature call with an error."""
|
||||
pass
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
class FeaturesArray(object):
|
||||
"""A sequence of features supported by a HID++ 2.0 device."""
|
||||
__slots__ = ('supported', 'device', 'features')
|
||||
assert int(FEATURE.ROOT) == 0x0000
|
||||
|
||||
def __init__(self, device):
|
||||
assert device is not None
|
||||
self.device = _proxy(device)
|
||||
self.supported = True
|
||||
self.features = None
|
||||
|
||||
def __del__(self):
|
||||
self.supported = False
|
||||
|
||||
def _check(self):
|
||||
# print (self.device, "check")
|
||||
if self.supported:
|
||||
assert self.device
|
||||
if self.features is not None:
|
||||
return True
|
||||
|
||||
protocol = self.device.protocol
|
||||
if protocol == 0:
|
||||
# device is not connected right now, will have to try later
|
||||
return False
|
||||
|
||||
# I _think_ this is universally true
|
||||
if protocol < 2.0:
|
||||
self.supported = False
|
||||
# self.device.features = None
|
||||
self.device = None
|
||||
return False
|
||||
|
||||
reply = self.device.request(0x0000, _pack(b'!H', FEATURE.FEATURE_SET))
|
||||
if reply is None:
|
||||
self.supported = False
|
||||
else:
|
||||
fs_index = ord(reply[0:1])
|
||||
if fs_index:
|
||||
count = self.device.request(fs_index << 8)
|
||||
if count is None:
|
||||
_log.warn("FEATURE_SET found, but failed to read features count")
|
||||
# most likely the device is unavailable
|
||||
return False
|
||||
else:
|
||||
count = ord(count[:1])
|
||||
assert count >= fs_index
|
||||
self.features = [None] * (1 + count)
|
||||
self.features[0] = FEATURE.ROOT
|
||||
self.features[fs_index] = FEATURE.FEATURE_SET
|
||||
return True
|
||||
else:
|
||||
self.supported = False
|
||||
|
||||
return False
|
||||
|
||||
__bool__ = __nonzero__ = _check
|
||||
|
||||
def __getitem__(self, index):
|
||||
if self._check():
|
||||
if isinstance(index, int):
|
||||
if index < 0 or index >= len(self.features):
|
||||
raise IndexError(index)
|
||||
|
||||
if self.features[index] is None:
|
||||
feature = self.device.feature_request(FEATURE.FEATURE_SET, 0x10, index)
|
||||
if feature:
|
||||
feature, = _unpack(b'!H', feature[:2])
|
||||
self.features[index] = FEATURE[feature]
|
||||
|
||||
return self.features[index]
|
||||
|
||||
elif isinstance(index, slice):
|
||||
indices = index.indices(len(self.features))
|
||||
return [self.__getitem__(i) for i in range(*indices)]
|
||||
|
||||
def __contains__(self, value):
|
||||
if self._check():
|
||||
ivalue = int(value)
|
||||
|
||||
may_have = False
|
||||
for f in self.features:
|
||||
if f is None:
|
||||
may_have = True
|
||||
elif ivalue == int(f):
|
||||
return True
|
||||
elif ivalue < int(f):
|
||||
break
|
||||
|
||||
if may_have:
|
||||
reply = self.device.request(0x0000, _pack(b'!H', ivalue))
|
||||
if reply:
|
||||
index = ord(reply[0:1])
|
||||
if index:
|
||||
self.features[index] = FEATURE[ivalue]
|
||||
return True
|
||||
|
||||
def index(self, value):
|
||||
if self._check():
|
||||
may_have = False
|
||||
ivalue = int(value)
|
||||
for index, f in enumerate(self.features):
|
||||
if f is None:
|
||||
may_have = True
|
||||
elif ivalue == int(f):
|
||||
return index
|
||||
elif ivalue < int(f):
|
||||
raise ValueError("%s not in list" % repr(value))
|
||||
|
||||
if may_have:
|
||||
reply = self.device.request(0x0000, _pack(b'!H', ivalue))
|
||||
if reply:
|
||||
index = ord(reply[0:1])
|
||||
self.features[index] = FEATURE[ivalue]
|
||||
return index
|
||||
|
||||
raise ValueError("%s not in list" % repr(value))
|
||||
|
||||
def __iter__(self):
|
||||
if self._check():
|
||||
yield FEATURE.ROOT
|
||||
index = 1
|
||||
last_index = len(self.features)
|
||||
while index < last_index:
|
||||
yield self.__getitem__(index)
|
||||
index += 1
|
||||
|
||||
def __len__(self):
|
||||
return len(self.features) if self._check() else 0
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
class KeysArray(object):
|
||||
"""A sequence of key mappings supported by a HID++ 2.0 device."""
|
||||
__slots__ = ('device', 'keys')
|
||||
|
||||
def __init__(self, device, count):
|
||||
assert device is not None
|
||||
self.device = _proxy(device)
|
||||
self.keys = [None] * count
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, int):
|
||||
if index < 0 or index >= len(self.keys):
|
||||
raise IndexError(index)
|
||||
|
||||
if self.keys[index] is None:
|
||||
keydata = feature_request(self.device, FEATURE.REPROGRAMMABLE_KEYS, 0x10, index)
|
||||
if keydata:
|
||||
key, key_task, flags = _unpack(b'!HHB', keydata[:5])
|
||||
self.keys[index] = _ReprogrammableKeyInfo(index, KEY[key], KEY[key_task], flags)
|
||||
|
||||
return self.keys[index]
|
||||
|
||||
elif isinstance(index, slice):
|
||||
indices = index.indices(len(self.keys))
|
||||
return [self.__getitem__(i) for i in range(*indices)]
|
||||
|
||||
def index(self, value):
|
||||
for index, k in enumerate(self.keys):
|
||||
if k is not None and int(value) == int(k.key):
|
||||
return index
|
||||
|
||||
for index, k in enumerate(self.keys):
|
||||
if k is None:
|
||||
k = self.__getitem__(index)
|
||||
if k is not None:
|
||||
return index
|
||||
|
||||
def __iter__(self):
|
||||
for k in range(0, len(self.keys)):
|
||||
yield self.__getitem__(k)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.keys)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def feature_request(device, feature, function=0x00, *params):
|
||||
if device.features:
|
||||
if feature in device.features:
|
||||
feature_index = device.features.index(int(feature))
|
||||
return device.request((feature_index << 8) + (function & 0xFF), *params)
|
||||
|
||||
|
||||
def get_firmware(device):
|
||||
"""Reads a device's firmware info.
|
||||
|
||||
:returns: a list of FirmwareInfo tuples, ordered by firmware layer.
|
||||
"""
|
||||
count = feature_request(device, FEATURE.FIRMWARE)
|
||||
if count:
|
||||
count = ord(count[:1])
|
||||
|
||||
fw = []
|
||||
for index in range(0, count):
|
||||
fw_info = feature_request(device, FEATURE.FIRMWARE, 0x10, index)
|
||||
if fw_info:
|
||||
level = ord(fw_info[:1]) & 0x0F
|
||||
if level == 0 or level == 1:
|
||||
name, version_major, version_minor, build = _unpack(b'!3sBBH', fw_info[1:8])
|
||||
version = '%02X.%02X' % (version_major, version_minor)
|
||||
if build:
|
||||
version += '.B%04X' % build
|
||||
extras = fw_info[9:].rstrip(b'\x00') or None
|
||||
fw_info = _FirmwareInfo(FIRMWARE_KIND[level], name.decode('ascii'), version, extras)
|
||||
elif level == FIRMWARE_KIND.Hardware:
|
||||
fw_info = _FirmwareInfo(FIRMWARE_KIND.Hardware, '', ord(fw_info[1:2]), None)
|
||||
else:
|
||||
fw_info = _FirmwareInfo(FIRMWARE_KIND.Other, '', '', None)
|
||||
|
||||
fw.append(fw_info)
|
||||
# _log.debug("device %d firmware %s", devnumber, fw_info)
|
||||
return tuple(fw)
|
||||
|
||||
|
||||
def get_kind(device):
|
||||
"""Reads a device's type.
|
||||
|
||||
:see DEVICE_KIND:
|
||||
:returns: a string describing the device type, or ``None`` if the device is
|
||||
not available or does not support the ``NAME`` feature.
|
||||
"""
|
||||
kind = feature_request(device, FEATURE.NAME, 0x20)
|
||||
if kind:
|
||||
kind = ord(kind[:1])
|
||||
# _log.debug("device %d type %d = %s", devnumber, kind, DEVICE_KIND[kind])
|
||||
return DEVICE_KIND[kind]
|
||||
|
||||
|
||||
def get_name(device):
|
||||
"""Reads a device's name.
|
||||
|
||||
:returns: a string with the device name, or ``None`` if the device is not
|
||||
available or does not support the ``NAME`` feature.
|
||||
"""
|
||||
name_length = feature_request(device, FEATURE.NAME)
|
||||
if name_length:
|
||||
name_length = ord(name_length[:1])
|
||||
|
||||
name = b''
|
||||
while len(name) < name_length:
|
||||
fragment = feature_request(device, FEATURE.NAME, 0x10, len(name))
|
||||
if fragment:
|
||||
name += fragment[:name_length - len(name)]
|
||||
else:
|
||||
_log.error("failed to read whole name of %s (expected %d chars)", device, name_length)
|
||||
return None
|
||||
|
||||
return name.decode('ascii')
|
||||
|
||||
|
||||
def get_battery(device):
|
||||
"""Reads a device's battery level.
|
||||
|
||||
:raises FeatureNotSupported: if the device does not support this feature.
|
||||
"""
|
||||
battery = feature_request(device, FEATURE.BATTERY)
|
||||
if battery:
|
||||
discharge, dischargeNext, status = _unpack(b'!BBB', battery[:3])
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("device %d battery %d%% charged, next level %d%% charge, status %d = %s",
|
||||
device.number, discharge, dischargeNext, status, BATTERY_STATUS[status])
|
||||
return discharge, BATTERY_STATUS[status]
|
||||
|
||||
|
||||
def get_keys(device):
|
||||
count = feature_request(device, FEATURE.REPROGRAMMABLE_KEYS)
|
||||
if count:
|
||||
return KeysArray(device, ord(count[:1]))
|
||||