JavaScript Analyzer
Use a JavaScript Analyzer to calculate values with script logic in WEDA Cloud. Bind existing DataStreams to named input objects, return a result, and map fields from that result to output DataStreams.
The shared field example has two scripts: convert 4–20 mA to 0–100 °C, then combine that temperature with another Celsius reading. The final Boolean is true only when both readings are above 80 °C. These scales and conditions are illustrative choices for the scripts.
When to Use JavaScript
Choose type: JavaScript when you need:
- Conditional logic: Validate a reading or choose a result based on conditions.
- Several inputs: Compare or calculate across one to ten input streams.
- Several outputs: Return several fields and select a result for each destination.
Transfer Function shows the standalone expression version of the conversion. Multilayer Analyzer explains how the JavaScript stages connect. Prepare the inputs and unbound destinations using Analyzer Basics.
Read Input Values and Timestamps
Each inputParameters entry binds a stream ID to a parameterName. In the script, that name is an object:
| Field | Meaning | Example access |
|---|---|---|
value | Incoming sample value. | temperature_a.value |
val | Alias for value. | temperature_a.val |
timestamp | Source sample time as Unix milliseconds. | temperature_a.timestamp |
ts | Alias for timestamp. | temperature_a.ts |
Read analog_current.value for the conversion and temperature_a.value / temperature_b.value for comparison. Choose valid JavaScript variable names and reference every declared input.
With two or more inputs, Multi-Input Processing controls waiting, timeout and incomplete-input handling. The two samples can have different source timestamps; a combined input set does not guarantee simultaneous sampling.
Write and Return a Result
Store a JavaScript function body in config.script, including the return that supplies its result.
Stage A: Convert current to temperature. The input already reports mA. This example skips values outside its defined 4–20 mA range:
const current = analog_current.value;
if (!Number.isFinite(current) || current < 4 || current > 20) {
return null;
}
return {
temperature: (current - 4) * 100 / 16,
timestamp: analog_current.timestamp
};
The result contains a numeric temperature and the original sample timestamp. The scale maps 4 mA to 0 °C and 20 mA to 100 °C; change it for your sensor's actual calibration.
Stage B: Check whether both temperatures are abnormal. Its inputs are Celsius values:
const a = temperature_a.value;
const b = temperature_b.value;
if (!Number.isFinite(a) || !Number.isFinite(b)) {
return null;
}
const aAbnormal = a > 80;
const bAbnormal = b > 80;
return {
aAbnormal,
bAbnormal,
bothAbnormal: aAbnormal && bAbnormal,
timestamp: Math.max(temperature_a.timestamp, temperature_b.timestamp)
};
- Use AND:
bothAbnormalis true only whenaAbnormalandbAbnormalare both true. - Keep the boundary explicit: Exactly 80 °C is not abnormal under this example's strict
> 80rule. - Choose the result time: This script uses the later input timestamp. That does not make the readings simultaneous.
A top-level null or undefined result produces no output for that evaluation. A Boolean false inside the returned result is a valid value to publish. Returned data must be JSON-serializable; a serialization failure prevents that evaluation from producing its result.
Map Results to Output DataStreams
Return field names and output paths must agree:
| Stage | Output destination | valueJsonPath | timeJsonPath | Value type |
|---|---|---|---|---|
| A | temperature_a | $.temperature | $.timestamp | double |
| B | both_abnormal | $.bothAbnormal | $.timestamp | boolean |
B also returns aAbnormal and bAbnormal. The request below selects only bothAbnormal; the other fields are not published to separate streams. To publish individual flags as well, prepare separate Boolean destinations and add output bindings selecting $.aAbnormal and $.bAbnormal, each with its own timestamp path.
The supported JSONPath subset selects properties and array indexes, such as $.bothAbnormal, $.result.value or $.items[0].value. $ selects the complete result. Wildcards, filters, slices and recursive searches are not supported.
Every JavaScript output requires valueJsonPath and timeJsonPath, including an Analyzer with only one output. Match the returned fields exactly. The time path must select an integer Unix-millisecond timestamp, not an ISO date string or a time in seconds.
A syntactically valid path can select a missing field. If a value or timestamp cannot be selected correctly, that output is skipped for the evaluation.
Example: Compare Two Temperature Readings
This complete request configures Stage B. Its two Celsius inputs already exist under the same Org and device:
| Stream | Role | Illustrative ID |
|---|---|---|
temperature_a | Converted temperature, input variable temperature_a | d4444444-4444-4444-8444-444444444444 |
temperature_b | Existing Celsius input, variable temperature_b | b2222222-2222-4222-8222-222222222222 |
both_abnormal | New, unbound Boolean output | e5555555-5555-4555-8555-555555555555 |
Use DataStreams to discover actual IDs and prepare the destination. Keep the two inputs and output on the same device. Use your deployment's API base URL, target Org and Device IDs, and an authorized token. Replace every illustrative ID in this Create Analyzer request:
POST /api/v1/orgs/{orgId}/devices/{deviceId}/analyzers
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN
{
"name": "Both temperatures abnormal",
"description": "Publish true only when both Celsius readings are above 80",
"type": "JavaScript",
"inputParameters": [
{
"parameterName": "temperature_a",
"dataStreamId": "d4444444-4444-4444-8444-444444444444"
},
{
"parameterName": "temperature_b",
"dataStreamId": "b2222222-2222-4222-8222-222222222222"
}
],
"config": {
"script": "const a = temperature_a.value; const b = temperature_b.value; if (!Number.isFinite(a) || !Number.isFinite(b)) { return null; } const aAbnormal = a > 80; const bAbnormal = b > 80; return { aAbnormal, bAbnormal, bothAbnormal: aAbnormal && bAbnormal, timestamp: Math.max(temperature_a.timestamp, temperature_b.timestamp) };"
},
"outputs": [
{
"dataStreamId": "e5555555-5555-4555-8555-555555555555",
"valueJsonPath": "$.bothAbnormal",
"timeJsonPath": "$.timestamp"
}
],
"dataAssemblyPolicy": {
"queueLimit": 100,
"timeoutSeconds": 5,
"onTimeout": "DropPartial"
}
}
The explicit policy uses the default waiting values. Its meaning and timing behavior are covered in Multi-Input Processing.
- After 201 Created, retain
analyzerId. GET the definition and confirm the input IDs, script, paths and effective policy. - Follow Enable and Check the Result. Creation leaves the Analyzer disabled; enabling does not prove an output exists.
- Wait for a complete input set, then query
both_abnormalwith Get Latest Value. InspectdataObject.valueanddataObject.timestamp.
| Temperature A | Temperature B | bothAbnormal |
|---|---|---|
| 81.25 °C | 90 °C | true |
| 75 °C | 90 °C | false |
| 81.25 °C | 75 °C | false |
| 75 °C | 75 °C | false |
| 80 °C | 90 °C | false |
Check the result: The output is a Boolean, and only the first row is true. If A's sample time is 1793001600000 and B's is 1793001601000, the result time is 1793001601000, shown by the latest API as 2026-10-26T08:00:01Z.
Use End-to-End Scenario for the full current-input setup and both JavaScript creation requests. For later changes, disable the affected Analyzer, read back the saved definition, re-enable it and verify new results.
Execution Limits and Troubleshooting
Keep the script small enough to finish within the execution budget:
| Limit | Value | When it applies |
|---|---|---|
| Input DataStreams | 1 to 10 | Checked when configuring the Analyzer. |
| Script length | 10,000 characters maximum | An overlong script is rejected during configuration. |
| Execution time | 2,000 ms by default | Budget for one evaluation. |
| Memory allocation | 4,000,000 bytes, about 4 MB, by default | Allocation budget for one evaluation. |
| Executed statements | 10,000 by default | Statement budget for one evaluation. |
The runtime budgets are deployment-configured; the table shows their defaults. Exceeding an execution budget stops that evaluation without a result. Fix an overlong script before saving; for runtime limits, reduce loops, work and allocations instead of assuming an accepted definition will always finish.
Script state does not persist between evaluations. Do not rely on a global counter, cached array or earlier sample remaining available on the next run. Scripts also cannot call the host application's APIs.
For statistics over a past time range, use DataStreams aggregate queries rather than expecting this two-input calculation to retain a history window.
| Observation | What to check |
|---|---|
| 400 Bad Request while saving | Script syntax, a result-producing return, script length, referenced input names and both paths on every output. |
| Enabled but the expected output has no new value | A complete input set, the script's branches and intentional return null, runtime budgets and the selected output IDs. |
| Only one output is missing | That output's returned field, value path and numeric timestamp path. Another output succeeding does not verify this binding. |
| Time is incorrect or a timestamp path fails | Return Unix milliseconds as an integer, and make timeJsonPath select that field. |
| Results are unexpected | Confirm both input units, numeric values, the > 80 conditions, Boolean AND and the input set being compared. |
| An evaluation exceeds a limit | Simplify the script or reduce its allocations and loop iterations. That failed evaluation supplies no result. |
Query and check every destination you depend on. A saved script, an enabled state or one successful output is not proof that all mappings produced the expected data.