# Zeppos Skill

> Expert guidance for building and porting ZeppOS applications.

- Skill: `matteocrippa/zeppos-skill` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add matteocrippa/zeppos-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/matteocrippa/zeppos-skill/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: matteocrippa (https://skillmd.com/u/matteocrippa)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/matteocrippa/zeppos-skill

---


# ZeppOS Development Skill File

## 1. Runtime Architecture
ZeppOS runs on a high-constraint embedded environment (QuickJS). The runtime is **Not Node.js** and **Not a Browser**.

### Critical Constraints
- **No Standard Library:** `window`, `document`, `navigator`, `fs`, `path`, and `http` modules do not exist.
- **Memory Strictness:** The runtime has a hard memory limit. Efficient memory management is mandatory.
- **Event Loop:** The runtime is single-threaded. Blocking the main thread (heavy loops) will freeze the watch UI and cause a crash (Watchdog Timeout).

### API Level Targeting
- Always check `deviceInfo` for screen shape (round/rect) and API level.
- Use feature detection (`if (hmSensor...)`) rather than assuming API availability.

## 2. Project Configuration (`app.json`)
Every project must define boundaries in `app.json`:
- **`appType`**: Either `app` (general application) or `watchface`.
- **`permissions`**: Explicitly request access (e.g., `gps`, `heartrate`). If not declared, the API will return `undefined` or fail.
- **`config`**: Define settings screens here if the app requires user configuration.

## 3. Application Lifecycle (Strict Pattern)
ZeppOS apps are lifecycle-driven. You must initialize resources in `onInit` and destroy them in `onDestroy` to prevent memory leaks.

### Standard Boilerplate (ES6 Class Pattern)
```javascript
App({
  globalData: {}, // Share data between pages (use sparingly)

  onCreate(options) {
    // Called once when the app process starts.
    // Initialize global non-UI resources here.
  },

  onDestroy(options) {
    // CRITICAL: Release all resources, stop timers, close files.
    // Failing to do this drains the watch battery.
  }
});
```

### Page Lifecycle
```javascript
Page({
  build() {
    // Called when the page is created.
    // Construct UI layout here.
    // Note: Heavy computation here will delay screen appearance.
  },
  onShow() {
    // Called when the page becomes visible.
  },
  onHide() {
    // Pause animations or sensors here.
  },
  onDestroy() {
    // Clean up page-specific widgets and listeners.
  }
});
```

## 4. UI Development Standards

### Coordinate System
- Origin (0,0) is typically the **Top-Left** corner.
- **Screen Adaptation:**
  - Use `hmSetting.getDeviceInfo()` to get `width` and `height`.
  - Handle round screens (calculating center points for circular progress bars).
  - Do not hardcode positions for the bottom of the screen; use `deviceInfo.height - offset`.

### Widget Management
- **Creation:** `const widget = hmUI.createWidget(widgetId, params)`
- **Properties:** Properties are set via a configuration object during creation.
- **Cleanup:** Always nullify widget references in `onDestroy`.

### Drawing & Refresh
- **No Auto-Repaint:** Unlike the web, changing a variable does not update the UI. You must explicitly call `setProperty` or manually redraw.
- **Canvas:** If using `WIDGET_CANVAS`, you must trigger `redraw()` manually.

## 5. Sensors & Async Handling

### Sensor Reading
- Sensors are hardware resources. Use `register` listeners to get data streams.
- **Rule:** Never leave a sensor listener running in the background. Unregister it in `onDestroy` or `onHide`.

### Timers
- Use `timer.createTimer(interval, repeat, callback)` instead of `setInterval`.
- `setTimeout` is available but ZeppOS timers are optimized for the low-power state management.

## 6. Linting & Syntax (ESLint Config)

Strict linting prevents runtime crashes on the device.

```javascript
module.exports = {
  env: {
    es6: true,
  },
  extends: "eslint:recommended",
  parserOptions: {
    ecmaVersion: 2020,
  },
  globals: {
    // System
    App: "readonly",
    Page: "readonly",
    deviceInfo: "readonly",
    // Modules
    hmUI: "readonly",
    hmSensor: "readonly",
    hmApp: "readonly",
    hmBle: "readonly",
    hmFS: "readonly",
    hmSetting: "readonly",
    // Utils
    gettext: "readonly", // i18n
    timer: "readonly",
    // Config
    __app_preset__dirname: "readonly",
  },
  rules: {
    // Prevent memory leaks
    "no-global-assign": "error",
    "no-implicit-globals": "error",
    // Enforce clean code
    "no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
    "prefer-const": "error",
    // Browser globals that crash ZeppOS
    "no-restricted-globals": [
      "error",
      "window",
      "document",
      "fetch",
      "XMLHttpRequest",
      "localStorage"
    ],
  },
};
```

## 7. Common Pitfalls (Anti-Patterns)

1.  **The Infinite Loop:** Do not use `while(true)` loops. Use `timer` or event callbacks.
2.  **The Asset Oversize:** Do not bundle large images. Compress assets. The app package size limit is strict (often < 1MB - 2MB depending on device).
3.  **String Concatenation in Render:** Avoid complex string manipulation inside the `build()` method or animation loops; pre-calculate data.
4.  **Ignored Destroy:** Assuming the OS cleans up. **It does not.** You must manually stop sensors and timers.
