Prerequisites: Node.js and mise
# Install toolchain from mise.toml
mise install
# Install JS dependencies
npm install
# Build dev (default)
mise build
# Build release
mise build releaseThis builds the project with the Pebble SDK version pinned in pebble-sdk-version and provisioned by the repo scripts. The .pbw output can be found in the build directory.
Telemetry uses Supabase Edge Functions + Postgres.
Install/update the CLI through mise's aqua backend (already pinned in mise.toml):
mise install
supabase --versionCreate a local telemetry hash secret (TELEMETRY_HASH_SECRET):
openssl rand -hex 32Use this value for TELEMETRY_HASH_SECRET.
Start the local Supabase stack from the repo root:
supabase startCopy .env.example to .env, then populate the telemetry keys there:
cp .env.example .envServe the telemetry edge function locally (from repo root):
mise telemetry-serveFor emulator validation, run:
mise install-emulator --logsTo verify inserts locally, open Supabase Studio at http://127.0.0.1:54323 and inspect public.telemetry_weather_fetch.
Authenticate and link the repo to your Supabase project:
supabase login
supabase link --project-ref <your-project-ref>Set function secrets in the hosted project:
supabase secrets set \
TELEMETRY_HASH_SECRET=<paste-openssl-value>Apply database migrations:
supabase db push --dry-run
supabase db pushDeploy the telemetry function:
supabase functions deploy telemetry-ingestWire release and preview builds to hosted telemetry by setting repository secret(s) used by CI workflows:
TELEMETRY_ENDPOINT=https://<your-project-ref>.supabase.co/functions/v1/telemetry-ingest
If you use Supabase GitHub sync/branching, Supabase can auto-apply migrations and deploy functions from the supabase/ directory when branches/PRs update. Enable it in Supabase Dashboard > Project Settings > Integrations.
package.json is generated from package.template.json and profile data in profiles/.
- Release profile:
profiles/package.release.json - Dev profile:
profiles/package.dev.json
mise build and mise build release automatically generate package.json from the template/profile before building.
If you want the extra Pebble heap debug logs, set ENABLE_MEMORY_LOGGING=1 in your .env before building or installing. This is independent of the dev/release package profile.
Release notification copy (optional “what’s new” toast on upgrade) lives in release-notifications.json, keyed by the exact version string from the template (e.g. "1.26.0"). prepare-package copies only the entry for the version being built into package.json; versions with no key ship without a notification.
If you want to regenerate package.json without building:
mise prepare-package # dev profile (default)
mise prepare-package release # release profileYou can run Pebble CLI commands directly, or use install tasks that build and install in one command:
# Option 1: set once in .env
cp .env.example .env
# then edit .env and set IP=<PHONE_IP>
# this installs the dev build by default
mise install-phone
# Option 2: pass IP explicitly
mise install-phone <PHONE_IP>
# Explicit release install
mise install-phone <PHONE_IP> release
# Pass through pebble install flags
mise install-phone --logs
# Legacy pass-through separator (still works)
mise install-phone -- --logs
# Install dev build via CloudPebble (default profile)
mise install-cloud
# Explicit release install via CloudPebble
mise install-cloud release
# Install dev build to emulator (defaults: profile=dev, emulator=basalt)
mise install-emulator
# Choose emulator platform
mise install-emulator aplite
# Choose emulator build type
mise install-emulator release
# Choose emulator platform and build type
mise install-emulator release aplite
# Pass through pebble install flags
mise install-emulator --logs
# Set default emulator in environment
PEBBLE_EMULATOR=aplite mise install-emulator
# Legacy pass-through separator (still works)
mise install-emulator -- --logs
# Take a screenshot from emulator (default platform: basalt)
mise screenshot-emulator
# Choose emulator platform
mise screenshot-emulator aplite
# Legacy flag form (still works)
mise screenshot-emulator -- --emulator chalk
# Set default emulator in environment
PEBBLE_EMULATOR=aplite mise screenshot-emulator
# Default output goes to screenshot/tmp/<timestamp>-<platform>.png
# Provide explicit output path / additional screenshot args
mise screenshot-emulator -- screenshot/my-capture.png --no-open --no-correction
# Take a screenshot from phone
mise screenshot-phone
# Or pass IP explicitly
mise screenshot-phone <PHONE_IP>
# Default output goes to screenshot/tmp/<timestamp>.png
# Provide explicit output path / additional screenshot args
mise screenshot-phone -- screenshot/my-capture.pngYou can create src/pkjs/dev-config.js to override Clay keys and local dev behavior.
Example:
module.exports.owmApiKey = 'abc123';Use this key in src/pkjs/dev-config.js to force a PKJS localStorage reset on each app boot while enabled:
clearPkjsStorageOnBoot = true
Example:
module.exports.clearPkjsStorageOnBoot = true;Notes:
- Keep this set to
trueonly while testing first-install behavior. - Set it back to
falsebefore testing upgrade-notification behavior. - This is local-only dev behavior and is not written into Clay settings.
Use this key in src/pkjs/dev-config.js to always show the notification for a specific version key from release-notifications.json on every app boot (ignores upgrade gating):
forceShowReleaseNotificationOnBoot = '1.26.0'(string must match a key inrelease-notifications.jsonexactly)
Example:
module.exports.forceShowReleaseNotificationOnBoot = '1.26.0';Notes:
- Useful when
package.jsonis still on an older version but you want to iterate on copy for the next release entry. - Remove the key (or comment it out) when testing normal upgrade behavior.
- This is local-only dev behavior and is not written into Clay settings.
Use these keys in src/pkjs/dev-config.js:
provider = 'mock'enables the mock provider.mockCitysets the city label independently.mockScenarioselects the active built-in scenario.
Scenario data is tracked in git inside src/pkjs/weather/mock.js (MOCK_SCENARIOS).
Minimal shape:
module.exports.provider = 'mock';
module.exports.mockCity = 'New York, NY';
module.exports.mockScenario = 'clearMorning';Notes:
- If
mockScenariois missing/invalid, the app falls back to the first built-in scenario. - To add/edit scenarios, update
MOCK_SCENARIOSinsrc/pkjs/weather/mock.js. startEpochandsunEvents[].epochshould be coherent in emulator local time (the graph and shading use watch localtime).
scripts/install-emulator.sh reads these keys from dev-config.js after install:
emuTimeFormat:12hor24hemuTime:HH:MM:SSor Unix seconds (e.g.1772870400)
Then run:
mise install-emulator --logsReset behavior when keys are removed:
emuTimeFormatdefaults to24hemuTimefallback order is:- explicit
emuTimeindev-config.js emuTimeof active mock scenario (whenprovider = 'mock', if present)startEpochof active mock scenario (whenprovider = 'mock')- current host time
- explicit
This project pins pipx:pebble-tool to an exact version in mise.toml (fully resolved in mise.lock).
To bump the pinned version:
mise upgrade "pipx:pebble-tool" --bump
mise install