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

iplayabc-iot-center-sdk

v0.1.0

Published

IoT Center SDK for Huawei cloud device commands with bootstrap config cache

Readme

iplayabc-iot-center-sdk

Client SDK for sending Huawei IoT device commands. Bootstrap configuration (Huawei AK/SK, product IDs) is fetched from iot-center and cached locally for disaster recovery.

Install

npm install iplayabc-iot-center-sdk iplayabc-speaker-iot-sdk

| Package | Role | |---------|------| | iplayabc-iot-center-sdk | Bootstrap, cache, domain API | | iplayabc-speaker-iot-sdk | Peer dependency — Huawei C2D sender | | ioredis | Optional — Redis cache for wifi/power lists |

Monorepo local link (before publish):

"iplayabc-iot-center-sdk": "file:../iot-center-v3/iplayabc-iot-center-sdk"

Requires Node.js >= 22.21.0.

Module formats

The package ships CJS and ESM:

| Field | File | |-------|------| | require() | dist/index.js | | import | dist/index.mjs |

TypeScript resolves types via exports (index.d.ts / index.d.mts).

Quick start

import { IotCenterSdk, IotCenterSdkError, SdkErrorCode } from 'iplayabc-iot-center-sdk';

const sdk = new IotCenterSdk({
  endpoint: 'https://iot.example.com', // iot-center root URL, no /api/v1 suffix
  licenseKey: 'iot_xxxxxxxx',          // License API Key from iot-center admin
});

await sdk.whenReady();

await sdk.volume.set('speaker-device-id', 5);
await sdk.wifi.add('speaker-device-id', { ssid: 'classroom', password: 'secret' });

Lifecycle

| Event | Behavior | |-------|----------| | new IotCenterSdk(...) | Starts bootstrap immediately (internal ready promise) | | await sdk.whenReady() | Waits for remote config or local fallback cache | | Domain methods (sdk.wifi.add, etc.) | Auto-await ready before sending commands | | Bootstrap fails with no cache | whenReady() rejects; subsequent calls fail |

Read-only state:

sdk.isReady       // boolean — Huawei sender configured
sdk.configSource  // 'remote' | 'cache' | null
sdk.configHash    // string | null — from bootstrap response

Config refresh

iot-center product settings can change without restarting your service.

Manual refresh

const result = await sdk.refreshConfig();
// { updated: boolean, configHash: string | null, configSource: 'remote' | 'cache' | null }

Call this after iot-center updates Huawei credentials. When updated is true, the in-memory sender has been hot-swapped and the local bootstrap cache file was updated.

If the refresh request fails while the SDK is already ready, the current sender is kept and the call rejects.

Lazy refresh on auth failure

Command sends automatically retry once when Huawei returns an auth error (HTTP 401/403 or known IAM/APIGW error codes). The SDK calls refreshConfig() internally, then retries the same command.

You do not need extra code for AK/SK rotation in most cases. For immediate effect after an admin change, still call await sdk.refreshConfig() explicitly.

Options

type IotCenterSdkOptions = {
  endpoint: string;            // Required — iot-center base URL
  licenseKey: string;          // Required — Bearer token (iot_...)
  bootstrapTimeoutMs?: number; // Default 10000
  commandTimeoutMs?: number;   // Reserved — not wired yet
  configPollIntervalMs?: number; // Reserved — not implemented yet
  cache?: {
    dir?: string;              // Default: os.tmpdir()/iot-center-sdk
    redis?: {
      url: string;
      keyPrefix?: string;      // Default: 'iot-center-sdk:'
    };
  };
};

Cache behavior

| Cache | Storage | Purpose | |-------|---------|---------| | Bootstrap config | Always file | Huawei credentials; survives iot-center outage | | WiFi / power lists | File or Redis | Same short-circuit logic as iot-center |

  • Set cache.dir to a persistent path in production (do not rely on system temp alone).
  • Use Redis when running multiple SDK instances so wifi/power list caches stay consistent.

Bootstrap endpoint: GET {endpoint}/api/v1/iot/sdk/bootstrap with Authorization: Bearer {licenseKey}.

API reference

All methods take a Huawei device ID as the first argument.

WiFi — sdk.wifi

await sdk.wifi.list(deviceId);                              // Uses cache when non-empty
await sdk.wifi.add(deviceId, { ssid: 'classroom', password: 'secret' });
await sdk.wifi.remove(deviceId, { ssid: 'classroom' });
await sdk.wifi.switch(deviceId, { ssid: 'classroom' });

Power schedules — sdk.power

await sdk.power.list(deviceId);                             // Uses cache when present
await sdk.power.add(deviceId, { on: 3600, off: 7200, day: [1, 2], enable: true });
await sdk.power.update(deviceId, schedule);
await sdk.power.remove(deviceId, schedule);

PowerSchedule: on / off are Unix seconds; day: [] means a one-time task.

Volume — sdk.volume

await sdk.volume.set(deviceId, 5); // Integer 1–8

Files — sdk.files

await sdk.files.list(deviceId);

Audio — sdk.audio

await sdk.audio.play(deviceId);
await sdk.audio.pause(deviceId);
await sdk.audio.prev(deviceId);
await sdk.audio.next(deviceId);

Shutdown — sdk.shutdown

await sdk.shutdown(deviceId);

Report / firmware — sdk.report / sdk.firmware

await sdk.report.sync(deviceId);
await sdk.firmware.setAutoUpgrade(deviceId, true);
await sdk.firmware.upgrade(deviceId);

Generic commands — sdk.command

await sdk.command.send(deviceId, {
  serviceId: 'BasicData',
  command: 'playControl',
  payload: { action: 'PLAY' },
});

await sdk.command.sendBatch(
  ['device-1', 'device-2'],
  {
    serviceId: 'BasicData',
    command: 'playControl',
    payload: { action: 'PLAY' },
  },
  { taskName: 'batch-play' },
);

Error handling

import {
  IotCenterSdkError,
  SdkErrorCode,
  DeviceCoreError,
  DeviceCoreErrorCode,
} from 'iplayabc-iot-center-sdk';

try {
  await sdk.wifi.add(deviceId, { ssid: 'home', password: 'secret' });
} catch (error) {
  if (error instanceof IotCenterSdkError) {
    // SDK_BOOTSTRAP_TIMEOUT, SDK_LICENSE_INVALID, SDK_NOT_READY, ...
    console.error(error.code, error.message, error.statusCode);
  }
  if (error instanceof DeviceCoreError) {
    // VALIDATION_ERROR, HUAWEI_COMMAND_FAILED (includes statusCode)
    console.error(error.code, error.message);
  }
}

| Phase | Error class | Common codes | |-------|-------------|--------------| | Constructor | IotCenterSdkError | SDK_VALIDATION_ERROR | | Bootstrap | IotCenterSdkError | SDK_BOOTSTRAP_TIMEOUT, SDK_LICENSE_INVALID, SDK_BOOTSTRAP_FAILED, SDK_PLATFORM_UNSUPPORTED | | Validation | DeviceCoreError | VALIDATION_ERROR | | Huawei C2D | DeviceCoreError | HUAWEI_COMMAND_FAILED |

NestJS integration

@Injectable()
export class IotSdkService implements OnModuleInit {
  readonly sdk: IotCenterSdk;

  constructor(private readonly config: ConfigService) {
    this.sdk = new IotCenterSdk({
      endpoint: config.getOrThrow('IOT_CENTER_ENDPOINT'),
      licenseKey: config.getOrThrow('IOT_LICENSE_KEY'),
      cache: {
        dir: config.get('IOT_SDK_CACHE_DIR'),
        redis: config.get('REDIS_URL')
          ? { url: config.get('REDIS_URL')! }
          : undefined,
      },
    });
  }

  async onModuleInit(): Promise<void> {
    await this.sdk.whenReady();
    if (this.sdk.configSource === 'cache') {
      // iot-center unreachable — using last known bootstrap config
    }
  }
}

Use one SDK instance per process per license key (one product).

SDK vs iot-center HTTP

| | HTTP POST /api/v1/iot/device/... | SDK | |---|-----------------------------------|-----| | Auth | Bearer license key | Same (bootstrap only) | | Command path | Center → DB lookup → Huawei | Direct to Huawei | | Device ownership | Center validates license vs device | Not validated — caller must ensure device ID is valid | | WiFi / power cache | MySQL | File or Redis | | Center down | Commands unavailable | Commands work if bootstrap cache exists |

Use HTTP for: device registration, product/license management, strict license–device authorization.

Use SDK for: high-volume device commands from trusted backends (e.g. reading-server).

HTTP → SDK mapping

| HTTP | SDK | |------|-----| | POST .../volume/setting | sdk.volume.set(id, volume) | | POST .../wifi/list etc. | sdk.wifi.list/add/remove/switch | | POST .../power/list etc. | sdk.power.list/add/update/remove | | POST .../files/list | sdk.files.list | | POST .../audio/* | sdk.audio.play/pause/prev/next | | POST .../shutdown | sdk.shutdown | | POST .../report/sync | sdk.report.sync | | POST .../firmware/* | sdk.firmware.setAutoUpgrade/upgrade | | POST .../command | sdk.command.send / sendBatch |

Limitations

  • Huawei only — non-Huawei products throw SDK_PLATFORM_UNSUPPORTED.
  • configPollIntervalMs is declared but not yet implemented (use refreshConfig() or rely on lazy auth refresh for now).
  • sendBatch requires devices under the same product app (Huawei sender constraint); SDK does not re-check DB ownership.
  • No app_id in client config — resolved by iot-center during bootstrap.

Publish

Only iplayabc-iot-center-sdk is published to npm. iplayabc-iot-device-core stays in the monorepo for iot-center and is bundled into the SDK at build time.

yarn build:sdk
yarn test:sdk
cd iplayabc-iot-center-sdk
npm publish --registry=https://registry.npmjs.org

Consumers install:

npm install iplayabc-iot-center-sdk iplayabc-speaker-iot-sdk

Development (monorepo root)

yarn test:sdk
yarn build:sdk   # bundles device-core into SDK dist via tsup