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

@etm-professional-control/oa-rx-js-api

v8.2.12

Published

This package is part of the development kit for creating individual widgets for the WinCC Open Architecture Dashboard with Angular and Nx.

Downloads

819

Readme

OA RxJS API

WebSocket API client for WinCC Open Architecture backend communication.

Overview

This library provides a reactive (RxJS) interface for web applications to communicate with WinCC OA backends via WebSocket. It handles:

  • Real-time data subscriptions (dpConnect, dpQueryConnect)
  • Read/query datapoint values and metadata (dpGet, dpQuery, dpNames)
  • Write datapoint values (dpSet)
  • Authentication and session management
  • Shared Worker support for multi-tab scenarios

Installation

npm install @etm-professional-control/oa-rx-js-api

Package Exports

| Export | Description | | ------------------------------------------------------ | ------------------------------------- | | @etm-professional-control/oa-rx-js-api | Main API client | | @etm-professional-control/oa-rx-js-api/worker | Shared Worker implementation | | @etm-professional-control/oa-rx-js-api/serviceworker | Service Worker for offline support | | @etm-professional-control/oa-rx-js-api/loader | Dynamic loader for API initialization |

Basic Usage

The API requires a SharedWorker factory function:

import { OaRxJsApi, SharedWorker } from '@etm-professional-control/oa-rx-js-api';

const sharedWorkerFactory = () =>
  new SharedWorker(new URL('@etm-professional-control/oa-rx-js-api/worker', import.meta.url), {
    type: 'module',
    name: 'WinCCOA oa-rx-js-api worker',
    // useWebWorker: true falls back to a per-tab Worker. Set false to use a
    // real SharedWorker for cross-tab connection sharing - real SharedWorkers
    // require a fully trusted certificate (warnings clicked through per-origin
    // do NOT propagate to a SharedWorker context).
    useWebWorker: true
  });

const api = new OaRxJsApi(sharedWorkerFactory);

The WinCC OA WebServer exposes /WebUI_Token, which mints a short-lived JWT for the WSS connection. Credentials are sent as an HTTP Basic Auth header; the response body is the token as plain text. Append ?dbrw to request a write-enabled token (required for dpSet).

const token = await fetch(`${https}/WebUI_Token`, {
  headers: { Authorization: 'Basic ' + btoa(`${user}:${pass}`) },
  credentials: 'include',
  cache: 'no-store'
}).then((r) => (r.ok ? r.text() : Promise.reject(`HTTP/${r.status}`)));

Tokens are short-lived - fetch a fresh one on reconnect.

/WebUI_Settings returns the project's runtime configuration: which auth method is active, which locales are available, which WSS servers to use.

const settings = await fetch(`${HTTPS}/WebUI_Settings`).then((r) => r.json());
// {
//   authorizationMethod: 'None' | 'basic',
//   languages: ['en_US.utf8', 'de_AT.utf8', ...],
//   wssServers: [{ hostName, port, resource }, ...],
//   wssServersGenerated: boolean,
//   useSharedWorker: boolean
// }

The exported electWebsocketUrl helper derives the correct wss:// URL(s) from these settings, the JWT payload, and the API origin:

import { electWebsocketUrl } from '@etm-professional-control/oa-rx-js-api';

const wssUrls = electWebsocketUrl(settings.wssServers, settings.wssServersGenerated, token, HTTPS);

Connect to the WinCC OA backend with the token from above:

api
  .connect(
    wssUrls, // from electWebsocketUrl(...)
    token, // from getWebUiToken(...)
    {
      // Service-type UUIDs declare the requested role / license. Common values:
      //   'b1733f6d-0075-47ab-9bcc-71b464ed2fda' - Reader (read-only)
      //   '9b07f826-6a89-49fe-aed0-4af4dd8e9fbd' - Editor (read + dpSet)
      serviceType: 'b1733f6d-0075-47ab-9bcc-71b464ed2fda',
      heartbeatSeconds: 10
    },
    HTTPS, // HTTP API URL
    'en_US.utf8' // locale (must be one of settings.languages)
  )
  .subscribe({
    next: (connectData) => console.log('Connected:', connectData),
    error: (err) => console.error('Connection failed:', err)
  });
// Single DP
api.dpConnect('System1:ExampleDp.value', true).subscribe((data) => {
  console.log('Value:', data.value[0]);
});

// Array of DPs in one subscription
api.dpConnect(['System1:Dp1.', 'System1:Dp2.'], true).subscribe((data) => {
  // emission shape: { dp: string[], value: unknown[] } - positionally aligned
});

The true second argument requests an immediate emission of the current value on subscribe.

Multi-DP caveats:

  • Invalid names fail the whole subscription. If any name in the array does not exist on the backend, the entire observable errors immediately. Use catchError to handle this gracefully.
  • Every emission includes all subscribed DPs. When any subscribed DP changes, the backend emits the current values of all DPs in the subscription - not just the one that changed. This is unlike dpQueryConnect, which emits only the changed rows in updates.

For shared/cached subscriptions across multiple components, use DpConnectHandler from @wincc-oa/wui-oarxjs-data.

For a bulk live view over many DPs at once (e.g. everything matching a wildcard) where one subscription is preferable to N dpConnect calls. Emits the full result set initially, then one changed row per subsequent emission. Maintain a local Map and rebuild your reactive list from it on each emission rather than replacing the whole list:

const dpMap = new Map<string, unknown>();

api.dpQueryConnect("SELECT '_original.._value' FROM 'MyDpType_*.float'", true).subscribe(({ result }) => {
  const dataRows = (result as unknown[][]).slice(1); // skip header row
  for (const row of dataRows) {
    dpMap.set(String(row[0]).replace('System1:', ''), row[1]);
  }
  // rebuild your reactive collection from dpMap
});

Result format:

result[0] = ["", ":_original.._value"]      // column headers
result[1] = ["System1:Foo.float", 42.3]     // data rows
result[2] = ["System1:Bar.float", 11.7]

Limitations:

  • Fires on value changes and DP creation, but NOT on DP deletion. A deleted DP stays in your accumulated Map with its last-known value until the page reloads. If you need delete semantics, use sysConnectDpDpt() (which emits DpEvent.Deleted) or unsubscribe + resubscribe periodically.
  • The first emission is the full result set; design consumers to be re-renderable from the accumulated Map.
// Single DP
api.dpSet(['System1:ExampleDp.value'], 42).subscribe();

// Multiple DPs at once (positional)
api.dpSet(['System1:Valve1.', 'System1:Valve2.'], [1, 0]).subscribe();

| Method | Returns | Best for | | ---------------------------------------- | -------------------------------------------- | ---------------------------------------------------------- | | dpGetPeriod(start, end, count, dpName) | Raw { data: number[], dataTime: number[] } | One-off historic queries; raw arrays for your own charting | | dpQuery('… TIMERANGE …') | SQL result table | Custom historic queries when you need SQL aggregations | | dpQueryConnect('SELECT … FROM …') | Initial full result, then per-row updates | Live tables / lists derived from a SQL query (see above) |

dpGetPeriod returns { data: number[], dataTime: number[] } for a single DP, or { data: number[][], dataTime: Date[][] } for an array of DPs. Not compatible with the wui-widget-trend series format - for trend widgets, use wui-context-generator with fetchMethod: 'historic' (which wraps dpQuery in the right shape; see @wincc-oa/wui-oarxjs-context README).

// Get description
api.dpGetDescription(['System1:ExampleDp'], 2).subscribe((description) => console.log('Description:', description));

// Get unit
api.dpGetUnit(['System1:ExampleDp']).subscribe((unit) => console.log('Unit:', unit));
api
  .customCommand<{ min: number; max: number }>('etm.core.ext.dpGetPvRange', {
    dpName: ['System1:ExampleDp']
  })
  .subscribe((result) => {
    console.log('Range:', result.min, '-', result.max);
  });

Troubleshooting

CORS errors when SPA and OA WebServer are on different origins. Configure OA's [httpServer] section using httpHeader = "Access-Control-Allow-Origin: <origin>" and httpHeader = "Access-Control-Allow-Headers: authorization". Do not use the accessControlAllowOrigin config key - it sets the header only on preflight responses, not on the actual response.

Documentation

For comprehensive usage information, see the WinCC OA Documentation and the oa-rx-js-api Documentation

License

MIT