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 +22 -0
- ud_co2s-0.1.1/PKG-INFO +126 -0
- ud_co2s-0.1.1/README.md +92 -0
- ud_co2s-0.1.1/pyproject.toml +156 -0
- ud_co2s-0.1.1/src/ud_co2s/__init__.py +1 -0
- ud_co2s-0.1.1/src/ud_co2s/__main__.py +5 -0
- ud_co2s-0.1.1/src/ud_co2s/_main.py +165 -0
- ud_co2s-0.1.1/src/ud_co2s/cli.py +55 -0
- ud_co2s-0.1.1/src/ud_co2s/py.typed +0 -0
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&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
|
+

|
|
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
|
+
|
ud_co2s-0.1.1/README.md
ADDED
|
@@ -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&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
|
+

|
|
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,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
|