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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
81 changes: 81 additions & 0 deletions .github/workflows/pr-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: PR Lint

on:
pull_request:

jobs:
prettier:
name: Prettier check
runs-on: ubuntu-latest
container:
image: node:lts-alpine

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install prettier
run: npm install --global prettier@latest

- name: Run prettier check
run: |
prettier --check \
--config .prettierrc.json \
--ignore-path .prettierignore \
"**/*.{js,json,md,yaml,yml}"

qml-check:
name: QML format and lint check
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"

- name: Install PySide6 (provides qmllint and qmlformat)
run: pip install "PySide6==6.11.1"

- name: Determine changed QML files
id: changed
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
base_ref="${{ github.base_ref }}"
git fetch --no-tags origin "$base_ref"
files=$(git diff --name-only --diff-filter=ACMR "origin/$base_ref"...HEAD -- '*.qml')
if [ -z "$files" ]; then
echo "No changed QML files in this PR."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "Changed QML files:"
echo "$files"
echo "has_changes=true" >> "$GITHUB_OUTPUT"
{
echo "files<<EOF"
echo "$files"
echo "EOF"
} >> "$GITHUB_OUTPUT"
fi

- name: Check qmlformat
if: steps.changed.outputs.has_changes == 'true'
run: |
echo "${{ steps.changed.outputs.files }}" | xargs pyside6-qmlformat -i
git diff --exit-code || {
echo ""
echo "qmlformat found formatting issues. Run locally to fix:"
echo " git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }}...HEAD -- '*.qml' | xargs pyside6-qmlformat -i"
exit 1
}

- name: Run qmllint
if: steps.changed.outputs.has_changes == 'true'
continue-on-error: true
run: |
echo "${{ steps.changed.outputs.files }}" | xargs pyside6-qmllint --bare
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.claude/
32 changes: 32 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: mixed-line-ending
args: [--fix=lf]
- id: check-merge-conflict
- id: check-added-large-files

- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.8.4
hooks:
- id: prettier
files: \.(js|json|md|ya?ml)$

- repo: local
hooks:
- id: qmlformat
name: qmlformat
language: python
additional_dependencies: [PySide6==6.11.1]
entry: pyside6-qmlformat -i
files: \.qml$

- id: qmllint
name: qmllint
language: python
additional_dependencies: [PySide6==6.11.1]
entry: pyside6-qmllint
files: \.qml$
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.claude/
9 changes: 9 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"useTabs": true,
"tabWidth": 4,
"printWidth": 120,
"semi": true,
"singleQuote": false,
"trailingComma": "none",
"endOfLine": "lf"
}
2 changes: 1 addition & 1 deletion Changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
- Add optional Waze travel time for up to two routes

1.0.1
- initial release
- initial release
40 changes: 40 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
MIT License

Copyright (c) 2026 Luuk Visser

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

---

Upstream notice
---------------

This repository is a fork of https://github.com/ToonSoftwareCollective/tscClock
(forked at commit af304ec, 27 December 2022), which itself builds on the
standard Toon clock app.

At the time of writing, the upstream repository publishes no license file. The
MIT grant above therefore covers only the changes and additions made in this
fork by the copyright holder named above; it cannot and does not grant rights
in the portions of the code that originate from ToonSoftwareCollective. Those
portions remain under their authors' copyright, with no license terms stated.

If you intend to redistribute this software, ask ToonSoftwareCollective to
state a license for the upstream code first. Several other repositories in
that organisation (nlalert, toonmqtt, ToonRooter) are MIT-licensed.
179 changes: 179 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# tscClock

A clock tile for the **Eneco Toon** thermostat display: time, seconds, date and day of
the week, plus optional **Waze travel times** for up to two routes.

This is a fork of
[ToonSoftwareCollective/tscClock](https://github.com/ToonSoftwareCollective/tscClock),
which itself extends the standard Toon clock app with a date and seconds. See
[Differences from upstream](#differences-from-upstream).

## The tile

Two layouts are available. The original one anchors the time and date at fixed offsets
from the top of the tile:

```
ma 07:31 42
31 januari
Thuis-Werk: 24 min
Werk-Thuis: 31 min
```

The centred layout stacks the same rows in a `Column` centred vertically in the tile,
and grows the time font when the day of the week has been moved to the date row:

```
07:31 42
ma 31 januari
Thuis-Werk: 24 min
Werk-Thuis: 31 min
```

Every row below the time is optional; a row that is switched off or has no value takes
up no vertical space. Tapping the tile opens the settings screen.

## Requirements

- A **rooted Toon** running the ToonStore client (`tsc`). Settings are stored in
`/mnt/data/tsc/`, which is the ToonStore data directory.
- For travel times only: the Toon must be able to reach `routing-livemap-row.waze.com`
over HTTPS. No account or API key is needed.

The clock itself works fully offline; leave the route coordinates blank and no network
requests are made.

## Installation

Copy the repository contents into a `tscClock` directory under the Toon's apps directory
and restart the UI:

```sh
scp *.qml version.txt root@<toon-ip>:/qmf/qml/apps/tscClock/
```

The exact apps path depends on your firmware and root method — check where your other
ToonStore apps live and use the same parent directory. After restarting the Toon UI, add
the tile via the tile screen's **+** button (category _general_, label _Klok-TSC_).

## Configuration

Tap the tile to open **TSC Klok configuratie**:

| Setting | Default | Description |
| ----------------------------------- | ------- | --------------------------------------------------------------------------- |
| `Toon datum` | on | Show the date row |
| `Toon seconden in de tijd` | on | Show seconds in small type next to the time |
| `Maand in datum voluit geschreven` | on | Month as a word (`31 januari`) instead of a number (`31-01`) |
| `Toon dag van de week` | on | Show the two-letter day abbreviation |
| `Verticaal gecentreerde weergave` | off | Use the centred layout instead of the fixed-offset one |
| `Dag voor datum (i.p.v. voor tijd)` | off | Move the day abbreviation to the date row, freeing width for a larger clock |

`Waze reistijd »` at the bottom opens the travel-time screen. Press **Opslaan** on either
screen to save; settings are written to `/mnt/data/tsc/tscClock.userSettings.json` and
reloaded on startup.

### Travel times

Two independent routes can be configured, each with the same set of fields:

| Field | Default (route 1 / route 2) | Description |
| ---------------------- | --------------------------------- | ----------------------------------------------------------------------- |
| `Periode` | `06:30`–`09:00` / `16:00`–`18:30` | Time window, `HH:MM`. Only used when `Tijdvenster` is on |
| `Label` | `Thuis-Werk:` / `Werk-Thuis:` | Prefix shown before the duration. Leave blank for the bare duration |
| `Van` / `Naar` Lat/Lon | _(empty)_ | Decimal coordinates, e.g. `52.3704` / `4.8952`. Commas are accepted too |
| `Tijdvenster` | off | Off: the route is always active. On: only inside `Periode` |
| `Alleen op werkdagen` | off | Restrict the route to Monday–Friday |

A route is shown when its coordinates are complete and it is currently active. Routes are
evaluated independently, so overlapping windows show both rows. Saving re-evaluates both
routes immediately rather than waiting for the next tick.

Finding coordinates: right-click a spot in Google Maps and copy the `lat, lon` pair it
shows, or read them from the URL after `@`.

## How it works

`TscClockApp.qml` ticks a 1-second `Timer` that rebuilds the time and date strings via
`i18n.dateTime` and then calls `checkTravelTimeWindow()`. A route that becomes active
triggers one Waze request; a route that goes inactive is cleared immediately, so a stale
duration can never linger on the tile. A second timer refreshes the active routes every
5 minutes.

Travel times come from Waze's public live-map routing endpoint
(`routing-livemap-row.waze.com/RoutingManager/routingRequest`), summing `crossTime` over
the returned route segments. Failures are logged to the Toon's console and leave the
previous value in place.

| File | Role |
| ------------------------------ | ------------------------------------------------------------ |
| `TscClockApp.qml` | App root: settings persistence, clock tick, Waze fetch logic |
| `TscClockTile.qml` | The tile — both layouts, switched by `app.centerLayout` |
| `TscClockSettings.qml` | Clock settings screen |
| `TscClockTravelSettings.qml` | Two-column travel-time settings screen |
| `version.txt`, `Changelog.txt` | Read by the ToonStore to detect and describe updates |

`isNxt` selects the larger font and margin values for Toon 2 (NXT) hardware. The UI is in
Dutch, matching the rest of the Toon interface.

## Differences from upstream

Upstream has been unchanged since December 2022 (version 1.0.1). This fork adds:

- **Waze travel times** for up to two routes, with per-route label, time window,
workdays-only toggle and a dedicated settings screen.
- **Vertically centred layout** as an alternative to the fixed baseline offsets.
- **Day of the week on the date row**, which frees horizontal space and allows a larger
time font (65 → 75 px on NXT).

Nothing was removed. Existing settings files keep working: the new keys simply fall back
to their defaults when absent.

## Versioning

`version.txt` follows [semantic versioning](https://semver.org/) and continues upstream's
numbering, which stopped at `1.0.1`. This fork is at **1.1.0** — a minor bump, which is
the right call: every change above is additive and backwards compatible, with no removed
settings and no change to the on-disk settings format.

Keep `version.txt` and `Changelog.txt` in step when releasing; the ToonStore uses the
former to detect updates and shows the latter to the user.

One caveat of sharing upstream's numbering line: if ToonSoftwareCollective ever releases
their own `1.1.0`, the two are indistinguishable by version alone. Given upstream's
dormancy this is a low risk, but a fork-specific suffix is the fix if it ever matters.

## Development

Formatting and linting run through [pre-commit](https://pre-commit.com/):

```sh
pip install pre-commit
pre-commit install
pre-commit run --all-files
```

That runs `prettier` on JSON/YAML/Markdown and `pyside6-qmlformat` / `pyside6-qmllint`
(from `PySide6`) on the QML. The same checks run on pull requests via
[`.github/workflows/pr-lint.yml`](.github/workflows/pr-lint.yml); `qmllint` is advisory,
since the Toon's `qb.components` types are not available outside the device.

There is no way to run the app off-device: it depends on Toon-private QML modules
(`qb.components`, `qb.base`, `FileIO`, `BxtClient`). Test on a real Toon.

## Known limitations

- The Waze endpoint is undocumented and unofficial. It can change or start refusing
requests at any time, and every fetch sends your route coordinates to Waze.
- `thumbnailIcon` points at `drawables/clock.svg`, which is not in this repository —
inherited from upstream, where the file is likewise absent.
- Travel times are fetched over HTTP without a timeout; an unreachable Waze leaves the
last value on the tile until the route goes inactive.
- The settings screens are Dutch-only and not translated via `qsTr`.

## License

[MIT](LICENSE), covering the changes made in this fork. The upstream
[ToonSoftwareCollective/tscClock](https://github.com/ToonSoftwareCollective/tscClock)
code this builds on declares no license of its own — see the notice in
[`LICENSE`](LICENSE) before redistributing.
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.0
1.1.0
Loading