Skip to content

ryanjames85/deasc

Repository files navigation

Deasc

Electron + React + BLE app for sit-stand desk control. Supports IKEA Idasen, LINAK DPG, Jiecang/Uplift, and any custom desk via user-configured BLE profiles.

Prerequisites

  • Node.js 18+
  • Windows 10+ (BLE via WinRT — no extra deps)
  • Linux: BlueZ + D-Bus (sudo apt install bluez)
  • macOS: CoreBluetooth (built-in, no deps)

Setup

npm install        # installs deps + runs electron-rebuild for native modules
npm run dev        # start in dev mode with hot reload

Build

npm run dist       # produces installer in /dist

Tests

npm test           # runs Vitest unit tests (db, BLE encoding, voice parser)

Desk support

The app auto-detects supported desks by matching the advertised BLE device name:

Desk family Match names Height encoding
IKEA Idasen idasen LINAK
LINAK DPG linak, dpg, desk LINAK
Jiecang / Uplift uplift, jiecang, autonomous, progressa, standing Jiecang

If auto-detection fails, open Settings → Custom Desk Profile and enter your desk's BLE service UUID, characteristic UUIDs, height range, and encoding type. The app will prefer your custom profile over all built-in ones.

If BLE is unavailable or noble isn't compiled, the app falls back to a mock desk automatically — all UI features work without real hardware.

Voice commands

Voice control is off by default. Enable it in Settings → Voice Commands.

You can give the app a custom wake word (default: desk). Speak the wake word followed by a command:

Say Action
desk stand / desk stand up Move to stand preset
desk sit / desk sit down Move to sit preset
desk position 1 Move to preset #1
desk preset 2 Move to preset #2
desk up / desk raise Move up continuously
desk down / desk lower Move down continuously
desk stop / desk halt Stop movement

Only the commands listed above are recognised. The voice engine uses word-boundary matching — partial word matches (e.g. "outstanding" containing "stand") are ignored.

Project structure

src/
  shared/
    deskTypes.ts      CustomDeskProfile type + DEFAULT_CUSTOM_PROFILE (shared main ↔ renderer)
  main/
    index.ts          Electron main process, window creation
    tray.ts           System tray icon + context menu
    driver.ts         DeskDriver interface — contract for any transport (BLE, ESP32, …)
    ble.ts            BLE implementation of DeskDriver; built-in + custom profiles
    db.ts             JSON file persistence — height history, presets, sessions
    store.ts          electron-store — settings (theme, reminders, voice, custom desk profile)
    ipc.ts            IPC handlers (main ↔ renderer); swap driver here to change transport
    __tests__/
      db.test.ts      Unit tests — height logging, presets, session tracking
      ble.test.ts     Unit tests — BLE profile encoding (LINAK, Jiecang, linear)
  preload/
    index.ts          contextBridge — exposes window.deskAPI
  renderer/src/
    App.tsx           Full UI (dashboard, history chart, settings, voice UI)
    voiceParser.ts    Pure voice command parser — no DOM dependencies
    stores/
      deskStore.ts    Zustand store — all renderer state + IPC calls
    __tests__/
      voiceParser.test.ts  Unit tests — all voice command patterns

Adding a new desk transport (ESP32 / REST)

The BLE driver and any future transport both implement the DeskDriver interface in src/main/driver.ts. To switch:

  1. Create a new class (e.g. RestDriver) implementing DeskDriver.

  2. In src/main/ipc.ts, change the one line:

    const driver: DeskDriver = new RestDriver('http://esp32.local')

No renderer or IPC code changes are needed.

About

BLE sit-stand desk controller built with Electron + React. Supports IKEA Idasen, LINAK, Jiecang/Uplift, and custom BLE profiles. Features presets, height history, sit/stand tracking, reminders, voice commands, and keyboard shortcuts.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors