diff --git a/Pipfile b/Pipfile index 6003e85..9d44681 100644 --- a/Pipfile +++ b/Pipfile @@ -20,3 +20,7 @@ s3cmd = "*" black = "==21.6b0" flake8-quotes = "*" flake8-black = "*" +circuitpython-stubs = "==7.0.0a6.dev195" +pyright = "*" +typing = "*" +mypy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index e18ce53..0c41bbb 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0a04ec24d4aef6828e4f5eefa0a7d2c312f21f2b2f18c42c7004cdbe0c02bd53" + "sha256": "cee0eeba8c8dad66dccffe0935656829132f7ca928569e3aa957f278e6e92da6" }, "pipfile-spec": 6, "requires": {}, @@ -45,6 +45,13 @@ "index": "pypi", "version": "==21.6b0" }, + "circuitpython-stubs": { + "hashes": [ + "sha256:5963ef6b41b03e97049d48142cb23778f3e4ca841620296d7e13b00025522569" + ], + "index": "pypi", + "version": "==7.0.0a6.dev195" + }, "click": { "hashes": [ "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a", @@ -71,11 +78,11 @@ }, "flake8-black": { "hashes": [ - "sha256:941514149cb8b489cb17a4bb1cf18d84375db3b34381bb018de83509437931a0", - "sha256:f26651bc10db786c03f4093414f7c9ea982ed8a244cec323c984feeffdf4c118" + "sha256:c199844bc1b559d91195ebe8620216f21ed67f2cc1ff6884294c91a0d2492684", + "sha256:cc080ba5b3773b69ba102b6617a00cc4ecbad8914109690cfda4d565ea435d96" ], "index": "pypi", - "version": "==0.2.1" + "version": "==0.2.3" }, "flake8-commas": { "hashes": [ @@ -87,11 +94,11 @@ }, "flake8-comprehensions": { "hashes": [ - "sha256:b07aef3277623db32310aa241a1cec67212b53c1d18e767d7e26d4d83aa05bf7", - "sha256:f24be9032587127f7a5bc6d066bf755b6e66834f694383adb8a673e229c1f559" + "sha256:4888de89248b7f7535159189ff693c77f8354f6d37a02619fa28c9921a913aa0", + "sha256:e9a010b99aa90c05790d45281ad9953df44a4a08a1a8f6cd41f98b4fc6a268a0" ], "index": "pypi", - "version": "==3.5.0" + "version": "==3.6.1" }, "flake8-isort": { "hashes": [ @@ -103,65 +110,66 @@ }, "flake8-quotes": { "hashes": [ - "sha256:3f1116e985ef437c130431ac92f9b3155f8f652fda7405ac22ffdfd7a9d1055e" + "sha256:f1dd87830ed77ff2ce47fc0ee0fd87ae20e8f045355354ffbf4dcaa18d528217" ], "index": "pypi", - "version": "==3.2.0" + "version": "==3.3.0" }, "greenlet": { "hashes": [ - "sha256:03f28a5ea20201e70ab70518d151116ce939b412961c33827519ce620957d44c", - "sha256:06d7ac89e6094a0a8f8dc46aa61898e9e1aec79b0f8b47b2400dd51a44dbc832", - "sha256:06ecb43b04480e6bafc45cb1b4b67c785e183ce12c079473359e04a709333b08", - "sha256:096cb0217d1505826ba3d723e8981096f2622cde1eb91af9ed89a17c10aa1f3e", - "sha256:0c557c809eeee215b87e8a7cbfb2d783fb5598a78342c29ade561440abae7d22", - "sha256:0de64d419b1cb1bfd4ea544bedea4b535ef3ae1e150b0f2609da14bbf48a4a5f", - "sha256:14927b15c953f8f2d2a8dffa224aa78d7759ef95284d4c39e1745cf36e8cdd2c", - "sha256:16183fa53bc1a037c38d75fdc59d6208181fa28024a12a7f64bb0884434c91ea", - "sha256:206295d270f702bc27dbdbd7651e8ebe42d319139e0d90217b2074309a200da8", - "sha256:22002259e5b7828b05600a762579fa2f8b33373ad95a0ee57b4d6109d0e589ad", - "sha256:2325123ff3a8ecc10ca76f062445efef13b6cf5a23389e2df3c02a4a527b89bc", - "sha256:258f9612aba0d06785143ee1cbf2d7361801c95489c0bd10c69d163ec5254a16", - "sha256:3096286a6072553b5dbd5efbefc22297e9d06a05ac14ba017233fedaed7584a8", - "sha256:3d13da093d44dee7535b91049e44dd2b5540c2a0e15df168404d3dd2626e0ec5", - "sha256:408071b64e52192869129a205e5b463abda36eff0cebb19d6e63369440e4dc99", - "sha256:598bcfd841e0b1d88e32e6a5ea48348a2c726461b05ff057c1b8692be9443c6e", - "sha256:5d928e2e3c3906e0a29b43dc26d9b3d6e36921eee276786c4e7ad9ff5665c78a", - "sha256:5f75e7f237428755d00e7460239a2482fa7e3970db56c8935bd60da3f0733e56", - "sha256:60848099b76467ef09b62b0f4512e7e6f0a2c977357a036de602b653667f5f4c", - "sha256:6b1d08f2e7f2048d77343279c4d4faa7aef168b3e36039cba1917fffb781a8ed", - "sha256:70bd1bb271e9429e2793902dfd194b653221904a07cbf207c3139e2672d17959", - "sha256:76ed710b4e953fc31c663b079d317c18f40235ba2e3d55f70ff80794f7b57922", - "sha256:7920e3eccd26b7f4c661b746002f5ec5f0928076bd738d38d894bb359ce51927", - "sha256:7db68f15486d412b8e2cfcd584bf3b3a000911d25779d081cbbae76d71bd1a7e", - "sha256:8833e27949ea32d27f7e96930fa29404dd4f2feb13cce483daf52e8842ec246a", - "sha256:944fbdd540712d5377a8795c840a97ff71e7f3221d3fddc98769a15a87b36131", - "sha256:9a6b035aa2c5fcf3dbbf0e3a8a5bc75286fc2d4e6f9cfa738788b433ec894919", - "sha256:9bdcff4b9051fb1aa4bba4fceff6a5f770c6be436408efd99b76fc827f2a9319", - "sha256:a9017ff5fc2522e45562882ff481128631bf35da444775bc2776ac5c61d8bcae", - "sha256:aa4230234d02e6f32f189fd40b59d5a968fe77e80f59c9c933384fe8ba535535", - "sha256:ad80bb338cf9f8129c049837a42a43451fc7c8b57ad56f8e6d32e7697b115505", - "sha256:adb94a28225005890d4cf73648b5131e885c7b4b17bc762779f061844aabcc11", - "sha256:b3090631fecdf7e983d183d0fad7ea72cfb12fa9212461a9b708ff7907ffff47", - "sha256:b33b51ab057f8a20b497ffafdb1e79256db0c03ef4f5e3d52e7497200e11f821", - "sha256:b97c9a144bbeec7039cca44df117efcbeed7209543f5695201cacf05ba3b5857", - "sha256:be13a18cec649ebaab835dff269e914679ef329204704869f2f167b2c163a9da", - "sha256:be9768e56f92d1d7cd94185bab5856f3c5589a50d221c166cc2ad5eb134bd1dc", - "sha256:c1580087ab493c6b43e66f2bdd165d9e3c1e86ef83f6c2c44a29f2869d2c5bd5", - "sha256:c35872b2916ab5a240d52a94314c963476c989814ba9b519bc842e5b61b464bb", - "sha256:c70c7dd733a4c56838d1f1781e769081a25fade879510c5b5f0df76956abfa05", - "sha256:c767458511a59f6f597bfb0032a1c82a52c29ae228c2c0a6865cfeaeaac4c5f5", - "sha256:c87df8ae3f01ffb4483c796fe1b15232ce2b219f0b18126948616224d3f658ee", - "sha256:ca1c4a569232c063615f9e70ff9a1e2fee8c66a6fb5caf0f5e8b21a396deec3e", - "sha256:cc407b68e0a874e7ece60f6639df46309376882152345508be94da608cc0b831", - "sha256:da862b8f7de577bc421323714f63276acb2f759ab8c5e33335509f0b89e06b8f", - "sha256:dfe7eac0d253915116ed0cd160a15a88981a1d194c1ef151e862a5c7d2f853d3", - "sha256:ed1377feed808c9c1139bdb6a61bcbf030c236dd288d6fca71ac26906ab03ba6", - "sha256:f42ad188466d946f1b3afc0a9e1a266ac8926461ee0786c06baac6bd71f8a6f3", - "sha256:f92731609d6625e1cc26ff5757db4d32b6b810d2a3363b0ff94ff573e5901f6f" + "sha256:04e1849c88aa56584d4a0a6e36af5ec7cc37993fdc1fda72b56aa1394a92ded3", + "sha256:05e72db813c28906cdc59bd0da7c325d9b82aa0b0543014059c34c8c4ad20e16", + "sha256:07e6d88242e09b399682b39f8dfa1e7e6eca66b305de1ff74ed9eb1a7d8e539c", + "sha256:090126004c8ab9cd0787e2acf63d79e80ab41a18f57d6448225bbfcba475034f", + "sha256:1796f2c283faab2b71c67e9b9aefb3f201fdfbee5cb55001f5ffce9125f63a45", + "sha256:2f89d74b4f423e756a018832cd7a0a571e0a31b9ca59323b77ce5f15a437629b", + "sha256:34e6675167a238bede724ee60fe0550709e95adaff6a36bcc97006c365290384", + "sha256:3e594015a2349ec6dcceda9aca29da8dc89e85b56825b7d1f138a3f6bb79dd4c", + "sha256:3f8fc59bc5d64fa41f58b0029794f474223693fd00016b29f4e176b3ee2cfd9f", + "sha256:3fc6a447735749d651d8919da49aab03c434a300e9f0af1c886d560405840fd1", + "sha256:40abb7fec4f6294225d2b5464bb6d9552050ded14a7516588d6f010e7e366dcc", + "sha256:44556302c0ab376e37939fd0058e1f0db2e769580d340fb03b01678d1ff25f68", + "sha256:476ba9435afaead4382fbab8f1882f75e3fb2285c35c9285abb3dd30237f9142", + "sha256:4870b018ca685ff573edd56b93f00a122f279640732bb52ce3a62b73ee5c4a92", + "sha256:4adaf53ace289ced90797d92d767d37e7cdc29f13bd3830c3f0a561277a4ae83", + "sha256:4eae94de9924bbb4d24960185363e614b1b62ff797c23dc3c8a7c75bbb8d187e", + "sha256:5317701c7ce167205c0569c10abc4bd01c7f4cf93f642c39f2ce975fa9b78a3c", + "sha256:5c3b735ccf8fc8048664ee415f8af5a3a018cc92010a0d7195395059b4b39b7d", + "sha256:5cde7ee190196cbdc078511f4df0be367af85636b84d8be32230f4871b960687", + "sha256:655ab836324a473d4cd8cf231a2d6f283ed71ed77037679da554e38e606a7117", + "sha256:6ce9d0784c3c79f3e5c5c9c9517bbb6c7e8aa12372a5ea95197b8a99402aa0e6", + "sha256:6e0696525500bc8aa12eae654095d2260db4dc95d5c35af2b486eae1bf914ccd", + "sha256:75ff270fd05125dce3303e9216ccddc541a9e072d4fc764a9276d44dee87242b", + "sha256:8039f5fe8030c43cd1732d9a234fdcbf4916fcc32e21745ca62e75023e4d4649", + "sha256:84488516639c3c5e5c0e52f311fff94ebc45b56788c2a3bfe9cf8e75670f4de3", + "sha256:84782c80a433d87530ae3f4b9ed58d4a57317d9918dfcc6a59115fa2d8731f2c", + "sha256:8ddb38fb6ad96c2ef7468ff73ba5c6876b63b664eebb2c919c224261ae5e8378", + "sha256:98b491976ed656be9445b79bc57ed21decf08a01aaaf5fdabf07c98c108111f6", + "sha256:990e0f5e64bcbc6bdbd03774ecb72496224d13b664aa03afd1f9b171a3269272", + "sha256:9b02e6039eafd75e029d8c58b7b1f3e450ca563ef1fe21c7e3e40b9936c8d03e", + "sha256:a11b6199a0b9dc868990456a2667167d0ba096c5224f6258e452bfbe5a9742c5", + "sha256:a414f8e14aa7bacfe1578f17c11d977e637d25383b6210587c29210af995ef04", + "sha256:a91ee268f059583176c2c8b012a9fce7e49ca6b333a12bbc2dd01fc1a9783885", + "sha256:ac991947ca6533ada4ce7095f0e28fe25d5b2f3266ad5b983ed4201e61596acf", + "sha256:b050dbb96216db273b56f0e5960959c2b4cb679fe1e58a0c3906fa0a60c00662", + "sha256:b97a807437b81f90f85022a9dcfd527deea38368a3979ccb49d93c9198b2c722", + "sha256:bad269e442f1b7ffa3fa8820b3c3aa66f02a9f9455b5ba2db5a6f9eea96f56de", + "sha256:bf3725d79b1ceb19e83fb1aed44095518c0fcff88fba06a76c0891cfd1f36837", + "sha256:c0f22774cd8294078bdf7392ac73cf00bfa1e5e0ed644bd064fdabc5f2a2f481", + "sha256:c1862f9f1031b1dee3ff00f1027fcd098ffc82120f43041fe67804b464bbd8a7", + "sha256:c8d4ed48eed7414ccb2aaaecbc733ed2a84c299714eae3f0f48db085342d5629", + "sha256:cf31e894dabb077a35bbe6963285d4515a387ff657bd25b0530c7168e48f167f", + "sha256:d15cb6f8706678dc47fb4e4f8b339937b04eda48a0af1cca95f180db552e7663", + "sha256:dfcb5a4056e161307d103bc013478892cfd919f1262c2bb8703220adcb986362", + "sha256:e02780da03f84a671bb4205c5968c120f18df081236d7b5462b380fd4f0b497b", + "sha256:e2002a59453858c7f3404690ae80f10c924a39f45f6095f18a985a1234c37334", + "sha256:e22a82d2b416d9227a500c6860cf13e74060cf10e7daf6695cbf4e6a94e0eee4", + "sha256:e41f72f225192d5d4df81dad2974a8943b0f2d664a2a5cfccdf5a01506f5523c", + "sha256:f253dad38605486a4590f9368ecbace95865fea0f2b66615d121ac91fd1a1563", + "sha256:fddfb31aa2ac550b938d952bca8a87f1db0f8dc930ffa14ce05b5c08d27e7fd1" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.1.0" + "version": "==1.1.1" }, "ipdb": { "hashes": [ @@ -172,11 +180,11 @@ }, "ipython": { "hashes": [ - "sha256:9bc24a99f5d19721fb8a2d1408908e9c0520a17fff2233ffe82620847f17f1b6", - "sha256:d513e93327cf8657d6467c81f1f894adc125334ffe0e4ddd1abbb1c78d828703" + "sha256:0cff04bb042800129348701f7bd68a430a844e8fb193979c08f6c99f28bb735e", + "sha256:892743b65c21ed72b806a3a602cca408520b3200b89d1924f4b3d2cdb3692362" ], "index": "pypi", - "version": "==7.24.1" + "version": "==7.26.0" }, "ipython-genutils": { "hashes": [ @@ -187,11 +195,11 @@ }, "isort": { "hashes": [ - "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6", - "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d" + "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899", + "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2" ], "index": "pypi", - "version": "==5.8.0" + "version": "==5.9.3" }, "jedi": { "hashes": [ @@ -249,6 +257,35 @@ ], "version": "==1.0.2" }, + "mypy": { + "hashes": [ + "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9", + "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a", + "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9", + "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e", + "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2", + "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212", + "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b", + "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885", + "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150", + "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703", + "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072", + "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457", + "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e", + "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0", + "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb", + "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97", + "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8", + "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811", + "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6", + "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de", + "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504", + "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921", + "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d" + ], + "index": "pypi", + "version": "==0.910" + }, "mypy-extensions": { "hashes": [ "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", @@ -263,6 +300,13 @@ "index": "pypi", "version": "==0.3.1" }, + "nodeenv": { + "hashes": [ + "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b", + "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7" + ], + "version": "==1.6.0" + }, "parso": { "hashes": [ "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398", @@ -273,10 +317,10 @@ }, "pathspec": { "hashes": [ - "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd", - "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d" + "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", + "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" ], - "version": "==0.8.1" + "version": "==0.9.0" }, "pexpect": { "hashes": [ @@ -295,11 +339,11 @@ }, "prompt-toolkit": { "hashes": [ - "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f", - "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88" + "sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c", + "sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.0.19" + "markers": "python_full_version >= '3.6.2'", + "version": "==3.0.20" }, "ptyprocess": { "hashes": [ @@ -326,11 +370,11 @@ }, "pygments": { "hashes": [ - "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f", - "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e" + "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380", + "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6" ], "markers": "python_version >= '3.5'", - "version": "==2.9.0" + "version": "==2.10.0" }, "pynvim": { "hashes": [ @@ -338,6 +382,14 @@ ], "version": "==0.4.3" }, + "pyright": { + "hashes": [ + "sha256:dd8e18c54321340be44a708b6037c0b967486c32b3f492741fffdc205cb82f15", + "sha256:e2668730cddf580e696d4a11946e740e2f5647df1eb45f7c55b7029376eac5a1" + ], + "index": "pypi", + "version": "==0.0.9" + }, "pyserial": { "hashes": [ "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", @@ -347,18 +399,19 @@ }, "python-dateutil": { "hashes": [ - "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", - "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.1" + "version": "==2.8.2" }, "python-dotenv": { "hashes": [ - "sha256:dd8fe852847f4fbfadabf6183ddd4c824a9651f02d51714fa075c95561959c7d", - "sha256:effaac3c1e58d89b3ccb4d04a40dc7ad6e0275fda25fd75ae9d323e2465e202d" + "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1", + "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172" ], - "version": "==0.18.0" + "markers": "python_version >= '3.5'", + "version": "==0.19.0" }, "python-magic": { "hashes": [ @@ -370,49 +423,49 @@ }, "regex": { "hashes": [ - "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5", - "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79", - "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31", - "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500", - "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11", - "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14", - "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3", - "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439", - "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c", - "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82", - "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711", - "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093", - "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a", - "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb", - "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8", - "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17", - "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000", - "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d", - "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480", - "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc", - "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0", - "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9", - "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765", - "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e", - "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a", - "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07", - "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f", - "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac", - "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7", - "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed", - "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968", - "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7", - "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2", - "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4", - "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87", - "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8", - "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10", - "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29", - "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605", - "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6", - "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042" + "sha256:0696eb934dee723e3292056a2c046ddb1e4dd3887685783a9f4af638e85dee76", + "sha256:105122fa63da98d8456d5026bc6ac5a1399fd82fa6bad22c6ea641b1572c9142", + "sha256:116c277774f84266044e889501fe79cfd293a8b4336b7a5e89b9f20f1e5a9f21", + "sha256:12eaf0bbe568bd62e6cade7937e0bf01a2a4cef49a82f4fd204401e78409e158", + "sha256:1401cfa4320691cbd91191ec678735c727dee674d0997b0902a5a38ad482faf5", + "sha256:19acdb8831a4e3b03b23369db43178d8fee1f17b99c83af6cd907886f76bd9d4", + "sha256:208851a2f8dd31e468f0b5aa6c94433975bd67a107a4e7da3bdda947c9f85e25", + "sha256:24d68499a27b2d93831fde4a9b84ea5b19e0ab141425fbc9ab1e5b4dad179df7", + "sha256:2778c6cb379d804e429cc8e627392909e60db5152b42c695c37ae5757aae50ae", + "sha256:2a0a5e323cf86760784ce2b91d8ab5ea09d0865d6ef4da0151e03d15d097b24e", + "sha256:2d9cbe0c755ab8b6f583169c0783f7278fc6b195e423b09c5a8da6f858025e96", + "sha256:2de1429e4eeab799c168a4f6e6eecdf30fcaa389bba4039cc8a065d6b7aad647", + "sha256:32753eda8d413ce4f208cfe01dd61171a78068a6f5d5f38ccd751e00585cdf1d", + "sha256:3ee8ad16a35c45a5bab098e39020ecb6fec3b0e700a9d88983d35cbabcee79c8", + "sha256:4f03fc0a25122cdcbf39136510d4ea7627f732206892db522adf510bc03b8c67", + "sha256:4f3e36086d6631ceaf468503f96a3be0d247caef0660c9452fb1b0c055783851", + "sha256:503c1ba0920a46a1844363725215ef44d59fcac2bd2c03ae3c59aa9d08d29bd6", + "sha256:507861cf3d97a86fbe26ea6cc04660ae028b9e4080b8290e28b99547b4e15d89", + "sha256:56ae6e3cf0506ec0c40b466e31f41ee7a7149a2b505ae0ee50edd9043b423d27", + "sha256:6530b7b9505123cdea40a2301225183ca65f389bc6129f0c225b9b41680268d8", + "sha256:6729914dd73483cd1c8aaace3ac082436fc98b0072743ac136eaea0b3811d42f", + "sha256:7406dd2e44c7cfb4680c0a45a03264381802c67890cf506c147288f04c67177d", + "sha256:7684016b73938ca12d160d2907d141f06b7597bd17d854e32bb7588be01afa1d", + "sha256:7db58ad61f3f6ea393aaf124d774ee0c58806320bc85c06dc9480f5c7219c250", + "sha256:83946ca9278b304728b637bc8d8200ab1663a79de85e47724594917aeed0e892", + "sha256:84057cfae5676f456b03970eb78b7e182fddc80c2daafd83465a3d6ca9ff8dbf", + "sha256:862b6164e9a38b5c495be2c2854e75fd8af12c5be4c61dc9b42d255980d7e907", + "sha256:8ddb4f9ce6bb388ecc97b4b3eb37e786f05d7d5815e8822e0d87a3dbd7100649", + "sha256:92eb03f47427fea452ff6956d11f5d5a3f22a048c90a0f34fa223e6badab6c85", + "sha256:a5f3bc727fea58f21d99c22e6d4fca652dc11dbc2a1e7cfc4838cd53b2e3691f", + "sha256:a6180dbf5945b27e9420e1b58c3cacfc79ad5278bdad3ea35109f5680fbe16d1", + "sha256:b158f673ae6a6523f13704f70aa7e4ce875f91e379bece4362c89db18db189d5", + "sha256:cd45b4542134de63e7b9dd653e0a2d7d47ffed9615e3637c27ca5f6b78ea68bb", + "sha256:d2404336fd16788ea757d4218a2580de60adb052d9888031e765320be8884309", + "sha256:db888d4fb33a2fd54b57ac55d5015e51fa849f0d8592bd799b4e47f83bd04e00", + "sha256:dde0ac721c7c5bfa5f9fc285e811274dec3c392f2c1225f7d07ca98a8187ca84", + "sha256:de0d06ccbc06af5bf93bddec10f4f80275c5d74ea6d28b456931f3955f58bc8c", + "sha256:e02dad60e3e8442eefd28095e99b2ac98f2b8667167493ac6a2f3aadb5d84a17", + "sha256:e960fe211496333b2f7e36badf4c22a919d740386681f79139ee346b403d1ca1", + "sha256:e9700c52749cb3e90c98efd72b730c97b7e4962992fca5fbcaf1363be8e3b849", + "sha256:ee318974a1fdacba1701bc9e552e9015788d6345416364af6fa987424ff8df53" ], - "version": "==2021.4.4" + "version": "==2021.8.27" }, "s3cmd": { "hashes": [ @@ -432,10 +485,10 @@ }, "testfixtures": { "hashes": [ - "sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda", - "sha256:9ed31e83f59619e2fa17df053b241e16e0608f4580f7b5a9333a0c9bdcc99137" + "sha256:0a6422737f6d89b45cdef1e2df5576f52ad0f507956002ce1020daa9f44211d6", + "sha256:486be7b01eb71326029811878a3317b7e7994324621c0ec633c8e24499d8d5b3" ], - "version": "==6.17.1" + "version": "==6.18.1" }, "toml": { "hashes": [ @@ -453,6 +506,22 @@ "markers": "python_version >= '3.7'", "version": "==5.0.5" }, + "typing": { + "hashes": [ + "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9", + "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5" + ], + "index": "pypi", + "version": "==3.7.4.3" + }, + "typing-extensions": { + "hashes": [ + "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", + "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", + "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + ], + "version": "==3.10.0.0" + }, "wcwidth": { "hashes": [ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", diff --git a/kmk/consts.py b/kmk/consts.py index b222ede..62bc121 100644 --- a/kmk/consts.py +++ b/kmk/consts.py @@ -11,3 +11,17 @@ class UnicodeMode: LINUX = IBUS = const(1) MACOS = OSX = RALT = const(2) WINC = const(3) + + +TYPING_PLATFORMS = [ + 'linux', + 'linux2', + 'win32', + 'cygwin', + 'msys', + 'darwin', + 'freebsd7', + 'freebsd8', + 'freebsdN', + 'openbsd6', +] diff --git a/kmk/extensions/__init__.py b/kmk/extensions/__init__.py index 1eba5e5..3f9dbdb 100644 --- a/kmk/extensions/__init__.py +++ b/kmk/extensions/__init__.py @@ -1,16 +1,21 @@ +from kmk.kmk_keyboard import KMKKeyboard + + class InvalidExtensionEnvironment(Exception): pass class Extension: - _enabled = True + _enabled = True # type: bool def enable(self, keyboard): + # type: (KMKKeyboard) -> None self._enabled = True self.on_runtime_enable(keyboard) def disable(self, keyboard): + # type (KMKKeyboard) -> None self._enabled = False self.on_runtime_disable(keyboard) @@ -18,34 +23,43 @@ class Extension: # The below methods should be implemented by subclasses def on_runtime_enable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def on_runtime_disable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def during_bootup(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def before_matrix_scan(self, keyboard): + # type: (KMKKeyboard) -> None ''' Return value will be injected as an extra matrix update ''' raise NotImplementedError def after_matrix_scan(self, keyboard): + # type: (KMKKeyboard) -> None ''' Return value will be replace matrix update if supplied ''' raise NotImplementedError def before_hid_send(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def after_hid_send(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def on_powersave_enable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def on_powersave_disable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError diff --git a/kmk/extensions/international.py b/kmk/extensions/international.py index eab8087..bb6aedb 100644 --- a/kmk/extensions/international.py +++ b/kmk/extensions/international.py @@ -1,6 +1,7 @@ '''Adds international keys''' from kmk.extensions import Extension from kmk.keys import make_key +from kmk.kmk_keyboard import KMKKeyboard class International(Extension): @@ -32,28 +33,37 @@ class International(Extension): make_key(code=152, names=('LANG9',)) def on_runtime_enable(self, sandbox): + # type: (KMKKeyboard) -> None return def on_runtime_disable(self, sandbox): + # type: (KMKKeyboard) -> None return def during_bootup(self, sandbox): + # type: (KMKKeyboard) -> None return def before_matrix_scan(self, sandbox): + # type: (KMKKeyboard) -> None return def after_matrix_scan(self, sandbox): + # type: (KMKKeyboard) -> None return def before_hid_send(self, sandbox): + # type: (KMKKeyboard) -> None return def after_hid_send(self, sandbox): + # type: (KMKKeyboard) -> None return def on_powersave_enable(self, sandbox): + # type: (KMKKeyboard) -> None return def on_powersave_disable(self, sandbox): + # type: (KMKKeyboard) -> None return diff --git a/kmk/handlers/stock.py b/kmk/handlers/stock.py index ba301eb..218f7a7 100644 --- a/kmk/handlers/stock.py +++ b/kmk/handlers/stock.py @@ -1,11 +1,29 @@ +import sys from time import sleep +from kmk.consts import TYPING_PLATFORMS + +if sys.platform in TYPING_PLATFORMS: + from typing import Any, Optional + + from kmk.keys import Key, KeyAttrDict + from kmk.kmk_keyboard import KMKKeyboard # Avoid cyclical imports + def passthrough(key, keyboard, *args, **kwargs): return keyboard -def default_pressed(key, keyboard, KC, coord_int=None, coord_raw=None, *args, **kwargs): +def default_pressed( + key, # type: Key + keyboard, # type: KMKKeyboard + KC, # type: KeyAttrDict + coord_int=None, # type: Optional[int] + coord_raw=None, # type: Optional[str] + *args, # type: Any + **kwargs, # type: Any +): + # type: (...) -> KMKKeyboard keyboard.hid_pending = True keyboard.keys_pressed.add(key) @@ -14,8 +32,15 @@ def default_pressed(key, keyboard, KC, coord_int=None, coord_raw=None, *args, ** def default_released( - key, keyboard, KC, coord_int=None, coord_raw=None, *args, **kwargs # NOQA + key, # type: Key + keyboard, # type: KMKKeyboard + KC, # type: KeyAttrDict + coord_int=None, # type: Optional[int] + coord_raw=None, # type: Optional[str] + *args, # type: Any + **kwargs, # type: Any # NOQA ): + # type: (...) -> KMKKeyboard keyboard.hid_pending = True keyboard.keys_pressed.discard(key) diff --git a/kmk/key_validators.py b/kmk/key_validators.py index 71af33d..b079497 100644 --- a/kmk/key_validators.py +++ b/kmk/key_validators.py @@ -1,3 +1,6 @@ +import sys + +from kmk.consts import TYPING_PLATFORMS from kmk.types import ( KeySeqSleepMeta, LayerKeyMeta, @@ -6,12 +9,20 @@ from kmk.types import ( UnicodeModeKeyMeta, ) +if sys.platform in TYPING_PLATFORMS: + from typing import List, Optional + + # Avoid cyclical imports + from kmk.keys import Key + def key_seq_sleep_validator(ms): + # type: (float) -> KeySeqSleepMeta return KeySeqSleepMeta(ms) def layer_key_validator(layer, kc=None): + # type: (int, Optional[Key]) -> LayerKeyMeta ''' Validates the syntax (but not semantics) of a layer key call. We won't have access to the keymap here, so we can't verify much of anything useful @@ -22,7 +33,8 @@ def layer_key_validator(layer, kc=None): return LayerKeyMeta(layer=layer, kc=kc) -def mod_tap_validator(kc, mods=None): +def mod_tap_validator(kc, mods): + # type: (Key, Optional[List[Key]]) -> ModTapKeyMeta ''' Validates that mod tap keys are correctly used ''' @@ -30,8 +42,10 @@ def mod_tap_validator(kc, mods=None): def tap_dance_key_validator(*codes): + # type: (*Key) -> TapDanceKeyMeta return TapDanceKeyMeta(codes) def unicode_mode_key_validator(mode): + # type: (int) -> UnicodeModeKeyMeta return UnicodeModeKeyMeta(mode) diff --git a/kmk/modules/__init__.py b/kmk/modules/__init__.py index e78b6c9..9bb95cf 100644 --- a/kmk/modules/__init__.py +++ b/kmk/modules/__init__.py @@ -1,3 +1,8 @@ +from typing import NoReturn + +from kmk.kmk_keyboard import KMKKeyboard + + class InvalidExtensionEnvironment(Exception): pass @@ -12,29 +17,29 @@ class Module: # The below methods should be implemented by subclasses - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> NoReturn: ''' Return value will be injected as an extra matrix update ''' raise NotImplementedError - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> NoReturn: ''' Return value will be replace matrix update if supplied ''' raise NotImplementedError - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError diff --git a/kmk/modules/encoder.py b/kmk/modules/encoder.py index a7aca5c..d3ba16b 100644 --- a/kmk/modules/encoder.py +++ b/kmk/modules/encoder.py @@ -1,66 +1,89 @@ import digitalio from supervisor import ticks_ms +from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union + +from kmk.keys import Key +from kmk.kmk_keyboard import KMKKeyboard +from kmk.kmktime import ticks_ms from kmk.modules import Module +EncoderMap = Tuple[ + List[Tuple[Key, Key, int]], + List[Tuple[Key, Key, int]], + List[Tuple[None, None, int]], +] + class EncoderPadState: - OFF = False - ON = True + OFF: bool = False + ON: bool = True class EndcoderDirection: - Left = False - Right = True + Left: bool = False + Right: bool = True class Encoder: def __init__( self, - pad_a, - pad_b, - button_pin=None, - ): - self.pad_a = self.PreparePin(pad_a) # board pin for enc pin a - self.pad_a_state = False - self.pad_b = self.PreparePin(pad_b) # board pin for enc pin b - self.pad_b_state = False - self.button_pin = self.PreparePin(button_pin) # board pin for enc btn + pad_a: Any, + pad_b: Any, + button_pin: Optional[Any] = None, + ) -> None: + self.pad_a: Union[digitalio.DigitalInOut, None] = self.PreparePin( + pad_a + ) # board pin for enc pin a + self.pad_a_state: bool = False + self.pad_b: Union[digitalio.DigitalInOut, None] = self.PreparePin( + pad_b + ) # board pin for enc pin b + self.pad_b_state: bool = False + self.button_pin: Union[digitalio.DigitalInOut, None] = self.PreparePin( + button_pin + ) # board pin for enc btn self.button_state = None # state of pushbutton on encoder if enabled - self.encoder_value = 0 # clarify what this value is - self.encoder_state = ( + self.encoder_value: int = 0 # clarify what this value is + self.encoder_state: Tuple[bool, bool] = ( self.pad_a_state, self.pad_b_state, ) # quaderature encoder state - self.encoder_direction = None # arbitrary, tells direction of knob - self.last_encoder_state = None # not used yet - self.resolution = 2 # number of keys sent per position change - self.revolution_count = 20 # position changes per revolution - self.has_button = False # enable/disable button functionality - self.encoder_data = None # 6tuple containing all encoder data - self.position_change = None # revolution count, inc/dec as knob turns - self.last_encoder_value = 0 # not used - self.is_inverted = False # switch to invert knob direction - self.vel_mode = False # enable the velocity output - self.vel_ts = None # velocity timestamp - self.last_vel_ts = 0 # last velocity timestamp - self.encoder_speed = None # ms per position change(4 states) - self.encoder_map = None - self.eps = EncoderPadState() - self.encoder_pad_lookup = { + self.encoder_direction: Optional[ + bool + ] = None # arbitrary, tells direction of knob + self.last_encoder_state: Optional[Tuple[bool, bool]] = None # not used yet + self.resolution: int = 2 # number of keys sent per position change + self.revolution_count: int = 20 # position changes per revolution + self.has_button: bool = False # enable/disable button functionality + self.encoder_data: Optional[Tuple] = None # 6tuple containing all encoder data + self.position_change: Optional[ + int + ] = None # revolution count, inc/dec as knob turns + self.last_encoder_value: int = 0 # not used + self.is_inverted: bool = False # switch to invert knob direction + self.vel_mode: bool = False # enable the velocity output + self.vel_ts: Optional[float] = None # velocity timestamp + self.last_vel_ts: float = 0 # last velocity timestamp + self.encoder_speed: Optional[float] = None # ms per position change(4 states) + self.encoder_map: Optional[EncoderMap] = None + self.eps: EncoderPadState = EncoderPadState() + self.encoder_pad_lookup: Dict[bool, bool] = { False: self.eps.OFF, True: self.eps.ON, } - self.edr = EndcoderDirection() # lookup for current encoder direction - self.encoder_dir_lookup = { + self.edr: EndcoderDirection = ( + EndcoderDirection() + ) # lookup for current encoder direction + self.encoder_dir_lookup: Dict[bool, bool] = { False: self.edr.Left, True: self.edr.Right, } - def __repr__(self, idx): + def __repr__(self, idx: int) -> str: return 'ENCODER_{}({})'.format(idx, self._to_dict()) - def _to_dict(self): + def _to_dict(self) -> Dict[str, Any]: return { 'Encoder_State': self.encoder_state, 'Direction': self.encoder_direction, @@ -71,7 +94,7 @@ class Encoder: } # adapted for CircuitPython from raspi - def PreparePin(self, num): + def PreparePin(self, num: Union[Any, None]) -> Union[digitalio.DigitalInOut, None]: if num is not None: pad = digitalio.DigitalInOut(num) pad.direction = digitalio.Direction.INPUT @@ -81,7 +104,7 @@ class Encoder: return None # checks encoder pins, reports encoder data - def report(self): + def report(self) -> Union[int, None]: new_encoder_state = ( self.encoder_pad_lookup[int(self.pad_a.value)], self.encoder_pad_lookup[int(self.pad_b.value)], @@ -143,14 +166,14 @@ class Encoder: return None # invert knob direction if encoder pins are soldered backwards - def invert_rotation(self, new, old): + def invert_rotation(self, new: int, old: int) -> int: if self.is_inverted: return -(new - old) else: return new - old # returns knob velocity as milliseconds between position changes(detents) - def vel_report(self): + def vel_report(self) -> float: self.encoder_speed = self.vel_ts - self.last_vel_ts self.last_vel_ts = self.vel_ts return self.encoder_speed @@ -158,50 +181,54 @@ class Encoder: class EncoderHandler(Module): - encoders = [] - debug_enabled = False # not working as inttended, do not use for now + encoders: ClassVar[List[Encoder]] = [] + debug_enabled: ClassVar[ + bool + ] = False # not working as inttended, do not use for now - def __init__(self, pad_a, pad_b, encoder_map): - self.pad_a = pad_a - self.pad_b = pad_b - self.encoder_count = len(self.pad_a) - self.encoder_map = encoder_map + def __init__( + self, pad_a: List[Any], pad_b: List[Any], encoder_map: EncoderMap + ) -> None: + self.pad_a: List[Any] = pad_a + self.pad_b: List[Any] = pad_b + self.encoder_count: int = len(self.pad_a) + self.encoder_map: EncoderMap = encoder_map self.make_encoders() - def on_runtime_enable(self, keyboard): + def on_runtime_enable(self, keyboard: KMKKeyboard) -> None: return - def on_runtime_disable(self, keyboard): + def on_runtime_disable(self, keyboard: KMKKeyboard) -> None: return - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: return - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> Union[KMKKeyboard, None]: ''' Return value will be injected as an extra matrix update ''' return self.get_reports(keyboard) - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: ''' Return value will be replace matrix update if supplied ''' return - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: return - def make_encoders(self): + def make_encoders(self) -> None: for i in range(self.encoder_count): self.encoders.append( Encoder( @@ -210,7 +237,9 @@ class EncoderHandler(Module): ) ) - def send_encoder_keys(self, keyboard, encoder_key, encoder_idx): + def send_encoder_keys( + self, keyboard: KMKKeyboard, encoder_key: int, encoder_idx: int + ) -> KMKKeyboard: # position in the encoder map tuple encoder_resolution = 2 for _ in range( @@ -221,7 +250,7 @@ class EncoderHandler(Module): ) return keyboard - def get_reports(self, keyboard): + def get_reports(self, keyboard: KMKKeyboard) -> Union[KMKKeyboard, None]: for idx in range(self.encoder_count): if self.debug_enabled: # not working as inttended, do not use for now print(self.encoders[idx].__repr__(idx)) diff --git a/kmk/modules/power.py b/kmk/modules/power.py index aa72eb1..814ed0e 100644 --- a/kmk/modules/power.py +++ b/kmk/modules/power.py @@ -4,21 +4,24 @@ from supervisor import ticks_ms from time import sleep +from typing import Any, Dict, Optional + from kmk.handlers.stock import passthrough as handler_passthrough -from kmk.keys import make_key +from kmk.keys import Key, make_key +from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import check_deadline from kmk.modules import Module class Power(Module): - def __init__(self, powersave_pin=None): - self.enable = False + def __init__(self, powersave_pin: Optional[Any] = None) -> None: + self.enable: bool = False self.powersave_pin = powersave_pin # Powersave pin board object - self._powersave_start = ticks_ms() - self._usb_last_scan = ticks_ms() - 5000 - self._psp = None # Powersave pin object - self._i2c = 0 - self._loopcounter = 0 + self._powersave_start: float = ticks_ms() + self._usb_last_scan: float = ticks_ms() - 5000 + self._psp: Optional[digitalio.DigitalInOut] = None # Powersave pin object + self._i2c: int = 0 + self._loopcounter: int = 0 make_key( names=('PS_TOG',), on_press=self._ps_tog, on_release=handler_passthrough @@ -30,10 +33,10 @@ class Power(Module): names=('PS_OFF',), on_press=self._ps_disable, on_release=handler_passthrough ) - def __repr__(self): + def __repr__(self) -> str: return f'Power({self._to_dict()})' - def _to_dict(self): + def _to_dict(self) -> Dict[str, Any]: return { 'enable': self.enable, 'powersave_pin': self.powersave_pin, @@ -42,24 +45,24 @@ class Power(Module): '_psp': self._psp, } - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: self._i2c_scan() - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> None: return - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: if keyboard.matrix_update or keyboard.secondary_matrix_update: self.psave_time_reset() - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: if self.enable: self.psleep() - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: '''Gives 10 cycles to allow other extensions to clean up before powersave''' if self._loopcounter > 10: self.enable_powersave(keyboard) @@ -68,11 +71,11 @@ class Power(Module): self._loopcounter += 1 return - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: self.disable_powersave(keyboard) return - def enable_powersave(self, keyboard): + def enable_powersave(self, keyboard: KMKKeyboard) -> None: '''Enables power saving features''' if keyboard.i2c_deinit_count >= self._i2c and self.powersave_pin: # Allows power save to prevent RGB drain. @@ -88,7 +91,7 @@ class Power(Module): keyboard._trigger_powersave_enable = False return - def disable_powersave(self, keyboard): + def disable_powersave(self, keyboard: KMKKeyboard) -> None: '''Disables power saving features''' if self._psp: self._psp.value = False @@ -99,7 +102,7 @@ class Power(Module): self.enable = False return - def psleep(self): + def psleep(self) -> None: ''' Sleeps longer and longer to save power the more time in between updates. ''' @@ -109,10 +112,10 @@ class Power(Module): sleep(180 / 1000) return - def psave_time_reset(self): + def psave_time_reset(self) -> None: self._powersave_start = ticks_ms() - def _i2c_scan(self): + def _i2c_scan(self) -> None: i2c = board.I2C() while not i2c.try_lock(): pass @@ -122,28 +125,46 @@ class Power(Module): i2c.unlock() return - def usb_rescan_timer(self): + def usb_rescan_timer(self) -> bool: return bool(check_deadline(ticks_ms(), self._usb_last_scan) > 5000) - def usb_time_reset(self): + def usb_time_reset(self) -> None: self._usb_last_scan = ticks_ms() return - def usb_scan(self): + def usb_scan(self) -> bool: # TODO Add USB detection here. Currently lies that it's connected # https://github.com/adafruit/circuitpython/pull/3513 return True - def _ps_tog(self, key, keyboard, *args, **kwargs): + def _ps_tog( + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, + ) -> None: if self.enable: keyboard._trigger_powersave_disable = True else: keyboard._trigger_powersave_enable = True - def _ps_enable(self, key, keyboard, *args, **kwargs): + def _ps_enable( + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, + ) -> None: if not self.enable: keyboard._trigger_powersave_enable = True - def _ps_disable(self, key, keyboard, *args, **kwargs): + def _ps_disable( + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, + ) -> None: if self.enable: keyboard._trigger_powersave_disable = True diff --git a/kmk/modules/split.py b/kmk/modules/split.py index c5dfd1c..f388826 100644 --- a/kmk/modules/split.py +++ b/kmk/modules/split.py @@ -4,22 +4,26 @@ from micropython import const from supervisor import ticks_ms from storage import getmount +from typing import Any, List, Optional, Type, Union +from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import check_deadline from kmk.matrix import intify_coordinate from kmk.modules import Module +UartBuffer = List[Optional[Union[bytes, None]]] + class SplitSide: - LEFT = const(1) - RIGHT = const(2) + LEFT: int = const(1) + RIGHT: int = const(2) class SplitType: - UART = const(1) - I2C = const(2) # unused - ONEWIRE = const(3) # unused - BLE = const(4) + UART: int = const(1) + I2C: int = const(2) # unused + ONEWIRE: int = const(3) # unused + BLE: int = const(4) class Split(Module): @@ -27,56 +31,58 @@ class Split(Module): def __init__( self, - split_flip=True, - split_side=None, - split_type=SplitType.UART, - split_target_left=True, - uart_interval=20, - data_pin=None, - data_pin2=None, - target_left=True, - uart_flip=True, - debug_enabled=False, - ): - self._is_target = True - self._uart_buffer = [] - self.split_flip = split_flip - self.split_side = split_side - self.split_type = split_type - self.split_target_left = split_target_left - self.split_offset = None - self.data_pin = data_pin - self.data_pin2 = data_pin2 - self.target_left = target_left - self.uart_flip = uart_flip - self._is_target = True - self._uart = None - self._uart_interval = uart_interval - self._debug_enabled = debug_enabled + split_flip: bool = True, + split_side: Optional[int] = None, + split_type: int = SplitType.UART, + split_target_left: bool = True, + uart_interval: int = 20, + data_pin: Optional[Any] = None, + data_pin2: Optional[Any] = None, + target_left: bool = True, + uart_flip: bool = True, + debug_enabled: bool = False, + ) -> None: + self._is_target: bool = True + self._uart_buffer: UartBuffer = [] + self.split_flip: bool = split_flip + self.split_side: Optional[int] = split_side + self.split_type: int = split_type + self.split_target_left: bool = split_target_left + self.split_offset: Optional[int] = None + self.data_pin: Optional[Any] = data_pin + self.data_pin2: Optional[Any] = data_pin2 + self.target_left: bool = target_left + self.uart_flip: bool = uart_flip + self._is_target: bool = True + self._uart: Optional[busio.UART] = None + self._uart_interval: int = uart_interval + self._debug_enabled: bool = debug_enabled if self.split_type == SplitType.BLE: try: - from adafruit_ble import BLERadio + from adafruit_ble import BLEConnection, BLERadio from adafruit_ble.advertising.standard import ( ProvideServicesAdvertisement, ) from adafruit_ble.services.nordic import UARTService - self.ProvideServicesAdvertisement = ProvideServicesAdvertisement - self.UARTService = UARTService + self.ProvideServicesAdvertisement: Type[ + ProvideServicesAdvertisement + ] = ProvideServicesAdvertisement + self.UARTService: Type[UARTService] = UARTService except ImportError: print('BLE Import error') return # BLE isn't supported on this platform - self._ble = BLERadio() - self._ble_last_scan = ticks_ms() - 5000 - self._connection_count = 0 - self._uart_connection = None - self._advertisment = None - self._advertising = False - self._psave_enable = False + self._ble: BLERadio = BLERadio() + self._ble_last_scan: float = ticks_ms() - 5000 + self._connection_count: int = 0 + self._uart_connection: Optional[BLEConnection] = None + self._advertisment: Optional[ProvideServicesAdvertisement] = None + self._advertising: bool = False + self._psave_enable: bool = False - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: # Set up name for target side detection and BLE advertisment - name = str(getmount('/').label) + name: str = str(getmount('/').label) if self.split_type == SplitType.BLE: self._ble.name = name else: @@ -128,7 +134,7 @@ class Split(Module): for cidx in range(cols_to_calc): keyboard.coord_mapping.append(intify_coordinate(ridx, cidx)) - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> None: if self.split_type == SplitType.BLE: self._check_all_connections() self._receive_ble(keyboard) @@ -139,7 +145,7 @@ class Split(Module): pass # Protocol needs written return - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: if keyboard.matrix_update: if self.split_type == SplitType.UART and self._is_target: pass # explicit pass just for dev sanity... @@ -156,28 +162,28 @@ class Split(Module): return - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: if not self._is_target: keyboard.hid_pending = False return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: if self.split_type == SplitType.BLE: if self._uart_connection and not self._psave_enable: self._uart_connection.connection_interval = self._uart_interval self._psave_enable = True - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: if self.split_type == SplitType.BLE: if self._uart_connection and self._psave_enable: self._uart_connection.connection_interval = 11.25 self._psave_enable = False - def _check_all_connections(self): + def _check_all_connections(self) -> None: '''Validates the correct number of BLE connections''' self._connection_count = len(self._ble.connections) if self._is_target and self._connection_count < 2: @@ -185,7 +191,7 @@ class Split(Module): elif not self._is_target and self._connection_count < 1: self._initiator_scan() - def _initiator_scan(self): + def _initiator_scan(self) -> None: '''Scans for target device''' self._uart = None self._uart_connection = None @@ -218,7 +224,7 @@ class Split(Module): break self._ble.stop_scan() - def _target_advertise(self): + def _target_advertise(self) -> None: '''Advertises the target for the initiator to find''' self._ble.stop_advertising() if self._debug_enabled: @@ -240,15 +246,15 @@ class Split(Module): break self._ble.stop_advertising() - def ble_rescan_timer(self): + def ble_rescan_timer(self) -> bool: '''If true, the rescan timer is up''' return bool(check_deadline(ticks_ms(), self._ble_last_scan) > 5000) - def ble_time_reset(self): + def ble_time_reset(self) -> None: '''Resets the rescan timer''' self._ble_last_scan = ticks_ms() - def _send_ble(self, update): + def _send_ble(self, update: List) -> None: if self._uart: try: if not self._is_target: @@ -266,7 +272,7 @@ class Split(Module): self._uart_connection = None self._uart = None - def _receive_ble(self, keyboard): + def _receive_ble(self, keyboard: KMKKeyboard) -> None: if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer: while self._uart.in_waiting >= 3: self._uart_buffer.append(self._uart.read(3)) @@ -274,7 +280,7 @@ class Split(Module): keyboard.secondary_matrix_update = bytearray(self._uart_buffer.pop(0)) return - def _send_uart(self, update): + def _send_uart(self, update: List) -> None: # Change offsets depending on where the data is going to match the correct # matrix location of the receiever if self._is_target: @@ -291,7 +297,7 @@ class Split(Module): if self._uart is not None: self._uart.write(update) - def _receive_uart(self, keyboard): + def _receive_uart(self, keyboard: KMKKeyboard) -> None: if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer: if self._uart.in_waiting >= 60: # This is a dirty hack to prevent crashes in unrealistic cases diff --git a/kmk/types.py b/kmk/types.py index 54a49b3..9676497 100644 --- a/kmk/types.py +++ b/kmk/types.py @@ -1,3 +1,14 @@ +import sys + +from kmk.consts import TYPING_PLATFORMS + +if sys.platform in TYPING_PLATFORMS: + from typing import List, Optional, Tuple, Union + + # Avoid cyclical imports + from kmk.keys import ConsumerKey, Key, ModifierKey + + class AttrDict(dict): ''' Primitive support for accessing dictionary entries in dot notation. @@ -8,36 +19,43 @@ class AttrDict(dict): ''' def __getattr__(self, key): + # type: (str) -> Optional[Union[Key, ModifierKey, ConsumerKey]] return self[key] class LayerKeyMeta: def __init__(self, layer, kc=None): - self.layer = layer - self.kc = kc + # type: (int, Optional[Key]) -> None + self.layer = layer # type: int + self.kc = kc # type: Optional[Key] class ModTapKeyMeta: def __init__(self, kc=None, mods=None): - self.mods = mods - self.kc = kc + # type: (Optional[Key], Optional[List[Key]]) -> None + self.mods = mods # type: Optional[List[Key]] + self.kc = kc # type: Optional[Key] class KeySequenceMeta: def __init__(self, seq): - self.seq = seq + # type: (List[Key]) -> None + self.seq = seq # type: List[Key] class KeySeqSleepMeta: def __init__(self, ms): - self.ms = ms + # type: (float) -> None + self.ms = ms # type: float class UnicodeModeKeyMeta: def __init__(self, mode): - self.mode = mode + # type: (int) -> None + self.mode = mode # type: int class TapDanceKeyMeta: def __init__(self, codes): - self.codes = codes + # type: (Tuple[Key, ...]) -> None + self.codes = codes # type: Tuple[Key, ...] diff --git a/pyproject.toml b/pyproject.toml index 859393f..7ea9f91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,3 +22,27 @@ exclude = ''' | mnt )/ ''' + +[tool.pyright] +strict = ["kmk"] +typeCheckingMode = "strict" +include = ["kmk"] +exclude = [ + "hardware", + ".venv", + "user_keymaps", + "boards", + ".git" +] +venvPath = ".venv" +# stops constant reporting of missing board module etc. +reportMissingModuleSource = false +# reports missing typestubs allowing for a code action to +# create new library typestubs +reportMissingTypeStubs = true +pythonVersion = "3.6" + +[tool.mypy] +exclude = "boards/|user_keymaps/" +ignore_missing_imports = true +python_version = "3.6" diff --git a/typings/adafruit_ble/__init__.pyi b/typings/adafruit_ble/__init__.pyi new file mode 100644 index 0000000..bb8377c --- /dev/null +++ b/typings/adafruit_ble/__init__.pyi @@ -0,0 +1,208 @@ +""" +This type stub file was generated by pyright. +""" + +import sys +import _bleio +from .services import Service +from .advertising import Advertisement + +""" + +This module provides higher-level BLE (Bluetooth Low Energy) functionality, +building on the native `_bleio` module. + +""" +if sys.implementation.name == "circuitpython" and sys.implementation.version[0] <= 4: + ... +__version__ = ... +__repo__ = ... +class BLEConnection: + """ + Represents a connection to a peer BLE device. + It acts as a map from a `Service` type to a `Service` instance for the connection. + + :param bleio_connection _bleio.Connection: the native `_bleio.Connection` object to wrap + + """ + def __init__(self, bleio_connection) -> None: + ... + + def __contains__(self, key): # -> bool: + """ + Allows easy testing for a particular Service class or a particular UUID + associated with this connection. + + Example:: + + if UARTService in connection: + # do something + + if StandardUUID(0x1234) in connection: + # do something + """ + ... + + def __getitem__(self, key): # -> None: + """Return the Service for the given Service class or uuid, if any.""" + ... + + @property + def connected(self): + """True if the connection to the peer is still active.""" + ... + + @property + def paired(self): + """True if the paired to the peer.""" + ... + + @property + def connection_interval(self): + """Time between transmissions in milliseconds. Will be multiple of 1.25ms. Lower numbers + increase speed and decrease latency but increase power consumption. + + When setting connection_interval, the peer may reject the new interval and + `connection_interval` will then remain the same. + + Apple has additional guidelines that dictate should be a multiple of 15ms except if HID + is available. When HID is available Apple devices may accept 11.25ms intervals.""" + ... + + @connection_interval.setter + def connection_interval(self, value): # -> None: + ... + + def pair(self, *, bond=...): + """Pair to the peer to increase security of the connection.""" + ... + + def disconnect(self): # -> None: + """Disconnect from peer.""" + ... + + + +class BLERadio: + """ + BLERadio provides the interfaces for BLE advertising, + scanning for advertisements, and connecting to peers. There may be + multiple connections active at once. + + It uses this library's `Advertisement` classes and the `BLEConnection` class.""" + def __init__(self, adapter=...) -> None: + """If no adapter is supplied, use the built-in `_bleio.adapter`. + If no built-in adapter is available, raise `RuntimeError`. + """ + ... + + def start_advertising(self, advertisement, scan_response=..., interval=..., timeout=...): # -> None: + """ + Starts advertising the given advertisement. + + :param buf scan_response: scan response data packet bytes. + If ``None``, a default scan response will be generated that includes + `BLERadio.name` and `BLERadio.tx_power`. + :param float interval: advertising interval, in seconds + :param int timeout: advertising timeout in seconds. + If None, no timeout. + + ``timeout`` is not available in CircuitPython 5.x and must be `None`. + """ + ... + + def stop_advertising(self): # -> None: + """Stops advertising.""" + ... + + def start_scan(self, *advertisement_types, buffer_size=..., extended=..., timeout=..., interval=..., window=..., minimum_rssi=..., active=...): # -> Generator[Advertisement | Unknown, None, None]: + """ + Starts scanning. Returns an iterator of advertisement objects of the types given in + advertisement_types. The iterator will block until an advertisement is heard or the scan + times out. + + If any ``advertisement_types`` are given, only Advertisements of those types are produced + by the returned iterator. If none are given then `Advertisement` objects will be + returned. + + Advertisements and scan responses are filtered and returned separately. + + :param int buffer_size: the maximum number of advertising bytes to buffer. + :param bool extended: When True, support extended advertising packets. + Increasing buffer_size is recommended when this is set. + :param float timeout: the scan timeout in seconds. + If None, will scan until `stop_scan` is called. + :param float interval: the interval (in seconds) between the start + of two consecutive scan windows + Must be in the range 0.0025 - 40.959375 seconds. + :param float window: the duration (in seconds) to scan a single BLE channel. + window must be <= interval. + :param int minimum_rssi: the minimum rssi of entries to return. + :param bool active: request and retrieve scan responses for scannable advertisements. + :return: If any ``advertisement_types`` are given, + only Advertisements of those types are produced by the returned iterator. + If none are given then `Advertisement` objects will be returned. + :rtype: iterable + """ + ... + + def stop_scan(self): # -> None: + """Stops any active scan. + + The scan results iterator will return any buffered results and then raise StopIteration + once empty.""" + ... + + def connect(self, advertisement, *, timeout=...): + """ + Initiates a `BLEConnection` to the peer that advertised the given advertisement. + + :param advertisement Advertisement: An `Advertisement` or a subclass of `Advertisement` + :param timeout float: how long to wait for a connection + :return: the connection to the peer + :rtype: BLEConnection + """ + ... + + @property + def connected(self): # -> bool: + """True if any peers are connected.""" + ... + + @property + def connections(self): # -> tuple[None, ...]: + """A tuple of active `BLEConnection` objects.""" + ... + + @property + def name(self): # -> str: + """The name for this device. Used in advertisements and + as the Device Name in the Generic Access Service, available to a connected peer. + """ + ... + + @name.setter + def name(self, value): # -> None: + ... + + @property + def tx_power(self): # -> Literal[0]: + """Transmit power, in dBm.""" + ... + + @tx_power.setter + def tx_power(self, value): + ... + + @property + def address_bytes(self): # -> bytes: + """The device address, as a ``bytes()`` object of length 6.""" + ... + + @property + def advertising(self): # -> bool: + """The advertising state""" + ... + + + diff --git a/typings/adafruit_ble/advertising/__init__.pyi b/typings/adafruit_ble/advertising/__init__.pyi new file mode 100644 index 0000000..0180bab --- /dev/null +++ b/typings/adafruit_ble/advertising/__init__.pyi @@ -0,0 +1,173 @@ +""" +This type stub file was generated by pyright. +""" + +import struct + +""" +Advertising is the first phase of BLE where devices can broadcast +""" +def to_hex(seq): # -> str: + """Pretty prints a byte sequence as hex values.""" + ... + +def to_bytes_literal(seq): # -> str: + """Prints a byte sequence as a Python bytes literal that only uses hex encoding.""" + ... + +def decode_data(data, *, key_encoding=...): # -> dict[Unknown, Unknown]: + """Helper which decodes length encoded structures into a dictionary with the given key + encoding.""" + ... + +def compute_length(data_dict, *, key_encoding=...): # -> int: + """Computes the length of the encoded data dictionary.""" + ... + +def encode_data(data_dict, *, key_encoding=...): # -> bytes: + """Helper which encodes dictionaries into length encoded structures with the given key + encoding.""" + ... + +class AdvertisingDataField: + """Top level class for any descriptor classes that live in Advertisement or its subclasses.""" + ... + + +class AdvertisingFlag: + """A single bit flag within an AdvertisingFlags object.""" + def __init__(self, bit_position) -> None: + ... + + def __get__(self, obj, cls): # -> AdvertisingFlag: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class AdvertisingFlags(AdvertisingDataField): + """Standard advertising flags""" + limited_discovery = ... + general_discovery = ... + le_only = ... + def __init__(self, advertisement, advertising_data_type) -> None: + ... + + def __len__(self): # -> Literal[1]: + ... + + def __bytes__(self): # -> bytes: + ... + + def __str__(self) -> str: + ... + + + +class String(AdvertisingDataField): + """UTF-8 encoded string in an Advertisement. + + Not null terminated once encoded because length is always transmitted.""" + def __init__(self, *, advertising_data_type) -> None: + ... + + def __get__(self, obj, cls): # -> String | str | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class Struct(AdvertisingDataField): + """`struct` encoded data in an Advertisement.""" + def __init__(self, struct_format, *, advertising_data_type) -> None: + ... + + def __get__(self, obj, cls): # -> Struct | Any | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class LazyObjectField(AdvertisingDataField): + """Non-data descriptor useful for lazily binding a complex object to an advertisement object.""" + def __init__(self, cls, attribute_name, *, advertising_data_type, **kwargs) -> None: + ... + + def __get__(self, obj, cls): # -> LazyObjectField | None: + ... + + @property + def advertising_data_type(self): + """Return the data type value used to indicate this field.""" + ... + + + +class Advertisement: + """Core Advertisement type. + + The class attribute ``match_prefixes``, if not ``None``, is a tuple of + bytestring prefixes to match against the multiple data structures in the advertisement. + """ + match_prefixes = ... + _prefix_bytes = ... + flags = ... + short_name = ... + complete_name = ... + tx_power = ... + appearance = ... + def __init__(self, *, entry=...) -> None: + """Create an empty advertising packet or one from a ScanEntry.""" + ... + + @property + def rssi(self): # -> None: + """Signal strength of the scanned advertisement. Only available on Advertisements returned + from `BLERadio.start_scan()`. (read-only)""" + ... + + @classmethod + def get_prefix_bytes(cls): # -> bytes | Any: + """Return a merged version of match_prefixes as a single bytes object, + with length headers. + """ + ... + + @classmethod + def matches(cls, entry): + """Returns ``True`` if the given `_bleio.ScanEntry` advertisement fields + matches all of the given prefixes in the `match_prefixes` tuple attribute. + Subclasses may override this to match any instead of all. + """ + ... + + @classmethod + def matches_prefixes(cls, entry, *, all_): + """Returns ``True`` if the given `_bleio.ScanEntry` advertisement fields + match any or all of the given prefixes in the `match_prefixes` tuple attribute. + If ``all_`` is ``True``, all the prefixes must match. If ``all_`` is ``False``, + returns ``True`` if at least one of the prefixes match. + """ + ... + + def __bytes__(self): # -> bytes: + """The raw packet bytes.""" + ... + + def __str__(self) -> str: + ... + + def __len__(self): # -> int: + ... + + def __repr__(self): # -> str: + ... + + + diff --git a/typings/adafruit_ble/advertising/standard.pyi b/typings/adafruit_ble/advertising/standard.pyi new file mode 100644 index 0000000..9556d96 --- /dev/null +++ b/typings/adafruit_ble/advertising/standard.pyi @@ -0,0 +1,122 @@ +""" +This type stub file was generated by pyright. +""" + +from . import Advertisement, AdvertisingDataField + +""" +:py:mod:`~adafruit_ble.advertising.standard` +==================================================== + +This module provides BLE standard defined advertisements. The Advertisements are single purpose +even though multiple purposes may actually be present in a single packet. + +""" +__version__ = ... +__repo__ = ... +class BoundServiceList: + """Sequence-like object of Service UUID objects. It stores both standard and vendor UUIDs.""" + def __init__(self, advertisement, *, standard_services, vendor_services) -> None: + ... + + def __contains__(self, key): # -> bool: + ... + + def __iter__(self): # -> Iterator[Unknown]: + ... + + def append(self, service): # -> None: + """Append a service to the list.""" + ... + + def extend(self, services): # -> None: + """Appends all services in the iterable to the list.""" + ... + + def __str__(self) -> str: + ... + + + +class ServiceList(AdvertisingDataField): + """Descriptor for a list of Service UUIDs that lazily binds a corresponding BoundServiceList.""" + def __init__(self, *, standard_services, vendor_services) -> None: + ... + + def __get__(self, obj, cls): # -> ServiceList | tuple[()]: + ... + + + +class ProvideServicesAdvertisement(Advertisement): + """Advertise what services that the device makes available upon connection.""" + match_prefixes = ... + services = ... + def __init__(self, *services, entry=...) -> None: + ... + + @classmethod + def matches(cls, entry): + """Only one kind of service list need be present in a ProvideServicesAdvertisement, + so override the default behavior and match any prefix, not all. + """ + ... + + + +class SolicitServicesAdvertisement(Advertisement): + """Advertise what services the device would like to use over a connection.""" + match_prefixes = ... + solicited_services = ... + def __init__(self, *services, entry=...) -> None: + ... + + + +class ManufacturerData(AdvertisingDataField): + """Encapsulates manufacturer specific keyed data bytes. The manufacturer is identified by the + company_id and the data is structured like an advertisement with a configurable key + format. The order of the serialized data is determined by the order that the + `ManufacturerDataField` attributes are set in - this can be useful for + `match_prefixes` in an `Advertisement` sub-class.""" + def __init__(self, obj, *, advertising_data_type=..., company_id, key_encoding=...) -> None: + ... + + def __len__(self): # -> int: + ... + + def __bytes__(self): # -> bytes: + ... + + def __str__(self) -> str: + ... + + + +class ManufacturerDataField: + """A single piece of data within the manufacturer specific data. The format can be repeated.""" + def __init__(self, key, value_format, field_names=...) -> None: + ... + + def __get__(self, obj, cls): # -> ManufacturerDataField | mdf_tuple | Any | Tuple[Any, ...] | tuple[None, ...] | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class ServiceData(AdvertisingDataField): + """Encapsulates service data. It is read as a memoryview which can be manipulated or set as a + bytearray to change the size.""" + def __init__(self, service) -> None: + ... + + def __get__(self, obj, cls): # -> ServiceData | memoryview | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + diff --git a/typings/adafruit_ble/attributes/__init__.pyi b/typings/adafruit_ble/attributes/__init__.pyi new file mode 100644 index 0000000..03c7968 --- /dev/null +++ b/typings/adafruit_ble/attributes/__init__.pyi @@ -0,0 +1,55 @@ +""" +This type stub file was generated by pyright. +""" + +import _bleio + +""" +:py:mod:`~adafruit_ble.attributes` +==================================================== + +This module provides definitions common to all kinds of BLE attributes, +specifically characteristics and descriptors. + +""" +__version__ = ... +__repo__ = ... +class Attribute: + """Constants describing security levels. + + .. data:: NO_ACCESS + + security mode: access not allowed + + .. data:: OPEN + + security_mode: no security (link is not encrypted) + + .. data:: ENCRYPT_NO_MITM + + security_mode: unauthenticated encryption, without man-in-the-middle protection + + .. data:: ENCRYPT_WITH_MITM + + security_mode: authenticated encryption, with man-in-the-middle protection + + .. data:: LESC_ENCRYPT_WITH_MITM + + security_mode: LESC encryption, with man-in-the-middle protection + + .. data:: SIGNED_NO_MITM + + security_mode: unauthenticated data signing, without man-in-the-middle protection + + .. data:: SIGNED_WITH_MITM + + security_mode: authenticated data signing, without man-in-the-middle protection""" + NO_ACCESS = ... + OPEN = ... + ENCRYPT_NO_MITM = ... + ENCRYPT_WITH_MITM = ... + LESC_ENCRYPT_WITH_MITM = ... + SIGNED_NO_MITM = ... + SIGNED_WITH_MITM = ... + + diff --git a/typings/adafruit_ble/characteristics/__init__.pyi b/typings/adafruit_ble/characteristics/__init__.pyi new file mode 100644 index 0000000..b368038 --- /dev/null +++ b/typings/adafruit_ble/characteristics/__init__.pyi @@ -0,0 +1,120 @@ +""" +This type stub file was generated by pyright. +""" + +import struct +import _bleio +from ..attributes import Attribute + +""" + +This module provides core BLE characteristic classes that are used within Services. + +""" +__version__ = ... +__repo__ = ... +class Characteristic: + """ + Top level Characteristic class that does basic binding. + + :param UUID uuid: The uuid of the characteristic + :param int properties: The properties of the characteristic, + specified as a bitmask of these values bitwise-or'd together: + `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. + :param int read_perm: Specifies whether the characteristic can be read by a client, + and if so, which security mode is required. + Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, + `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, + `Attribute.LESC_ENCRYPT_WITH_MITM`, + `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. + :param int write_perm: Specifies whether the characteristic can be written by a client, + and if so, which security mode is required. Values allowed are the same as ``read_perm``. + :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed + by the BLE specification is 512. On nRF, if ``fixed_length`` is ``True``, the maximum + is 510. The default value is 20, which is the maximum + number of data bytes that fit in a single BLE 4.x ATT packet. + :param bool fixed_length: True if the characteristic value is of fixed length. + :param buf initial_value: The initial value for this characteristic. If not given, will be + filled with zeros. + + .. data:: BROADCAST + + property: allowed in advertising packets + + .. data:: INDICATE + + property: server will indicate to the client when the value is set and wait for a response + + .. data:: NOTIFY + + property: server will notify the client when the value is set + + .. data:: READ + + property: clients may read this characteristic + + .. data:: WRITE + + property: clients may write this characteristic; a response will be sent back + + .. data:: WRITE_NO_RESPONSE + + property: clients may write this characteristic; no response will be sent back""" + BROADCAST = ... + INDICATE = ... + NOTIFY = ... + READ = ... + WRITE = ... + WRITE_NO_RESPONSE = ... + def __init__(self, *, uuid=..., properties=..., read_perm=..., write_perm=..., max_length=..., fixed_length=..., initial_value=...) -> None: + ... + + def __get__(self, service, cls=...): # -> Characteristic: + ... + + def __set__(self, service, value): # -> None: + ... + + + +class ComplexCharacteristic: + """ + Characteristic class that does complex binding where the subclass returns a full object for + interacting with the characteristic data. The Characteristic itself will be shadowed once it + has been bound to the corresponding instance attribute. + """ + def __init__(self, *, uuid=..., properties=..., read_perm=..., write_perm=..., max_length=..., fixed_length=..., initial_value=...) -> None: + ... + + def bind(self, service): + """Binds the characteristic to the local Service or remote Characteristic object given.""" + ... + + def __get__(self, service, cls=...): # -> ComplexCharacteristic: + ... + + + +class StructCharacteristic(Characteristic): + """ + Data descriptor for a structure with a fixed format. + + :param struct_format: a `struct` format string describing how to pack multiple values + into the characteristic bytestring + :param UUID uuid: The uuid of the characteristic + :param int properties: see `Characteristic` + :param int read_perm: see `Characteristic` + :param int write_perm: see `Characteristic` + :param buf initial_value: see `Characteristic` + """ + def __init__(self, struct_format, *, uuid=..., properties=..., read_perm=..., write_perm=..., initial_value=...) -> None: + ... + + def __get__(self, obj, cls=...): # -> StructCharacteristic | Tuple[Any, ...] | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + diff --git a/typings/adafruit_ble/characteristics/stream.pyi b/typings/adafruit_ble/characteristics/stream.pyi new file mode 100644 index 0000000..d2c64b0 --- /dev/null +++ b/typings/adafruit_ble/characteristics/stream.pyi @@ -0,0 +1,49 @@ +""" +This type stub file was generated by pyright. +""" + +from . import ComplexCharacteristic + +""" +`stream` +==================================================== + +This module provides stream characteristics that bind readable or writable objects to the Service +object they are on. + +""" +__version__ = ... +__repo__ = ... +class BoundWriteStream: + """Writes data out to the peer.""" + def __init__(self, bound_characteristic) -> None: + ... + + def write(self, buf): # -> None: + """Write data from buf out to the peer.""" + ... + + + +class StreamOut(ComplexCharacteristic): + """Output stream from the Service server.""" + def __init__(self, *, uuid=..., timeout=..., buffer_size=..., properties=..., read_perm=..., write_perm=...) -> None: + ... + + def bind(self, service): # -> CharacteristicBuffer | BoundWriteStream: + """Binds the characteristic to the given Service.""" + ... + + + +class StreamIn(ComplexCharacteristic): + """Input stream into the Service server.""" + def __init__(self, *, uuid=..., timeout=..., buffer_size=..., properties=..., write_perm=...) -> None: + ... + + def bind(self, service): # -> BoundWriteStream | CharacteristicBuffer: + """Binds the characteristic to the given Service.""" + ... + + + diff --git a/typings/adafruit_ble/services/__init__.pyi b/typings/adafruit_ble/services/__init__.pyi new file mode 100644 index 0000000..efd308a --- /dev/null +++ b/typings/adafruit_ble/services/__init__.pyi @@ -0,0 +1,36 @@ +""" +This type stub file was generated by pyright. +""" + +import _bleio +from ..characteristics import Characteristic, ComplexCharacteristic + +""" + +This module provides the top level Service definition. + +""" +__version__ = ... +__repo__ = ... +class Service: + """Top level Service class that handles the hard work of binding to a local or remote service. + + Providers of a local service should instantiate their Service with service=None, the default. + The local Service's characteristics will be lazily made available to clients as they are used + locally. In other words, a characteristic won't be available to remote clients until it has + been read or written locally. + + To use a remote Service, get the item with the key of the Service type on the + `BLEConnection`. For example, ``connection[UartService]`` will return the UartService + instance for the connection's peer. + """ + def __init__(self, *, service=..., secondary=..., **initial_values) -> None: + ... + + @property + def remote(self): # -> bool: + """True if the service is provided by a peer and accessed remotely.""" + ... + + + diff --git a/typings/adafruit_ble/services/nordic.pyi b/typings/adafruit_ble/services/nordic.pyi new file mode 100644 index 0000000..aef9441 --- /dev/null +++ b/typings/adafruit_ble/services/nordic.pyi @@ -0,0 +1,77 @@ +""" +This type stub file was generated by pyright. +""" + +from . import Service + +""" +`nordic` +==================================================== + +This module provides Services used by Nordic Semiconductors. + +""" +__version__ = ... +__repo__ = ... +class UARTService(Service): + """ + Provide UART-like functionality via the Nordic NUS service. + + :param int timeout: the timeout in seconds to wait + for the first character and between subsequent characters. + :param int buffer_size: buffer up to this many bytes. + If more bytes are received, older bytes will be discarded. + + See ``examples/ble_uart_echo_test.py`` for a usage example. + """ + uuid = ... + _server_tx = ... + _server_rx = ... + def __init__(self, service=...) -> None: + ... + + def read(self, nbytes=...): + """ + Read characters. If ``nbytes`` is specified then read at most that many bytes. + Otherwise, read everything that arrives until the connection times out. + Providing the number of bytes expected is highly recommended because it will be faster. + + :return: Data read + :rtype: bytes or None + """ + ... + + def readinto(self, buf, nbytes=...): + """ + Read bytes into the ``buf``. If ``nbytes`` is specified then read at most + that many bytes. Otherwise, read at most ``len(buf)`` bytes. + + :return: number of bytes read and stored into ``buf`` + :rtype: int or None (on a non-blocking error) + """ + ... + + def readline(self): + """ + Read a line, ending in a newline character. + + :return: the line read + :rtype: bytes or None + """ + ... + + @property + def in_waiting(self): + """The number of bytes in the input buffer, available to be read.""" + ... + + def reset_input_buffer(self): # -> None: + """Discard any unread characters in the input buffer.""" + ... + + def write(self, buf): # -> None: + """Write a buffer of bytes.""" + ... + + + diff --git a/typings/adafruit_ble/uuid/__init__.pyi b/typings/adafruit_ble/uuid/__init__.pyi new file mode 100644 index 0000000..7f68c63 --- /dev/null +++ b/typings/adafruit_ble/uuid/__init__.pyi @@ -0,0 +1,48 @@ +""" +This type stub file was generated by pyright. +""" + +import struct +import _bleio + +""" + +This module provides core Unique ID (UUID) classes. + +""" +__version__ = ... +__repo__ = ... +class UUID: + """Top level UUID""" + def __hash__(self) -> int: + ... + + def __eq__(self, other) -> bool: + ... + + def __str__(self) -> str: + ... + + def __bytes__(self): # -> bytes: + ... + + def pack_into(self, buffer, offset=...): # -> None: + """Packs the UUID into the buffer at the given offset.""" + ... + + + +class StandardUUID(UUID): + """Standard 16-bit UUID defined by the Bluetooth SIG.""" + def __init__(self, uuid16) -> None: + ... + + + +class VendorUUID(UUID): + """Vendor defined, 128-bit UUID.""" + def __init__(self, uuid128) -> None: + ... + + + diff --git a/typings/micropython.pyi b/typings/micropython.pyi new file mode 100644 index 0000000..f2c1dc4 --- /dev/null +++ b/typings/micropython.pyi @@ -0,0 +1,5 @@ +from typing import TypeVar + +T = TypeVar('T') + +def const(x: T) -> T: ...