From 3bbb9bd7b16eebeb603737c125aa09c97fc9b1b7 Mon Sep 17 00:00:00 2001 From: Nguyen Marc Date: Fri, 19 Feb 2021 04:16:31 +0100 Subject: [PATCH] Big Refactoring --- .gitattributes | 2 - .gitignore | 141 +++- .vscode/settings.json | 6 + LICENSE | 2 +- Pipfile | 19 + Pipfile.lock | 255 +++++++ README.md | 152 +--- csgo_gsi_arduino_lcd/__init__.py | 15 - csgo_gsi_arduino_lcd/__main__.py | 3 + csgo_gsi_arduino_lcd/appui.py | 201 ------ .../{data => assets}/csgo-128.ico | Bin .../{data => assets}/csgo-16.ico | Bin .../{data => assets}/csgo-20.ico | Bin .../{data => assets}/csgo-24.ico | Bin .../{data => assets}/csgo-256.ico | Bin .../{data => assets}/csgo-32.ico | Bin .../{data => assets}/csgo-48.ico | Bin .../{data => assets}/csgo-512.ico | Bin .../{data => assets}/csgo-64.ico | Bin csgo_gsi_arduino_lcd/data/__init__.py | 0 csgo_gsi_arduino_lcd/data/arduino_mediator.py | 182 +++++ .../data/csgo_request_handler.py | 82 +++ csgo_gsi_arduino_lcd/data/server_thread.py | 55 ++ csgo_gsi_arduino_lcd/debug/__init__.py | 0 .../payload_viewer_thread.py} | 34 +- csgo_gsi_arduino_lcd/entities/__init__.py | 0 csgo_gsi_arduino_lcd/entities/state.py | 21 + csgo_gsi_arduino_lcd/entities/status.py | 22 + csgo_gsi_arduino_lcd/httpserver.py | 129 ---- csgo_gsi_arduino_lcd/main.py | 11 +- csgo_gsi_arduino_lcd/messenger.py | 240 ------- csgo_gsi_arduino_lcd/server.py | 58 -- csgo_gsi_arduino_lcd/ui/__init__.py | 0 csgo_gsi_arduino_lcd/ui/csgo_window.py | 197 ++++++ csgogsilcd | 5 - csgogsilcd.bat | 5 - releases/csgo-gsi-arduino-lcd | 655 ------------------ requirements.txt | Bin 0 -> 126 bytes setup.py | 23 - tableviewerold.py | 51 -- 40 files changed, 1025 insertions(+), 1541 deletions(-) delete mode 100644 .gitattributes create mode 100644 .vscode/settings.json create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 csgo_gsi_arduino_lcd/__main__.py delete mode 100644 csgo_gsi_arduino_lcd/appui.py rename csgo_gsi_arduino_lcd/{data => assets}/csgo-128.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-16.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-20.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-24.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-256.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-32.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-48.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-512.ico (100%) rename csgo_gsi_arduino_lcd/{data => assets}/csgo-64.ico (100%) create mode 100644 csgo_gsi_arduino_lcd/data/__init__.py create mode 100644 csgo_gsi_arduino_lcd/data/arduino_mediator.py create mode 100644 csgo_gsi_arduino_lcd/data/csgo_request_handler.py create mode 100644 csgo_gsi_arduino_lcd/data/server_thread.py create mode 100644 csgo_gsi_arduino_lcd/debug/__init__.py rename csgo_gsi_arduino_lcd/{tableviewer.py => debug/payload_viewer_thread.py} (61%) create mode 100644 csgo_gsi_arduino_lcd/entities/__init__.py create mode 100644 csgo_gsi_arduino_lcd/entities/state.py create mode 100644 csgo_gsi_arduino_lcd/entities/status.py delete mode 100644 csgo_gsi_arduino_lcd/httpserver.py delete mode 100644 csgo_gsi_arduino_lcd/messenger.py delete mode 100644 csgo_gsi_arduino_lcd/server.py create mode 100644 csgo_gsi_arduino_lcd/ui/__init__.py create mode 100644 csgo_gsi_arduino_lcd/ui/csgo_window.py delete mode 100644 csgogsilcd delete mode 100644 csgogsilcd.bat delete mode 100644 releases/csgo-gsi-arduino-lcd create mode 100644 requirements.txt delete mode 100644 setup.py delete mode 100644 tableviewerold.py diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 736e7ee..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Autodetect text files -* text=auto eol=lf diff --git a/.gitignore b/.gitignore index 8447c52..5391d87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,138 @@ -# Compiled python modules. -*.pyc +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest *.spec -# Setuptools distribution folder. -/dist/ +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ -# Python egg metadata, regenerated from source files by setuptools. -/*.egg-info +# pytype static type analyzer +.pytype/ -/build/ +# Cython debug symbols +cython_debug/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1b74e19 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "python.linting.pylintEnabled": false, + "python.linting.pylamaEnabled": true, + "python.pythonPath": "C:\\Users\\nguye\\.virtualenvs\\csgo-gsi-arduino-lcd-ZH56k7sE\\Scripts\\python.exe", + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 339382e..04d7006 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Tsuri Kamppuri +Copyright (c) 2021 Marc NGUYEN Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..2da4ee0 --- /dev/null +++ b/Pipfile @@ -0,0 +1,19 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pyserial = "*" +qtpy = "*" +pyqt5 = "*" + +[dev-packages] +pylama = "*" +black = "*" + +[requires] +python_version = "3.8" + +[pipenv] +allow_prereleases = true diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..c0c6026 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,255 @@ +{ + "_meta": { + "hash": { + "sha256": "0e26bcdfdf4d484744434e44c3ca46f7c0d597f814365d198356501b027b4f87" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "pyqt5": { + "hashes": [ + "sha256:29889845688a54d62820585ad5b2e0200a36b304ff3d7a555e95599f110ba4ce", + "sha256:372b08dc9321d1201e4690182697c5e7ffb2e0770e6b4a45519025134b12e4fc", + "sha256:894ca4ae767a8d6cf5903784b71f755073c78cb8c167eecf6e4ed6b3b055ac6a", + "sha256:ea24f24b7679bf393dd2e4f53fe0ce65021be18304c1ff7a226c2fc5c356d0da", + "sha256:faaecb76ec65e12673a968e7f5bc02495957e6996f0a3fa0d98895f9e4113746" + ], + "index": "pypi", + "version": "==5.15.2" + }, + "pyqt5-sip": { + "hashes": [ + "sha256:0304ca9114b9817a270f67f421355075b78ff9fc25ac58ffd72c2601109d2194", + "sha256:0cd969be528c27bbd4755bd323dff4a79a8fdda28215364e6ce3e069cb56c2a9", + "sha256:2f35e82fd7ec1e1f6716e9154721c7594956a4f5bd4f826d8c6a6453833cc2f0", + "sha256:30e944db9abee9cc757aea16906d4198129558533eb7fadbe48c5da2bd18e0bd", + "sha256:34dcd29be47553d5f016ff86e89e24cbc5eebae92eb2f96fb32d2d7ba028c43c", + "sha256:5a011aeff89660622a6d5c3388d55a9d76932f3b82c95e82fc31abd8b1d2990d", + "sha256:6c1ebee60f1d2b3c70aff866b7933d8d8d7646011f7c32f9321ee88c290aa4f9", + "sha256:7b81382ce188d63890a0e35abe0f9bb946cabc873a31873b73583b0fc84ac115", + "sha256:832fd60a264de4134c2824d393320838f3ab648180c9c357ec58a74524d24507", + "sha256:84ba7746762bd223bed22428e8561aa267a229c28344c2d28c5d5d3f8970cffb", + "sha256:9312ec47cac4e33c11503bc1cbeeb0bdae619620472f38e2078c5a51020a930f", + "sha256:a1b8ef013086e224b8e86c93f880f776d01b59195bdfa2a8e0b23f0480678fec", + "sha256:a29e2ac399429d3b7738f73e9081e50783e61ac5d29344e0802d0dcd6056c5a2", + "sha256:b6d42250baec52a5f77de64e2951d001c5501c3a2df2179f625b241cbaec3369", + "sha256:bb5a87b66fc1445915104ee97f7a20a69decb42f52803e3b0795fa17ff88226c", + "sha256:c317ab1263e6417c498b81f5c970a9b1af7acefab1f80b4cc0f2f8e661f29fc5", + "sha256:c9800729badcb247765e4ffe2241549d02da1fa435b9db224845bc37c3e99cb0", + "sha256:c9d6d448c29dc6606bb7974696608f81f4316c8234f7c7216396ed110075e777", + "sha256:da9c9f1e65b9d09e73bd75befc82961b6b61b5a3b9d0a7c832168e1415f163c6", + "sha256:ed897c58acf4a3cdca61469daa31fe6e44c33c6c06a37c3f21fab31780b3b86a", + "sha256:f168f0a7f32b81bfeffdf003c36f25d81c97dee5eb67072a5183e761fe250f13" + ], + "markers": "python_version >= '3.5'", + "version": "==12.8.1" + }, + "pyserial": { + "hashes": [ + "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", + "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0" + ], + "index": "pypi", + "version": "==3.5" + }, + "qtpy": { + "hashes": [ + "sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d", + "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea" + ], + "index": "pypi", + "version": "==1.9.0" + } + }, + "develop": { + "appdirs": { + "hashes": [ + "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", + "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" + ], + "version": "==1.4.4" + }, + "black": { + "hashes": [ + "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea" + ], + "index": "pypi", + "version": "==20.8b1" + }, + "click": { + "hashes": [ + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==7.1.2" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, + "pathspec": { + "hashes": [ + "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd", + "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d" + ], + "version": "==0.8.1" + }, + "pycodestyle": { + "hashes": [ + "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", + "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.6.0" + }, + "pydocstyle": { + "hashes": [ + "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325", + "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678" + ], + "markers": "python_version >= '3.5'", + "version": "==5.1.1" + }, + "pyflakes": { + "hashes": [ + "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", + "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.2.0" + }, + "pylama": { + "hashes": [ + "sha256:9bae53ef9c1a431371d6a8dca406816a60d547147b60a4934721898f553b7d8f", + "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15" + ], + "index": "pypi", + "version": "==7.7.1" + }, + "regex": { + "hashes": [ + "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538", + "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4", + "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc", + "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa", + "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444", + "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1", + "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af", + "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8", + "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9", + "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88", + "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba", + "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364", + "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e", + "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7", + "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0", + "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31", + "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683", + "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee", + "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b", + "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884", + "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c", + "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e", + "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562", + "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85", + "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c", + "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6", + "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d", + "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b", + "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70", + "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b", + "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b", + "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f", + "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0", + "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5", + "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5", + "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f", + "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e", + "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512", + "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d", + "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917", + "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f" + ], + "version": "==2020.11.13" + }, + "snowballstemmer": { + "hashes": [ + "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2", + "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914" + ], + "version": "==2.1.0" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" + }, + "typed-ast": { + "hashes": [ + "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1", + "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d", + "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6", + "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd", + "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37", + "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151", + "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07", + "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440", + "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70", + "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496", + "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea", + "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400", + "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc", + "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606", + "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc", + "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581", + "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412", + "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a", + "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2", + "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787", + "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f", + "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937", + "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64", + "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487", + "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b", + "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41", + "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a", + "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3", + "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166", + "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10" + ], + "version": "==1.4.2" + }, + "typing-extensions": { + "hashes": [ + "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", + "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", + "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" + ], + "version": "==3.7.4.3" + } + } +} diff --git a/README.md b/README.md index 4b8251d..252cc8d 100644 --- a/README.md +++ b/README.md @@ -20,70 +20,11 @@ The informations are received by the program made in python and are shown on an ## Requirements - Arduino (tested on UNO rev3) - - Arduino LCD KeyPad Shield (tested on v1.1) - - USB Ports +- [Python 3.8+](https://www.python.org/downloads/) -If you are not going to use Windows Binaries: - -- [Python 3.x](https://www.python.org/downloads/) (tested on 3.6) - -- pySerial package - - Open a terminal: - - ```sh - pip install pyserial - ``` - - or, if anaconda installed: - - ```sh - conda install pyserial - ``` - -- qtpy package - - Open a terminal: - - ```sh - pip install qtpy - ``` - - or, if anaconda installed: - - ```sh - conda install qtpy - ``` - -- PyQt5 or PyQt4 or PySide (tested with PyQt5) - - Open a terminal: - - ```sh - pip install PyQt5 - ``` - - ```sh - pip install PyQt4 - ``` - - ```sh - pip install PySide - ``` - - or, if anaconda installed: - - ```sh - conda install pyqt - ``` - - ```sh - conda install pyside - ``` - -### Do not forget +### Check the pins in Arduino Verify if the pin are the good ones on the arduino program for the LCD shield. (line 10) @@ -110,82 +51,41 @@ For LCD shield v1.0. Comment line 17-22 and uncomment line 24-30 in the serialse if (adc_key_in < 790) return btnSELECT; ``` -## Install & Usage - -### Using portable binaries - -1. Download windows/linux portable binaries - -1. Move gamestate_integration_arduinotrack.cfg in Program Files (x86)\Steam\SteamApps\common\Counter-Strike Global Offensive\csgo\cfg. - - -1. Mount the shield on the arduino (obviously) and [push](https://www.arduino.cc/en/main/howto) the serialsend.ino in the arduino (remember the COM port) - -1. Execute csgo-gsi-arduino-lcd - -### Using from source - -1. Clone/download this git - -1. Move gamestate_integration_arduinotrack.cfg in Program Files (x86)\Steam\SteamApps\common\Counter-Strike Global Offensive\csgo\cfg. - -1. Mount the shield on the arduino (obviously) and [push](https://www.arduino.cc/en/main/howto) the serialsend.ino in the arduino (remember the COM port). - -1. Install the python program: - - - ```sh - pip install . - ``` - - Or manually: - - - ```sh - python setup.py install - ``` - -1. Launch CSGO - -1. Run the python program (choose one and make sure you have added /Python/Scripts to Path) - - Windows: +## Usage - ```sh - csgogsilcd.bat - ``` +1. Clone/download this repository : - Linux: + ```sh + git clone git@github.com:Darkness4/csgo-gsi-arduino-lcd.git + ``` - ```sh - csgogsilcd - ``` +1. Move *gamestate_integration_arduinotrack.cfg* in *Counter-Strike Global Offensive\csgo\cfg*. - All: +1. Mount the LCD shield on the arduino and [push](https://www.arduino.cc/en/main/howto) the *serialsend.ino* in the arduino (remember the COM port). - ```sh - python csgogsilcd - ``` +1. Install the dependencies: -1. Choose the right COM + - Using pip : -1. Play some CSGO and enjoy! + ```sh + pip install -r requirements.txt + ``` -## F.A.Q + - Using pipenv : -1. The needed port (3000 by default) is occupied. What should i do? + ```sh + pipenv install + pipenv shell + ``` - Replace in csgogsi.py, '3000' which is the default port (line 195, col 33) with the new corresponding port: +5. Launch CS:GO - ```python - SERVER = MyServer(('localhost', 3000), MyRequestHandler) - ``` +6. Run the python program (choose one and make sure you have added /Python/Scripts to Path) - And in gamestate_integration_arduinotrack.cfg, line 3: - ```cfg - "uri" "http://127.0.0.1:3000" - ``` + ```sh + python -m csgo_gsi_arduino_lcd + ``` -1. Can i gather others informations than the ones proposed? +7. Choose the right COM - Yes, you can. The out.txt is an example of the payload which is storing all the informations from CS:GO. For more information, you can follow this [guide](https://github.com/tsuriga/csgo-gsi-qsguide) and [the official wiki about CS:GO GSI](https://developer.valvesoftware.com/wiki/Counter-Strike:_Global_Offensive_Game_State_Integration). +8. Play some CSGO and enjoy! \ No newline at end of file diff --git a/csgo_gsi_arduino_lcd/__init__.py b/csgo_gsi_arduino_lcd/__init__.py index b2a5a32..e69de29 100644 --- a/csgo_gsi_arduino_lcd/__init__.py +++ b/csgo_gsi_arduino_lcd/__init__.py @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -""" -CSGO's informations displayed on an Arduino featuring a bomb timer. - -@auteur: tsuriga, Darkness4 -""" - -from .main import main - -__title__ = "csgo-gsi-arduino-lcd" -__version__ = "1.3.2" -__project_url__ = 'https://github.com/Darkness4/csgo-gsi-arduino-lcd' -__credits__ = ["tsuriga", "Darkness4"] - -print(__title__, __version__) diff --git a/csgo_gsi_arduino_lcd/__main__.py b/csgo_gsi_arduino_lcd/__main__.py new file mode 100644 index 0000000..4b18d0e --- /dev/null +++ b/csgo_gsi_arduino_lcd/__main__.py @@ -0,0 +1,3 @@ +from csgo_gsi_arduino_lcd.main import main + +main() diff --git a/csgo_gsi_arduino_lcd/appui.py b/csgo_gsi_arduino_lcd/appui.py deleted file mode 100644 index cbb8684..0000000 --- a/csgo_gsi_arduino_lcd/appui.py +++ /dev/null @@ -1,201 +0,0 @@ -# -*- coding: utf-8 -*- -""" -User interface. - -@auteur: Darkness4 -""" -from os.path import dirname, abspath -from serial.tools import list_ports -from qtpy.QtWidgets import (QPushButton, QComboBox, - QVBoxLayout, QHBoxLayout, QWidget) -from qtpy.QtCore import Slot, Qt, QSize -from qtpy.QtGui import QIcon -from .server import ServerThread -from .tableviewer import PayloadViewerThread - -__dir__ = dirname(abspath(__file__)) - - -class Csgogsi(QWidget): - """ - App UI. - - Attributes - ---------- - comcb : QComboBox - Combo Box responsible for listing devices. - connect_btn : QPushButton - Button responsible for connection. - payload_viewer_btn : QPushButton - Button responsible to view the payload. - refresh_btn :QPushButton - Button responsible to refresh Serial. - server_thread : ServerThread - Server. - - Methods - ------- - close_all(*args, **kwargs) - Close everything before closing app. - connect() - Connect to COM. - refresh() - Refresh list. - start_payload_viewer() - Start Payload Viewer. - stop_server() - Stop the Server. - stop_payload_viewer() - Stop Payload Viewer. - - """ - - def __init__(self) -> None: - """Init UI.""" - super(Csgogsi, self).__init__() - # Widgets - self.server_thread = None - self.connect_btn = QPushButton('Connect') - self.connect_btn.clicked.connect(self.connect) - - self.comcb = QComboBox() - list_ports_device = [port.device for port in list_ports.comports()] - self.comcb.addItems(list_ports_device) - if list_ports_device == []: - self.connect_btn.setDisabled(True) - else: - self.connect_btn.setDisabled(False) - - self.refresh_btn = QPushButton('Refresh') - self.refresh_btn.resize(self.refresh_btn.sizeHint()) - self.refresh_btn.clicked.connect(self.refresh) - - self.payload_viewer_btn = QPushButton('View payload') - self.payload_viewer_btn.setDisabled(True) - - # Container - vbox = QVBoxLayout() - hbox = QHBoxLayout() - vbox.addStretch(1) - hbox.addWidget(self.comcb) - hbox.addWidget(self.refresh_btn) - vbox.addLayout(hbox) - vbox.addWidget(self.payload_viewer_btn) - vbox.addWidget(self.connect_btn) - self.setLayout(vbox) - # Icon - app_icon = QIcon() - app_icon.addFile( - __dir__ + "\\data\\csgo-16.ico", QSize(16, 16)) - app_icon.addFile( - __dir__ + "\\data\\csgo-20.ico", QSize(20, 20)) - app_icon.addFile( - __dir__ + "\\data\\csgo-24.ico", QSize(24, 24)) - app_icon.addFile( - __dir__ + "\\data\\csgo-32.ico", QSize(32, 32)) - app_icon.addFile( - __dir__ + "\\data\\csgo-48.ico", QSize(48, 48)) - app_icon.addFile( - __dir__ + "\\data\\csgo-64.ico", QSize(64, 64)) - app_icon.addFile( - __dir__ + "\\data\\csgo-128.ico", QSize(128, 128)) - app_icon.addFile( - __dir__ + "\\data\\csgo-256.ico", QSize(256, 256)) - app_icon.addFile( - __dir__ + "\\data\\csgo-512.ico", QSize(512, 512)) - # Window - self.setWindowIcon(app_icon) - self.setWindowTitle('CSGO GSI on LCD') - self.setWindowFlags(Qt.WindowCloseButtonHint) - - self.show() - self.setFixedSize(self.size()) - - @Slot() - def refresh(self) -> None: - """Refresh COM ports.""" - self.comcb.clear() - list_ports_device = [port.device for port in list_ports.comports()] - self.comcb.addItems(list_ports_device) - if list_ports_device == []: - self.connect_btn.setDisabled(True) - else: - self.connect_btn.setDisabled(False) - - @Slot() - def connect(self) -> None: - """Connect to the server.""" - # Disable buttons - self.comcb.setDisabled(True) - self.connect_btn.setDisabled(True) - self.refresh_btn.setDisabled(True) - # Server start - if self.server_thread is None: - self.server_thread = ServerThread(str(self.comcb.currentText())) - self.server_thread.start() - # Change connect button's function to "stop" - self.connect_btn.clicked.disconnect() - self.connect_btn.clicked.connect(self.stop_server) - self.connect_btn.setDisabled(False) - self.connect_btn.setText('Stop') - # Enable payload_viewer - self.payload_viewer_btn.clicked.connect(self.start_payload_viewer) - self.payload_viewer_btn.setDisabled(False) - - @Slot() - def stop_server(self) -> None: - """Stop the server.""" - # Disable buttons - self.payload_viewer_btn.setDisabled(True) - # Kill the messenger and server - self.server_thread.server.messenger.shutdown() - if self.server_thread.server.payload_viewer is not None \ - and self.server_thread.server.payload_viewer.is_alive(): - self.server_thread.server.payload_viewer.shutdown() - self.server_thread.server.payload_viewer = None - self.server_thread.server.shutdown() - self.server_thread = None - # Change button function - self.connect_btn.clicked.disconnect() - self.connect_btn.clicked.connect(self.connect) - self.connect_btn.setText('Connect') - self.payload_viewer_btn.clicked.disconnect() - self.payload_viewer_btn.clicked.connect(self.start_payload_viewer) - self.payload_viewer_btn.setText('View payload') - # Enable buttons - self.comcb.setDisabled(False) - self.connect_btn.setDisabled(False) - self.refresh_btn.setDisabled(False) - - @Slot() - def start_payload_viewer(self) -> None: - """Start Payload Viewer.""" - # Start payload vierwer - self.server_thread.server.payload_viewer = PayloadViewerThread() - self.server_thread.server.payload_viewer.start() - - # Change button function - self.payload_viewer_btn.clicked.disconnect() - self.payload_viewer_btn.clicked.connect(self.stop_payload_viewer) - self.payload_viewer_btn.setText('Hide payload') - - @Slot() - def stop_payload_viewer(self) -> None: - """Stop Payload Viewer.""" - # Stop payload viewer - self.server_thread.server.payload_viewer.shutdown() - self.server_thread.server.payload_viewer = None - - # Change button function - self.payload_viewer_btn.clicked.disconnect() - self.payload_viewer_btn.clicked.connect(self.start_payload_viewer) - self.payload_viewer_btn.setText('View payload') - - def close_all(self, *args, **kwargs) -> None: - """Close everything before closing app.""" - super(Csgogsi, self).closeEvent(*args, **kwargs) - if self.server_thread is not None \ - and self.server_thread.server.messenger.is_alive(): - self.server_thread.server.messenger.shutdown() - if self.server_thread is not None and self.server_thread.isRunning(): - self.server_thread.server.shutdown() diff --git a/csgo_gsi_arduino_lcd/data/csgo-128.ico b/csgo_gsi_arduino_lcd/assets/csgo-128.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-128.ico rename to csgo_gsi_arduino_lcd/assets/csgo-128.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-16.ico b/csgo_gsi_arduino_lcd/assets/csgo-16.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-16.ico rename to csgo_gsi_arduino_lcd/assets/csgo-16.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-20.ico b/csgo_gsi_arduino_lcd/assets/csgo-20.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-20.ico rename to csgo_gsi_arduino_lcd/assets/csgo-20.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-24.ico b/csgo_gsi_arduino_lcd/assets/csgo-24.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-24.ico rename to csgo_gsi_arduino_lcd/assets/csgo-24.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-256.ico b/csgo_gsi_arduino_lcd/assets/csgo-256.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-256.ico rename to csgo_gsi_arduino_lcd/assets/csgo-256.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-32.ico b/csgo_gsi_arduino_lcd/assets/csgo-32.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-32.ico rename to csgo_gsi_arduino_lcd/assets/csgo-32.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-48.ico b/csgo_gsi_arduino_lcd/assets/csgo-48.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-48.ico rename to csgo_gsi_arduino_lcd/assets/csgo-48.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-512.ico b/csgo_gsi_arduino_lcd/assets/csgo-512.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-512.ico rename to csgo_gsi_arduino_lcd/assets/csgo-512.ico diff --git a/csgo_gsi_arduino_lcd/data/csgo-64.ico b/csgo_gsi_arduino_lcd/assets/csgo-64.ico similarity index 100% rename from csgo_gsi_arduino_lcd/data/csgo-64.ico rename to csgo_gsi_arduino_lcd/assets/csgo-64.ico diff --git a/csgo_gsi_arduino_lcd/data/__init__.py b/csgo_gsi_arduino_lcd/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csgo_gsi_arduino_lcd/data/arduino_mediator.py b/csgo_gsi_arduino_lcd/data/arduino_mediator.py new file mode 100644 index 0000000..701dc31 --- /dev/null +++ b/csgo_gsi_arduino_lcd/data/arduino_mediator.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +""" +ArduinoMediator. + +@auteur: Darkness4 +""" +import logging +from threading import Thread +from time import sleep, time +from typing import Optional + +from csgo_gsi_arduino_lcd.entities.state import State +from csgo_gsi_arduino_lcd.entities.status import Status +from serial import Serial + + +class ArduinoMediator(Thread): + """Give order to the arduino.""" + + state: Optional[State] = None + __refresh = False # Order to refresh informations + __start = True # Order to start/stop + __status: Status = Status.NONE + ser_arduino: Serial + + def __init__(self, ser_arduino: Serial): + """Init save.""" + super(ArduinoMediator, self).__init__() + self.ser_arduino = ser_arduino + + @property + def status(self) -> Status: + return self.__status + + @status.setter + def status(self, status: Status): + """Change Messenger behavior.""" + self.__status = status + self.__refresh = True # Informations need to be refreshed + + def run(self): + """Thread start.""" + while self.__start: + self.refresh() if self.__refresh else sleep(0.1) + logging.info("Messenger is dead.") + + def refresh(self): + self.__refresh = False + # Has refreshed + if self.__status in ( + Status.BOMB, + Status.DEFUSED, + Status.EXPLODED, + ): # Bomb + self.draw_bomb_timer() + elif self.__status == Status.NONE: + self.draw_idling() + else: # Default status + self.write_player_stats() + + def draw_bomb_timer(self): + """40 sec bomb timer on arduino.""" + offset = time() + actualtime: int = int(40 - time() + offset) + while actualtime > 0 and self.__status == Status.BOMB: + oldtime = actualtime + sleep(0.1) + actualtime = int(40 - time() + offset) + if oldtime != actualtime: # Actualization only integer change + self.ser_arduino.write(b"BOMB PLANTED") + # Wait for second line + sleep(0.1) + for i in range(0, 40, 5): + self.ser_arduino.write( + ArduinoMediator.progress(actualtime - i) + ) + self.ser_arduino.write(str(actualtime).encode()) + sleep(0.1) + if self.__status == Status.DEFUSED: + self.ser_arduino.write(b"BOMB DEFUSED") + # Wait for second line + sleep(0.1) + self.ser_arduino.write(b" ") + sleep(0.1) + elif self.__status == Status.EXPLODED: + self.ser_arduino.write(b"BOMB EXPLODED") + # Wait for second line + sleep(0.1) + self.ser_arduino.write(b" ") + sleep(0.1) + + def write_player_stats(self): + """Player stats writer.""" + # Not too fast + sleep(0.1) + + # Writing health and armor in Serial + self.draw_health_and_armor() + + # Wait for second line + sleep(0.1) + + # Kill or Money + if self.__status == Status.NOT_FREEZETIME: + self.draw_kills() + elif self.__status == Status.FREEZETIME: + self.draw_money() + sleep(0.1) + + def draw_kills(self): + """Show kills in one line.""" + # HS and Kill counter + self.ser_arduino.write(b"K: ") + for _ in range( + self.state.round_kills - self.state.round_killhs + ): # counting + self.ser_arduino.write(b"\x00") # Byte 0 char : kill no HS + for _ in range(self.state.round_killhs): # counting + self.ser_arduino.write(b"\x01") # Byte 1 char : HS + + def draw_money(self): + """Show money in one line.""" + self.ser_arduino.write(f"M: {self.state.money}".encode()) + + def draw_health_and_armor(self): + """Show health and armor in one line.""" + self.ser_arduino.write(b"H: ") + self.ser_arduino.write( + ArduinoMediator.progress(self.state.health // 5) + ) + self.ser_arduino.write( + ArduinoMediator.progress((self.state.health - 25) // 5) + ) + self.ser_arduino.write( + ArduinoMediator.progress((self.state.health - 50) // 5) + ) + self.ser_arduino.write( + ArduinoMediator.progress((self.state.health - 75) // 5) + ) + self.ser_arduino.write(b" A: ") + self.ser_arduino.write(ArduinoMediator.progress(self.state.armor // 5)) + self.ser_arduino.write( + ArduinoMediator.progress((self.state.armor - 25) // 5) + ) + self.ser_arduino.write( + ArduinoMediator.progress((self.state.armor - 50) // 5) + ) + self.ser_arduino.write( + ArduinoMediator.progress((self.state.armor - 75) // 5) + ) + + def draw_idling(self): + """Print text while idling.""" + self.ser_arduino.write(b"Waiting for") + sleep(0.1) + self.ser_arduino.write(b"matches") + + def shutdown(self): + """Stop the mediator.""" + self.__start = False + + @staticmethod + def progress(i: int) -> bytes: + """ + Progress bar, for arduino 5px large. + + Parameters + ---------- + i : int + Select which character to send to Arduino. + + Returns + ------- + bytes : Character send to Arduino. + + """ + if i <= 0: + return b"\x07" + elif 1 <= i <= 5: + return bytes([i + 1]) + else: + return b"\x06" diff --git a/csgo_gsi_arduino_lcd/data/csgo_request_handler.py b/csgo_gsi_arduino_lcd/data/csgo_request_handler.py new file mode 100644 index 0000000..b188a18 --- /dev/null +++ b/csgo_gsi_arduino_lcd/data/csgo_request_handler.py @@ -0,0 +1,82 @@ +from http.server import BaseHTTPRequestHandler +from json import loads +from typing import Optional + +from csgo_gsi_arduino_lcd.data.arduino_mediator import ArduinoMediator +from csgo_gsi_arduino_lcd.debug.payload_viewer_thread import ( + PayloadViewerThread, +) +from csgo_gsi_arduino_lcd.entities.state import State +from csgo_gsi_arduino_lcd.entities.status import Status + + +class CsgoRequestHandler(BaseHTTPRequestHandler): + """CSGO's requests handler.""" + + is_waiting: bool = False + payload_viewer: PayloadViewerThread + arduino: ArduinoMediator + + def __init__( + self, + arduino_mediator: ArduinoMediator, + payload_viewer: PayloadViewerThread, + *args, + **kwargs + ): + super(CsgoRequestHandler, self).__init__(*args, **kwargs) + self.arduino = arduino_mediator + self.payload_viewer = payload_viewer + + def do_POST(self): + """Receive CSGO's informations.""" + length = int(self.headers["Content-Length"]) + body = self.rfile.read(length).decode("utf-8") + + self.parse_payload(loads(body)) + + self.send_header("Content-type", "text/html") + self.send_response(200) + self.end_headers() + + # Parsing and actions + def parse_payload( + self, + payload: dict, + ): + """ + Search payload and execute arduino's codes. + + Parameters + ---------- + payload : dict + Payload containing all CSGO's informations. + + """ + round_phase = payload["round"]["phase"] + + if round_phase is not None: + self.is_waiting = False + state: Optional[State] = None + state = State.from_dict(payload["player"]["state"]) + self.arduino.status = Status.from_bomb_dict( + payload["round"]["bomb"] + ) + + if state != self.arduino.state: # if the state has changed + self.arduino.status = Status.NOT_FREEZETIME + # Progress bar HP AM + self.arduino.state = state + + if round_phase != "freezetime": + self.arduino.status = Status.NOT_FREEZETIME + else: # Not kill streak + self.arduino.status = Status.FREEZETIME + elif not self.is_waiting: + self.is_waiting = True # is_waiting + self.arduino.status = Status.NONE + + # Start the payload viewer + if payload != self.payload_viewer.payload: + self.payload_viewer.payload = payload + self.payload_viewer.refresh() diff --git a/csgo_gsi_arduino_lcd/data/server_thread.py b/csgo_gsi_arduino_lcd/data/server_thread.py new file mode 100644 index 0000000..c035432 --- /dev/null +++ b/csgo_gsi_arduino_lcd/data/server_thread.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +""" +Server Thread. + +@auteur: Darkness4 +""" + +import logging +from functools import partial +from http.server import ThreadingHTTPServer +from typing import Optional + +from csgo_gsi_arduino_lcd.data.arduino_mediator import ArduinoMediator +from csgo_gsi_arduino_lcd.data.csgo_request_handler import CsgoRequestHandler +from csgo_gsi_arduino_lcd.debug.payload_viewer_thread import ( + PayloadViewerThread, +) +from qtpy.QtCore import QThread +from serial import Serial + + +class ServerThread(QThread): + """Server thread.""" + + payload_viewer = PayloadViewerThread() + arduino_mediator: ArduinoMediator + server: Optional[ThreadingHTTPServer] = None + ser_arduino: Serial + + def __init__(self, com_str: str): + """Start thread and save the COM port.""" + QThread.__init__(self) + self.com_str = com_str + self.ser_arduino = Serial(self.com_str, 9600) + logging.info("Arduino detected") + + # Launch server + self.arduino_mediator = ArduinoMediator(self.ser_arduino) + self.arduino_mediator.start() + logging.info("Arduino Meditor started") + + def run(self): + """Start the server.""" + handler = partial( + CsgoRequestHandler, + self.arduino_mediator, + self.payload_viewer, + ) + with ThreadingHTTPServer(("localhost", 3000), handler) as server: + self.server = server + logging.info("CS:GO GSI Quick Start server starting") + server.serve_forever() + logging.info("CS:GO GSI Quick Start server stopped") + self.ser_arduino.close() + logging.info("Serial stopped") diff --git a/csgo_gsi_arduino_lcd/debug/__init__.py b/csgo_gsi_arduino_lcd/debug/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csgo_gsi_arduino_lcd/tableviewer.py b/csgo_gsi_arduino_lcd/debug/payload_viewer_thread.py similarity index 61% rename from csgo_gsi_arduino_lcd/tableviewer.py rename to csgo_gsi_arduino_lcd/debug/payload_viewer_thread.py index 9dc1ba6..51cb851 100644 --- a/csgo_gsi_arduino_lcd/tableviewer.py +++ b/csgo_gsi_arduino_lcd/debug/payload_viewer_thread.py @@ -5,8 +5,10 @@ @auteur: Darkness4 """ -from json import dumps +import json +import logging from threading import Thread +from typing import Optional class PayloadViewerThread(Thread): @@ -35,33 +37,31 @@ class PayloadViewerThread(Thread): running = True # Order to start/stop refreshable = False - __payload = None + payload: Optional[dict] = None + __pause = True - def __init__(self) -> None: + def __init__(self): """Start thread.""" super(PayloadViewerThread, self).__init__() - def run(self) -> None: + def run(self): """Print payload.""" while self.running: - if self.refreshable: - print(dumps(self.payload, indent=4)) - self.refreshable = False + if not self.__pause: + if self.refreshable: + logging.debug(json.dumps(self.payload, indent=4)) + self.refreshable = False - def shutdown(self) -> None: + def shutdown(self): """Shutdown thread.""" self.running = False - @property - def payload(self) -> dict: - """Get the payload.""" - return self.__payload + def pause(self): + self.__pause = True - @payload.setter - def payload(self, payload: dict) -> None: - """Set the payload.""" - self.__payload = payload + def resume(self): + self.__pause = False - def refresh(self) -> None: + def refresh(self): """Refresh.""" self.refreshable = True diff --git a/csgo_gsi_arduino_lcd/entities/__init__.py b/csgo_gsi_arduino_lcd/entities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csgo_gsi_arduino_lcd/entities/state.py b/csgo_gsi_arduino_lcd/entities/state.py new file mode 100644 index 0000000..85ada50 --- /dev/null +++ b/csgo_gsi_arduino_lcd/entities/state.py @@ -0,0 +1,21 @@ +from dataclasses import dataclass +from typing import Any, Dict + + +@dataclass +class State: + health: int + armor: int + round_kills: int + round_killhs: int + money: int + + @classmethod + def from_dict(cls, data: Dict[str, Any]): + return cls( + health=int(data["health"]), + armor=int(data["armor"]), + round_kills=int(data["round_kills"]), + round_killhs=int(data["round_killhs"]), + money=int(data["money"]), + ) diff --git a/csgo_gsi_arduino_lcd/entities/status.py b/csgo_gsi_arduino_lcd/entities/status.py new file mode 100644 index 0000000..26823dd --- /dev/null +++ b/csgo_gsi_arduino_lcd/entities/status.py @@ -0,0 +1,22 @@ +from enum import Enum +from typing import Dict + + +class Status(Enum): + NONE = 0 + BOMB = 1 + NOT_FREEZETIME = 2 + FREEZETIME = 3 + DEFUSED = 4 + EXPLODED = 5 + + @classmethod + def from_bomb_dict(cls, data: str): + if data == "planted": + return cls.BOMB + elif data == "defused": + return cls.DEFUSED + elif data == "exploded": + return cls.EXPLODED + else: + return cls.NONE diff --git a/csgo_gsi_arduino_lcd/httpserver.py b/csgo_gsi_arduino_lcd/httpserver.py deleted file mode 100644 index 4fab60b..0000000 --- a/csgo_gsi_arduino_lcd/httpserver.py +++ /dev/null @@ -1,129 +0,0 @@ -# -*- coding: utf-8 -*- -""" -HTTP server Thread. - -@auteur: tsuriga, Darkness4 -""" -from http.server import BaseHTTPRequestHandler, HTTPServer -from json import loads -from time import asctime -from .messenger import Messenger - - -class HTTPCSGOServer(HTTPServer): - """Server storing CSGO's information.""" - def __init__(self, ser_arduino, *args, **kwargs) -> None: - """ - You can store states over multiple requests in the server. - - Parameters - ---------- - ser_arduino : Serial - Arduino in Serial. - - """ - # HTTPServer.__init__(self, *args, **kwargs) - super(HTTPCSGOServer, self).__init__(*args, **kwargs) - self.round_phase = None - self.bomb = None - self.state = None - self.is_waiting = False - self.payload_viewer = None - self.ser_arduino = ser_arduino - self.messenger = Messenger(ser_arduino) - self.messenger.start() - print(asctime(), '-', "Messenger is online.") - - -class CSGORequestHandler(BaseHTTPRequestHandler): - """ - CSGO's requests handler. - - Methods - ------- - do_POST() - Receive CSGO's informations. - parse_payload(payload: dict) - Search payload and execute arduino's codes. - log_message(self, format, *args) - Prevent requests from printing into the console. - - """ - - def do_POST(self): - """Receive CSGO's informations.""" - length = int(self.headers['Content-Length']) - body = self.rfile.read(length).decode('utf-8') - - self.parse_payload(loads(body)) - - self.send_header('Content-type', 'text/html') - self.send_response(200) - self.end_headers() - - # Parsing and actions - def parse_payload(self, payload: dict) -> None: - """ - Search payload and execute arduino's codes. - - Parameters - ---------- - payload : dict - Payload containing all CSGO's informations. - - """ - round_phase = None - if 'round' in payload and 'phase' in payload['round']: - round_phase = payload['round']['phase'] - - if round_phase is not None: - self.server.is_waiting = False - bomb = None - state = None - - if 'round' in payload and 'bomb' in payload['round']: - bomb = payload['round']['bomb'] - - if 'player' in payload and 'state' in payload['player']: - state = {'health': payload['player']['state']['health'], - 'armor': payload['player']['state']['armor'], - 'round_kills': payload['player']['state']['round_kills'], - 'round_killhs': payload['player']['state']['round_killhs'], - 'money': payload['player']['state']['money']} - - if bomb != self.server.bomb: - self.server.bomb = bomb - if bomb == 'planted': - self.server.messenger.status = "Bomb" - elif bomb == 'defused': - self.server.messenger.status = "Defused" - elif bomb == 'exploded': - self.server.messenger.status = "Exploded" - else: - self.server.messenger.status = "None" - elif state != self.server.state: # if the state has changed - self.server.messenger.status = "!Freezetime" - self.server.state = state # Gather player's state - # Progress bar HP AM - self.server.messenger.health = int(state['health']) - self.server.messenger.armor = int(state['armor']) - self.server.messenger.money = int(state['money']) - self.server.messenger.kills = (state['round_kills'], - state['round_killhs']) - if round_phase != 'freezetime': - self.server.messenger.status = "!Freezetime" - else: # Not kill streak - self.server.messenger.status = "Freezetime" - elif not self.server.is_waiting: - self.server.is_waiting = True # is_waiting - self.server.messenger.status = "None" - - # Start the payload viewer - if self.server.payload_viewer is not None \ - and payload != self.server.payload_viewer.payload: - self.server.payload_viewer.payload = payload - self.server.payload_viewer.refresh() - - def log_message(self, format, *args) -> None: - """Prevent requests from printing into the console.""" - return diff --git a/csgo_gsi_arduino_lcd/main.py b/csgo_gsi_arduino_lcd/main.py index d0f9824..b8632e6 100644 --- a/csgo_gsi_arduino_lcd/main.py +++ b/csgo_gsi_arduino_lcd/main.py @@ -5,15 +5,14 @@ @auteur: tsuriga, Darkness4 """ -from sys import argv, exit +import sys + from qtpy.QtWidgets import QApplication -from .appui import Csgogsi +from csgo_gsi_arduino_lcd.ui.csgo_window import CsgoWindow def main(): - """Launch.""" - app = None - app = QApplication(argv) - ex = Csgogsi() + app = QApplication(sys.argv) + w = CsgoWindow() exit(app.exec_()) diff --git a/csgo_gsi_arduino_lcd/messenger.py b/csgo_gsi_arduino_lcd/messenger.py deleted file mode 100644 index 209c0ca..0000000 --- a/csgo_gsi_arduino_lcd/messenger.py +++ /dev/null @@ -1,240 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Messenger Thread. - -@auteur: Darkness4 -""" -from threading import Thread -from time import asctime, sleep, time - - -def progress(i: int) -> bytes: - """ - Progress bar, for arduino 5px large. - - Parameters - ---------- - i : int - Select which character to send to Arduino. - - Returns - ------- - bytes : Character send to Arduino. - - """ - switcher = {i <= 0: b"\x07", - i == 1: b"\x02", - i == 2: b"\x03", - i == 3: b"\x04", - i == 4: b"\x05", - i >= 5: b"\x06"} - return switcher[True] - - -class Messenger(Thread): - """ - Give order to the arduino. - - Attributes - ---------- - __armor : int - Armor points. - __health : int - Health points. - __kills : tuple - Number of kills and heads. - __money : int - Money left. - __refresh : bool - Status of the refresher. - __start : bool - Status of Messenger. - __status : string - Status of the round. - ser_arduino : Serial - Serial class of the Arduino. - - Methods - ------- - bomb_timer() - Start a bomb timer. - idle() - Put Messenger on idle and write a message. - run() - Start the Thread and run Messenger. - shutdown() - Shutdown Messenger. - write_player_stats() - Write the player stats on Arduino. - - """ - __armor = None - __health = None - __kills = None # tuple (total kills - hs, hs) - __money = None - __refresh = False # Order to refresh informations - __start = True # Order to start/stop - __status = "None" - - def __init__(self, ser_arduino) -> None: - """Init save.""" - super(Messenger, self).__init__() - self.ser_arduino = ser_arduino - - @property - def armor(self) -> int: - """Get the armor.""" - return self.__armor - - @armor.setter - def armor(self, armor: int) -> None: - """Set the armor.""" - self.__armor = armor - - @property - def money(self) -> int: - """Get the money.""" - return self.__money - - @money.setter - def money(self, money: int) -> None: - """Set the money.""" - self.__money = money - - @property - def health(self) -> int: - """Get the health.""" - return self.__health - - @health.setter - def health(self, health: int) -> None: - """Set the health.""" - self.__health = health - - @property - def status(self) -> str: - """Get the status.""" - return self.__status - - @status.setter - def status(self, status: str) -> None: - """ - Change Messenger behavior. - - Available status: - 'None' - 'Bomb' - '!Freezetime' - 'Freezetime' - 'Defused' - 'Exploded' - """ - self.__status = status - self.__refresh = True # Informations need to be refreshed - - @property - def kills(self) -> tuple: - """Get the kills (K, HS).""" - return self.__kills - - @kills.setter - def kills(self, kills_heads: tuple) -> None: - """Set the number of kills (K, HS).""" - self.__kills = (int(kills_heads[0])-int(kills_heads[1]), - int(kills_heads[1])) - - def run(self) -> None: - """Thread start.""" - while self.__start: - if self.__refresh: - self.__refresh = False # Has refreshed - if self.status in ("Bomb", "Defused", "Exploded"): # Bomb - self.bomb_timer() - - elif self.status == "None": - self.idle() - - else: # Default status - self.write_player_stats() - else: - sleep(0.1) # Saving consumption - print(asctime(), "-", "Messenger is dead.") - - def bomb_timer(self) -> None: - """40 sec bomb timer on arduino.""" - offset = time() - actualtime = 40 - time() + offset - while actualtime > 0 and self.status == "Bomb": - oldtime = int(actualtime) - sleep(0.1) - actualtime = 40 - time() + offset - if oldtime != int(actualtime): # Actualization only integer change - self.ser_arduino.write(b'BOMB PLANTED') - # Wait for second line - sleep(0.1) - self.ser_arduino.write(progress(int(actualtime))) # 5s - self.ser_arduino.write(progress(int(actualtime - 5))) # 10s - self.ser_arduino.write(progress(int(actualtime - 10))) # 15s - self.ser_arduino.write(progress(int(actualtime - 15))) # 20s - self.ser_arduino.write(progress(int(actualtime - 20))) # 25s - self.ser_arduino.write(progress(int(actualtime - 25))) - self.ser_arduino.write(progress(int(actualtime - 30))) - self.ser_arduino.write(progress(int(actualtime - 35))) - self.ser_arduino.write(bytes(str(int(actualtime)).encode())) - sleep(0.1) - if self.status == "Defused": - self.ser_arduino.write(b'BOMB DEFUSED') - # Wait for second line - sleep(0.1) - self.ser_arduino.write(b' ') - sleep(0.1) - elif self.status == "Exploded": - self.ser_arduino.write(b'BOMB EXPLODED') - # Wait for second line - sleep(0.1) - self.ser_arduino.write(b' ') - sleep(0.1) - - def write_player_stats(self) -> None: - """Player stats writer.""" - # Not too fast - sleep(0.1) - - # Writing health and armor in Serial - self.ser_arduino.write(b'H: ') - self.ser_arduino.write(progress(int(self.health / 5))) - self.ser_arduino.write(progress(int((self.health - 25) / 5))) - self.ser_arduino.write(progress(int((self.health - 50) / 5))) - self.ser_arduino.write(progress(int((self.health - 75) / 5))) - self.ser_arduino.write(b' A: ') - self.ser_arduino.write(progress(int(self.armor / 5))) - self.ser_arduino.write(progress(int((self.armor - 25) / 5))) - self.ser_arduino.write(progress(int((self.armor - 50) / 5))) - self.ser_arduino.write(progress(int((self.armor - 75) / 5))) - - # Wait for second line - sleep(0.1) - - # Kill or Money - if self.status == "!Freezetime": - # HS and Kill counter - self.ser_arduino.write(b'K: ') - for _ in range(self.kills[0]): # counting - self.ser_arduino.write(b'\x00') # Byte 0 char : kill no HS - for _ in range(self.kills[1]): # counting - self.ser_arduino.write(b'\x01') # Byte 1 char : HS - # Not kill streak - elif self.status == "Freezetime": - self.ser_arduino.write( - bytes('M: {}'.format(self.money).encode())) - sleep(0.1) - - def idle(self) -> None: - """Print text while idling.""" - self.ser_arduino.write(b'Waiting for') - sleep(0.1) - self.ser_arduino.write(b'matches') - - def shutdown(self) -> None: - """Stop Messenger.""" - self.__start = False diff --git a/csgo_gsi_arduino_lcd/server.py b/csgo_gsi_arduino_lcd/server.py deleted file mode 100644 index 1e324a2..0000000 --- a/csgo_gsi_arduino_lcd/server.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Server Thread. - -@auteur: Darkness4 -""" - -from time import asctime, sleep - -from qtpy.QtCore import QThread -from serial import Serial - -from .httpserver import CSGORequestHandler, HTTPCSGOServer - - -class ServerThread(QThread): - """ - Server's thread. - - Attributes - ---------- - com_str : str - COM Port in str. - ser_arduino : Serial - Status of Messenger. - server : Server - Status of the refresher. - - Methods - ------- - run() - Start the Thread and run Server. - - """ - - ser_arduino = None - server = None - - def __init__(self, com_str) -> None: - """Start thread and save the COM port.""" - QThread.__init__(self) - self.com_str = com_str - - def run(self) -> None: - """Start the server.""" - self.ser_arduino = Serial(self.com_str, 9600) - sleep(2) # Wait for arduino - print(asctime(), '-', "Arduino detected") - # Launch server - self.server = HTTPCSGOServer(self.ser_arduino, - ('localhost', 3000), - CSGORequestHandler) - print(asctime(), '-', 'CS:GO GSI Quick Start server starting') - self.server.serve_forever() # Run - self.server.server_close() # Close server - print(asctime(), '-', 'CS:GO GSI Quick Start server stopped') - self.ser_arduino.close() # Close COM port - print(asctime(), '-', 'Serial stopped') diff --git a/csgo_gsi_arduino_lcd/ui/__init__.py b/csgo_gsi_arduino_lcd/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csgo_gsi_arduino_lcd/ui/csgo_window.py b/csgo_gsi_arduino_lcd/ui/csgo_window.py new file mode 100644 index 0000000..b953f0c --- /dev/null +++ b/csgo_gsi_arduino_lcd/ui/csgo_window.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +""" +User interface. + +@auteur: Darkness4 +""" +import os +import sys + +from csgo_gsi_arduino_lcd.data.server_thread import ServerThread +from qtpy.QtCore import QSize, Qt, Slot +from qtpy.QtGui import QIcon +from qtpy.QtWidgets import ( + QComboBox, + QHBoxLayout, + QPushButton, + QVBoxLayout, + QWidget, +) +from serial.tools import list_ports + + +def getExecPath(): + try: + sFile = os.path.abspath(sys.modules["__main__"].__file__) + except Exception: + sFile = sys.executable + return os.path.dirname(sFile) + + +class CsgoWindow(QWidget): + """App UI.""" + + def __init__(self): + """Init UI.""" + super(CsgoWindow, self).__init__() + # Widgets + self.server_thread = None + self.connect_btn = QPushButton("Connect") + + self.comcb = QComboBox() + list_ports_device = [port.device for port in list_ports.comports()] + self.comcb.addItems(list_ports_device) + if list_ports_device == []: + self.connect_btn.setDisabled(True) + else: + self.connect_btn.setDisabled(False) + + self.refresh_btn = QPushButton("Refresh") + self.refresh_btn.resize(self.refresh_btn.sizeHint()) + self.refresh_btn.clicked.connect(self.refresh) + + self.payload_viewer_btn = QPushButton("View payload") + self.payload_viewer_btn.setDisabled(True) + + # Container + vbox = QVBoxLayout() + hbox = QHBoxLayout() + vbox.addStretch(1) + hbox.addWidget(self.comcb) + hbox.addWidget(self.refresh_btn) + vbox.addLayout(hbox) + vbox.addWidget(self.payload_viewer_btn) + vbox.addWidget(self.connect_btn) + self.setLayout(vbox) + + # Icon + app_icon = QIcon() + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-16.ico"), QSize(16, 16) + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-20.ico"), QSize(20, 20) + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-24.ico"), QSize(24, 24) + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-32.ico"), QSize(32, 32) + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-48.ico"), QSize(48, 48) + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-64.ico"), QSize(64, 64) + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-128.ico"), + QSize(128, 128), + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-256.ico"), + QSize(256, 256), + ) + app_icon.addFile( + os.path.join(getExecPath(), "assets/csgo-512.ico"), + QSize(512, 512), + ) + + # Window + self.setWindowIcon(app_icon) + self.setWindowTitle("CSGO GSI on LCD") + self.setWindowFlags(Qt.WindowCloseButtonHint) + + self.show() + self.setFixedSize(self.size()) + + @Slot() + def refresh(self): + """Refresh COM ports.""" + self.comcb.clear() + list_ports_device = [port.device for port in list_ports.comports()] + self.comcb.addItems(list_ports_device) + if list_ports_device == []: + self.connect_btn.setDisabled(True) + else: + self.connect_btn.setDisabled(False) + + @Slot() + def connect(self): + """Connect to the server.""" + # Disable buttons + self.comcb.setDisabled(True) + self.connect_btn.setDisabled(True) + self.refresh_btn.setDisabled(True) + + # Server start + if self.server_thread is None: + self.server_thread = ServerThread(str(self.comcb.currentText())) + self.server_thread.start() + + # Change connect button's function to "stop" + self.connect_btn.clicked.disconnect() + self.connect_btn.clicked.connect(self.stop_server) + self.connect_btn.setDisabled(False) + self.connect_btn.setText("Stop") + + # Enable payload_viewer + self.payload_viewer_btn.clicked.connect(self.resume_payload_viewer) + self.payload_viewer_btn.setDisabled(False) + + @Slot() + def stop_server(self): + """Stop the server.""" + # Disable buttons + self.payload_viewer_btn.setDisabled(True) + + # Kill the messenger and server + self.server_thread.arduino_mediator.shutdown() + self.server_thread.payload_viewer.shutdown() + self.server_thread.server.shutdown() + self.server_thread = None + + # Change button function + self.connect_btn.clicked.disconnect() + self.connect_btn.clicked.connect(self.connect) + self.connect_btn.setText("Connect") + self.payload_viewer_btn.clicked.disconnect() + self.payload_viewer_btn.clicked.connect(self.resume_payload_viewer) + self.payload_viewer_btn.setText("View payload") + + # Enable buttons + self.comcb.setDisabled(False) + self.connect_btn.setDisabled(False) + self.refresh_btn.setDisabled(False) + + @Slot() + def resume_payload_viewer(self): + """Start Payload Viewer.""" + # Start payload vierwer + self.server_thread.payload_viewer.resume() + + # Change button function + self.payload_viewer_btn.clicked.disconnect() + self.payload_viewer_btn.clicked.connect(self.pause_payload_viewer) + self.payload_viewer_btn.setText("Hide payload") + + @Slot() + def pause_payload_viewer(self): + """Stop Payload Viewer.""" + # Stop payload viewer + if self.server_thread.payload_viewer is not None: + self.server_thread.payload_viewer.pause() + + # Change button function + self.payload_viewer_btn.clicked.disconnect() + self.payload_viewer_btn.clicked.connect(self.resume_payload_viewer) + self.payload_viewer_btn.setText("View payload") + + def close_all(self, *args, **kwargs): + """Close everything before closing app.""" + super(CsgoWindow, self).closeEvent(*args, **kwargs) + if self.server_thread is not None: + self.server_thread.arduino_mediator.shutdown() + self.server_thread.payload_viewer.shutdown() + self.server_thread.server.shutdown() + self.server_thread = None diff --git a/csgogsilcd b/csgogsilcd deleted file mode 100644 index e0cf483..0000000 --- a/csgogsilcd +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/python3 - -from csgo_gsi_arduino_lcd import main - -main() diff --git a/csgogsilcd.bat b/csgogsilcd.bat deleted file mode 100644 index 8e84051..0000000 --- a/csgogsilcd.bat +++ /dev/null @@ -1,5 +0,0 @@ -@setlocal enableextensions enabledelayedexpansion -@ECHO OFF -FOR /f %%p in ('where python') do SET PYTHONPATH=%%p -set PYTHONPATH=!PYTHONPATH:~0,-10! -python %PYTHONPATH%\Scripts\csgogsilcd diff --git a/releases/csgo-gsi-arduino-lcd b/releases/csgo-gsi-arduino-lcd deleted file mode 100644 index 03cd5f8..0000000 --- a/releases/csgo-gsi-arduino-lcd +++ /dev/null @@ -1,655 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -CSGO's informations displayed on an Arduino featuring a bomb timer. - -@auteur: tsuriga, Darkness4 -""" - -from http.server import BaseHTTPRequestHandler, HTTPServer -from json import dumps, loads -from sys import argv -from threading import Thread -from time import asctime, sleep, time - -from qtpy.QtCore import Qt, QThread, Slot -from qtpy.QtWidgets import (QApplication, QComboBox, QHBoxLayout, QPushButton, - QVBoxLayout, QWidget) -from serial import Serial -from serial.tools import list_ports - -__title__ = "csgo-gsi-arduino-lcd" -__version__ = "1.3.2" -__project_url__ = 'https://github.com/Darkness4/csgo-gsi-arduino-lcd' -__credits__ = ["tsuriga", "Darkness4"] - -print(__title__+" "+__version__) - -# httpserver.py -class HTTPCSGOServer(HTTPServer): - """Server storing CSGO's information.""" - def __init__(self, ser_arduino, *args, **kwargs) -> None: - """ - You can store states over multiple requests in the server. - - Parameters - ---------- - ser_arduino : Serial - Arduino in Serial. - - """ - # HTTPServer.__init__(self, *args, **kwargs) - super(HTTPCSGOServer, self).__init__(*args, **kwargs) - self.round_phase = None - self.bomb = None - self.state = None - self.is_waiting = False - self.payload_viewer = None - self.ser_arduino = ser_arduino - self.messenger = Messenger(ser_arduino) - self.messenger.start() - print(asctime(), '-', "Messenger is online.") - - -class CSGORequestHandler(BaseHTTPRequestHandler): - """ - CSGO's requests handler. - - Methods - ------- - do_POST() - Receive CSGO's informations. - parse_payload(payload: dict) - Search payload and execute arduino's codes. - log_message(self, format, *args) - Prevent requests from printing into the console. - - """ - - def do_POST(self): - """Receive CSGO's informations.""" - length = int(self.headers['Content-Length']) - body = self.rfile.read(length).decode('utf-8') - - self.parse_payload(loads(body)) - - self.send_header('Content-type', 'text/html') - self.send_response(200) - self.end_headers() - - # Parsing and actions - def parse_payload(self, payload: dict) -> None: - """ - Search payload and execute arduino's codes. - - Parameters - ---------- - payload : dict - Payload containing all CSGO's informations. - - """ - round_phase = None - if 'round' in payload and 'phase' in payload['round']: - round_phase = payload['round']['phase'] - - if round_phase is not None: - self.server.is_waiting = False - bomb = None - state = None - - if 'round' in payload and 'bomb' in payload['round']: - bomb = payload['round']['bomb'] - - if 'player' in payload and 'state' in payload['player']: - state = {'health': payload['player']['state']['health'], - 'armor': payload['player']['state']['armor'], - 'round_kills': payload['player']['state']['round_kills'], - 'round_killhs': payload['player']['state']['round_killhs'], - 'money': payload['player']['state']['money']} - - if bomb != self.server.bomb: - self.server.bomb = bomb - if bomb == 'planted': - self.server.messenger.status = "Bomb" - elif bomb == 'defused': - self.server.messenger.status = "Defused" - elif bomb == 'exploded': - self.server.messenger.status = "Exploded" - else: - self.server.messenger.status = "None" - elif state != self.server.state: # if the state has changed - self.server.messenger.status = "!Freezetime" - self.server.state = state # Gather player's state - # Progress bar HP AM - self.server.messenger.health = int(state['health']) - self.server.messenger.armor = int(state['armor']) - self.server.messenger.money = int(state['money']) - self.server.messenger.kills = (state['round_kills'], - state['round_killhs']) - if round_phase != 'freezetime': - self.server.messenger.status = "!Freezetime" - else: # Not kill streak - self.server.messenger.status = "Freezetime" - elif not self.server.is_waiting: - self.server.is_waiting = True # is_waiting - self.server.messenger.status = "None" - - # Start the payload viewer - if self.server.payload_viewer is not None \ - and payload != self.server.payload_viewer.payload: - self.server.payload_viewer.payload = payload - self.server.payload_viewer.refresh() - - def log_message(self, format, *args) -> None: - """Prevent requests from printing into the console.""" - return - - -# server.py -class ServerThread(QThread): - """ - Server's thread. - - Attributes - ---------- - com_str : str - COM Port in str. - ser_arduino : Serial - Status of Messenger. - server : Server - Status of the refresher. - - Methods - ------- - run() - Start the Thread and run Server. - - """ - - ser_arduino = None - server = None - - def __init__(self, com_str) -> None: - """Start thread and save the COM port.""" - QThread.__init__(self) - self.com_str = com_str - - def run(self) -> None: - """Start the server.""" - self.ser_arduino = Serial(self.com_str, 9600) - sleep(2) # Wait for arduino - print(asctime(), '-', "Arduino detected") - # Launch server - self.server = HTTPCSGOServer(self.ser_arduino, - ('localhost', 3000), - CSGORequestHandler) - print(asctime(), '-', 'CS:GO GSI Quick Start server starting') - self.server.serve_forever() # Run - self.server.server_close() # Close server - print(asctime(), '-', 'CS:GO GSI Quick Start server stopped') - self.ser_arduino.close() # Close COM port - print(asctime(), '-', 'Serial stopped') - - -# appui.py (without icon) -class Csgogsi(QWidget): - """ - App UI. - - Attributes - ---------- - comcb : QComboBox - Combo Box responsible for listing devices. - connect_btn : QPushButton - Button responsible for connection. - payload_viewer_btn : QPushButton - Button responsible to view the payload. - refresh_btn :QPushButton - Button responsible to refresh Serial. - server_thread : ServerThread - Server. - - Methods - ------- - close_all(*args, **kwargs) - Close everything before closing app. - connect() - Connect to COM. - refresh() - Refresh list. - start_payload_viewer() - Start Payload Viewer. - stop_server() - Stop the Server. - stop_payload_viewer() - Stop Payload Viewer. - - """ - - def __init__(self) -> None: - """Init UI.""" - super(Csgogsi, self).__init__() - # Widgets - self.server_thread = None - self.connect_btn = QPushButton('Connect') - self.connect_btn.clicked.connect(self.connect) - - self.comcb = QComboBox() - list_ports_device = [port.device for port in list_ports.comports()] - self.comcb.addItems(list_ports_device) - if list_ports_device == []: - self.connect_btn.setDisabled(True) - else: - self.connect_btn.setDisabled(False) - - self.refresh_btn = QPushButton('Refresh') - self.refresh_btn.resize(self.refresh_btn.sizeHint()) - self.refresh_btn.clicked.connect(self.refresh) - - self.payload_viewer_btn = QPushButton('View payload') - self.payload_viewer_btn.setDisabled(True) - - # Container - vbox = QVBoxLayout() - hbox = QHBoxLayout() - vbox.addStretch(1) - hbox.addWidget(self.comcb) - hbox.addWidget(self.refresh_btn) - vbox.addLayout(hbox) - vbox.addWidget(self.payload_viewer_btn) - vbox.addWidget(self.connect_btn) - self.setLayout(vbox) - # Window - self.setWindowTitle('CSGO GSI on LCD') - self.setWindowFlags(Qt.WindowCloseButtonHint) - - self.show() - self.setFixedSize(self.size()) - - @Slot() - def refresh(self) -> None: - """Refresh COM ports.""" - self.comcb.clear() - list_ports_device = [port.device for port in list_ports.comports()] - self.comcb.addItems(list_ports_device) - if list_ports_device == []: - self.connect_btn.setDisabled(True) - else: - self.connect_btn.setDisabled(False) - - @Slot() - def connect(self) -> None: - """Connect to the server.""" - # Disable buttons - self.comcb.setDisabled(True) - self.connect_btn.setDisabled(True) - self.refresh_btn.setDisabled(True) - # Server start - if self.server_thread is None: - self.server_thread = ServerThread(str(self.comcb.currentText())) - self.server_thread.start() - # Change connect button's function to "stop" - self.connect_btn.clicked.disconnect() - self.connect_btn.clicked.connect(self.stop_server) - self.connect_btn.setDisabled(False) - self.connect_btn.setText('Stop') - # Enable payload_viewer - self.payload_viewer_btn.clicked.connect(self.start_payload_viewer) - self.payload_viewer_btn.setDisabled(False) - - @Slot() - def stop_server(self) -> None: - """Stop the server.""" - # Disable buttons - self.payload_viewer_btn.setDisabled(True) - # Kill the messenger and server - self.server_thread.server.messenger.shutdown() - if self.server_thread.server.payload_viewer is not None \ - and self.server_thread.server.payload_viewer.is_alive(): - self.server_thread.server.payload_viewer.shutdown() - self.server_thread.server.payload_viewer = None - self.server_thread.server.shutdown() - self.server_thread = None - # Change button function - self.connect_btn.clicked.disconnect() - self.connect_btn.clicked.connect(self.connect) - self.connect_btn.setText('Connect') - self.payload_viewer_btn.clicked.disconnect() - self.payload_viewer_btn.clicked.connect(self.start_payload_viewer) - self.payload_viewer_btn.setText('View payload') - # Enable buttons - self.comcb.setDisabled(False) - self.connect_btn.setDisabled(False) - self.refresh_btn.setDisabled(False) - - @Slot() - def start_payload_viewer(self) -> None: - """Start Payload Viewer.""" - # Start payload vierwer - self.server_thread.server.payload_viewer = PayloadViewerThread() - self.server_thread.server.payload_viewer.start() - - # Change button function - self.payload_viewer_btn.clicked.disconnect() - self.payload_viewer_btn.clicked.connect(self.stop_payload_viewer) - self.payload_viewer_btn.setText('Hide payload') - - @Slot() - def stop_payload_viewer(self) -> None: - """Stop Payload Viewer.""" - # Stop payload viewer - self.server_thread.server.payload_viewer.shutdown() - self.server_thread.server.payload_viewer = None - - # Change button function - self.payload_viewer_btn.clicked.disconnect() - self.payload_viewer_btn.clicked.connect(self.start_payload_viewer) - self.payload_viewer_btn.setText('View payload') - - def close_all(self, *args, **kwargs) -> None: - """Close everything before closing app.""" - super(Csgogsi, self).closeEvent(*args, **kwargs) - if self.server_thread is not None \ - and self.server_thread.server.messenger.is_alive(): - self.server_thread.server.messenger.shutdown() - if self.server_thread is not None and self.server_thread.isRunning(): - self.server_thread.server.shutdown() - - -# messenger.py -def progress(i: int) -> bytes: - """ - Progress bar, for arduino 5px large. - - Parameters - ---------- - i : int - Select which character to send to Arduino. - - Returns - ------- - bytes : Character send to Arduino. - - """ - switcher = {i <= 0: b"\x07", - i == 1: b"\x02", - i == 2: b"\x03", - i == 3: b"\x04", - i == 4: b"\x05", - i >= 5: b"\x06"} - return switcher[True] - - -class Messenger(Thread): - """ - Give order to the arduino. - - Attributes - ---------- - __armor : int - Armor points. - __health : int - Health points. - __kills : tuple - Number of kills and heads. - __money : int - Money left. - __refresh : bool - Status of the refresher. - __start : bool - Status of Messenger. - __status : string - Status of the round. - ser_arduino : Serial - Serial class of the Arduino. - - Methods - ------- - bomb_timer() - Start a bomb timer. - idle() - Put Messenger on idle and write a message. - run() - Start the Thread and run Messenger. - shutdown() - Shutdown Messenger. - write_player_stats() - Write the player stats on Arduino. - - """ - __armor = None - __health = None - __kills = None # tuple (total kills - hs, hs) - __money = None - __refresh = False # Order to refresh informations - __start = True # Order to start/stop - __status = "None" - - def __init__(self, ser_arduino) -> None: - """Init save.""" - super(Messenger, self).__init__() - self.ser_arduino = ser_arduino - - @property - def armor(self) -> int: - """Get the armor.""" - return self.__armor - - @armor.setter - def armor(self, armor: int) -> None: - """Set the armor.""" - self.__armor = armor - - @property - def money(self) -> int: - """Get the money.""" - return self.__money - - @money.setter - def money(self, money: int) -> None: - """Set the money.""" - self.__money = money - - @property - def health(self) -> int: - """Get the health.""" - return self.__health - - @health.setter - def health(self, health: int) -> None: - """Set the health.""" - self.__health = health - - @property - def status(self) -> str: - """Get the status.""" - return self.__status - - @status.setter - def status(self, status: str) -> None: - """ - Change Messenger behavior. - - Available status: - 'None' - 'Bomb' - '!Freezetime' - 'Freezetime' - 'Defused' - 'Exploded' - """ - self.__status = status - self.__refresh = True # Informations need to be refreshed - - @property - def kills(self) -> tuple: - """Get the kills (K, HS).""" - return self.__kills - - @kills.setter - def kills(self, kills_heads: tuple) -> None: - """Set the number of kills (K, HS).""" - self.__kills = (int(kills_heads[0])-int(kills_heads[1]), - int(kills_heads[1])) - - def run(self) -> None: - """Thread start.""" - while self.__start: - if self.__refresh: - self.__refresh = False # Has refreshed - if self.status in ("Bomb", "Defused", "Exploded"): # Bomb - self.bomb_timer() - - elif self.status == "None": - self.idle() - - else: # Default status - self.write_player_stats() - else: - sleep(0.1) # Saving consumption - print(asctime(), "-", "Messenger is dead.") - - def bomb_timer(self) -> None: - """40 sec bomb timer on arduino.""" - offset = time() - actualtime = 40 - time() + offset - while actualtime > 0 and self.status == "Bomb": - oldtime = int(actualtime) - sleep(0.1) - actualtime = 40 - time() + offset - if oldtime != int(actualtime): # Actualization only integer change - self.ser_arduino.write(b'BOMB PLANTED') - # Wait for second line - sleep(0.1) - self.ser_arduino.write(progress(int(actualtime))) # 5s - self.ser_arduino.write(progress(int(actualtime - 5))) # 10s - self.ser_arduino.write(progress(int(actualtime - 10))) # 15s - self.ser_arduino.write(progress(int(actualtime - 15))) # 20s - self.ser_arduino.write(progress(int(actualtime - 20))) # 25s - self.ser_arduino.write(progress(int(actualtime - 25))) - self.ser_arduino.write(progress(int(actualtime - 30))) - self.ser_arduino.write(progress(int(actualtime - 35))) - self.ser_arduino.write(bytes(str(int(actualtime)).encode())) - sleep(0.1) - if self.status == "Defused": - self.ser_arduino.write(b'BOMB DEFUSED') - # Wait for second line - sleep(0.1) - self.ser_arduino.write(b' ') - sleep(0.1) - elif self.status == "Exploded": - self.ser_arduino.write(b'BOMB EXPLODED') - # Wait for second line - sleep(0.1) - self.ser_arduino.write(b' ') - sleep(0.1) - - def write_player_stats(self) -> None: - """Player stats writer.""" - # Not too fast - sleep(0.1) - - # Writing health and armor in Serial - self.ser_arduino.write(b'H: ') - self.ser_arduino.write(progress(int(self.health / 5))) - self.ser_arduino.write(progress(int((self.health - 25) / 5))) - self.ser_arduino.write(progress(int((self.health - 50) / 5))) - self.ser_arduino.write(progress(int((self.health - 75) / 5))) - self.ser_arduino.write(b' A: ') - self.ser_arduino.write(progress(int(self.armor / 5))) - self.ser_arduino.write(progress(int((self.armor - 25) / 5))) - self.ser_arduino.write(progress(int((self.armor - 50) / 5))) - self.ser_arduino.write(progress(int((self.armor - 75) / 5))) - - # Wait for second line - sleep(0.1) - - # Kill or Money - if self.status == "!Freezetime": - # HS and Kill counter - self.ser_arduino.write(b'K: ') - for _ in range(self.kills[0]): # counting - self.ser_arduino.write(b'\x00') # Byte 0 char : kill no HS - for _ in range(self.kills[1]): # counting - self.ser_arduino.write(b'\x01') # Byte 1 char : HS - # Not kill streak - elif self.status == "Freezetime": - self.ser_arduino.write( - bytes('M: {}'.format(self.money).encode())) - sleep(0.1) - - def idle(self) -> None: - """Print text while idling.""" - self.ser_arduino.write(b'Waiting for') - sleep(0.1) - self.ser_arduino.write(b'matches') - - def shutdown(self) -> None: - """Stop Messenger.""" - self.__start = False - - -# tableviewer.py -class PayloadViewerThread(Thread): - """ - Payload Viewer. - - Attributes - ---------- - __payload : dict - Payload from CSGO. - refreshable : bool - Can be refreshed. - running : bool - Order to run. - - - Methods - ------- - run() - Start the Thread and run Payload Viewer. - shutdown() - Shutdown the Payload Viewer. - refresh() - Order to refresh the Payload. - """ - - running = True # Order to start/stop - refreshable = False - __payload = None - - def __init__(self) -> None: - """Start thread.""" - super(PayloadViewerThread, self).__init__() - - def run(self) -> None: - """Print payload.""" - while self.running: - if self.refreshable: - print(dumps(self.payload, indent=4)) - self.refreshable = False - - def shutdown(self) -> None: - """Shutdown thread.""" - self.running = False - - @property - def payload(self) -> dict: - """Get the payload.""" - return self.__payload - - @payload.setter - def payload(self, payload: dict) -> None: - """Set the payload.""" - self.__payload = payload - - def refresh(self) -> None: - """Refresh.""" - self.refreshable = True - - -if __name__ == '__main__': - APP = None - APP = QApplication(argv) - EX = Csgogsi() - exit(APP.exec_()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..51f8930797447534c1ba94ea79f69e48eaa56756 GIT binary patch literal 126 zcmYL>F$#b{3DZ*bSFtVUtul(BNS(~Bl=Slv3-5BiSU&dCQD{}L4d literal 0 HcmV?d00001 diff --git a/setup.py b/setup.py deleted file mode 100644 index 0d61269..0000000 --- a/setup.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup -import sys - -scripts = ["csgogsilcd"] -if sys.platform == "win32": - scripts.append("csgogsilcd.bat") -setup(name='csgo_gsi_arduino_lcd', - version='1.3.2', - description="CSGO's informations displayed on an Arduino featuring a bomb timer.", - url='https://github.com/Darkness4/csgo-gsi-arduino-lcd', - packages=['csgo_gsi_arduino_lcd'], - scripts=scripts, - package_data={'csgo_gsi_arduino_lcd': ['data/csgo-16.ico', - 'data/csgo-20.ico', - 'data/csgo-24.ico', - 'data/csgo-32.ico', - 'data/csgo-48.ico', - 'data/csgo-64.ico', - 'data/csgo-128.ico', - 'data/csgo-256.ico', - 'data/csgo-512.ico']}, - zip_safe=False) diff --git a/tableviewerold.py b/tableviewerold.py deleted file mode 100644 index b5a10fc..0000000 --- a/tableviewerold.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Payload printer. - -@auteur: Darkness4 -""" - -from http.server import BaseHTTPRequestHandler, HTTPServer -from json import loads, dumps - - -class MyRequestHandler(BaseHTTPRequestHandler): - """CSGO's requests handler.""" - - def do_POST(self): - """Receive CSGO's informations.""" - length = int(self.headers['Content-Length']) - body = self.rfile.read(length).decode('utf-8') - - self.parse_payload(loads(body)) - - self.send_header('Content-type', 'text/html') - self.send_response(200) - self.end_headers() - - # Parsing and actions - def parse_payload(self, payload): - """Search payload.""" - if self.server.payload != payload: - self.server.payload = payload - print(dumps(payload, indent=4)) - - def log_message(self, format, *args): - """Prevents requests from printing into the console.""" - return - - -class MyServer(HTTPServer): - """Server storing CSGO's information.""" - - payload = None - - -server = MyServer(('localhost', 3000), MyRequestHandler) - -try: - server.serve_forever() -except (KeyboardInterrupt, SystemExit): - pass - -server.server_close()