diff --git a/python/ruview-meta/README.md b/python/ruview-meta/README.md new file mode 100644 index 00000000..a9c6b694 --- /dev/null +++ b/python/ruview-meta/README.md @@ -0,0 +1,58 @@ +# ruview + +**Ambient intelligence from WiFi CSI.** Detect human presence, count +people, read breathing and heart rate, and estimate skeletal pose — +using only the WiFi signal already in your home. No cameras. No +wearables. Works through walls and in the dark. + +`ruview` is the brand-facing meta-package for the +[RuView](https://github.com/ruvnet/RuView) sensing stack. It installs +the compiled PyO3 wheel published as +[`wifi-densepose`](https://pypi.org/project/wifi-densepose/) and +re-exports its full API under the `ruview` namespace — so you can +write either of these and they do the same thing: + +```python +from ruview import BreathingExtractor, SensingClient +from wifi_densepose import BreathingExtractor, SensingClient +``` + +## Install + +```bash +pip install ruview # core DSP +pip install "ruview[client]" # + WebSocket/MQTT clients +``` + +## Usage + +```python +from ruview import BreathingExtractor + +br = BreathingExtractor.esp32_default() # 56 subcarriers @ 100 Hz, 30s window +for residuals, weights in csi_source: + est = br.extract(residuals=residuals, weights=weights) + if est is not None: + print(f"{est.value_bpm:.1f} BPM (confidence={est.confidence:.2f})") +``` + +Full API + WebSocket / MQTT / Home Assistant integration docs: +[wifi-densepose on PyPI](https://pypi.org/project/wifi-densepose/). + +## Why two PyPI names? + +Historic: `wifi-densepose` is the technical / academic name (the +project started as a WiFi-based DensePose implementation). +`ruview` is the brand the v2 ambient-intelligence platform ships +under. Both are the same code. You pick the import that reads +better in your project. + +## Links + +- **Repository** — https://github.com/ruvnet/RuView +- **Modernization plan** — [ADR-117](https://github.com/ruvnet/RuView/blob/main/docs/adr/ADR-117-pip-wifi-densepose-modernization.md) +- **Issues** — https://github.com/ruvnet/RuView/issues + +## License + +MIT. diff --git a/python/ruview-meta/pyproject.toml b/python/ruview-meta/pyproject.toml new file mode 100644 index 00000000..401805c2 --- /dev/null +++ b/python/ruview-meta/pyproject.toml @@ -0,0 +1,62 @@ +# ADR-117 sibling release — `ruview` meta-package. +# +# Pure-Python wheel that re-exports everything from `wifi-densepose` +# under the alias `ruview`. They're the same code, distributed under +# two PyPI names so users can `pip install ruview` (the brand) or +# `pip install wifi-densepose` (the technical name) — both end up +# with the same compiled DSP available. +# +# Build: +# cd python/ruview-meta +# python -m build + +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" + +[project] +name = "ruview" +version = "2.0.0a1" +description = "RuView — ambient intelligence from WiFi CSI. Meta-package; installs `wifi-densepose` and re-exports it under the `ruview` namespace. See https://github.com/ruvnet/RuView." +readme = "README.md" +requires-python = ">=3.10" +license = { text = "MIT" } +authors = [{ name = "rUv", email = "ruv@ruv.net" }] +keywords = [ + "wifi", "csi", "pose-estimation", "vital-signs", + "biometric", "ambient-intelligence", "home-assistant", "matter", + "ruview", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Typing :: Typed", +] +dependencies = [ + # Pin to the matching v2 release so an alpha-pin `pip install ruview` + # always gets a compatible wifi-densepose. + "wifi-densepose==2.0.0a1", +] + +[project.optional-dependencies] +client = ["wifi-densepose[client]==2.0.0a1"] + +[project.urls] +Homepage = "https://github.com/ruvnet/RuView" +Repository = "https://github.com/ruvnet/RuView" +Issues = "https://github.com/ruvnet/RuView/issues" +Documentation = "https://github.com/ruvnet/RuView/tree/main/docs" + +[tool.setuptools] +packages = ["ruview"] +package-dir = { "" = "src" } diff --git a/python/ruview-meta/src/ruview/__init__.py b/python/ruview-meta/src/ruview/__init__.py new file mode 100644 index 00000000..3115e851 --- /dev/null +++ b/python/ruview-meta/src/ruview/__init__.py @@ -0,0 +1,50 @@ +"""RuView — ambient intelligence from WiFi CSI. + +This package is a thin alias around `wifi-densepose`. Both PyPI names +ship the same code and the same compiled Rust core; `ruview` is the +brand-facing name and `wifi-densepose` is the technical name. Pick +whichever you prefer: + + pip install ruview + pip install wifi-densepose + +Both make this work: + + from ruview import BreathingExtractor, hello + # or equivalently: + from wifi_densepose import BreathingExtractor, hello + +The actual compiled DSP, the Python facade, and every public class +live in `wifi_densepose` — `ruview` just re-exports the surface so the +two names are interchangeable in application code. +""" + +from __future__ import annotations + +import wifi_densepose as _wdp + +# Re-export everything `wifi_densepose.__all__` declares. +for _name in _wdp.__all__: + globals()[_name] = getattr(_wdp, _name) + +# Version + diagnostic fields — surface them under the ruview name +# too so users can `print(ruview.__rust_version__)` without reaching +# into the wifi_densepose module. +__version__: str = _wdp.__version__ +__rust_version__: str = _wdp.__rust_version__ +__rust_build_tag__: str = _wdp.__rust_build_tag__ +__build_features__ = list(_wdp.__build_features__) + +# The client sub-package is also aliased for symmetry. +try: + from wifi_densepose import client # type: ignore[import-not-found] # noqa: F401 +except ImportError: + # client extras not installed — that's fine for the core import. + pass + +__all__ = list(_wdp.__all__) + [ + "__version__", + "__rust_version__", + "__rust_build_tag__", + "__build_features__", +]