ud-co2s 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ud_co2s-0.1.1/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2024 34j
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
ud_co2s-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.1
2
+ Name: ud-co2s
3
+ Version: 0.1.1
4
+ Summary: Python package for reading sensor data from IODATA UD-CO2S or Pocket CO2 Sensor
5
+ Home-page: https://github.com/34j/ud-co2s
6
+ License: MIT
7
+ Author: 34j
8
+ Author-email: 34j.95a2p@simplelogin.com
9
+ Requires-Python: >=3.9,<4.0
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Dist: aioserial (>=1.3.1,<2.0.0)
22
+ Requires-Dist: attrs (>=23.2.0,<24.0.0)
23
+ Requires-Dist: plotext (>=5.2.8,<6.0.0)
24
+ Requires-Dist: pyserial (>=3.5,<4.0)
25
+ Requires-Dist: pytest-asyncio (>=0.23.7,<0.24.0)
26
+ Requires-Dist: rich (>=10)
27
+ Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
28
+ Project-URL: Bug Tracker, https://github.com/34j/ud-co2s/issues
29
+ Project-URL: Changelog, https://github.com/34j/ud-co2s/blob/main/CHANGELOG.md
30
+ Project-URL: Documentation, https://ud-co2s.readthedocs.io
31
+ Project-URL: Repository, https://github.com/34j/ud-co2s
32
+ Description-Content-Type: text/markdown
33
+
34
+ # IODATA UD-CO2S Sensor Data Reader
35
+
36
+ <p align="center">
37
+ <a href="https://github.com/34j/ud-co2s/actions/workflows/ci.yml?query=branch%3Amain">
38
+ <img src="https://img.shields.io/github/actions/workflow/status/34j/ud-co2s/ci.yml?branch=main&label=CI&logo=github&style=flat-square" alt="CI Status" >
39
+ </a>
40
+ <a href="https://ud-co2s.readthedocs.io">
41
+ <img src="https://img.shields.io/readthedocs/ud-co2s.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
42
+ </a>
43
+ <a href="https://codecov.io/gh/34j/ud-co2s">
44
+ <img src="https://img.shields.io/codecov/c/github/34j/ud-co2s.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
45
+ </a>
46
+ </p>
47
+ <p align="center">
48
+ <a href="https://python-poetry.org/">
49
+ <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
50
+ </a>
51
+ <a href="https://github.com/ambv/black">
52
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square" alt="black">
53
+ </a>
54
+ <a href="https://github.com/pre-commit/pre-commit">
55
+ <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
56
+ </a>
57
+ </p>
58
+ <p align="center">
59
+ <a href="https://pypi.org/project/ud-co2s/">
60
+ <img src="https://img.shields.io/pypi/v/ud-co2s.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
61
+ </a>
62
+ <img src="https://img.shields.io/pypi/pyversions/ud-co2s.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
63
+ <img src="https://img.shields.io/pypi/l/ud-co2s.svg?style=flat-square" alt="License">
64
+ </p>
65
+
66
+ ---
67
+
68
+ **Documentation**: <a href="https://ud-co2s.readthedocs.io" target="_blank">https://ud-co2s.readthedocs.io </a>
69
+
70
+ **Source Code**: <a href="https://github.com/34j/ud-co2s" target="_blank">https://github.com/34j/ud-co2s </a>
71
+
72
+ ---
73
+
74
+ Python package for reading sensor data from IODATA UD-CO2S or Pocket CO2 Sensor
75
+
76
+ ## Installation
77
+
78
+ Install this via pip or pipx (or your favourite package manager):
79
+
80
+ ```shell
81
+ pipx install ud-co2s
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ```shell
87
+ ud-co2s --plot --log
88
+ ```
89
+
90
+ ![CLI](docs/_static/cli.png)
91
+
92
+ ## Contributors ✨
93
+
94
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
95
+
96
+ <!-- prettier-ignore-start -->
97
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
98
+ <!-- markdownlint-disable -->
99
+ <!-- markdownlint-enable -->
100
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
101
+ <!-- prettier-ignore-end -->
102
+
103
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
104
+
105
+ ## Alternatives
106
+
107
+ ### 非公式
108
+
109
+ - [bootjp/ud\-co2s\-exporter: IODATA CO2 Sensor UD\-CO2S prometheus exporter](https://github.com/bootjp/ud-co2s-exporter)
110
+ - [jitenshap/IODATA_UD\-CO2S\-Python: IODATA CO2 Sensor data receiver using pyserial](https://github.com/jitenshap/IODATA_UD-CO2S-Python)
111
+ - [Raspberry Piを使った温湿度CO2濃度ロガーの製作 \- 白旗製作所](https://dededemio.hatenablog.jp/entry/2024/03/13/012629#22-UD-CO2S%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E8%AA%BF%E6%9F%BB)
112
+ - [rubyでRaspberry piに繋いだIO dataのCO2センサから値を読み出す。 \#Ruby \- Qiita](https://qiita.com/tatsushi/items/42903f1113cd5c1f8891)
113
+
114
+ ### 公式
115
+
116
+ - [Pocket CO2 Sensor Windows10アプリ](http://www.google.com/url?q=http%3A%2F%2Fradiation-watch.sakuraweb.com%2Fshare%2Fco2%2FPocketCO2SensorPro.exe&sa=D&sntz=1&usg=AOvVaw0PZSKJ8zRAljo4TmzzrWlh)
117
+ - [Pocket CO2 Sensor – Apps on Google Play](https://play.google.com/store/apps/details?id=org.radiation_watch.co2checker)
118
+ - [CO2換気モニター \| ソフトウェア \| サポートライブラリ \| アイ・オー・データ機器 I\-O DATA](https://www.iodata.jp/lib/software/c/2284.htm)
119
+
120
+ ## Credits
121
+
122
+ This package was created with
123
+ [Copier](https://copier.readthedocs.io/) and the
124
+ [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
125
+ project template.
126
+
@@ -0,0 +1,92 @@
1
+ # IODATA UD-CO2S Sensor Data Reader
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/34j/ud-co2s/actions/workflows/ci.yml?query=branch%3Amain">
5
+ <img src="https://img.shields.io/github/actions/workflow/status/34j/ud-co2s/ci.yml?branch=main&label=CI&logo=github&style=flat-square" alt="CI Status" >
6
+ </a>
7
+ <a href="https://ud-co2s.readthedocs.io">
8
+ <img src="https://img.shields.io/readthedocs/ud-co2s.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
9
+ </a>
10
+ <a href="https://codecov.io/gh/34j/ud-co2s">
11
+ <img src="https://img.shields.io/codecov/c/github/34j/ud-co2s.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
12
+ </a>
13
+ </p>
14
+ <p align="center">
15
+ <a href="https://python-poetry.org/">
16
+ <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
17
+ </a>
18
+ <a href="https://github.com/ambv/black">
19
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square" alt="black">
20
+ </a>
21
+ <a href="https://github.com/pre-commit/pre-commit">
22
+ <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
23
+ </a>
24
+ </p>
25
+ <p align="center">
26
+ <a href="https://pypi.org/project/ud-co2s/">
27
+ <img src="https://img.shields.io/pypi/v/ud-co2s.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
28
+ </a>
29
+ <img src="https://img.shields.io/pypi/pyversions/ud-co2s.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
30
+ <img src="https://img.shields.io/pypi/l/ud-co2s.svg?style=flat-square" alt="License">
31
+ </p>
32
+
33
+ ---
34
+
35
+ **Documentation**: <a href="https://ud-co2s.readthedocs.io" target="_blank">https://ud-co2s.readthedocs.io </a>
36
+
37
+ **Source Code**: <a href="https://github.com/34j/ud-co2s" target="_blank">https://github.com/34j/ud-co2s </a>
38
+
39
+ ---
40
+
41
+ Python package for reading sensor data from IODATA UD-CO2S or Pocket CO2 Sensor
42
+
43
+ ## Installation
44
+
45
+ Install this via pip or pipx (or your favourite package manager):
46
+
47
+ ```shell
48
+ pipx install ud-co2s
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ```shell
54
+ ud-co2s --plot --log
55
+ ```
56
+
57
+ ![CLI](docs/_static/cli.png)
58
+
59
+ ## Contributors ✨
60
+
61
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
62
+
63
+ <!-- prettier-ignore-start -->
64
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
65
+ <!-- markdownlint-disable -->
66
+ <!-- markdownlint-enable -->
67
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
68
+ <!-- prettier-ignore-end -->
69
+
70
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
71
+
72
+ ## Alternatives
73
+
74
+ ### 非公式
75
+
76
+ - [bootjp/ud\-co2s\-exporter: IODATA CO2 Sensor UD\-CO2S prometheus exporter](https://github.com/bootjp/ud-co2s-exporter)
77
+ - [jitenshap/IODATA_UD\-CO2S\-Python: IODATA CO2 Sensor data receiver using pyserial](https://github.com/jitenshap/IODATA_UD-CO2S-Python)
78
+ - [Raspberry Piを使った温湿度CO2濃度ロガーの製作 \- 白旗製作所](https://dededemio.hatenablog.jp/entry/2024/03/13/012629#22-UD-CO2S%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E8%AA%BF%E6%9F%BB)
79
+ - [rubyでRaspberry piに繋いだIO dataのCO2センサから値を読み出す。 \#Ruby \- Qiita](https://qiita.com/tatsushi/items/42903f1113cd5c1f8891)
80
+
81
+ ### 公式
82
+
83
+ - [Pocket CO2 Sensor Windows10アプリ](http://www.google.com/url?q=http%3A%2F%2Fradiation-watch.sakuraweb.com%2Fshare%2Fco2%2FPocketCO2SensorPro.exe&sa=D&sntz=1&usg=AOvVaw0PZSKJ8zRAljo4TmzzrWlh)
84
+ - [Pocket CO2 Sensor – Apps on Google Play](https://play.google.com/store/apps/details?id=org.radiation_watch.co2checker)
85
+ - [CO2換気モニター \| ソフトウェア \| サポートライブラリ \| アイ・オー・データ機器 I\-O DATA](https://www.iodata.jp/lib/software/c/2284.htm)
86
+
87
+ ## Credits
88
+
89
+ This package was created with
90
+ [Copier](https://copier.readthedocs.io/) and the
91
+ [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
92
+ project template.
@@ -0,0 +1,156 @@
1
+ [tool.poetry]
2
+ name = "ud-co2s"
3
+ version = "0.1.1"
4
+ description = "Python package for reading sensor data from IODATA UD-CO2S or Pocket CO2 Sensor"
5
+ authors = ["34j <34j.95a2p@simplelogin.com>"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ repository = "https://github.com/34j/ud-co2s"
9
+ documentation = "https://ud-co2s.readthedocs.io"
10
+ classifiers = [
11
+ "Development Status :: 2 - Pre-Alpha",
12
+ "Intended Audience :: Developers",
13
+ "Natural Language :: English",
14
+ "Operating System :: OS Independent",
15
+ "Topic :: Software Development :: Libraries",
16
+ ]
17
+ packages = [
18
+ { include = "ud_co2s", from = "src" },
19
+ ]
20
+
21
+ [tool.poetry.urls]
22
+ "Bug Tracker" = "https://github.com/34j/ud-co2s/issues"
23
+ "Changelog" = "https://github.com/34j/ud-co2s/blob/main/CHANGELOG.md"
24
+
25
+ [tool.poetry.scripts]
26
+ ud-co2s = "ud_co2s.cli:app"
27
+
28
+ [tool.poetry.dependencies]
29
+ python = "^3.9"
30
+ rich = ">=10"
31
+ typer = {extras = ["all"], version = "^0.9.0"}
32
+ pyserial = "^3.5"
33
+ aioserial = "^1.3.1"
34
+ attrs = "^23.2.0"
35
+ pytest-asyncio = "^0.23.7"
36
+ plotext = "^5.2.8"
37
+
38
+ [tool.poetry.group.dev.dependencies]
39
+ pytest = "^7.0"
40
+ pytest-cov = "^3.0"
41
+
42
+ [tool.poetry.group.docs]
43
+ optional = true
44
+
45
+ [tool.poetry.group.docs.dependencies]
46
+ myst-parser = { version = ">=0.16", python = ">=3.11"}
47
+ sphinx = { version = ">=4.0", python = ">=3.11"}
48
+ furo = { version = ">=2023.5.20", python = ">=3.11"}
49
+ sphinx-autobuild = { version = ">=2024.0.0", python = ">=3.11"}
50
+
51
+ [tool.semantic_release]
52
+ version_toml = ["pyproject.toml:tool.poetry.version"]
53
+ version_variables = [
54
+ "src/ud_co2s/__init__.py:__version__",
55
+ "docs/conf.py:release",
56
+ ]
57
+ build_command = "pip install poetry && poetry build"
58
+
59
+ [tool.semantic_release.changelog]
60
+ exclude_commit_patterns = [
61
+ "chore*",
62
+ "ci*",
63
+ ]
64
+
65
+ [tool.semantic_release.changelog.environment]
66
+ keep_trailing_newline = true
67
+
68
+ [tool.semantic_release.branches.main]
69
+ match = "main"
70
+
71
+ [tool.semantic_release.branches.noop]
72
+ match = "(?!main$)"
73
+ prerelease = true
74
+
75
+ [tool.pytest.ini_options]
76
+ addopts = "-v -Wdefault --cov=ud_co2s --cov-report=term-missing:skip-covered"
77
+ pythonpath = ["src"]
78
+
79
+ [tool.coverage.run]
80
+ branch = true
81
+
82
+ [tool.coverage.report]
83
+ exclude_lines = [
84
+ "pragma: no cover",
85
+ "@overload",
86
+ "if TYPE_CHECKING",
87
+ "raise NotImplementedError",
88
+ 'if __name__ == "__main__":',
89
+ ]
90
+
91
+ [tool.ruff]
92
+ target-version = "py38"
93
+ line-length = 88
94
+ ignore = [
95
+ "D203", # 1 blank line required before class docstring
96
+ "D212", # Multi-line docstring summary should start at the first line
97
+ "D100", # Missing docstring in public module
98
+ "D104", # Missing docstring in public package
99
+ "D107", # Missing docstring in `__init__`
100
+ "D401", # First line of docstring should be in imperative mood
101
+ ]
102
+ select = [
103
+ "B", # flake8-bugbear
104
+ "D", # flake8-docstrings
105
+ "C4", # flake8-comprehensions
106
+ "S", # flake8-bandit
107
+ "F", # pyflake
108
+ "E", # pycodestyle
109
+ "W", # pycodestyle
110
+ "UP", # pyupgrade
111
+ "I", # isort
112
+ "RUF", # ruff specific
113
+ ]
114
+
115
+ [tool.ruff.per-file-ignores]
116
+ "tests/**/*" = [
117
+ "D100",
118
+ "D101",
119
+ "D102",
120
+ "D103",
121
+ "D104",
122
+ "S101",
123
+ ]
124
+ "setup.py" = ["D100"]
125
+ "conftest.py" = ["D100"]
126
+ "docs/conf.py" = ["D100"]
127
+
128
+ [tool.ruff.isort]
129
+ known-first-party = ["ud_co2s", "tests"]
130
+
131
+ [tool.mypy]
132
+ check_untyped_defs = true
133
+ disallow_any_generics = true
134
+ disallow_incomplete_defs = true
135
+ disallow_untyped_defs = true
136
+ mypy_path = "src/"
137
+ no_implicit_optional = true
138
+ show_error_codes = true
139
+ warn_unreachable = true
140
+ warn_unused_ignores = true
141
+ exclude = [
142
+ 'docs/.*',
143
+ 'setup.py',
144
+ ]
145
+
146
+ [[tool.mypy.overrides]]
147
+ module = "tests.*"
148
+ allow_untyped_defs = true
149
+
150
+ [[tool.mypy.overrides]]
151
+ module = "docs.*"
152
+ ignore_errors = true
153
+
154
+ [build-system]
155
+ requires = ["poetry-core>=1.0.0"]
156
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
@@ -0,0 +1,5 @@
1
+ """Make the CLI runnable using python -m ud_co2s."""
2
+
3
+ from .cli import app
4
+
5
+ app(prog_name="ud-co2s")
@@ -0,0 +1,165 @@
1
+ from __future__ import annotations
2
+
3
+ import itertools
4
+ import re
5
+ import warnings
6
+ from typing import AsyncIterable, Iterable
7
+
8
+ import aioserial
9
+ import attrs
10
+ import serial
11
+ import serial.tools.list_ports
12
+
13
+
14
+ @attrs.frozen(kw_only=True)
15
+ class CO2Data:
16
+ """CO2 data."""
17
+
18
+ co2_ppm: int
19
+ """The CO2 concentration in parts per million."""
20
+ humidity_percent: float
21
+ """The relative humidity in percent."""
22
+ temperature: float
23
+ """The temperature is for calibration purposes
24
+ and might be higher than the actual room temperature."""
25
+
26
+
27
+ def _find_port() -> str:
28
+ candidates = [
29
+ port for port in serial.tools.list_ports.comports() if "USB" in port.description
30
+ ]
31
+ if not candidates:
32
+ raise RuntimeError("No suitable port found")
33
+ elif len(candidates) > 1:
34
+ warnings.warn(
35
+ f"Multiple candidates found: {candidates}, "
36
+ "using the first one,"
37
+ "please consider specifying the port explicitly",
38
+ UserWarning,
39
+ stacklevel=2,
40
+ )
41
+ return candidates[0].device
42
+
43
+
44
+ def read_co2(count: int | None = 1, port: str | None = None) -> Iterable[CO2Data]:
45
+ """
46
+ Read CO2 data from the sensor.
47
+
48
+ Parameters
49
+ ----------
50
+ count : int, optional
51
+ The number of data points to read, by default 1
52
+ port : str | None, optional
53
+ The serial port to use, by default None
54
+
55
+ Returns
56
+ -------
57
+ Iterable[CO2Data]
58
+ The CO2 data.
59
+
60
+ Yields
61
+ ------
62
+ Iterator[Iterable[CO2Data]]
63
+ The CO2 data.
64
+
65
+ Raises
66
+ ------
67
+ RuntimeError
68
+ If the status or data is invalid.
69
+
70
+ """
71
+ # find the port
72
+ port = port or _find_port()
73
+
74
+ # connect to the sensor
75
+ try:
76
+ with serial.Serial(
77
+ port=port,
78
+ baudrate=9600,
79
+ bytesize=8,
80
+ ) as seri:
81
+ # Reset the sensor
82
+ # seri.write(b"STP\r\n")
83
+ seri.write(b"STA\r\n")
84
+ status = seri.read_until(b"\r\n")
85
+ if "STA" not in status.decode():
86
+ raise RuntimeError(f"Invalid status: {status}")
87
+
88
+ # read the CO2 data
89
+ for i in itertools.count():
90
+ if count is not None and i >= count:
91
+ break
92
+ text = seri.read_until(b"\r\n")
93
+ # CO2=1000,HUM=50.0,TMP=25.0
94
+ match = re.match(
95
+ r"CO2=(\d+),HUM=(\d+\.\d+),TMP=(\d+\.\d+)", text.decode()
96
+ )
97
+ if not match:
98
+ raise RuntimeError(f"Invalid data: {text}")
99
+ yield CO2Data(
100
+ co2_ppm=int(match.group(1)),
101
+ humidity_percent=float(match.group(2)),
102
+ temperature=float(match.group(3)),
103
+ )
104
+ finally:
105
+ seri.write(b"STP\r\n")
106
+
107
+
108
+ async def read_co2_async(
109
+ count: int | None = 1, port: str | None = None
110
+ ) -> AsyncIterable[CO2Data]:
111
+ """
112
+ Read CO2 data from the sensor.
113
+
114
+ Parameters
115
+ ----------
116
+ count : int, optional
117
+ The number of data points to read, by default 1
118
+ port : str | None, optional
119
+ The serial port to use, by default None
120
+
121
+ Returns
122
+ -------
123
+ AsyncIterable[CO2Data]
124
+ The CO2 data.
125
+
126
+ Yields
127
+ ------
128
+ AsyncIterator[Iterable[CO2Data]]
129
+ The CO2 data.
130
+
131
+ Raises
132
+ ------
133
+ RuntimeError
134
+ If the status or data is invalid.
135
+
136
+ """
137
+ # find the port
138
+ port = port or _find_port()
139
+
140
+ # connect to the sensor
141
+ seri = aioserial.AioSerial(port=port, baudrate=9600, bytesize=8)
142
+
143
+ try:
144
+ # Reset the sensor
145
+ # await seri.write_async(b"STP\r\n")
146
+ await seri.write_async(b"STA\r\n")
147
+ status = await seri.read_until_async(b"\r\n")
148
+ if "STA" not in status.decode():
149
+ raise RuntimeError(f"Invalid status: {status}")
150
+
151
+ # read the CO2 data
152
+ for i in itertools.count():
153
+ if count is not None and i >= count:
154
+ break
155
+ text = await seri.read_until_async(b"\r\n")
156
+ match = re.match(r"CO2=(\d+),HUM=(\d+\.\d+),TMP=(\d+\.\d+)", text.decode())
157
+ if not match:
158
+ raise RuntimeError(f"Invalid data: {text}")
159
+ yield CO2Data(
160
+ co2_ppm=int(match.group(1)),
161
+ humidity_percent=float(match.group(2)),
162
+ temperature=float(match.group(3)),
163
+ )
164
+ finally:
165
+ await seri.write_async(b"STP\r\n")
@@ -0,0 +1,55 @@
1
+ from datetime import datetime
2
+ from pathlib import Path
3
+ from typing import Annotated
4
+
5
+ import plotext as plt
6
+ import typer
7
+ from rich.console import Console
8
+
9
+ from ._main import read_co2
10
+
11
+ app = typer.Typer()
12
+
13
+
14
+ @app.command()
15
+ def _main(
16
+ once: Annotated[bool, typer.Option(help="Only get values once")] = False,
17
+ plot: Annotated[bool, typer.Option(help="Plot the values")] = False,
18
+ log: Annotated[bool, typer.Option(help="Log the values")] = False,
19
+ log_path: Annotated[Path, typer.Option(help="Path to the log file")] = Path(
20
+ "ud-co2s.log"
21
+ ),
22
+ ) -> None:
23
+ c = Console()
24
+ if plot:
25
+ dates = []
26
+ ppms = []
27
+ for data in read_co2(1 if once else None):
28
+ c.clear()
29
+ c.print(
30
+ f"CO2: {data.co2_ppm} ppm, "
31
+ f"Humidity: {data.humidity_percent}%, "
32
+ f"Temperature: {data.temperature}°C"
33
+ )
34
+ if log:
35
+ with log_path.open("a") as file:
36
+ file.write(
37
+ f"{datetime.now().astimezone().isoformat()},{data.co2_ppm},"
38
+ f"{data.humidity_percent},{data.temperature}\n"
39
+ )
40
+ if plot:
41
+ ppms.append(data.co2_ppm)
42
+ dates.append(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
43
+ if len(ppms) > 2:
44
+ try:
45
+ plt.clear_figure()
46
+ plt.canvas_color("black")
47
+ plt.axes_color("black")
48
+ plt.ticks_color("white")
49
+ plt.plot_size(height=(plt.terminal_height() or 10) - 2)
50
+ plt.date_form("Y/m/d H:M:S")
51
+ plt.plot(dates, ppms)
52
+ plt.show()
53
+ except Exception as e:
54
+ c.print(e)
55
+ pass
File without changes