Metadata-Version: 2.4
Name: wattchli
Version: 0.0.1
Summary: indoor cycling HUD
Author: The wattchli Contributors
License-Expression: GPL-3.0-or-later
License-File: LICENSES/GPL-3.0-or-later.txt
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: End Users/Desktop
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: bleak>=3.0.1
Requires-Dist: cssselect2>=0.9.0
Requires-Dist: msgspec>=0.21.1
Requires-Dist: pycycling>=0.4.1
Requires-Dist: pyobjc-core>=12.1 ; sys_platform == 'darwin'
Requires-Dist: pyside6-essentials>=6.11.1,<6.12
Requires-Dist: simpleeval>=1.0.7,<2
Requires-Dist: tinycss2>=1.5.1
Requires-Python: >=3.12
Project-URL: Homepage, https://codeberg.org/cyctlr/wattchli
Project-URL: Repository, https://codeberg.org/cyctlr/wattchli
Project-URL: Issues, https://codeberg.org/cyctlr/wattchli/issues
Description-Content-Type: text/markdown

<!--
SPDX-FileCopyrightText: 2026 The wattchli Contributors
SPDX-License-Identifier: GPL-3.0-or-later
-->

# wattchli

_wattchli_ is a simple HUD that allows you to keep an eye on your cycling stats while playing games or watching videos.

![SuperTuxKart](docs/res/stk.jpg)

![Big Buck Bunny](docs/res/bbb.jpg)

## Installation

_wattchli_ is a Python application, so you can install/run it in whatever way you want.

For example:

```bash
# option 1: run from manual local clone
git clone https://codeberg.org/cyctlr/wattchli.git
cd wattchli
uv sync
uv run wattchli

# option 2: install with pipx
pipx install git+https://codeberg.org/cyctlr/wattchli.git
wattchli

# option 3: install with uv tool
uv tool install git+https://codeberg.org/cyctlr/wattchli.git
wattchli
```

## Interaction

The HUD is a frameless window but there's a drag handle at the bottom-right that allows you to move it around by pressing the left mouse button in that area. It's usually necessary to move the HUD into the right position before opening a game in fullscreen mode.
The context menu can be opened by right-clicking on the drag handle.

## Sensors

The app supports cycling power sensors, heart rate sensors and cycling speed & cadence sensors.

Currently the app connects automatically to nearby sensors.

## Customization

_wattchli_ can be customized via `config.json` and `style.css` placed into `~/.config/wattchli` (or `%APPDATA%\wattchli`).

It is recommended to start wattchli with `--mock_sensors` while configuring things. This mocks the sensors and gives instant feedback.

### Configuration

`config.json` allows you to configure every aspect of the application, most importantly, the metrics you want to display (via pages/rows/cols).

Here's an example with all configuration values. It should be mostly self-explanatory.

```json
{
  "wheelCircumferenceMm": 2100,
  "pages": [
    {
      "name": "page0",
      "rows": [
        {
          "cols": [
            { "widget": { "widgetType": "metric", "metricType": "heartRate" } },
            { "widget": { "widgetType": "metric", "metricType": "power" } }
          ]
        },
        {
          "cols": [
            { "widget": { "widgetType": "metric", "metricType": "power3s" } },
            { "widget": { "widgetType": "metric", "metricType": "cadence" } }
          ]
        },
        {
          "cols": [
            { "widget": { "widgetType": "metric", "metricType": "speed" } },
            { "widget": { "widgetType": "metric", "metricType": "distance" } }
          ]
        }
      ]
    },
    {
      "name": "page1",
      "rows": [
        {
          "cols": [
            { "widget": { "widgetType": "metric", "metricType": "speed" } },
            { "widget": { "widgetType": "blank" } }
          ]
        },
        {
          "cols": [
            { "widget": { "widgetType": "blank" } },
            {
              "widget": {
                "widgetType": "metric",
                "metricType": "heartRate",
                "formatter": "f'{value}<span style=\"color:red;\">\u2764</span>'"
              }
            }
          ]
        }
      ]
    }
  ],
  "disableClickthrough": false,
  "cursorPollMs": 50,
  "mockSensors": false,
  "logLevel": "info"
}
```

The values can also be overridden with CLI arguments (see --help).

#### Advanced

It's possible to configure a custom `formatter`. The `formatter` is a Python expression evaluated by [simpleeval](https://github.com/danthedeckie/simpleeval). The `value` variable is available as a float and must be converted to a string.
The string supports rich text. So it's possible to pass simple HTML.

Examples:

- `f'{value}'`
- `f'{value:.0f}'`
- `f'{value}<span style=\"color:red;\">\u2764</span>'`

### Styling

The app can be styled with CSS.

The app's base stylesheet is the following, and the user's stylesheet is appended to it.

```css
/* common to all metric texts: title, value, unit */
.metric-text {
  color: white;
  outline-color: black;
  font-weight: bold;
}

.metric-value {
  font-family: system-fixed;
  font-size: 3em;
}

.metric-title {
  font-family: system-title;
}
.metric-unit {
  font-family: system-small;
  /* hide if not needed */
  /* display: none; */
}

page {
  margin: 20px 10px 20px 10px;
  row-gap: 10px;
  column-gap: 10px;
}

window {
  color: #40000000;
  border-color: #40ffffff;
  border-width: 1px;
  border-radius: 16px;
}
```

## Platform Notes

### macOS

#### Cold Start Times

Cold starts on macOS might be slow (~5s) due to Apple scanning dynamically loaded libraries with `syspolicyd`.
Consecutive runs will be much faster.

(It is not recommended to do so, but to temporarily avoid this overhead, add and activate your terminal under _Privacy & Security_ -> _Developer Tools_, then restart your terminal.)

### Windows

Note that the HUD may not appear over exclusive fullscreen games. If possible switch to borderless windowed mode.

## License

GNU General Public License v3.0 or later - see [LICENSE](LICENSE) for details.
