Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ __pycache__
*.egg-info
*/dist/
*.org
*.1
*.1
78e43b91-b6a2-4e0a-99c2-3f6f74828063_ExportBlock-935e0d48-7286-4b74-aa60-ccd18217ac01
node_modules/
CLAUDE.md
2 changes: 2 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
npx --no-install commitlint --edit "$1"
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Husky hook file doesn’t include a shebang (e.g. #!/usr/bin/env sh). Depending on platform and how Git executes the hook, a missing shebang can cause the hook to fail to run. Add the standard Husky header (and any required initialization for your Husky major version) so the hook executes reliably.

Copilot uses AI. Check for mistakes.
2 changes: 2 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
npx --no-install validate-branch-name && npx --no-install git-precommit-checks && npx --no-install lint-staged
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Husky hook files don’t include a shebang (e.g. #!/usr/bin/env sh). Depending on platform and how Git executes the hook, a missing shebang can cause the hook to fail to run. Add the standard Husky header (and any required initialization for your Husky major version) so the hooks execute reliably.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +2
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Mark Husky hook scripts executable

These hook files were added with mode 100644, so Git ignores them instead of executing them (.husky/pre-commit/.husky/commit-msg are treated as non-executable). In a fresh clone this means the new commit-msg and pre-commit checks are silently bypassed, so invalid commit messages and unlinted staged Python changes can still be committed even after make setup.

Useful? React with 👍 / 👎.

9 changes: 9 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"endOfLine": "lf",
"semi": true,
"singleQuote": true,
"tabWidth": 4,
"arrowParens": "avoid",
"trailingComma": "all",
"useTabs": false
}
27 changes: 27 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"branches": ["main"],
"repositoryUrl": "https://github.com/steamicc/micropython-steami-lib.git",
"debug": false,
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file. See\n[Conventional Commits](https://conventionalcommits.org) for commit guidelines."
}
],
[
"@semantic-release/github",
{
"assets": "pack/*.tgz"
}
],
[
"@semantic-release/git",
{
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
4 changes: 4 additions & 0 deletions .validate-branch-namerc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
pattern: "^(main)$|^(feat|fix|docs|tooling|ci|test|style|chore|refactor)\/([a-z0-9]+-)*[a-z0-9]+$|^release\/v([0-9]+)\\.([0-9]+)\\.([0-9]+)([a-z0-9-]*)$",
errorMsg: "🤨 La branche que tu essaies de pusher ne respecte pas nos conventions, tu peux la renommer avec `git branch -m <nom-actuel> <nouveau-nom>`",
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch-name validation error message is in French and includes emojis, while the repo docs are primarily English. Consider switching to an English message (or making language configurable) so the hook output is understandable for all contributors.

Suggested change
errorMsg: "🤨 La branche que tu essaies de pusher ne respecte pas nos conventions, tu peux la renommer avec `git branch -m <nom-actuel> <nouveau-nom>`",
errorMsg: "The branch name does not follow our conventions. You can rename it with: `git branch -m <current-name> <new-name>`",

Copilot uses AI. Check for mistakes.
}
35 changes: 18 additions & 17 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,46 +48,46 @@ lib/<component>/

## Linting

The project uses `ruff` for linting.

```bash
ruff check .
make lint
```

To automatically fix issues:

```bash
ruff check . --fix
make lint-fix
```

## Commit messages

Use the following format:
Commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) format, enforced by commitlint via a git hook:

```
<scope>: <Description starting with a capital letter ending with a period.>
<type>[(<scope>)]: <Description starting with a capital letter ending with a period.>
```

The scope is the driver name or domain (`hts221`, `ism330dl`, `docs`, `tests`, `ci`...). There is no predefined list of types — the scope is free-form.
**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `ci`, `build`, `chore`, `perf`, `revert`, `tooling`

**Scopes** (optional): driver names (`hts221`, `ism330dl`, `wsen-pads`...) or domains (`ci`, `docs`, `style`, `tests`, `tooling`). The scope is recommended for driver-specific changes but can be omitted for cross-cutting changes.

### Examples

```
hts221: Fix missing self parameter in get_av method.
ism330dl: Add driver support for temperature reading.
wsen-pads: Correct pressure conversion formula.
fix(hts221): Fix missing self parameter in get_av method.
feat(ism330dl): Add driver support for temperature reading.
fix(wsen-pads): Correct pressure conversion formula.
docs: Update README driver table.
tests: Add mock scenarios for mcp23009e driver.
test(mcp23009e): Add mock scenarios for mcp23009e driver.
```

## Workflow

1. Create a branch from main (`git checkout -b my-new-feature`)
2. Write your code and add tests in `tests/scenarios/<driver>.yaml`
3. Run `ruff check` and `python -m pytest tests/ -v -k mock locally`
4. Commit your changes following the commit message format
5. Push your branch to the repository
6. Open a Pull Request
1. Set up your environment: `make setup`
2. Create a branch from main (format: `feat/`, `fix/`, `docs/`, `tooling/`, `ci/`, `test/`, `style/`, `chore/`, `refactor/`)
3. Write your code and add tests in `tests/scenarios/<driver>.yaml`
4. Run `make ci` to verify everything passes (lint + tests + examples)
5. Commit — the git hooks will automatically check your commit message and run ruff on staged files
6. Push your branch and open a Pull Request

## Continuous Integration

Expand All @@ -98,6 +98,7 @@ All pull requests must pass these checks:
| Commit messages | `check-commits.yml` | Validates commit message format |
| Linting | `python-linter.yml` | Runs `ruff check` |
| Mock tests | `tests.yml` | Runs mock driver tests |
| Example validation | `tests.yml` | Validates example files syntax and imports |

## Notes

Expand Down
120 changes: 120 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
.DEFAULT_GOAL := help

.ONESHELL: # Applies to every targets

include env.mk

# --- Setup ---

# npm install is re-run only when package.json changes
node_modules/.package-lock.json: package.json package-lock.json
npm install
@touch $@
Comment on lines +10 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail fast when npm install fails in setup target

Because .ONESHELL is enabled globally, this multi-line recipe runs in one shell without -e; if npm install fails but node_modules/ already exists, the subsequent touch still succeeds and the target exits 0, leaving a false success stamp. That can make make install/make setup proceed with stale or missing Node tooling while reporting success, which breaks the reproducibility this target is meant to provide.

Useful? React with 👍 / 👎.


.PHONY: prepare
prepare: node_modules/.package-lock.json ## Install git hooks
husky
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Run Husky via npm exec in prepare target

make setup invokes prepare directly from make, but husky is only installed in node_modules/.bin and that directory is not on PATH outside npm lifecycle scripts. In a normal fresh checkout this makes make setup fail at prepare with husky: No such file or directory, even after npm install; call Husky through npx --no-install husky (or npm exec husky) here so the advertised setup command works reliably.

Useful? React with 👍 / 👎.

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepare runs husky with no arguments. Husky typically needs an explicit install step (e.g. setting core.hooksPath and creating .husky/_) for hooks to actually run; calling husky alone is likely a no-op or help output. Please ensure make prepare performs the actual installation step so git hooks are activated after make setup.

Suggested change
husky
husky install

Copilot uses AI. Check for mistakes.

.PHONY: setup
setup: install prepare ## Full dev environment setup

.PHONY: install
install: node_modules/.package-lock.json ## Install dev tools (pip + npm)
python3 -m pip install -e ".[dev,test]"

Comment on lines +21 to +24
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pip install -e ... can pick up the wrong Python environment (e.g., if pip points to a different interpreter than python3). Using python3 -m pip install -e ... (or $(PYTHON) -m pip with a configurable PYTHON var) makes the install target more reliable.

Copilot uses AI. Check for mistakes.
# --- Linting ---

.PHONY: lint
lint: ## Run ruff linter
ruff check

.PHONY: lint-fix
lint-fix: ## Auto-fix lint issues
ruff check --fix

# --- Testing ---

# Dynamic per-scenario targets (test-apds9960, test-hts221, etc.)
# Uses 'driver' field for driver scenarios, filename stem for board scenarios.
# Convention: for board scenarios, the YAML 'name' field must match the filename.
SCENARIOS := $(shell python3 -c "import yaml,glob,os; [print(d.get('driver',os.path.basename(f).replace('.yaml',''))) for f in sorted(glob.glob('tests/scenarios/*.yaml')) for d in [yaml.safe_load(open(f))]]" 2>/dev/null)
$(foreach s,$(SCENARIOS),$(eval .PHONY: test-$(s))$(eval test-$(s): ; python3 -m pytest tests/ -v -k "$(s)" --port $$(PORT) -s))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match per-scenario filters to pytest scenario IDs

The generated test-<scenario> targets filter with -k "$(s)", where $(s) comes from the YAML filename stem, but test IDs are built from scenario name/driver (tests/test_scenarios.py), not necessarily from the filename. For example, tests/scenarios/wsen_hids.yaml uses driver: wsen-hids and tests/scenarios/board_temperature_comparison.yaml uses name: "Temperature comparison (all sensors)", so make test-wsen_hids and make test-board_temperature_comparison can select zero tests and fail with "no tests collected". Use the same identifier source as pytest (or --driver) when generating these targets.

Useful? React with 👍 / 👎.

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic per-scenario targets are generated from YAML filenames (e.g. wsen_hids.yaml -> make test-wsen_hids), but pytest IDs use scenario.get('name', scenario.get('driver', ...)). For wsen_hids.yaml/wsen_pads.yaml, driver is wsen-hids/wsen-pads, so -k "wsen_hids" won't match and the generated make targets won't run any tests. Consider generating targets from the scenario name/driver fields (parsed from YAML) or aligning filenames/name with the driver string used in test IDs (e.g., rename YAMLs or add a name: matching the filename).

Suggested change
$(foreach s,$(SCENARIOS),$(eval .PHONY: test-$(s))$(eval test-$(s): ; python3 -m pytest tests/ -v -k "$(s)" --port $$(PORT) -s))
$(foreach s,$(SCENARIOS),$(eval .PHONY: test-$(s))$(eval test-$(s): ; python3 -m pytest tests/ -v -k "$(s) or $(subst _,-,$(s))" --port $$(PORT) -s))

Copilot uses AI. Check for mistakes.

.PHONY: test-mock
test-mock: ## Run mock tests (no hardware needed)
python3 -m pytest tests/ -v -k mock

.PHONY: test
test: test-mock ## Run mock tests (use 'make test-all' for mock + hardware)
@echo ""
@echo "ℹ️ Only mock tests were run. Use 'make test-all' to include hardware tests."

.PHONY: test-hardware
test-hardware: ## Run all hardware tests (needs board on PORT)
python3 -m pytest tests/ -v --port $(PORT) -s -k hardware

.PHONY: test-board
test-board: ## Run board tests only (buttons, LEDs, buzzer, screen)
python3 -m pytest tests/ -v --port $(PORT) -s -k "board_ and hardware"

.PHONY: test-sensors
test-sensors: ## Run sensor driver hardware tests (I2C devices)
python3 -m pytest tests/ -v --port $(PORT) -s -k "hardware and not board_"
Comment on lines +58 to +62
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Select board tests by marker instead of name substring

These targets split board vs sensor suites using -k substring checks on board_, but board scenarios are identified by the board marker in the test generator (tests/test_scenarios.py marks board scenarios explicitly). Any board scenario whose ID does not contain board_ (e.g. name: "Temperature comparison (all sensors)") is omitted from test-board and incorrectly included by test-sensors, so both commands run the wrong scope. Use marker-based filtering (-m board and -m "hardware and not board") to make the target behavior correct.

Useful? React with 👍 / 👎.


.PHONY: test-all
test-all: ## Run all tests (mock + hardware)
python3 -m pytest tests/ -v --port $(PORT) -s

.PHONY: test-examples
test-examples: ## Validate all example files (syntax + imports)
python3 -m pytest tests/test_examples.py -v

# --- CI ---

.PHONY: ci
ci: lint test test-examples ## Run all CI checks (lint + tests + examples)

# --- Build / Package ---

.PHONY: build
build: lint test ## Build (lint + test)

# --- Hardware ---

.PHONY: repl
repl: ## Open MicroPython REPL on the board
mpremote connect $(PORT)

.PHONY: mount
mount: ## Mount lib/ on the board for live testing
mpremote connect $(PORT) mount lib/

# --- Utilities ---

.PHONY: clean
clean: ## Remove build artifacts and caches
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true
find . -type d -name .ruff_cache -exec rm -rf {} + 2>/dev/null || true
find . -type d -name .mypy_cache -exec rm -rf {} + 2>/dev/null || true

.PHONY: deepclean
deepclean: clean ## Remove everything including node_modules
rm -rf node_modules

.PHONY: help
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'

# A useful debug Make Target - found from
# http://lists.gnu.org/archive/html/help-make/2005-08/msg00137.html
.PHONY: printvars
printvars:
@$(foreach V,$(sort $(.VARIABLES)), \
$(if $(filter-out environment% default automatic, \
$(origin $V)),$(warning $V=$($V) ($(value $V)))))

# Affiche toutes les cibles disponibles dans le Makefile
.PHONY: list
list:
@LC_ALL=C $(MAKE) -pRrq -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$'
58 changes: 55 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,67 @@ mpremote cp -r lib/ssd1327/ssd1327 :lib/

Once copied, the driver can be imported normally without mounting.

## Testing
## Development

### Setup

```bash
make setup # Install pip + npm dependencies and git hooks
```

### Available commands

Run `make help` to see all available targets:

| Command | Description |
|---------|-------------|
| `make lint` | Run ruff linter |
| `make lint-fix` | Auto-fix lint issues |
| `make test` | Run mock tests (no hardware) |
| `make test-mock` | Run mock tests |
| `make test-hardware` | Run all hardware tests (needs board) |
| `make test-board` | Board tests only (buttons, LEDs, buzzer) |
| `make test-sensors` | Sensor driver hardware tests |
| `make test-all` | All tests (mock + hardware) |
| `make test-examples` | Validate example files |
| `make test-<scenario>` | Test a specific scenario (e.g. `make test-hts221`) |
| `make ci` | Full CI pipeline (lint + tests + examples) |
| `make repl` | Open MicroPython REPL |
| `make mount` | Mount lib/ on the board |
| `make clean` | Remove caches |
| `make deepclean` | Remove everything including node_modules |

Per-scenario targets are generated automatically from `tests/scenarios/*.yaml`.

### Git hooks

Git hooks are managed by [husky](https://typicode.github.io/husky/) and run automatically on commit:

- **commit-msg** — validates commit message format via [commitlint](https://commitlint.js.org/)
- **pre-commit** — branch name validation, content checks (conflict markers, TODO), ruff on staged `.py` files

### Testing

Run the full mock test suite:

```bash
python -m pytest tests/ -v -k mock
make test
```

Run hardware tests (requires a STeaMi board on `/dev/ttyACM0`):

```bash
make test-hardware
```

See full details in [tests/TESTING.md](tests/TESTING.md)
Run tests for a specific driver:

```bash
make test-hts221
```

See full details in [tests/TESTING.md](tests/TESTING.md).

## Contributing

Contributions are welcome! Please follow the project guidelines.
Expand Down
58 changes: 58 additions & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
parserPreset: {
parserOpts: {
issuePrefixes: ['#'],
},
},
rules: {
'subject-case': [0],
'subject-full-stop': [0],
'header-max-length': [2, 'always', 78],
'scope-enum': [
1,
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scope-enum is configured with severity 1 (warning), which means invalid scopes will typically not fail the commit-msg hook. If the intent is to enforce the allowed scopes (as described in the PR), set the rule severity to 2 so invalid scopes are rejected.

Suggested change
1,
2,

Copilot uses AI. Check for mistakes.
'always',
[
'apds9960',
'bme280',
'bq27441',
'daplink_flash',
'gc9a01',
'hts221',
'im34dt05',
'ism330dl',
'lis2mdl',
'mcp23009e',
'ssd1327',
'steami_config',
'vl53l1x',
'wsen-hids',
'wsen-pads',
'ci',
'docs',
'style',
'tests',
'tooling',
],
],
'type-enum': [
2,
'always',
[
'build',
Comment on lines +38 to +42
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow existing scope-only commit subjects

The new type-enum restricts the leading token to conventional types and excludes driver scopes (e.g. hts221, ism330dl), so commit messages in the repository’s established <scope>: <Description.> format are rejected by the new commit-msg hook. This blocks contributors using the documented/project-native format unless they switch to a different convention, which creates workflow breakage.

Useful? React with 👍 / 👎.

'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test',
'tooling',
],
],
'body-max-line-length': [1, 'always', 100],
},
};
2 changes: 2 additions & 0 deletions env.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export PATH := $(CURDIR)/node_modules/.bin:$(PATH)
PORT ?= /dev/ttyACM0
Loading
Loading