something-x-dev 1.7.0.dev15__tar.gz → 1.8.0.dev18__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.
- something_x_dev-1.8.0.dev18/.github/CODEOWNERS +1 -0
- something_x_dev-1.8.0.dev18/.github/workflows/ci.yml +64 -0
- something_x_dev-1.8.0.dev18/.github/workflows/release-dev.yml +108 -0
- something_x_dev-1.8.0.dev18/.github/workflows/release.yml +122 -0
- something_x_dev-1.8.0.dev18/.gitignore +46 -0
- something_x_dev-1.8.0.dev18/DEVICES.md +37 -0
- {something_x_dev-1.7.0.dev15/something_x_dev.egg-info → something_x_dev-1.8.0.dev18}/PKG-INFO +1 -1
- something_x_dev-1.8.0.dev18/PKGBUILD +24 -0
- something_x_dev-1.8.0.dev18/ROADMAP.md +69 -0
- something_x_dev-1.8.0.dev18/cliff.toml +41 -0
- something_x_dev-1.8.0.dev18/data/style.css +355 -0
- something_x_dev-1.8.0.dev18/docs/RELEASING.md +192 -0
- something_x_dev-1.8.0.dev18/docs/docs.html +802 -0
- something_x_dev-1.8.0.dev18/docs/index.html +588 -0
- something_x_dev-1.8.0.dev18/flake.nix +66 -0
- something_x_dev-1.8.0.dev18/main.py +10 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/__init__.py +4 -5
- something_x_dev-1.8.0.dev18/nothing_app/_version.py +24 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/pyproject.toml +31 -2
- something_x_dev-1.8.0.dev18/requirements.txt +3 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18/something_x_dev.egg-info}/PKG-INFO +1 -1
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/something_x_dev.egg-info/SOURCES.txt +20 -0
- something_x_dev-1.8.0.dev18/somethingx +3 -0
- something_x_dev-1.8.0.dev18/tests/__init__.py +0 -0
- something_x_dev-1.8.0.dev18/tests/conftest.py +40 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/LICENSE +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/README.md +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/application.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/bluetooth.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/data/__init__.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/data/com.something.x.omarchy.desktop +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/data/style.css +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/pages/__init__.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/pages/device.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/pages/home.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/profiles.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/protocol.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/splash.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/tray.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/nothing_app/window.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/setup.cfg +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/something_x_dev.egg-info/dependency_links.txt +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/something_x_dev.egg-info/entry_points.txt +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/something_x_dev.egg-info/requires.txt +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/something_x_dev.egg-info/top_level.txt +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/tests/test_bluetooth.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/tests/test_crc.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/tests/test_profiles.py +0 -0
- {something_x_dev-1.7.0.dev15 → something_x_dev-1.8.0.dev18}/tests/test_protocol.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @SoaOaoS
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches-ignore: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
pull-requests: write # needed for reviewdog to post inline comments
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Lint & format
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.11"
|
|
24
|
+
|
|
25
|
+
- name: Install ruff
|
|
26
|
+
run: pip install ruff
|
|
27
|
+
|
|
28
|
+
- name: Setup reviewdog
|
|
29
|
+
uses: reviewdog/action-setup@v1
|
|
30
|
+
with:
|
|
31
|
+
reviewdog_version: latest
|
|
32
|
+
|
|
33
|
+
# Posts inline review comments on the PR diff for every ruff finding
|
|
34
|
+
- name: Lint with reviewdog
|
|
35
|
+
run: ruff check --output-format rdjson nothing_app/ | reviewdog -f=rdjson -reporter=github-pr-review -fail-on-error
|
|
36
|
+
env:
|
|
37
|
+
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
38
|
+
|
|
39
|
+
# Still fail the job if formatting is wrong
|
|
40
|
+
- name: Check formatting
|
|
41
|
+
run: ruff format --check nothing_app/
|
|
42
|
+
|
|
43
|
+
test:
|
|
44
|
+
name: Unit tests
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
|
|
50
|
+
- name: Install system dependencies
|
|
51
|
+
run: |
|
|
52
|
+
sudo apt-get update -q
|
|
53
|
+
sudo apt-get install -y python3-gi python3-gi-cairo gir1.2-gtk-4.0 libgirepository1.0-dev
|
|
54
|
+
|
|
55
|
+
# Use a venv that inherits system site-packages so apt-installed gi is visible.
|
|
56
|
+
# PyGObject cannot be pip-built without Cairo/GLib headers; use the distro package.
|
|
57
|
+
- name: Create venv with system site-packages
|
|
58
|
+
run: python3 -m venv --system-site-packages .venv
|
|
59
|
+
|
|
60
|
+
- name: Install test dependencies
|
|
61
|
+
run: .venv/bin/pip install pytest pytest-cov
|
|
62
|
+
|
|
63
|
+
- name: Run tests
|
|
64
|
+
run: .venv/bin/pytest --tb=short --cov=nothing_app --cov-report=term-missing
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
name: Release Dev
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [develop]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release-dev:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
# ── Compute next version with python-semantic-release ───────────────
|
|
22
|
+
# PSR reads ALL commits since the last vX.Y.Z tag and applies the
|
|
23
|
+
# highest bump type (feat beats fix, breaking beats all).
|
|
24
|
+
# We strip PSR's own prerelease counter and use github.run_number instead.
|
|
25
|
+
- name: Compute dev version
|
|
26
|
+
id: version
|
|
27
|
+
run: |
|
|
28
|
+
pip install python-semantic-release
|
|
29
|
+
SHORT_SHA=$(git rev-parse --short HEAD)
|
|
30
|
+
|
|
31
|
+
# --print: compute next version with no side effects
|
|
32
|
+
# On develop (prerelease=true in pyproject.toml) PSR outputs e.g. "1.8.0.dev1"
|
|
33
|
+
NEXT_PRE=$(semantic-release version --print 2>/dev/null || echo "")
|
|
34
|
+
|
|
35
|
+
if [ -n "$NEXT_PRE" ]; then
|
|
36
|
+
# Strip PSR's counter (.dev1 / -dev.1) — we use run_number instead
|
|
37
|
+
NEXT_BASE=$(echo "$NEXT_PRE" | sed -E 's/[.-]dev[.-]?[0-9]*//')
|
|
38
|
+
DEV_VERSION="${NEXT_BASE}.dev${{ github.run_number }}"
|
|
39
|
+
else
|
|
40
|
+
# No user-facing commits — still build, bump patch as fallback
|
|
41
|
+
LAST=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null | sed 's/^v//' || echo "0.0.0")
|
|
42
|
+
MAJOR=$(echo $LAST | cut -d. -f1)
|
|
43
|
+
MINOR=$(echo $LAST | cut -d. -f2)
|
|
44
|
+
PATCH=$(echo $LAST | cut -d. -f3)
|
|
45
|
+
DEV_VERSION="${MAJOR}.${MINOR}.$((PATCH+1)).dev${{ github.run_number }}"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
echo "Dev version: $DEV_VERSION (sha: $SHORT_SHA)"
|
|
49
|
+
echo "dev_version=$DEV_VERSION" >> $GITHUB_OUTPUT
|
|
50
|
+
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
|
|
51
|
+
|
|
52
|
+
# ── Patch package name for dev channel ─────────────────────────────
|
|
53
|
+
- name: Patch package name
|
|
54
|
+
run: |
|
|
55
|
+
sed -i 's/^name\s*=\s*"something-x"/name = "something-x-dev"/' pyproject.toml
|
|
56
|
+
sed -i 's/^something-x = /something-x-dev = /' pyproject.toml
|
|
57
|
+
|
|
58
|
+
# ── Build ───────────────────────────────────────────────────────────
|
|
59
|
+
- name: Set up Python
|
|
60
|
+
uses: actions/setup-python@v5
|
|
61
|
+
with:
|
|
62
|
+
python-version: "3.11"
|
|
63
|
+
|
|
64
|
+
- name: Build distribution
|
|
65
|
+
env:
|
|
66
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ steps.version.outputs.dev_version }}
|
|
67
|
+
run: |
|
|
68
|
+
pip install build
|
|
69
|
+
python -m build
|
|
70
|
+
ls dist/
|
|
71
|
+
|
|
72
|
+
# ── Release notes ───────────────────────────────────────────────────
|
|
73
|
+
- name: Generate pre-release notes
|
|
74
|
+
run: |
|
|
75
|
+
pip install git-cliff
|
|
76
|
+
git cliff --unreleased --strip all 2>/dev/null > /tmp/cliff_body.md
|
|
77
|
+
[ -s /tmp/cliff_body.md ] || echo "No user-facing changes since last release." > /tmp/cliff_body.md
|
|
78
|
+
|
|
79
|
+
cat > /tmp/release_notes.md <<HEADER
|
|
80
|
+
## something-x-dev ${{ steps.version.outputs.dev_version }}
|
|
81
|
+
|
|
82
|
+
> Pre-release build from \`develop\` — not for production use.
|
|
83
|
+
> **Commit**: \`${{ steps.version.outputs.short_sha }}\`
|
|
84
|
+
|
|
85
|
+
### Changes since last stable release
|
|
86
|
+
|
|
87
|
+
HEADER
|
|
88
|
+
cat /tmp/cliff_body.md >> /tmp/release_notes.md
|
|
89
|
+
cat >> /tmp/release_notes.md <<'FOOTER'
|
|
90
|
+
|
|
91
|
+
### Install
|
|
92
|
+
```bash
|
|
93
|
+
pip install something-x-dev==${{ steps.version.outputs.dev_version }}
|
|
94
|
+
```
|
|
95
|
+
FOOTER
|
|
96
|
+
|
|
97
|
+
# ── GitHub pre-release ──────────────────────────────────────────────
|
|
98
|
+
- name: Create GitHub pre-release
|
|
99
|
+
uses: softprops/action-gh-release@v2
|
|
100
|
+
with:
|
|
101
|
+
tag_name: "dev-${{ steps.version.outputs.short_sha }}"
|
|
102
|
+
prerelease: true
|
|
103
|
+
body_path: /tmp/release_notes.md
|
|
104
|
+
files: dist/*
|
|
105
|
+
|
|
106
|
+
# ── Publish to PyPI ─────────────────────────────────────────────────
|
|
107
|
+
- name: Publish to PyPI
|
|
108
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
token: ${{ secrets.RELEASE_PAT }}
|
|
21
|
+
|
|
22
|
+
# ── Compute stable version with python-semantic-release ─────────────
|
|
23
|
+
# PSR reads ALL commits since last vX.Y.Z tag, highest bump wins.
|
|
24
|
+
# docs/chore/ci/test commits produce no output → release skipped.
|
|
25
|
+
- name: Compute release version
|
|
26
|
+
id: version
|
|
27
|
+
run: |
|
|
28
|
+
pip install python-semantic-release
|
|
29
|
+
NEW=$(semantic-release version --print 2>/dev/null || echo "")
|
|
30
|
+
if [ -z "$NEW" ]; then
|
|
31
|
+
echo "No releasable commits (docs/chore/style/ci/test only). Skipping."
|
|
32
|
+
echo "skip=true" >> $GITHUB_OUTPUT
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
echo "New version: $NEW"
|
|
36
|
+
echo "new=$NEW" >> $GITHUB_OUTPUT
|
|
37
|
+
echo "skip=false" >> $GITHUB_OUTPUT
|
|
38
|
+
|
|
39
|
+
# ── Tag and push ────────────────────────────────────────────────────
|
|
40
|
+
- name: Create and push release tag
|
|
41
|
+
if: steps.version.outputs.skip != 'true'
|
|
42
|
+
run: |
|
|
43
|
+
git config user.name "github-actions[bot]"
|
|
44
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
45
|
+
git tag "v${{ steps.version.outputs.new }}"
|
|
46
|
+
git push origin "v${{ steps.version.outputs.new }}"
|
|
47
|
+
|
|
48
|
+
# ── Build ───────────────────────────────────────────────────────────
|
|
49
|
+
- name: Set up Python
|
|
50
|
+
if: steps.version.outputs.skip != 'true'
|
|
51
|
+
uses: actions/setup-python@v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.11"
|
|
54
|
+
|
|
55
|
+
- name: Build distribution
|
|
56
|
+
if: steps.version.outputs.skip != 'true'
|
|
57
|
+
env:
|
|
58
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ steps.version.outputs.new }}
|
|
59
|
+
run: |
|
|
60
|
+
pip install build
|
|
61
|
+
python -m build
|
|
62
|
+
ls dist/
|
|
63
|
+
|
|
64
|
+
# ── Release notes ───────────────────────────────────────────────────
|
|
65
|
+
- name: Generate release notes
|
|
66
|
+
if: steps.version.outputs.skip != 'true'
|
|
67
|
+
run: |
|
|
68
|
+
pip install git-cliff
|
|
69
|
+
git cliff --latest --strip all 2>/dev/null > /tmp/cliff_body.md
|
|
70
|
+
[ -s /tmp/cliff_body.md ] || echo "No user-facing changes in this release." > /tmp/cliff_body.md
|
|
71
|
+
|
|
72
|
+
cat > /tmp/release_notes.md <<HEADER
|
|
73
|
+
## Something X v${{ steps.version.outputs.new }}
|
|
74
|
+
|
|
75
|
+
HEADER
|
|
76
|
+
cat /tmp/cliff_body.md >> /tmp/release_notes.md
|
|
77
|
+
cat >> /tmp/release_notes.md <<'FOOTER'
|
|
78
|
+
|
|
79
|
+
### Install
|
|
80
|
+
```bash
|
|
81
|
+
# Arch / Omarchy
|
|
82
|
+
sudo pacman -S python-gobject python-cairo gtk4 libadwaita
|
|
83
|
+
pip install something-x==${{ steps.version.outputs.new }}
|
|
84
|
+
```
|
|
85
|
+
FOOTER
|
|
86
|
+
|
|
87
|
+
# ── GitHub Release ──────────────────────────────────────────────────
|
|
88
|
+
- name: Create GitHub Release
|
|
89
|
+
if: steps.version.outputs.skip != 'true'
|
|
90
|
+
uses: softprops/action-gh-release@v2
|
|
91
|
+
with:
|
|
92
|
+
tag_name: "v${{ steps.version.outputs.new }}"
|
|
93
|
+
body_path: /tmp/release_notes.md
|
|
94
|
+
files: dist/*
|
|
95
|
+
|
|
96
|
+
# ── Publish to PyPI ─────────────────────────────────────────────────
|
|
97
|
+
- name: Publish to PyPI
|
|
98
|
+
if: steps.version.outputs.skip != 'true'
|
|
99
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
100
|
+
|
|
101
|
+
# ── Update AUR ──────────────────────────────────────────────────────
|
|
102
|
+
- name: Update PKGBUILD
|
|
103
|
+
if: steps.version.outputs.skip != 'true'
|
|
104
|
+
env:
|
|
105
|
+
NEW_VERSION: ${{ steps.version.outputs.new }}
|
|
106
|
+
run: |
|
|
107
|
+
SHA256=$(sha256sum dist/something_x-${NEW_VERSION}.tar.gz | awk '{print $1}')
|
|
108
|
+
sed -i "s/^pkgver=.*/pkgver=${NEW_VERSION}/" PKGBUILD
|
|
109
|
+
sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD
|
|
110
|
+
sed -i "s/sha256sums=('[^']*')/sha256sums=('${SHA256}')/" PKGBUILD
|
|
111
|
+
|
|
112
|
+
- name: Deploy to AUR
|
|
113
|
+
if: steps.version.outputs.skip != 'true'
|
|
114
|
+
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1
|
|
115
|
+
with:
|
|
116
|
+
pkgname: something-x
|
|
117
|
+
pkgbuild: ./PKGBUILD
|
|
118
|
+
commit_username: github-actions[bot]
|
|
119
|
+
commit_email: github-actions[bot]@users.noreply.github.com
|
|
120
|
+
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
|
121
|
+
commit_message: "Update to ${{ steps.version.outputs.new }}"
|
|
122
|
+
allow_empty_commits: false
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.so
|
|
7
|
+
*.egg
|
|
8
|
+
*.egg-info/
|
|
9
|
+
.eggs/
|
|
10
|
+
MANIFEST
|
|
11
|
+
|
|
12
|
+
# Build / distribution
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
*.whl
|
|
16
|
+
*.tar.gz
|
|
17
|
+
|
|
18
|
+
# Virtualenvs
|
|
19
|
+
.venv/
|
|
20
|
+
venv/
|
|
21
|
+
env/
|
|
22
|
+
.env
|
|
23
|
+
ENV/
|
|
24
|
+
|
|
25
|
+
# Tools
|
|
26
|
+
.cache/
|
|
27
|
+
.mypy_cache/
|
|
28
|
+
.ruff_cache/
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.vscode/
|
|
31
|
+
.idea/
|
|
32
|
+
*.swp
|
|
33
|
+
*.swo
|
|
34
|
+
*~
|
|
35
|
+
|
|
36
|
+
# OS
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
39
|
+
|
|
40
|
+
# setuptools-scm generated version file (written at install/build time)
|
|
41
|
+
nothing_app/_version.py
|
|
42
|
+
|
|
43
|
+
# APK reverse-engineering artifacts (local research only)
|
|
44
|
+
*apkm*
|
|
45
|
+
apkm_decompiled/
|
|
46
|
+
apkm_extracted/
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Something X — Device & Platform Compatibility
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Supported devices
|
|
6
|
+
|
|
7
|
+
The RFCOMM `0x55` protocol is shared across the entire Nothing Ear lineup. Confirmed working on **Nothing Ear (2)**; the same activation sequence, ANC wire values, and EQ command IDs are present in the APK for all models listed below.
|
|
8
|
+
|
|
9
|
+
| Device | Protocol confirmed | Community reports |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Nothing Ear (2) | ✅ reverse-engineered from APK | ✅ |
|
|
12
|
+
| Nothing Ear (1) | ✅ same APK protocol class | needs field testing |
|
|
13
|
+
| Nothing Ear (a) | ✅ same APK protocol class | needs field testing |
|
|
14
|
+
| Nothing Ear (stick) | ✅ (no ANC cmd) | needs field testing |
|
|
15
|
+
| CMF Buds / Buds Pro | ⚠️ likely same, different cmd IDs possible | needs field testing |
|
|
16
|
+
| Nothing Ear (open) | ❓ ANC not applicable | not yet tested |
|
|
17
|
+
| CMF Watch | ❓ different protocol expected | not started |
|
|
18
|
+
|
|
19
|
+
**If your device doesn't work:** open an issue with the raw RFCOMM dump (run with `SOMETHING_X_DEBUG=1 ./somethingx`).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Linux distros
|
|
24
|
+
|
|
25
|
+
The app runs on any Linux system with BlueZ. System package names vary:
|
|
26
|
+
|
|
27
|
+
| Distro | Install command |
|
|
28
|
+
|---|---|
|
|
29
|
+
| Arch / Omarchy | `sudo pacman -S python-gobject python-dbus python-cairo gtk4 libadwaita` |
|
|
30
|
+
| Ubuntu 24.04+ | `sudo apt install python3-gi python3-dbus python3-cairo gir1.2-gtk-4.0 gir1.2-adw-1` |
|
|
31
|
+
| Fedora 39+ | `sudo dnf install python3-gobject python3-dbus python3-cairo gtk4 libadwaita` |
|
|
32
|
+
| openSUSE | `sudo zypper install python3-gobject python3-dbus python3-cairo gtk4 libadwaita` |
|
|
33
|
+
| NixOS | see `flake.nix` |
|
|
34
|
+
|
|
35
|
+
Requirements: `bluetoothd` running, BlueZ ≥ 5.6, GTK4 ≥ 4.6, libadwaita ≥ 1.2.
|
|
36
|
+
|
|
37
|
+
Volume control requires `pactl` — available on any PulseAudio or PipeWire system.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Maintainer: Raphael <raphael.girard.iut@gmail.com>
|
|
2
|
+
pkgname=something-x
|
|
3
|
+
pkgver=1.0.0
|
|
4
|
+
pkgrel=1
|
|
5
|
+
pkgdesc="GTK4 device manager for Nothing earbuds on Linux"
|
|
6
|
+
arch=('any')
|
|
7
|
+
url="https://github.com/SoaOaoS/nothingonmarchy"
|
|
8
|
+
license=('MIT')
|
|
9
|
+
depends=('python' 'python-gobject' 'python-dbus' 'gtk4' 'libadwaita')
|
|
10
|
+
makedepends=('python-build' 'python-installer' 'python-wheel' 'python-setuptools')
|
|
11
|
+
optdepends=('libpulse: volume control via pactl')
|
|
12
|
+
source=("https://files.pythonhosted.org/packages/source/s/something-x/something_x-${pkgver}.tar.gz")
|
|
13
|
+
sha256sums=('SKIP')
|
|
14
|
+
|
|
15
|
+
build() {
|
|
16
|
+
cd "something_x-${pkgver}"
|
|
17
|
+
python -m build --wheel --no-isolation
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
package() {
|
|
21
|
+
cd "something_x-${pkgver}"
|
|
22
|
+
python -m installer --destdir="$pkgdir" dist/*.whl
|
|
23
|
+
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
|
24
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Something X — Roadmap
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Known Issues
|
|
6
|
+
|
|
7
|
+
- Window control buttons (close/minimize/maximize) in the headerbar had a double-hover effect on non-tiling desktops due to app CSS overriding the system theme — fixed on `feat/fix-headerbar-window-controls`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## v1.x — Near term
|
|
12
|
+
|
|
13
|
+
### Docs
|
|
14
|
+
- [x] **GitHub Pages** — project landing page + full documentation site (`docs/`)
|
|
15
|
+
|
|
16
|
+
### Distribution
|
|
17
|
+
- [x] AUR package — AUR package for Arch users to replace pip
|
|
18
|
+
- [x] **NixOS flake** — `flake.nix` for Nix users
|
|
19
|
+
- [x] **`.desktop` file** — ships and auto-installs so the app appears in Walker/Rofi/GNOME launcher
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
- [x] **Low battery desktop notification** — `notify-send` when any bud drops below 20 %
|
|
23
|
+
- [x] **Background mode** — closing the window hides it; relaunch shows it again (Gio single-instance)
|
|
24
|
+
- [x] **Per-device profiles** — ANC + EQ saved per device address to `~/.config/something-x/profiles.json`, restored automatically on reconnect
|
|
25
|
+
- [x] **CLI quick-toggle** — `something-x --anc off|on|transparency`, `something-x --eq bass`, `something-x --battery`
|
|
26
|
+
- [x] **System tray icon** — show battery on hover via StatusNotifierItem (Waybar/SNI)
|
|
27
|
+
- [x] **Wearing detection display** — show L/R in-ear status on the earbud visual
|
|
28
|
+
- [x] **Auto-connect RFCOMM** — connect as soon as BlueZ reports the device connected, no tap needed
|
|
29
|
+
|
|
30
|
+
### Fixes
|
|
31
|
+
- [x] **Headerbar button hover** — window controls now respect the system theme on all desktops
|
|
32
|
+
|
|
33
|
+
### Protocol
|
|
34
|
+
- [x] **Debug mode** — `SOMETHING_X_DEBUG=1` env var dumps raw RFCOMM frames for contributors
|
|
35
|
+
- [x] **Graceful ANC mode detection** — infer supported modes from RFCOMM level response; hide unsupported buttons
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## v2.x — Medium term
|
|
40
|
+
|
|
41
|
+
### Features
|
|
42
|
+
- [ ] **Multiple devices simultaneously** — open two device pages, manage both at once
|
|
43
|
+
- [ ] **Quick-settings panel** — small floating overlay triggered by keybind
|
|
44
|
+
- [ ] **Touch controls remapping** — if the protocol exposes it (found in APK, not yet implemented)
|
|
45
|
+
- [ ] **Equalizer graph** — visual EQ curve instead of preset pills; custom presets with sliders
|
|
46
|
+
- [ ] **Firmware update check** — compare device firmware against latest known version, show badge
|
|
47
|
+
|
|
48
|
+
### Tech
|
|
49
|
+
- [ ] **Async BlueZ** — replace `dbus-python` with `dbus-fast` for non-blocking I/O
|
|
50
|
+
- [ ] **Unit tests** — protocol encode/decode, CRC, ANC mode mapping
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## v3.x — Long term / ideas
|
|
55
|
+
|
|
56
|
+
- [ ] **CMF Watch support** — different protocol; would need fresh APK reverse-engineering
|
|
57
|
+
- [ ] **Nothing Phone integration** — glyph control, notification mirroring
|
|
58
|
+
- [ ] **GNOME Shell extension** — battery indicator in the top bar
|
|
59
|
+
- [ ] **KDE Plasma widget** — same idea for KDE users
|
|
60
|
+
- [ ] **macOS port** — replace BlueZ D-Bus with CoreBluetooth via `pyobjc` (long shot)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Won't do
|
|
65
|
+
|
|
66
|
+
- **Windows** — no BlueZ, RFCOMM access requires a different stack entirely
|
|
67
|
+
- **Wayland screenshot / screen capture control** — out of scope
|
|
68
|
+
- **Streaming / media playback control** — MPRIS already handles this system-wide
|
|
69
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[changelog]
|
|
2
|
+
header = ""
|
|
3
|
+
body = """
|
|
4
|
+
{% for group, commits in commits | group_by(attribute="group") %}
|
|
5
|
+
### {{ group }}
|
|
6
|
+
{% for commit in commits | sort(attribute="message") %}
|
|
7
|
+
- {% if commit.scope %}**{{ commit.scope }}**: {% endif %}\
|
|
8
|
+
{{ commit.message | upper_first }}\
|
|
9
|
+
{% endfor %}
|
|
10
|
+
{% endfor %}
|
|
11
|
+
"""
|
|
12
|
+
footer = ""
|
|
13
|
+
trim = true
|
|
14
|
+
|
|
15
|
+
[git]
|
|
16
|
+
conventional_commits = true
|
|
17
|
+
filter_unconventional = true
|
|
18
|
+
split_commits = false
|
|
19
|
+
commit_parsers = [
|
|
20
|
+
{ message = "^feat", group = "Features" },
|
|
21
|
+
{ message = "^fix", group = "Bug Fixes" },
|
|
22
|
+
{ message = "^perf", group = "Performance" },
|
|
23
|
+
{ message = "^refactor", group = "Refactoring" },
|
|
24
|
+
{ message = "^docs", group = "Documentation" },
|
|
25
|
+
{ message = "^style", group = "Styling" },
|
|
26
|
+
{ message = "^test", group = "Testing" },
|
|
27
|
+
# skip anything that isn't useful in a user-facing changelog
|
|
28
|
+
{ message = "^chore: release", skip = true },
|
|
29
|
+
{ message = "^chore", skip = true },
|
|
30
|
+
{ message = "^ci", skip = true },
|
|
31
|
+
{ message = "^build", skip = true },
|
|
32
|
+
# skip all merge commits regardless of how they are worded
|
|
33
|
+
{ message = "^[Mm]erge[ /]", skip = true },
|
|
34
|
+
{ message = "^Merged", skip = true },
|
|
35
|
+
]
|
|
36
|
+
filter_commits = false
|
|
37
|
+
# semver release tags only; dev-* tags are ignored for "previous release" calculation
|
|
38
|
+
tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+"
|
|
39
|
+
ignore_tags = "dev-.*"
|
|
40
|
+
topo_order = false
|
|
41
|
+
sort_commits = "oldest"
|