cheerlights-api 0.1.2__tar.gz → 0.2.0__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.
- cheerlights_api-0.2.0/.gitattributes +2 -0
- cheerlights_api-0.2.0/.github/workflows/ci.yml +27 -0
- cheerlights_api-0.2.0/.github/workflows/publish.yml +57 -0
- cheerlights_api-0.2.0/.gitignore +185 -0
- cheerlights_api-0.2.0/PKG-INFO +112 -0
- cheerlights_api-0.2.0/README.md +86 -0
- cheerlights_api-0.2.0/docs/reference.md +90 -0
- cheerlights_api-0.2.0/pyproject.toml +60 -0
- cheerlights_api-0.2.0/src/cheerlights_api/__init__.py +39 -0
- cheerlights_api-0.2.0/src/cheerlights_api/cheerlights_api.py +138 -0
- cheerlights_api-0.2.0/src/cheerlights_api/py.typed +0 -0
- cheerlights_api-0.2.0/tests/test_cheerlights_api.py +138 -0
- cheerlights_api-0.2.0/uv.lock +711 -0
- cheerlights_api-0.1.2/PKG-INFO +0 -62
- cheerlights_api-0.1.2/README.md +0 -46
- cheerlights_api-0.1.2/cheerlights_api/__init__.py +0 -21
- cheerlights_api-0.1.2/cheerlights_api/cheerlights_api.py +0 -121
- cheerlights_api-0.1.2/cheerlights_api.egg-info/PKG-INFO +0 -62
- cheerlights_api-0.1.2/cheerlights_api.egg-info/SOURCES.txt +0 -12
- cheerlights_api-0.1.2/cheerlights_api.egg-info/dependency_links.txt +0 -1
- cheerlights_api-0.1.2/cheerlights_api.egg-info/requires.txt +0 -1
- cheerlights_api-0.1.2/cheerlights_api.egg-info/top_level.txt +0 -1
- cheerlights_api-0.1.2/pyproject.toml +0 -3
- cheerlights_api-0.1.2/setup.cfg +0 -4
- cheerlights_api-0.1.2/setup.py +0 -25
- cheerlights_api-0.1.2/tests/tests_cheerlights_api.py +0 -50
- {cheerlights_api-0.1.2 → cheerlights_api-0.2.0}/LICENSE +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
20
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python }}
|
|
23
|
+
- run: uv sync --locked
|
|
24
|
+
- run: uv run pytest
|
|
25
|
+
- run: uv run ruff check .
|
|
26
|
+
- run: uv run ruff format --check .
|
|
27
|
+
- run: uv run mypy src
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Publishes to PyPI when a GitHub release is published, using PyPI Trusted Publishing:
|
|
2
|
+
# PyPI trusts this workflow directly, so no API token is stored anywhere.
|
|
3
|
+
# One-time setup on pypi.org: a trusted publisher for owner cheerlights, repository cheerlights-python-package,
|
|
4
|
+
# workflow publish.yml, environment pypi.
|
|
5
|
+
name: Publish to PyPI
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python }}
|
|
25
|
+
- run: uv sync --locked
|
|
26
|
+
- run: uv run pytest
|
|
27
|
+
|
|
28
|
+
build:
|
|
29
|
+
needs: test
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
33
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
34
|
+
- name: Check the release tag matches the package version
|
|
35
|
+
run: |
|
|
36
|
+
version=$(uv version --short)
|
|
37
|
+
test "v$version" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME does not match version $version"; exit 1; }
|
|
38
|
+
- run: uv build
|
|
39
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
|
|
44
|
+
publish:
|
|
45
|
+
needs: build
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
environment:
|
|
48
|
+
name: pypi
|
|
49
|
+
url: https://pypi.org/project/cheerlights-api/
|
|
50
|
+
permissions:
|
|
51
|
+
id-token: write
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
110
|
+
.pdm.toml
|
|
111
|
+
|
|
112
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
113
|
+
__pypackages__/
|
|
114
|
+
|
|
115
|
+
# Celery stuff
|
|
116
|
+
celerybeat-schedule
|
|
117
|
+
celerybeat.pid
|
|
118
|
+
|
|
119
|
+
# SageMath parsed files
|
|
120
|
+
*.sage.py
|
|
121
|
+
|
|
122
|
+
# Environments
|
|
123
|
+
.env
|
|
124
|
+
.venv
|
|
125
|
+
env/
|
|
126
|
+
venv/
|
|
127
|
+
ENV/
|
|
128
|
+
env.bak/
|
|
129
|
+
venv.bak/
|
|
130
|
+
|
|
131
|
+
# Spyder project settings
|
|
132
|
+
.spyderproject
|
|
133
|
+
.spyproject
|
|
134
|
+
|
|
135
|
+
# Rope project settings
|
|
136
|
+
.ropeproject
|
|
137
|
+
|
|
138
|
+
# mkdocs documentation
|
|
139
|
+
/site
|
|
140
|
+
|
|
141
|
+
# mypy
|
|
142
|
+
.mypy_cache/
|
|
143
|
+
.dmypy.json
|
|
144
|
+
dmypy.json
|
|
145
|
+
|
|
146
|
+
# Pyre type checker
|
|
147
|
+
.pyre/
|
|
148
|
+
|
|
149
|
+
# pytype static type analyzer
|
|
150
|
+
.pytype/
|
|
151
|
+
|
|
152
|
+
# Cython debug symbols
|
|
153
|
+
cython_debug/
|
|
154
|
+
|
|
155
|
+
# PyCharm
|
|
156
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
157
|
+
# be added to the global gitignore or merged into this project gitignore. For a PyCharm
|
|
158
|
+
# project, it is recommended to exclude the entire .idea directory.
|
|
159
|
+
.idea/
|
|
160
|
+
|
|
161
|
+
# VS Code
|
|
162
|
+
.vscode/
|
|
163
|
+
|
|
164
|
+
# macOS
|
|
165
|
+
.DS_Store
|
|
166
|
+
|
|
167
|
+
# Windows
|
|
168
|
+
Thumbs.db
|
|
169
|
+
ehthumbs.db
|
|
170
|
+
Desktop.ini
|
|
171
|
+
|
|
172
|
+
# Linux
|
|
173
|
+
*~
|
|
174
|
+
|
|
175
|
+
# Temporary files
|
|
176
|
+
*.tmp
|
|
177
|
+
*.temp
|
|
178
|
+
*.swp
|
|
179
|
+
*.swo
|
|
180
|
+
|
|
181
|
+
# uv
|
|
182
|
+
.python-version
|
|
183
|
+
|
|
184
|
+
# MCP specific
|
|
185
|
+
*.mcp
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cheerlights-api
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Read the live CheerLights color, its history, and the CheerLights palette
|
|
5
|
+
Project-URL: Homepage, https://cheerlights.com
|
|
6
|
+
Project-URL: Repository, https://github.com/cheerlights/cheerlights-python-package
|
|
7
|
+
Project-URL: Issues, https://github.com/cheerlights/cheerlights-python-package/issues
|
|
8
|
+
Author-email: CheerLights Community <info@cheerlights.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cheerlights,color,iot,led,thingspeak
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Education
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Home Automation
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: requests>=2.32
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# CheerLights API for Python
|
|
28
|
+
|
|
29
|
+
A small Python package that reads the live CheerLights color, its recent history, and the CheerLights palette.
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/cheerlights-api/) [](https://pypi.org/project/cheerlights-api/) [](https://github.com/cheerlights/cheerlights-python-package/actions/workflows/ci.yml) [](https://github.com/cheerlights/cheerlights-python-package/blob/main/LICENSE)
|
|
32
|
+
|
|
33
|
+
[CheerLights](https://cheerlights.com) is a global network of lights that all show the same color, set by anyone on the [CheerLights Discord](https://cheerlights.com/discord).
|
|
34
|
+
This package reads the public CheerLights feed (ThingSpeak channel 1417), so a Python script on a laptop or a Raspberry Pi can follow the color.
|
|
35
|
+
It needs no API key and depends only on `requests`.
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
Install it from [PyPI](https://pypi.org/project/cheerlights-api/) (Python 3.10 or later):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install cheerlights-api
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then read the current color:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import cheerlights_api
|
|
49
|
+
|
|
50
|
+
print(cheerlights_api.get_current_color())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Example
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import cheerlights_api
|
|
57
|
+
|
|
58
|
+
current = cheerlights_api.get_current_color()
|
|
59
|
+
print(current)
|
|
60
|
+
print(cheerlights_api.hex_to_rgb(current["hex"]))
|
|
61
|
+
|
|
62
|
+
for change in cheerlights_api.get_color_history(3):
|
|
63
|
+
print(change)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Output from version 0.2.0 on 2026-10-29:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
{'color': 'red', 'hex': '#ff0000'}
|
|
70
|
+
(255, 0, 0)
|
|
71
|
+
{'color': 'cyan', 'hex': '#00ffff', 'timestamp': '2026-10-29T17:01:36Z'}
|
|
72
|
+
{'color': 'purple', 'hex': '#800080', 'timestamp': '2026-10-29T17:20:53Z'}
|
|
73
|
+
{'color': 'red', 'hex': '#ff0000', 'timestamp': '2026-10-29T18:00:53Z'}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The `(255, 0, 0)` tuple is what an RGB LED or a NeoPixel library takes.
|
|
77
|
+
|
|
78
|
+
## Features
|
|
79
|
+
|
|
80
|
+
- The current color as a name and a HEX value: `get_current_color()`, `get_current_color_name()`, `get_current_hex()`.
|
|
81
|
+
- The last 1 to 8000 color changes with the time each was set, oldest first: `get_color_history(count)`.
|
|
82
|
+
- The 11 official CheerLights colors with their HEX values, as the feed publishes them: `COLORS`.
|
|
83
|
+
- Name and HEX helpers: `color_name_to_hex("Old Lace")` gives `#fdf5e6`, `hex_to_rgb("#ffa500")` gives `(255, 165, 0)`, `is_valid_color("pink")` gives `True`.
|
|
84
|
+
- Type hints throughout, checked with mypy in strict mode.
|
|
85
|
+
- A 10 second timeout on every request, and clear errors: `requests` exceptions when the feed cannot be reached, `LookupError` when it returns no color, `ValueError` for bad input.
|
|
86
|
+
|
|
87
|
+
## Limits
|
|
88
|
+
|
|
89
|
+
- It reads the color; it cannot set it. Anyone sets the color with the `/cheerlights` command on the [CheerLights Discord](https://cheerlights.com/discord).
|
|
90
|
+
- It polls the feed. For a light that changes the moment the color does, subscribe to MQTT instead: broker `mqtt.cheerlights.com`, port 1883, topic `cheerlights` for the name or `hex` for the HEX value.
|
|
91
|
+
- History comes back oldest first, the order the feed returns it.
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
- [Reference](https://github.com/cheerlights/cheerlights-python-package/blob/main/docs/reference.md): every function, its return value, and its errors.
|
|
96
|
+
- [Changes in 0.2.0](https://github.com/cheerlights/cheerlights-python-package/blob/main/docs/reference.md#changes-in-020): what changed from 0.1.2, including the fixed green and lowercase HEX values.
|
|
97
|
+
- [CheerLights](https://cheerlights.com): the project, the feeds, and how to build your first light.
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
git clone https://github.com/cheerlights/cheerlights-python-package
|
|
103
|
+
cd cheerlights-python-package
|
|
104
|
+
uv sync
|
|
105
|
+
uv run pytest # offline, against a fake feed
|
|
106
|
+
uv run pytest -m live # one check against the real feed
|
|
107
|
+
uv run ruff check . && uv run mypy src
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT. See [LICENSE](https://github.com/cheerlights/cheerlights-python-package/blob/main/LICENSE).
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# CheerLights API for Python
|
|
2
|
+
|
|
3
|
+
A small Python package that reads the live CheerLights color, its recent history, and the CheerLights palette.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/cheerlights-api/) [](https://pypi.org/project/cheerlights-api/) [](https://github.com/cheerlights/cheerlights-python-package/actions/workflows/ci.yml) [](https://github.com/cheerlights/cheerlights-python-package/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
[CheerLights](https://cheerlights.com) is a global network of lights that all show the same color, set by anyone on the [CheerLights Discord](https://cheerlights.com/discord).
|
|
8
|
+
This package reads the public CheerLights feed (ThingSpeak channel 1417), so a Python script on a laptop or a Raspberry Pi can follow the color.
|
|
9
|
+
It needs no API key and depends only on `requests`.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
Install it from [PyPI](https://pypi.org/project/cheerlights-api/) (Python 3.10 or later):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install cheerlights-api
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then read the current color:
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import cheerlights_api
|
|
23
|
+
|
|
24
|
+
print(cheerlights_api.get_current_color())
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import cheerlights_api
|
|
31
|
+
|
|
32
|
+
current = cheerlights_api.get_current_color()
|
|
33
|
+
print(current)
|
|
34
|
+
print(cheerlights_api.hex_to_rgb(current["hex"]))
|
|
35
|
+
|
|
36
|
+
for change in cheerlights_api.get_color_history(3):
|
|
37
|
+
print(change)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Output from version 0.2.0 on 2026-10-29:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
{'color': 'red', 'hex': '#ff0000'}
|
|
44
|
+
(255, 0, 0)
|
|
45
|
+
{'color': 'cyan', 'hex': '#00ffff', 'timestamp': '2026-10-29T17:01:36Z'}
|
|
46
|
+
{'color': 'purple', 'hex': '#800080', 'timestamp': '2026-10-29T17:20:53Z'}
|
|
47
|
+
{'color': 'red', 'hex': '#ff0000', 'timestamp': '2026-10-29T18:00:53Z'}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The `(255, 0, 0)` tuple is what an RGB LED or a NeoPixel library takes.
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- The current color as a name and a HEX value: `get_current_color()`, `get_current_color_name()`, `get_current_hex()`.
|
|
55
|
+
- The last 1 to 8000 color changes with the time each was set, oldest first: `get_color_history(count)`.
|
|
56
|
+
- The 11 official CheerLights colors with their HEX values, as the feed publishes them: `COLORS`.
|
|
57
|
+
- Name and HEX helpers: `color_name_to_hex("Old Lace")` gives `#fdf5e6`, `hex_to_rgb("#ffa500")` gives `(255, 165, 0)`, `is_valid_color("pink")` gives `True`.
|
|
58
|
+
- Type hints throughout, checked with mypy in strict mode.
|
|
59
|
+
- A 10 second timeout on every request, and clear errors: `requests` exceptions when the feed cannot be reached, `LookupError` when it returns no color, `ValueError` for bad input.
|
|
60
|
+
|
|
61
|
+
## Limits
|
|
62
|
+
|
|
63
|
+
- It reads the color; it cannot set it. Anyone sets the color with the `/cheerlights` command on the [CheerLights Discord](https://cheerlights.com/discord).
|
|
64
|
+
- It polls the feed. For a light that changes the moment the color does, subscribe to MQTT instead: broker `mqtt.cheerlights.com`, port 1883, topic `cheerlights` for the name or `hex` for the HEX value.
|
|
65
|
+
- History comes back oldest first, the order the feed returns it.
|
|
66
|
+
|
|
67
|
+
## Documentation
|
|
68
|
+
|
|
69
|
+
- [Reference](https://github.com/cheerlights/cheerlights-python-package/blob/main/docs/reference.md): every function, its return value, and its errors.
|
|
70
|
+
- [Changes in 0.2.0](https://github.com/cheerlights/cheerlights-python-package/blob/main/docs/reference.md#changes-in-020): what changed from 0.1.2, including the fixed green and lowercase HEX values.
|
|
71
|
+
- [CheerLights](https://cheerlights.com): the project, the feeds, and how to build your first light.
|
|
72
|
+
|
|
73
|
+
## Development
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/cheerlights/cheerlights-python-package
|
|
77
|
+
cd cheerlights-python-package
|
|
78
|
+
uv sync
|
|
79
|
+
uv run pytest # offline, against a fake feed
|
|
80
|
+
uv run pytest -m live # one check against the real feed
|
|
81
|
+
uv run ruff check . && uv run mypy src
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT. See [LICENSE](https://github.com/cheerlights/cheerlights-python-package/blob/main/LICENSE).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Reference
|
|
2
|
+
|
|
3
|
+
Everything `cheerlights_api` exports, as of version 0.2.0.
|
|
4
|
+
Every function that reads the feed makes one HTTPS request to `https://api.thingspeak.com/channels/1417/feed.json` with a 10 second timeout.
|
|
5
|
+
|
|
6
|
+
## Reading the feed
|
|
7
|
+
|
|
8
|
+
### `get_current_color() -> Color`
|
|
9
|
+
|
|
10
|
+
The current color as `{"color": "red", "hex": "#ff0000"}`.
|
|
11
|
+
The name is lowercase; the HEX value is the one the feed publishes, lowercase with a leading `#`.
|
|
12
|
+
|
|
13
|
+
Raises `requests.RequestException` (for example `requests.HTTPError` or `requests.Timeout`) when the feed cannot be reached or answers with an error, and `LookupError` when it returns no color.
|
|
14
|
+
|
|
15
|
+
### `get_current_color_name() -> str`
|
|
16
|
+
|
|
17
|
+
The name of the current color, for example `"red"`.
|
|
18
|
+
Same errors as `get_current_color()`.
|
|
19
|
+
|
|
20
|
+
### `get_current_hex() -> str`
|
|
21
|
+
|
|
22
|
+
The HEX value of the current color, for example `"#ff0000"`.
|
|
23
|
+
Same errors as `get_current_color()`.
|
|
24
|
+
|
|
25
|
+
### `get_color_history(count: int = 10) -> list[ColorChange]`
|
|
26
|
+
|
|
27
|
+
The last `count` color changes, oldest first, each as `{"color": "cyan", "hex": "#00ffff", "timestamp": "2026-10-29T17:01:36Z"}`.
|
|
28
|
+
The timestamp is the time the color was set, in UTC, ISO 8601.
|
|
29
|
+
|
|
30
|
+
`count` must be a whole number from 1 to 8000 (`MAX_HISTORY`, the most the feed returns at once); anything else raises `ValueError` before any request is made.
|
|
31
|
+
Network errors are the same as for `get_current_color()`.
|
|
32
|
+
|
|
33
|
+
Very old entries in the feed have no HEX value; for those the HEX value comes from the palette.
|
|
34
|
+
|
|
35
|
+
## The palette
|
|
36
|
+
|
|
37
|
+
### `COLORS: dict[str, str]`
|
|
38
|
+
|
|
39
|
+
The 11 official CheerLights colors and their HEX values:
|
|
40
|
+
|
|
41
|
+
| Name | HEX |
|
|
42
|
+
|---|---|
|
|
43
|
+
| red | `#ff0000` |
|
|
44
|
+
| green | `#008000` |
|
|
45
|
+
| blue | `#0000ff` |
|
|
46
|
+
| cyan | `#00ffff` |
|
|
47
|
+
| white | `#ffffff` |
|
|
48
|
+
| oldlace | `#fdf5e6` |
|
|
49
|
+
| purple | `#800080` |
|
|
50
|
+
| magenta | `#ff00ff` |
|
|
51
|
+
| yellow | `#ffff00` |
|
|
52
|
+
| orange | `#ffa500` |
|
|
53
|
+
| pink | `#ffc0cb` |
|
|
54
|
+
|
|
55
|
+
### `ALIASES: dict[str, str]`
|
|
56
|
+
|
|
57
|
+
Older names that still mean a CheerLights color: `warmwhite` is `oldlace`.
|
|
58
|
+
|
|
59
|
+
### `color_name_to_hex(color_name: str) -> str | None`
|
|
60
|
+
|
|
61
|
+
The HEX value of a color name, or `None` when the name is not a CheerLights color.
|
|
62
|
+
Case and spaces do not matter, and aliases count: `"Old Lace"` and `"warmwhite"` both give `"#fdf5e6"`.
|
|
63
|
+
|
|
64
|
+
### `is_valid_color(color_name: str) -> bool`
|
|
65
|
+
|
|
66
|
+
Whether a name is a CheerLights color, with the same rules as `color_name_to_hex()`.
|
|
67
|
+
|
|
68
|
+
### `hex_to_rgb(hex_code: str) -> tuple[int, int, int]`
|
|
69
|
+
|
|
70
|
+
A HEX value as a `(red, green, blue)` tuple of 0 to 255: `hex_to_rgb("#ffa500")` gives `(255, 165, 0)`.
|
|
71
|
+
The `#` is optional; anything other than six hexadecimal digits raises `ValueError`.
|
|
72
|
+
|
|
73
|
+
## Types and constants
|
|
74
|
+
|
|
75
|
+
- `Color`: a `TypedDict` with `color` and `hex`.
|
|
76
|
+
- `ColorChange`: a `TypedDict` with `color`, `hex`, and `timestamp`.
|
|
77
|
+
- `CHEERLIGHTS_FEED_URL`, `TIMEOUT` (10.0 seconds), `MAX_HISTORY` (8000).
|
|
78
|
+
- `__version__`: the installed version.
|
|
79
|
+
|
|
80
|
+
The functions can also be imported from `cheerlights_api.cheerlights_api`, the path versions before 0.2.0 used.
|
|
81
|
+
|
|
82
|
+
## Changes in 0.2.0
|
|
83
|
+
|
|
84
|
+
These change what 0.1.2 returned:
|
|
85
|
+
|
|
86
|
+
- `green` is `#008000`, the CheerLights green; 0.1.2 gave `#00ff00`.
|
|
87
|
+
- HEX values are lowercase everywhere, matching the feed, so `color_name_to_hex(get_current_color_name()) == get_current_hex()` holds; 0.1.2 returned uppercase from `color_name_to_hex()`.
|
|
88
|
+
- `get_color_history()` checks `count` and raises `ValueError` outside 1 to 8000; 0.1.2 passed any value to the feed.
|
|
89
|
+
- `hex_to_rgb()` raises `ValueError` for anything but six hexadecimal digits.
|
|
90
|
+
- An empty feed raises `LookupError` with a message; 0.1.2 raised a bare `IndexError` (also a `LookupError`).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cheerlights-api"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Read the live CheerLights color, its history, and the CheerLights palette"
|
|
9
|
+
authors = [{ name = "CheerLights Community", email = "info@cheerlights.com" }]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
keywords = ["cheerlights", "iot", "thingspeak", "led", "color"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Education",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Topic :: Home Automation",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
dependencies = ["requests>=2.32"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://cheerlights.com"
|
|
32
|
+
Repository = "https://github.com/cheerlights/cheerlights-python-package"
|
|
33
|
+
Issues = "https://github.com/cheerlights/cheerlights-python-package/issues"
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=9.0",
|
|
38
|
+
"ruff>=0.17",
|
|
39
|
+
"mypy>=2.0",
|
|
40
|
+
"types-requests",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/cheerlights_api"]
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
target-version = "py310"
|
|
48
|
+
line-length = 110
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = ["E", "W", "F", "I", "B", "C4", "UP"]
|
|
52
|
+
|
|
53
|
+
[tool.mypy]
|
|
54
|
+
python_version = "3.10"
|
|
55
|
+
strict = true
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
addopts = "-ra -q -m 'not live'"
|
|
59
|
+
testpaths = ["tests"]
|
|
60
|
+
markers = ["live: calls the real CheerLights feed (run with -m live)"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Read the live CheerLights color, its history, and the CheerLights palette."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
from .cheerlights_api import (
|
|
6
|
+
ALIASES,
|
|
7
|
+
CHEERLIGHTS_FEED_URL,
|
|
8
|
+
COLORS,
|
|
9
|
+
Color,
|
|
10
|
+
ColorChange,
|
|
11
|
+
color_name_to_hex,
|
|
12
|
+
get_color_history,
|
|
13
|
+
get_current_color,
|
|
14
|
+
get_current_color_name,
|
|
15
|
+
get_current_hex,
|
|
16
|
+
hex_to_rgb,
|
|
17
|
+
is_valid_color,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
__version__ = version("cheerlights-api")
|
|
22
|
+
except PackageNotFoundError: # running from a source tree that is not installed
|
|
23
|
+
__version__ = "0.0.0"
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"ALIASES",
|
|
27
|
+
"CHEERLIGHTS_FEED_URL",
|
|
28
|
+
"COLORS",
|
|
29
|
+
"Color",
|
|
30
|
+
"ColorChange",
|
|
31
|
+
"__version__",
|
|
32
|
+
"color_name_to_hex",
|
|
33
|
+
"get_color_history",
|
|
34
|
+
"get_current_color",
|
|
35
|
+
"get_current_color_name",
|
|
36
|
+
"get_current_hex",
|
|
37
|
+
"hex_to_rgb",
|
|
38
|
+
"is_valid_color",
|
|
39
|
+
]
|