npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ray-js/electrician-timing-sdk

v1.0.2-beta.1

Published

`@ray-js/electrician-timing-sdk` provides a unified timing solution for electrician devices, including:

Readme

Electrician Timing SDK

@ray-js/electrician-timing-sdk provides a unified timing solution for electrician devices, including:

  • Electrician timers (cycle / random / inching)
  • Countdown
  • Cloud timers
  • Conflict detection and conflict resolution
  • Online status checks

Installation

npm i @ray-js/electrician-timing-sdk

API Overview

Lifecycle

| API | Description | | --- | --- | | init(options) | Initialize SDK and load runtime state | | changeConfig(options) | Update config; re-init when target device/group changes | | destroy() | Clear internal state, listeners, and config |

Cloud Timer

| API | Description | | --- | --- | | addCloudTimer(data, option?) | Add one cloud timer | | batchAddCloudTimer(dataList, option?) | Add cloud timers in batch | | updateCloudTimer(data, option?) | Update cloud timer content | | updateCloudTimerStatus(data, option?) | Enable/disable cloud timer | | removeCloudTimer(id) | Remove cloud timer | | onCloudUpdate(cb) | Subscribe cloud timer update event | | offCloudUpdate(cb) | Unsubscribe cloud timer update event |

Countdown

| API | Description | | --- | --- | | createCountdown(code, countdown, option?) | Create countdown | | cancelCountdown(code) | Cancel countdown |

Custom DP Timer

| API | Description | | --- | --- | | addDpTimer(code, data, option?) | Add custom DP timer | | updateDpTimer(code, data, option?) | Update custom DP timer | | updateDpTimerStatus(code, id, status, option?) | Enable/disable custom DP timer | | removeDpTimer(code, id) | Remove custom DP timer |

Electrician Shortcuts (electri)

| API | Description | | --- | --- | | electri.cycle.add/update/updateStatus/remove | CRUD + status for cycle timer | | electri.cycle.getConfig/validateMax/validateRepeat | Cycle timer config and validation | | electri.random.add/update/updateStatus/remove | CRUD + status for random timer | | electri.random.getConfig/validateMax/validateRepeat | Random timer config and validation | | electri.inching.add/update/updateStatus/remove | CRUD + status for inching timer | | electri.inching.getConfig/validateMax/validateRepeat | Inching timer config and validation |

Online Status

| API | Description | | --- | --- | | isLANOnline() | Check LAN-online status | | isLocalOnline() | Check local-online status |

Initialization

Basic rules

  • You must provide at least one of devId or groupId.
  • devId takes priority when both are provided.
  • type supports ele and custom.

Common options of init(options)

| Field | Required | Description | | --- | --- | --- | | devId | No | Device ID | | groupId | No | Group ID | | type | Yes | ele or custom | | category | No | Cloud timer category, default sdk_scheduleModifications to new projects are not recommended| | conflictModallId | No | Default conflict modal id | | is24Hour | No | 24-hour display | | combineSameData | No | Merge same timer data across channels, default true |

Extra options for type: 'ele'

| Field | Required | Description | | --- | --- | --- | | switches | No | Switch codes; auto-detected when omitted | | countdowns | No | Countdown config list or countdown code list | | dpTimerConfig | No | Custom DP timer config map | | supportCycle/supportRandom/supportInching/supportCloud | No | Capability flag: auto / y / n | | cycleCode/randomCode/inchingCode | No | Explicit timer dp code |

Extra options for type: 'custom'

| Field | Required | Description | | --- | --- | --- | | switches | Yes | Switch code list | | countdowns | Yes | Countdown config list | | dpTimerConfig | Yes | Custom timer dp config map | | supportCloud | No | Cloud timer capability |

Conflict modal (ConflictPopup)

When timer APIs are called with { useDefaultModal: true }, the SDK resolves conflicts by taking the top page in the stack, calling selectComponent('#<id>') on the mounted conflict component, and invoking show(conflictData, validateData) to render the conflict list. After the user confirms, the component runs the same resolution path as the main package (DP publish / cloud timers, etc.). Therefore conflictModallId passed to init (default smart-conflict-popup) must exactly match the popup node id in your page; otherwise the SDK throws because the component cannot be found.

flowchart TB
  A[Timer API + useDefaultModal: true] --> B[SDK validates conflicts]
  B -->|No conflict| C[Continue / pass]
  B -->|Conflict| D["selectComponent('#conflictModallId').show(...)"]
  D --> E[ConflictPopup renders list]
  E --> F{User action}
  F -->|Confirm| G[Internal resolve applies changes]
  F -->|Cancel/close| H[API returns cancel]
  G --> I[success, or toast + reject on failure]

Using ConflictPopup in React

import { ConflictPopup } from '@ray-js/electrician-timing-sdk/lib/components';

// Mount near the page root; id must match init({ conflictModallId }) (default can be omitted)
<ConflictPopup
  id="smart-conflict-popup"
  title="Timer conflict"
  description="Replace conflicting timers?"
  locale={locale}
  cancelText="Cancel"
  okText="Confirm"
/>

List content is injected when the SDK calls show; you normally only set title, description, locale, and button labels. Follow your Ray + React build setup for component registration.

ConflictPopup props

| Prop | Required | Description | | --- | --- | --- | | title | Yes | Modal title | | locale | Yes | Locale object: weekdays, type labels, conflictFailure, DP strings, etc. | | id | No | Must match init’s conflictModallId for selectComponent | | description | No | Subtitle / helper text | | cancelText / okText | No | Button labels (defaults exist) | | themeColor | No | Icon background color | | hideMask | No | Hide overlay mask | | visble | No | Visibility flag (same spelling as source; show usually drives display) | | usesolt | No | Use slot for custom list area (same spelling as source) | | data / validateData | No | Normally passed by SDK in show, not hard-coded in template |

Complete API Examples

1) Initialize

flowchart LR
  A[Pass devId/groupId + type] --> B[init loads runtime state]
  B --> C[Cloud/electri/countdown capabilities]
  C --> D[Ready for timer APIs]
import { init } from '@ray-js/electrician-timing-sdk';

await init({
  devId: 'your-device-id',
  type: 'ele',
  supportCloud: 'auto',
});

2) Cycle timer

flowchart LR
  A[Build cycle timer data] --> B[electri.cycle.add]
  B --> C{Conflict check}
  C -->|Pass| D[Publish DP / success]
  C -->|Conflict| E[Return conflict + validateData]
  C -->|Cancel| F[cancel]
import { electri } from '@ray-js/electrician-timing-sdk';

await electri.cycle.add(
  {
    id: -1,
    type: 'sdk_cycle',
    startTime: 8 * 60,
    endTime: 18 * 60,
    week: [1, 1, 1, 1, 1, 0, 0],
    status: true,
    actions: [{ code: 'switch_1' }],
    onHoldTime: 100,
    offHoldTime: 100,
  },
  { useDefaultModal: true }
);

3) Random timer

flowchart LR
  A[Build random timer data] --> B[electri.random.add]
  B --> C{Conflict check}
  C -->|Pass| D[Publish DP / success]
  C -->|Conflict| E[Return conflict + validateData]
  C -->|Cancel| F[cancel]
import { electri } from '@ray-js/electrician-timing-sdk';

await electri.random.add(
  {
    id: -1,
    type: 'sdk_random',
    startTime: 10 * 60,
    endTime: 12 * 60,
    week: [0, 0, 1, 0, 0, 0, 0],
    status: true,
    actions: [{ code: 'switch_1' }],
  },
  { useDefaultModal: true }
);

4) Inching timer

flowchart LR
  A[Build inching data time + actions] --> B[electri.inching.add]
  B --> C{Conflict check}
  C -->|Pass| D[Publish DP / success]
  C -->|Conflict| E[Return conflict + validateData]
  C -->|Cancel| F[cancel]
import { electri } from '@ray-js/electrician-timing-sdk';

await electri.inching.add(
  {
    id: -1,
    type: 'sdk_inching',
    time: 300,
    status: true,
    actions: [{ code: 'switch_1' }],
  },
  { useDefaultModal: true }
);

5) Cloud timer (single + batch)

flowchart TB
  subgraph single["Single add"]
    S1[addCloudTimer single data] --> S2{Conflict check}
    S2 --> S3[success / cancel / conflict]
  end
  subgraph batch["Batch add"]
    B1[batchAddCloudTimer data list] --> B2{Conflict check}
    B2 --> B3[success / cancel / conflict]
  end
import { addCloudTimer, batchAddCloudTimer } from '@ray-js/electrician-timing-sdk';

await addCloudTimer(
  {
    aliasName: 'morning',
    startTime: 9 * 60,
    week: [1, 1, 1, 1, 1, 1, 1],
    status: true,
    actions: [{ code: 'switch_1', value: true }],
    isAppPush: false,
  },
  { useDefaultModal: true }
);

await batchAddCloudTimer(
  [
    {
      aliasName: 'workday',
      startTime: 8 * 60,
      week: [1, 1, 1, 1, 1, 0, 0],
      status: true,
      actions: [{ code: 'switch_1', value: true }],
      isAppPush: false,
    },
  ],
  { useDefaultModal: true }
);

6) Countdown

flowchart LR
  A[createCountdown code + duration] --> B{Conflict check}
  B -->|Pass| C[Publish countdown DP]
  B -->|Conflict/Cancel| D[Return result]
  C --> E[Optional: cancelCountdown]
import { createCountdown, cancelCountdown } from '@ray-js/electrician-timing-sdk';

await createCountdown('countdown_1', 1800, { useDefaultModal: true });
await cancelCountdown('countdown_1');

7) Custom DP timer

flowchart TB
  A[addDpTimer code + data] --> B{Conflict check}
  B --> C[success / conflict]
  C --> D[updateDpTimerStatus enable/disable]
  C --> E[removeDpTimer delete]
  D --> F[Maintain lifecycle]
  E --> F
import { addDpTimer, updateDpTimerStatus, removeDpTimer } from '@ray-js/electrician-timing-sdk';

await addDpTimer(
  'cycle_timing',
  {
    id: 1,
    type: 'sdk_cycle',
    startTime: 360,
    endTime: 480,
    week: [1, 1, 1, 1, 1, 1, 1],
    status: true,
    actions: [{ code: 'switch_1' }],
    onHoldTime: 60,
    offHoldTime: 60,
  },
  { useDefaultModal: true }
);

await updateDpTimerStatus('cycle_timing', 1, false);
await removeDpTimer('cycle_timing', 1);

Recommended Timing Workflow

Use this order for stable behavior:

  1. Initialize with init(options) before any timer API call.
  2. Check capability (cloud/electri/countdown) based on product needs.
  3. Create timer via one of: electri.*, addCloudTimer, createCountdown, addDpTimer.
  4. Handle conflict result:
    • success: done.
    • cancel: user canceled conflict handling.
    • { conflict, validateData }: continue with your conflict UI/process.
  5. Maintain timer lifecycle with update* and update*Status.
  6. Listen for updates using onConflictChange and onCloudUpdate.
  7. Cleanup with off* listeners and destroy() when leaving page/scene.

Conflict-related return values

  • success: operation completed
  • cancel: user canceled conflict handling
  • pass: internal "validated, continue" state
  • { conflict, validateData }: conflict detail payload

Error Codes

| errCode | errMsg | | --- | --- | | 1001 | Supports a maximum of timers | | 1002 | Timer dp config not found | | 1003 | Timer id not found | | 1004 | Cloud timer not supported | | 1005 | Countdown cancel value is missing | | 1006 | Countdown config not found | | 1007 | DP schema not found | | 1008 | Invalid value | | 1009 | Cycle start time and end time cannot be equal | | 1010 | Loop unit duration cannot exceed total duration | | 1011 | Random timer duration must be at least 30 minutes |

Notes

  • Cloud timer is not supported in group mode by default.
  • Conflict validation happens on enable-path operations (create/update-enable/enable status).
  • For enum-based countdown total values, configure totalRange and cancelValue correctly.
  • combineSameData changes how same-content timers across channels are merged.