Product: Mingcedar CONTROL
Custom-page SDK API reference | Mingcedar
The SDK provides project HTML with realtime Tags, typed reads and writes, history, alarms, user, permissions, language, navigation, device popups, and table helpers. Project code handles layout and business calculations.
<p class="manual-start-link">New to the SDK? <a href="/en/hqserver/manual/custom-page-tutorial/">Start with the step-by-step tutorial</a>. Use this page to look up parameters and return values.</p><div class="manual-doc-article manual-doc-article-section"><h2 id="custom-page-sdk">SDK: Custom-page SDK API reference</h2>
Native HTML/Vue integration and public API reference. Tag reads and writes use the PPT ReadTag*/WriteTag* names; the system LANG control switches languages.
<div class="doc-card-list doc-tutorial">
<article class="doc-card">
<h3 id="custom-page-sdk-item-1">What the SDK owns</h3>
<p>The SDK provides project HTML with realtime Tags, typed reads and writes, history, alarms, user, permissions, language, navigation, device popups, and table helpers. Project code handles layout and business calculations.</p>
<ul>
<li>Good fits: shift production reports, daily energy reports, equipment health pages, quality trace pages, work-order boards, and customer views.</li>
<li>Page forms: plain HTML, native ES modules, or built Vue pages; ship CSS, charts and components with the project.</li>
<li>Data sources: declared tags, history data, alarm data, and language resources.</li>
<li>Responsibility boundary: Mingcedar provides data and permission boundaries; the project page owns presentation and business calculations.</li>
<li>SDK version: 1.5.0.</li>
</ul>
<p class="doc-step-result"><strong>Expected result</strong> Start with SDK quick start. Except for complete pages/components, snippets assume api, the required DOM elements, and project renderers are ready.</p>
<ul class="doc-related-links">
<li><a href="/en/hqserver/manual/custom-page-tutorial/">Start by reading one Tag</a></li>
</ul>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-2">Runtime origin and permission boundaries</h3>
<p>Project pages run in an iframe on a separate Screen Origin, isolated from the Portal. The bridge validates origin, window, session, and document identity. Pages may use their own origin’s cookies, storage, URL parameters, and ordinary Web Workers, but not Service Workers. They cannot access Portal cookies, administration DOM, or configuration APIs. Data and writes remain subject to page declarations and server authorization.</p>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-3">What page-owned JavaScript can do</h3>
<p>Custom pages may use DOM, CSS, components, chart libraries, and interactions allowed by the browser and iframe sandbox. The SDK governs Mingcedar Host capabilities.</p>
<ul>
<li>Supported: DOM/component logic and <code>alert</code>, <code>confirm</code>, and <code>prompt</code>.</li>
<li>Supported: <code>window.open</code>, form submission, browser navigation, and user-initiated downloads; browser popup and user-activation rules still apply.</li>
<li>Clipboard API is supported only when secure-context, user activation, browser permission, and enterprise-policy requirements are met.</li>
<li>Service Worker registration is unsupported. The page does not receive Portal cookies, login tokens, Admin DOM, configuration APIs, or raw Host service objects.</li>
<li>The page cannot poll a PLC directly; the SDK does not provide alarm acknowledge, shelve, or close operations.</li>
<li><code>api.supports()</code> checks Host Bridge capabilities, not browser API availability or permission.</li>
</ul>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-4">SDK loading is automatic; module imports are optional</h3>
<p>Runtime loads the SDK for published HTML/Vue pages. Call await HQVISU_CUSTOM_PAGE.ready(); no extra script tags are needed.</p>
<ul>
<li>An ES module can import { ready } from "/runtime/hqVisuDesigner/hq-custom-page.mjs"; it reuses the installed SDK.</li>
<li>Only non-injected environments such as a custom Mock need hq-common.js followed by hq-custom-page.js. Loading files does not create runtime permissions.</li>
<li>Static Tag declarations remain in entry HTML. No SDK npm package is required. A Vite dev server or file:// is not the runtime environment.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#005CC5">HQVISU_CUSTOM_PAGE</span><span style="color:#24292E">.</span><span style="color:#6F42C1">ready</span><span style="color:#24292E">().</span><span style="color:#6F42C1">then</span><span style="color:#24292E">(</span><span style="color:#D73A49">async</span><span style="color:#24292E"> (</span><span style="color:#E36209">api</span><span style="color:#24292E">) </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#6A737D"> // Use the examples below here. Reuse this api object.</span></span>
<span class="line"><span style="color:#24292E">}).</span><span style="color:#6F42C1">catch</span><span style="color:#24292E">(</span><span style="color:#E36209">error</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> console.</span><span style="color:#6F42C1">error</span><span style="color:#24292E">(error.message));</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-5">How to declare tag scope</h3>
<p>The server grants page capabilities from supported HTML JSON and data declarations. Use full dotted Tag codes, such as <code><device-code>.<temperature-tag-code></code>, and canonical bit references <code>[0]</code>–<code>[31]</code>. Invalid or over-limit declarations affect capability grants.</p>
<ul>
<li>Supported config block ids: <code>hqvisu-custom-page</code>, <code>hqvisu-custom-page-config</code>, <code>hqvisu-page-config</code>, and <code>hqvisu-dependencies</code>.</li>
<li>Readable aliases: <code>readableTags</code>, <code>tags</code>, <code>tagNames</code>, <code>readTags</code>, and <code>watchTags</code>.</li>
<li>Writable aliases: <code>writableTags</code> and <code>writeTags</code>; matching declarations under <code>outputs</code> are also collected.</li>
<li>Under <code>dependencies</code>, supported fields are <code>tags</code>, <code>tagNames</code>, <code>readableTags</code>, <code>readTags</code>, <code>writableTags</code>, and <code>writeTags</code>.</li>
<li>Values may be arrays or strings separated by newline, comma, or semicolon.</li>
<li>Bit references such as <code><device-code>.<status-word-tag-code>[3]</code> are read-only, for example ReadTagBoolean(name, null). Write a bit with WriteTagBit("<device-code>.<status-word-tag-code>", 3, "set") and declare the parent Tag readable and writable. No write method accepts [3] in its target. History also queries the parent Tag.</li>
<li><code><device-code></code> and <code><tag-code></code> in this manual are placeholders. Replace them with actual project codes before publishing.</li>
<li>ready({ tags }) requests tracking; it does not enlarge server write, history, alarm or Popup scope. Put a controlled Tag that needs readback in both readableTags and writableTags.</li>
</ul>
<div class="manual-code" data-code-language="html"><div class="manual-code-toolbar"><span>HTML</span><span class="manual-code-tools"><small>HTML</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: HTML">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"application/json"</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"hqvisu-custom-page"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#24292E"> "readableTags": [</span></span>
<span class="line"><span style="color:#24292E"> "<device-code>.<temperature-tag-code>",</span></span>
<span class="line"><span style="color:#24292E"> "<device-code>.<pressure-tag-code>",</span></span>
<span class="line"><span style="color:#24292E"> "<device-code>.<run-state-tag-code>",</span></span>
<span class="line"><span style="color:#24292E"> "<device-code>.<output-tag-code>",</span></span>
<span class="line"><span style="color:#24292E"> "<device-code>.<energy-tag-code>",</span></span>
<span class="line"><span style="color:#24292E"> "<device-code>.<setpoint-tag-code>"</span></span>
<span class="line"><span style="color:#24292E"> ],</span></span>
<span class="line"><span style="color:#24292E"> "writableTags": ["<device-code>.<setpoint-tag-code>"]</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-6">Dynamic exact-on-use Tags from parameters, loops, and composition</h3>
<p>Use <code>exact-on-use</code> for full Tag codes built from runtime parameters, loops, or functions. Each exact Tag is checked for format, existence, enabled state, page session, and read/write rules. Wildcards, catalog enumeration, and unlimited access are unsupported.</p>
<ul>
<li>liveTagRead.maxActiveTags accepts 1–8000. This small-page example budgets 64 read Tags and 16 write Tags. Size it for actual use; quotas are not a performance guarantee.</li>
<li>The formal <code>liveTagWrite.maxActiveTags</code> limit is 2000.</li>
<li>Good fits: select a device from page parameters, loop over a known device list, build a full Tag in a function, or bind a component to a business object.</li>
<li>Unsupported: <code><device-code>.*</code> search, Tag catalog reads, device enumeration, or bypassing per-Tag Host validation.</li>
<li>Dynamic declaration expands exact live read/write only. History, alarms, device popups, and prefetch retain their own scopes.</li>
<li>Use static declarations for fixed Tags to support initial prefetch, subscriptions, and scope management.</li>
</ul>
<div class="manual-code" data-code-language="html"><div class="manual-code-toolbar"><span>HTML</span><span class="manual-code-tools"><small>HTML</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: HTML">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"application/json"</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"hqvisu-custom-page"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#24292E"> "readableTags": ["<device-code>.<run-state-tag-code>"],</span></span>
<span class="line"><span style="color:#24292E"> "dependencies": {</span></span>
<span class="line"><span style="color:#24292E"> "runtimeAccess": {</span></span>
<span class="line"><span style="color:#24292E"> "liveTagRead": { "mode": "exact-on-use", "maxActiveTags": 64 },</span></span>
<span class="line"><span style="color:#24292E"> "liveTagWrite": { "mode": "exact-on-use", "maxActiveTags": 16 }</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">const deviceCode = new URLSearchParams(location.search).get("device");</span></span>
<span class="line"><span style="color:#24292E">const temperatureTag = `${deviceCode}.<</span><span style="color:#22863A">temperature-tag-code</span><span style="color:#24292E">>`;</span></span>
<span class="line"><span style="color:#24292E">api.track(temperatureTag);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-7">ready() and resource lifetime</h3>
<p>HQVISU_CUSTOM_PAGE.ready(options?), ready(callback), and ESM ready(options?) return Promise<PageApi>. Initialization confirms the bridge, not GOOD values for every Tag. One SDK belongs to the HTML document and is reused across Vue mounts.</p>
<ul>
<li>tags: string[] adds tracked Tags; prefer static declarations. Display initialization errors with try/catch; do not loop ready retries.</li>
<li>subscribe, alarms.subscribe, language.onChange and bindJsonTable return cleanup functions. Call each component-owned cleanup on unmount.</li>
<li>destroy() permanently ends the document. A later ready() in that document rejects with RUNTIME_PAGE_DESTROYED. Do not destroy on ordinary Vue unmount; only end the whole document.</li>
<li>pagehide with persisted=true means BFCache retention, not permanent destruction. An unmounted component must not register subscriptions after async setup resolves.</li>
<li>timeoutMs controls the host wait (10000 ms in the examples), not PLC read/write timeouts. Check each Tag quality after ready resolves.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> api</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#005CC5"> HQVISU_CUSTOM_PAGE</span><span style="color:#24292E">.</span><span style="color:#6F42C1">ready</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> stop</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">subscribe</span><span style="color:#24292E">([</span><span style="color:#032F62">"<device-code>.<tag-code>"</span><span style="color:#24292E">], renderValue);</span></span>
<span class="line"><span style="color:#6A737D">// renderValue is your UI renderer.</span></span>
<span class="line"><span style="color:#24292E">window.</span><span style="color:#6F42C1">addEventListener</span><span style="color:#24292E">(</span><span style="color:#032F62">"pagehide"</span><span style="color:#24292E">, </span><span style="color:#E36209">event</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">event.persisted) { </span><span style="color:#6F42C1">stop</span><span style="color:#24292E">(); api.</span><span style="color:#6F42C1">destroy</span><span style="color:#24292E">(); }</span></span>
<span class="line"><span style="color:#24292E">});</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-8">SDK global-object API</h3>
<p>The global object is <code>HQVISU_CUSTOM_PAGE</code>, with compatibility alias <code>HQVisuCustomPage</code>. These members are available before <code>ready()</code>; page data requires a ready Host connection.</p>
<ul>
<li><code>version</code>: string <code>1.5.0</code>.</li>
<li><code>ready(options | callback)</code>: returns <code>Promise<PageApi></code>.</li>
<li><code>whenVisualReady({ timeoutMs? })</code>: waits for the first authorized visual snapshot and returns <code>Promise<void></code>; ordinary pages use <code>ready()</code> to obtain the api.</li>
<li><code>destroy()</code>: permanently ends this document's SDK connection. On component unmount, cancel only subscriptions owned by that component.</li>
<li><code>normalizeTagName(value)</code>: stringifies, trims, and removes one matching outer quote pair; returns a string.</li>
<li><code>tagList(value)</code>: accepts strings, nested arrays, or a scalar; splits newline/comma/semicolon text, normalizes, drops empty values and legacy placeholders shaped like <code><TagName></code>, and deduplicates; returns <code>string[]</code>.</li>
<li><code>renderTable(target, rows, options)</code>: renders a lightweight HTML table and returns <code>{ rowCount, columnCount }</code>.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> tag</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "<device-code>.<tag-code>"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> normalized</span><span style="color:#D73A49"> =</span><span style="color:#005CC5"> HQVISU_CUSTOM_PAGE</span><span style="color:#24292E">.</span><span style="color:#6F42C1">normalizeTagName</span><span style="color:#24292E">(tag);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> tags</span><span style="color:#D73A49"> =</span><span style="color:#005CC5"> HQVISU_CUSTOM_PAGE</span><span style="color:#24292E">.</span><span style="color:#6F42C1">tagList</span><span style="color:#24292E">([tag, tag]);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-9">PageApi root object and capability matrix</h3>
<p>Use the root methods returned by <code>ready()</code> and stable namespaces such as <code>api.auth</code>, <code>api.permissions</code>, <code>api.history</code>, and <code>api.alarms</code>. Low-level <code>api.host</code> interfaces vary by transport.</p>
<ul>
<li>Properties: <code>version</code>, <code>bridgeVersion</code>, <code>schemaRevision</code>, frozen <code>capabilities</code>, and low-level <code>host</code>.</li>
<li>Discovery: <code>getCapabilities()</code> returns a mutable copy; <code>supports(name)</code> returns a boolean and false for an unknown capability.</li>
<li>Namespaces: <code>auth</code>, <code>permissions</code>, <code>language</code>, <code>history</code>, <code>alarms</code>, <code>navigation</code>, and <code>dialog</code>.</li>
<li>Root methods: <code>track</code>, <code>subscribe</code>, all read/write methods, <code>queryHistory</code>, <code>translate</code> / <code>t</code>, <code>renderTable</code>, <code>bindJsonTable</code>, and <code>destroy</code>.</li>
<li>Capability names: <code>page.parameters</code>, <code>tag.read</code>, <code>tag.write</code>, <code>history.read</code>, <code>user.read</code>, <code>navigation</code>, <code>popup</code>, <code>alarm.read</code>, and <code>log.write</code>.</li>
<li><code>supports()</code> indicates transport exposure only; it is not user permission and does not enlarge page scope.</li>
<li>log.write is optional: when supports("log.write") is false, do not call it or report logging success. PageApi exposes no standalone root log.write API.</li>
</ul>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-10">Current-user and permission API</h3>
<p>The stable session result is <code>{ user, permissions }</code>. For an authenticated user, <code>user</code> contains <code>username</code>, <code>displayName</code>, and <code>permissions</code>. An anonymous direct Screen Gateway returns <code>user: null</code> and an empty permission array. Tokens, cookies, roles, and page-capability secrets are never exposed.</p>
<ul>
<li><code>api.auth.getSession()</code>: session snapshot. Use <code>await api.auth.getSession()</code> for both synchronous and asynchronous Hosts.</li>
<li>api.auth.getCurrentUser() / getUser() → user snapshot or null; getUsername() → username; getDisplayName() → display name, falling back to username.</li>
<li><code>api.auth.hasPermission(code)</code> / <code>api.permissions.has(code)</code>: returns <code>Promise<boolean></code>.</li>
<li><code>api.auth.requirePermission(code)</code> / <code>api.permissions.require(code)</code>: resolves true when allowed and rejects when denied.</li>
<li>These methods belong to PageApi.auth. Prefer this public wrapper over transport-specific objects under api.host.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> api</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#005CC5"> HQVISU_CUSTOM_PAGE</span><span style="color:#24292E">.</span><span style="color:#6F42C1">ready</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> session</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.auth.</span><span style="color:#6F42C1">getSession</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> canWrite</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag.write"</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> &&</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.permissions.</span><span style="color:#6F42C1">has</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag:write"</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1">renderCurrentUser</span><span style="color:#24292E">(session.user);</span></span>
<span class="line"><span style="color:#6F42C1">setWriteControlsEnabled</span><span style="color:#24292E">(canWrite);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-11">Read API: the same names as PPT</h3>
<p>Reads synchronously return the current collected snapshot without polling a PLC. Typed reads with explicit fallback return it for non-GOOD/missing quality or conversion failure; omitting fallback is not a quality check. ReadTag returns raw values without typed conversion or quality filtering. Use ReadTagDouble(name, null) and show quality to distinguish a real zero from unavailable data.</p>
<ul>
<li><code>ReadTag(name, fallback?)</code> → raw snapshot value, without typed conversion.</li>
<li><code>ReadTagBoolean(name, fallback?)</code> → boolean; recognizes booleans, finite numbers and true/false, 1/0, on/off, yes/no text; default false.</li>
<li><code>ReadTagInteger(name, fallback?)</code> → safe integer truncated toward zero; default 0. Preserve large integers as raw/text instead of coercing to Number.</li>
<li><code>ReadTagDouble(name, fallback?)</code> → finite number; default 0.</li>
<li><code>ReadTagString(name, fallback?)</code> → display text, preferring displayValue and numeric precision/round rules; default empty string. GOOD empty strings are valid.</li>
<li><code>ReadTagJson(name, fallback?)</code> → parsed JSON value; default null. It may be an object, array or scalar.</li>
<li><code>ReadTagDateTime(name, fallback?)</code> → valid Date or fallback; default null. See the UTC-field rule for PLC calendars below.</li>
<li><code>ReadCalculatedTag(name, fallback?)</code> → calculated number/boolean with calculated-source and GOOD checks; default null. Use the calculated Tag code and declare its read scope.</li>
<li><code>ReadTagQuality(name, fallback?)</code> → <code>"good" | "bad"</code>; <code>ReadTagQualityGood(name)</code> → <code>boolean</code>. Missing quality defaults to bad; do not pass good to conceal missing quality.</li>
<li><code>getMeta(name)</code> → metadata object. dataType, qualityDetail, timestamp, source, and other fields depend on the supplied snapshot; stale/pending diagnostics do not add a third ReadTagQuality result.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> name</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "<device-code>.<tag-code>"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> raw</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTag</span><span style="color:#24292E">(name);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> quality</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagQuality</span><span style="color:#24292E">(name); </span><span style="color:#6A737D">// "good" | "bad"</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> usable</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagQualityGood</span><span style="color:#24292E">(name);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> value</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagDouble</span><span style="color:#24292E">(name, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"value"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> usable </span><span style="color:#D73A49">&&</span><span style="color:#24292E"> value </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> null</span><span style="color:#D73A49"> ?</span><span style="color:#6F42C1"> String</span><span style="color:#24292E">(value) </span><span style="color:#D73A49">:</span><span style="color:#032F62"> "—"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> meta</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">getMeta</span><span style="color:#24292E">(name); </span><span style="color:#6A737D">// source, dataType, ts/persistenceVersion when supplied</span></span>
<span class="line"><span style="color:#6A737D">// Choose the reader matching each Tag's type; do not cast one Tag to every type.</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> running</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagBoolean</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<run-state-tag-code>"</span><span style="color:#24292E">, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> count</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagInteger</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<count-tag-code>"</span><span style="color:#24292E">, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> label</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagString</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<text-tag-code>"</span><span style="color:#24292E">, </span><span style="color:#032F62">"—"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> rows</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagJson</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<json-tag-code>"</span><span style="color:#24292E">, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> date</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagDateTime</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<datetime-tag-code>"</span><span style="color:#24292E">, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> calculated</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadCalculatedTag</span><span style="color:#24292E">(</span><span style="color:#032F62">"<calculated-tag-code>"</span><span style="color:#24292E">, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-12">Subscribe to realtime changes</h3>
<p><code>track(tags)</code> normalizes and tracks Tags, returning deduplicated names. <code>subscribe(tags, callback)</code> returns an idempotent unsubscribe function and calls <code>(items, api)</code>. Read the snapshot for initial rendering, then subscribe to changes.</p>
<ul>
<li>Each change includes <code>tagName</code> and <code>value</code>; <code>fullCode</code>, <code>quality</code>, <code>timestamp</code>, <code>source</code>, and other metadata are provided when available.</li>
<li>A page may hold at most 8000 active unique Tags. Invalid, missing, disabled, or rejected candidates do not permanently consume the budget.</li>
<li>The wire boundary carries at most 256 Tags per batch and the SDK runs up to four batches concurrently; a large page still uses one logical realtime subscription and one WebSocket.</li>
<li>Use the callback's second argument to read a consistent current snapshot; group high-frequency Tags and update UI in one pass.</li>
<li>Unsubscribe when scope changes or the component exits. <code>destroy()</code> cleans up remaining Tag and alarm subscriptions.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> tag</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "<device-code>.<process-tag-code>"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">function</span><span style="color:#6F42C1"> renderValue</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> value</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagDouble</span><span style="color:#24292E">(tag, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"value"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span></span>
<span class="line"><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagQualityGood</span><span style="color:#24292E">(tag) </span><span style="color:#D73A49">&&</span><span style="color:#24292E"> value </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> null</span><span style="color:#D73A49"> ?</span><span style="color:#6F42C1"> String</span><span style="color:#24292E">(value) </span><span style="color:#D73A49">:</span><span style="color:#032F62"> "—"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#6F42C1">renderValue</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> stopRealtime</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">subscribe</span><span style="color:#24292E">([tag], renderValue);</span></span>
<span class="line"><span style="color:#6A737D">// stopRealtime() when this view no longer needs the subscription.</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-13">Write API: parameters, prerequisites and returns</h3>
<p>Typed writes use (name, value, options?). Always await the result: a Host may return synchronously, a Promise, or undefined; do not assume .then() exists. Writes require tag.write capability, admitted scope, user tag:write permission, Tag control permission and valid quality; execution rechecks Owner and user action.</p>
<ul>
<li><code>WriteTagBoolean(name, value, options?)</code>: use boolean, 0/1 or explicit boolean text; "false" means false, not JavaScript truthiness.</li>
<li><code>WriteTagInteger(name, value, options?)</code>: number must be a safe integer; BigInt or decimal integer text preserves precision. The service checks the actual device range.</li>
<li><code>WriteTagDouble(name, value, options?)</code>: finite numeric value or valid numeric text; reject NaN, Infinity and blank form input.</li>
<li><code>WriteTagString(name, value, options?)</code>: text; empty string is an intentional value, still subject to PLC encoding and length limits.</li>
<li><code>WriteTagJson(name, value, options?)</code>: pass the actual JSON object/array/value, without JSON.stringify on an object. Restore original empty text with WriteTagString.</li>
<li><code>WriteTagDateTime(name, value, options?)</code>: Date or date text; PLC calendar metadata selects serialization, as explained below.</li>
<li><code>WriteTag(name, value, options?)</code> / <code>WriteTag({ TagName, Value, ValueType, ... })</code>: generic entry, also accepting a camelCase payload; false, 0, empty text and JSON values are retained.</li>
<li><code>WriteTagBit(name, bitIndex, bitAction, options?)</code>: name is the parent Tag, with GOOD quality and a supported 8/16/32-bit integer type. The index must fit that type and be within 0–31; actions are set/reset/toggle. Blank indexes are invalid. Protocol support varies; Modbus requires confirmed FC22 support, with no toggle or full-word read/modify/write fallback.</li>
<li>options.reason supplies an operation reason; typed helpers fix their type and caller source. Precheck with await api.auth.hasPermission("tag:write"); capability tag.write is not the permission code.</li>
<li>Initiate from a user click handler. Page confirmation does not replace configured Host confirmation. Do not write automatically from timers, loading or subscriptions.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>Bind a write button inside ready (id="write", result id="result")</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: Bind a write button inside ready (id="write", result id="result")">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> button</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"write"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> output</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"result"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">button.disabled </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> !</span><span style="color:#24292E">(api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag.write"</span><span style="color:#24292E">) </span><span style="color:#D73A49">&&</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.auth.</span><span style="color:#6F42C1">hasPermission</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag:write"</span><span style="color:#24292E">));</span></span>
<span class="line"><span style="color:#24292E">button.</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> async</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (button.disabled) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> tag</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "<device-code>.<tag-code>"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">api.</span><span style="color:#6F42C1">ReadTagQualityGood</span><span style="color:#24292E">(tag)) { output.textContent </span><span style="color:#D73A49">=</span><span style="color:#032F62"> "Tag unavailable"</span><span style="color:#24292E">; </span><span style="color:#D73A49">return</span><span style="color:#24292E">; }</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#6F42C1">confirm</span><span style="color:#24292E">(</span><span style="color:#032F62">"Write 12.5?"</span><span style="color:#24292E">)) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> button.disabled </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> result</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">WriteTagDouble</span><span style="color:#24292E">(tag, </span><span style="color:#005CC5">12.5</span><span style="color:#24292E">, { reason: </span><span style="color:#032F62">"Operator setpoint update"</span><span style="color:#24292E"> });</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> done</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> result?.accepted </span><span style="color:#D73A49">===</span><span style="color:#005CC5"> true</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.status </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "done"</span></span>
<span class="line"><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.outcomeUncertain </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.operatorVerificationRequired </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span></span>
<span class="line"><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.transportOnly </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> output.textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (done </span><span style="color:#D73A49">?</span><span style="color:#032F62"> "Command completed; verify collected value: "</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> "Result unconfirmed; verify before another write: "</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> +</span><span style="color:#005CC5"> JSON</span><span style="color:#24292E">.</span><span style="color:#6F42C1">stringify</span><span style="color:#24292E">(result </span><span style="color:#D73A49">??</span><span style="color:#005CC5"> null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) {</span></span>
<span class="line"><span style="color:#24292E"> output.textContent </span><span style="color:#D73A49">=</span><span style="color:#032F62"> "Result unconfirmed: "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> error.message </span><span style="color:#D73A49">+</span><span style="color:#032F62"> " "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> (error.commandId </span><span style="color:#D73A49">||</span><span style="color:#24292E"> error.result?.commandId </span><span style="color:#D73A49">||</span><span style="color:#032F62"> ""</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#6A737D"> // Reconcile the result before another write. Never retry automatically.</span></span>
<span class="line"><span style="color:#24292E">};</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-14">Classify the command, then verify normal collected readback</h3>
<p>accepted=true means admitted. Inspect status, commandId, journalId and uncertainty fields. currentValue may be a runtime-write echo and is not proof of later normal collection. In this fragment nextValue and reason come from validated page inputs.</p>
<ul>
<li>done: command completed; verify normal collected source, newer timestamp/version and quality before confirming the device value. Do not overwrite live display with the target.</li>
<li>proxied: inspect actual completion evidence after forwarding. sent or transportOnly=true proves transport only, not PLC execution.</li>
<li>readback_failed/readback_mismatch/latest_superseded: show reconciliation required, not a confirmed collected value.</li>
<li>pending_verification/outcome_uncertain, timeout, disconnect, undefined or unknown status: keep operations blocked, retain commandId, reconcile records and actual values, without automatic retry.</li>
<li>accepted=false, cancelled, failed or page_changed: no confirmed completion; show the actual result.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D">// Call from the existing async click handler, after input/quality/permission checks.</span></span>
<span class="line"><span style="color:#D73A49">try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> result</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">WriteTagDouble</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<setpoint-tag-code>"</span><span style="color:#24292E">, nextValue, { reason });</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> confirmed</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> result?.accepted </span><span style="color:#D73A49">===</span><span style="color:#005CC5"> true</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.status </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "done"</span></span>
<span class="line"><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.outcomeUncertain </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.operatorVerificationRequired </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span></span>
<span class="line"><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.transportOnly </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"write-result"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span></span>
<span class="line"><span style="color:#24292E"> (confirmed </span><span style="color:#D73A49">?</span><span style="color:#032F62"> "Command completed; verify normal collected state: "</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> "Verify before another write: "</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> +</span><span style="color:#005CC5"> JSON</span><span style="color:#24292E">.</span><span style="color:#6F42C1">stringify</span><span style="color:#24292E">(result </span><span style="color:#D73A49">??</span><span style="color:#005CC5"> null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6A737D"> // Do not overwrite the live value with nextValue or result.currentValue.</span></span>
<span class="line"><span style="color:#6A737D"> // Reconcile the command record and a later GOOD collected sample before another operation.</span></span>
<span class="line"><span style="color:#24292E">} </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) {</span></span>
<span class="line"><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"write-result"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span></span>
<span class="line"><span style="color:#032F62"> "Result unconfirmed; do not retry automatically. "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> error.message;</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre></div>
<p class="doc-step-result"><strong>Expected result</strong> PageApi has no public command-journal query method. Retain commandId from the receipt or error for an authorized administration/operations integration to reconcile. Use subscribe and getMeta to inspect later GOOD normal collection. Keep controls disabled while unconfirmed; allow a new operator action only after reconciliation, never based solely on a delay, refresh or value equality.</p>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-15">Query history and trends</h3>
<p><code>api.history.query(request)</code> and root alias <code>api.queryHistory(request)</code> both return <code>Promise<HistoryQueryResult></code>. The request reads the history store and does not poll a PLC. Every Tag must be inside the page history-read scope.</p>
<ul>
<li>Request fields: <code>tags</code>, <code>start</code>, <code>end</code>, <code>interval</code>, and <code>aggregate</code>, with optional <code>points</code>.</li>
<li><code>tags</code> is required. The submitted array accepts at most 16 entries, including duplicates; the server then deduplicates them. History does not accept <code>[0]</code> through <code>[31]</code> bit references; query the parent Tag.</li>
<li><code>start</code> and <code>end</code> must be RFC 3339 timestamps, with a maximum range of 366 days.</li>
<li><code>interval</code> supports <code>m</code>, <code>h</code>, <code>d</code>, and <code>w</code>, for example <code>15m</code>, <code>1h</code>, <code>1d</code>, or <code>1w</code>; minimum 1 minute.</li>
<li><code>aggregate</code> supports <code>avg</code>, <code>min</code>, <code>max</code>, <code>sum</code>, <code>count</code>, <code>first</code>, <code>last</code>, <code>delta</code>, and <code>raw</code>.</li>
<li><code>delta</code> fits meters and accumulating counters such as energy, water, or production total.</li>
<li>The example queries the last seven days. The target Tags need recorded history for data to appear.</li>
<li>Omitted or zero <code>points</code> defaults to 20000. Maximum is 20000 and it cannot be smaller than the deduplicated Tag count.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> weekEnd</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> new</span><span style="color:#6F42C1"> Date</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> weekStart</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> new</span><span style="color:#6F42C1"> Date</span><span style="color:#24292E">(weekEnd.</span><span style="color:#6F42C1">getTime</span><span style="color:#24292E">() </span><span style="color:#D73A49">-</span><span style="color:#005CC5"> 7</span><span style="color:#D73A49"> *</span><span style="color:#005CC5"> 24</span><span style="color:#D73A49"> *</span><span style="color:#005CC5"> 60</span><span style="color:#D73A49"> *</span><span style="color:#005CC5"> 60</span><span style="color:#D73A49"> *</span><span style="color:#005CC5"> 1000</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> weekEnergy</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.history.</span><span style="color:#6F42C1">query</span><span style="color:#24292E">({</span></span>
<span class="line"><span style="color:#24292E"> tags: [</span></span>
<span class="line"><span style="color:#032F62"> "<primary-device-code>.<energy-tag-code>"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#032F62"> "<secondary-device-code>.<energy-tag-code>"</span></span>
<span class="line"><span style="color:#24292E"> ],</span></span>
<span class="line"><span style="color:#24292E"> start: weekStart.</span><span style="color:#6F42C1">toISOString</span><span style="color:#24292E">(),</span></span>
<span class="line"><span style="color:#24292E"> end: weekEnd.</span><span style="color:#6F42C1">toISOString</span><span style="color:#24292E">(),</span></span>
<span class="line"><span style="color:#24292E"> interval: </span><span style="color:#032F62">"1d"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> aggregate: </span><span style="color:#032F62">"delta"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> points: </span><span style="color:#005CC5">20000</span></span>
<span class="line"><span style="color:#24292E">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1">renderEnergyReport</span><span style="color:#24292E">(weekEnergy.items, {</span></span>
<span class="line"><span style="color:#24292E"> interval: weekEnergy.effectiveInterval,</span></span>
<span class="line"><span style="color:#24292E"> truncated: weekEnergy.truncated</span></span>
<span class="line"><span style="color:#24292E">});</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-16">How to consume a history result</h3>
<p>The result is <code>{ items, effectiveInterval, rangeStart, rangeEnd, truncated }</code>. <code>items</code> contains one series per requested Tag. A series is <code>{ tag, tagId, displayName, points }</code>; every point is <code>{ t, value, quality }</code>.</p>
<ul>
<li><code>tag</code> is the full Tag code, <code>tagId</code> is the server identifier, and <code>displayName</code> may be empty.</li>
<li><code>points[].t</code> is RFC 3339, <code>value</code> is raw or aggregated, and <code>quality</code> is that point's quality.</li>
<li><code>effectiveInterval</code> is the interval actually used and may be coarser than requested when the point budget is tight.</li>
<li><code>rangeStart</code> and <code>rangeEnd</code> are the actual UTC/RFC 3339 range.</li>
<li><code>truncated: true</code> means the aggregation interval was coarsened or raw records were cut off by the point budget. Show the effective interval and an incomplete-result notice. To retrieve complete raw records, query smaller time windows.</li>
<li>Read each series with <code>series.points || []</code> to handle missing data. Render an empty state instead of treating absent records as zero.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">const</span><span style="color:#005CC5"> series</span><span style="color:#D73A49"> of</span><span style="color:#24292E"> weekEnergy.items </span><span style="color:#D73A49">||</span><span style="color:#24292E"> []) {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> chartPoints</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> (series.points </span><span style="color:#D73A49">||</span><span style="color:#24292E"> []).</span><span style="color:#6F42C1">map</span><span style="color:#24292E">((</span><span style="color:#E36209">point</span><span style="color:#24292E">) </span><span style="color:#D73A49">=></span><span style="color:#24292E"> ({</span></span>
<span class="line"><span style="color:#24292E"> time: </span><span style="color:#D73A49">new</span><span style="color:#6F42C1"> Date</span><span style="color:#24292E">(point.t),</span></span>
<span class="line"><span style="color:#24292E"> value: point.quality </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "good"</span><span style="color:#D73A49"> ?</span><span style="color:#24292E"> point.value </span><span style="color:#D73A49">:</span><span style="color:#005CC5"> null</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> quality: point.quality</span></span>
<span class="line"><span style="color:#24292E"> }));</span></span>
<span class="line"><span style="color:#6F42C1"> drawEnergySeries</span><span style="color:#24292E">(series.displayName </span><span style="color:#D73A49">||</span><span style="color:#24292E"> series.tag, chartPoints);</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#6F42C1">showActualInterval</span><span style="color:#24292E">(weekEnergy.effectiveInterval, weekEnergy.truncated </span><span style="color:#D73A49">===</span><span style="color:#005CC5"> true</span><span style="color:#24292E">);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-17">Alarm-scope declaration</h3>
<p>The server mints alarm scope from page declarations; client query fields cannot enlarge it. Declare dimensions under <code>alarm</code>, <code>alarmScope</code>, or <code>pageAlarm</code> in the JSON configuration. Readable Tags are also added automatically to the alarm Tag scope.</p>
<ul>
<li>Supported dimensions: <code>tags</code>, <code>plcCodes</code>, <code>functionGroupCodes</code>, <code>deviceGroupCodes</code>, and <code>signalGroupCodes</code>.</li>
<li>With no alarm scope, active and historical alarm queries return an empty scope.</li>
<li>Dynamic <code>liveTagRead</code> does not enlarge alarms. Statically declare required alarm Tags or groups.</li>
<li>The server overwrites any client scope fields with the minted capability; additive JSON cannot reveal more alarms.</li>
<li>Group codes need the device prefix. Empty scope means unbound, not all alarms. showWindow controls the page alarm rail, separately from the equipment Popup.</li>
</ul>
<div class="manual-code" data-code-language="html"><div class="manual-code-toolbar"><span>HTML</span><span class="manual-code-tools"><small>HTML</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: HTML">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"application/json"</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"hqvisu-custom-page"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#24292E"> "readableTags": ["<device-code>.<temperature-tag-code>"],</span></span>
<span class="line"><span style="color:#24292E"> "alarmScope": {</span></span>
<span class="line"><span style="color:#24292E"> "plcCodes": ["<device-code>"],</span></span>
<span class="line"><span style="color:#24292E"> "functionGroupCodes": ["<device-code>.<function-group-code>"],</span></span>
<span class="line"><span style="color:#24292E"> "deviceGroupCodes": [],</span></span>
<span class="line"><span style="color:#24292E"> "signalGroupCodes": []</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-18">Query and subscribe to alarms</h3>
<p>Alarm APIs provide synchronous snapshots, asynchronous queries, and change subscriptions. <code>getActive()</code>, <code>getActiveAlarms()</code>, and <code>queryHistory(request)</code> return paged <code>Promise<AlarmQueryResult></code> results.</p>
<ul>
<li><code>getSnapshot()</code> returns the current Host snapshot synchronously. A Host may return an array or a wrapper such as <code>{ alarms }</code> or <code>{ activeAlarms }</code>.</li>
<li>Query fields: <code>start</code>, <code>end</code>, <code>severity</code>, <code>state</code>, <code>source</code> / <code>q</code>, <code>limit</code>, <code>offset</code>, <code>cursorTs</code>, and <code>cursorId</code>.</li>
<li>History defaults to the most recent 24 hours when times are omitted; one query spans at most 24 hours.</li>
<li>The example queries the last hour; the history result is empty if no alarm occurred in that period.</li>
<li>Omitted or zero <code>limit</code> is 500 and the maximum is 500; larger values are clamped. <code>offset</code> must be nonnegative.</li>
<li>Result: <code>{ items, count, hasMore, nextCursorTs, nextCursorId }</code>. When more data exists, pass both next-cursor fields into the next request.</li>
<li><code>subscribe(callback)</code> returns an idempotent unsubscribe function and calls <code>(items, api)</code>. A Host may send a complete snapshot or change items; use it as a refresh signal rather than blindly appending.</li>
<li>getActive(request?) accepts the filters/pagination above; getActiveAlarms() is the no-argument convenience entry.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">function</span><span style="color:#6F42C1"> alarmItemsFromSnapshot</span><span style="color:#24292E">(</span><span style="color:#E36209">snapshot</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (Array.</span><span style="color:#6F42C1">isArray</span><span style="color:#24292E">(snapshot)) </span><span style="color:#D73A49">return</span><span style="color:#24292E"> snapshot;</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (Array.</span><span style="color:#6F42C1">isArray</span><span style="color:#24292E">(snapshot?.alarms)) </span><span style="color:#D73A49">return</span><span style="color:#24292E"> snapshot.alarms;</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (Array.</span><span style="color:#6F42C1">isArray</span><span style="color:#24292E">(snapshot?.activeAlarms)) </span><span style="color:#D73A49">return</span><span style="color:#24292E"> snapshot.activeAlarms;</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> [];</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1">renderAlarmSummary</span><span style="color:#24292E">(</span><span style="color:#6F42C1">alarmItemsFromSnapshot</span><span style="color:#24292E">(api.alarms.</span><span style="color:#6F42C1">getSnapshot</span><span style="color:#24292E">()));</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> activeResult</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.alarms.</span><span style="color:#6F42C1">getActiveAlarms</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#6F42C1">renderAlarmSummary</span><span style="color:#24292E">(activeResult.items </span><span style="color:#D73A49">||</span><span style="color:#24292E"> []);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> alarmEnd</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> new</span><span style="color:#6F42C1"> Date</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> alarmStart</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> new</span><span style="color:#6F42C1"> Date</span><span style="color:#24292E">(alarmEnd.</span><span style="color:#6F42C1">getTime</span><span style="color:#24292E">() </span><span style="color:#D73A49">-</span><span style="color:#005CC5"> 60</span><span style="color:#D73A49"> *</span><span style="color:#005CC5"> 60</span><span style="color:#D73A49"> *</span><span style="color:#005CC5"> 1000</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> historyResult</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.alarms.</span><span style="color:#6F42C1">queryHistory</span><span style="color:#24292E">({</span></span>
<span class="line"><span style="color:#24292E"> start: alarmStart.</span><span style="color:#6F42C1">toISOString</span><span style="color:#24292E">(),</span></span>
<span class="line"><span style="color:#24292E"> end: alarmEnd.</span><span style="color:#6F42C1">toISOString</span><span style="color:#24292E">(),</span></span>
<span class="line"><span style="color:#24292E"> severity: [</span><span style="color:#032F62">"critical"</span><span style="color:#24292E">, </span><span style="color:#032F62">"high"</span><span style="color:#24292E">],</span></span>
<span class="line"><span style="color:#24292E"> state: [</span><span style="color:#032F62">"active"</span><span style="color:#24292E">, </span><span style="color:#032F62">"recovered"</span><span style="color:#24292E">],</span></span>
<span class="line"><span style="color:#24292E"> limit: </span><span style="color:#005CC5">200</span></span>
<span class="line"><span style="color:#24292E">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> stopAlarms</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.alarms.</span><span style="color:#6F42C1">subscribe</span><span style="color:#24292E">(() </span><span style="color:#D73A49">=></span><span style="color:#6F42C1"> scheduleActiveAlarmRefresh</span><span style="color:#24292E">());</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-19">Alarm item and paging fields</h3>
<p>Identify events by id and display state from state and timestamps. Leave missing optional fields empty; use messageText/displayName when translations are missing, without exposing internal keys.</p>
<ul>
<li>Identity: <code>id</code>, <code>deviceId</code>, <code>deviceCode</code>, <code>deviceName</code>, <code>tagId</code>, <code>tagCode</code>, <code>fullCode</code>, and <code>displayName</code>.</li>
<li>Content: <code>messageText</code>, <code>alarmKind</code>, <code>severity</code>, <code>state</code>, <code>displayValue</code>, and <code>quality</code>.</li>
<li>Times: <code>activeTs</code>, <code>recoverTs</code>, <code>closeTs</code>, <code>ackTs</code>, <code>shelvedUntil</code>, and <code>serverTs</code>.</li>
<li>Flags: <code>suppressed</code>; some items also carry <code>ackUserId</code>, <code>areaCode</code>, <code>objectIdentifier</code>, and <code>groupCode</code>.</li>
<li><code>count</code> is this page's item count and <code>hasMore</code> signals another page. Cursor paging avoids offset duplicates or gaps while alarms change.</li>
<li>The SDK provides alarm reads and subscriptions, but no acknowledge, shelve, or close operations.</li>
</ul>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-20">Navigation: parent, child, assets root and URL</h3>
<p>Custom api.navigation.gotoPage and PPT hqVisuDesigner.openPage share resolution. Targets need not all be prelisted. Mapping provides names/breadcrumbs; explicit paths take precedence over same-name mappings.</p>
<ul>
<li><code>gotoPage(target, options?)</code>: resolve ./, ../, child and sibling paths from the current screen URL; append .html to local extensionless paths. Navigate across folders while remaining under the assets root.</li>
<li>assets/<folder>/... and /assets/<folder>/... address the current Runtime Unit user assets root, including navigation from published or built-in screens. Other local relative paths in those sources retain their package boundary.</li>
<li><code>openUrl(target, options?)</code>: uses the same rules; write external targets as http://, https:// or //. Dotted folder names are not guessed to be domains. Preserve query/hash.</li>
<li><code>gotoAndon(target, options?)</code>: request an Andon/fullscreen host window.</li>
<li><code>gotoScreen(screenCode)</code>: navigate by screen menu code, not an arbitrary file path.</li>
<li>options: openInNewWindow, frameTarget, parameters:[{key,value}]. At most 64 parameters, keys ≤256 and values ≤2048 characters; read them with URLSearchParams.</li>
<li>Legal HTTP(S) addresses can navigate; third-party iframe embedding depends on its CSP/X-Frame-Options. Open Portal administration routes top-level/in a new window. External sites gain no Tag or Portal identity.</li>
<li>Reject escapes above assets, javascript:/data:/file:, backslashes and ambiguous encodings. Each destination HTML needs its own static data scope. true means dispatch, not confirmed target loading.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D">// Inside the ready() scope; the HTML contains the corresponding buttons.</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"parent"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span></span>
<span class="line"><span style="color:#24292E"> api.navigation.</span><span style="color:#6F42C1">gotoPage</span><span style="color:#24292E">(</span><span style="color:#032F62">"../overview.html?view=summary#status"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"child"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span></span>
<span class="line"><span style="color:#24292E"> api.navigation.</span><span style="color:#6F42C1">gotoPage</span><span style="color:#24292E">(</span><span style="color:#032F62">"details/equipment"</span><span style="color:#24292E">, { parameters: [{ key: </span><span style="color:#032F62">"device"</span><span style="color:#24292E">, value: </span><span style="color:#032F62">"<device-code>"</span><span style="color:#24292E"> }] });</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"other-package"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span></span>
<span class="line"><span style="color:#24292E"> api.navigation.</span><span style="color:#6F42C1">gotoPage</span><span style="color:#24292E">(</span><span style="color:#032F62">"/assets/<page-folder>/overview.html"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"external"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span></span>
<span class="line"><span style="color:#24292E"> api.navigation.</span><span style="color:#6F42C1">openUrl</span><span style="color:#24292E">(</span><span style="color:#032F62">"https://<server-host>/manual"</span><span style="color:#24292E">, { openInNewWindow: </span><span style="color:#005CC5">true</span><span style="color:#24292E"> });</span></span>
<span class="line"><span style="color:#6A737D">// In the destination page:</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> device</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> new</span><span style="color:#6F42C1"> URLSearchParams</span><span style="color:#24292E">(location.search).</span><span style="color:#6F42C1">get</span><span style="color:#24292E">(</span><span style="color:#032F62">"device"</span><span style="color:#24292E">);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-21">Device-popup API</h3>
<p><code>api.dialog.openDevicePopup(payload)</code> asks the Host to open the formal device popup; <code>api.dialog.close()</code> asks it to close the current Host dialog. Check <code>api.supports("popup")</code> and call from a real click. A true result means dispatched only.</p>
<ul>
<li><code>plc</code> or <code>deviceCode</code> is required, and that PLC must have at least one Tag inside page read scope.</li>
<li>Optional <code>focusTagNames</code> contains at most 64 Tags, all inside page read scope.</li>
<li><code>templateCode</code> / <code>id</code> can select a popup template.</li>
<li><code>diagnosisControl</code> and <code>groupRightIndex</code> are optional popup context.</li>
<li>A device popup does not expand page read/write scope or expose popup service objects.</li>
<li>Configure bound Tags, controls, alarms and trend areas in Popup Templates first. Real Popup writes still enforce user permissions, control flags, quality and Host confirmation.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D">// Add <button id="open-popup">Open equipment</button></span></span>
<span class="line"><span style="color:#6A737D">// and <button id="close-popup">Close</button> to the HTML.</span></span>
<span class="line"><span style="color:#6A737D">// Place this code inside the existing ready() scope.</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"open-popup"</span><span style="color:#24292E">).disabled </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> !</span><span style="color:#24292E">api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"popup"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"open-popup"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> dispatched</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.dialog.</span><span style="color:#6F42C1">openDevicePopup</span><span style="color:#24292E">({</span></span>
<span class="line"><span style="color:#24292E"> plc: </span><span style="color:#032F62">"<device-code>"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> templateCode: </span><span style="color:#032F62">"<popup-template-code>"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> focusTagNames: [</span><span style="color:#032F62">"<device-code>.<setpoint-tag-code>"</span><span style="color:#24292E">]</span></span>
<span class="line"><span style="color:#24292E"> });</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (dispatched </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#24292E">) </span><span style="color:#D73A49">throw</span><span style="color:#D73A49"> new</span><span style="color:#6F42C1"> Error</span><span style="color:#24292E">(</span><span style="color:#032F62">"Popup request was not dispatched"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6A737D"> // Verify the actual drawer and target Tag; true is not a load-complete receipt.</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) { document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> error.message; }</span></span>
<span class="line"><span style="color:#24292E">};</span></span>
<span class="line"><span style="color:#24292E">document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"close-popup"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> api.dialog.</span><span style="color:#6F42C1">close</span><span style="color:#24292E">();</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-22">Complete monitoring page (optional)</h3>
<p>This HTML includes a live value, one setpoint write, active alarms, and trend history. Replace Tags and declarations, and adjust the input range before publishing. Runtime supplies the SDK.</p>
<details class="manual-code-disclosure"><summary>View complete source</summary><div class="manual-code" data-code-language="html"><div class="manual-code-toolbar"><span>HTML</span><span class="manual-code-tools"><small>HTML</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: HTML">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E"><!</span><span style="color:#22863A">doctype</span><span style="color:#6F42C1"> html</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">html</span><span style="color:#6F42C1"> lang</span><span style="color:#24292E">=</span><span style="color:#032F62">"en"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">head</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">meta</span><span style="color:#6F42C1"> charset</span><span style="color:#24292E">=</span><span style="color:#032F62">"utf-8"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">meta</span><span style="color:#6F42C1"> name</span><span style="color:#24292E">=</span><span style="color:#032F62">"viewport"</span><span style="color:#6F42C1"> content</span><span style="color:#24292E">=</span><span style="color:#032F62">"width=device-width, initial-scale=1"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">title</span><span style="color:#24292E">>Mingcedar equipment monitor</</span><span style="color:#22863A">title</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"application/json"</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"hqvisu-custom-page"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> {"readableTags":["<device-code>.<process-tag-code>","<device-code>.<setpoint-tag-code>"],</span></span>
<span class="line"><span style="color:#24292E"> "writableTags":["<device-code>.<setpoint-tag-code>"]}</span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> window.</span><span style="color:#005CC5">HQVISU_PAGE_META</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> { layoutMode: </span><span style="color:#032F62">"native"</span><span style="color:#24292E"> };</span></span>
<span class="line"><span style="color:#24292E"> window.</span><span style="color:#005CC5">HQVISU_PAGE_NAME</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "equipment-monitor"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> window.</span><span style="color:#005CC5">HQVISU_PAGE_ALARM</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E"> showWindow: </span><span style="color:#005CC5">true</span><span style="color:#24292E">, tags: [</span><span style="color:#032F62">"<device-code>.<process-tag-code>"</span><span style="color:#24292E">],</span></span>
<span class="line"><span style="color:#24292E"> plcCodes: [], functionGroupCodes: [], deviceGroupCodes: [], signalGroupCodes: []</span></span>
<span class="line"><span style="color:#24292E"> };</span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">style</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#22863A"> body</span><span style="color:#24292E"> { </span><span style="color:#005CC5">font</span><span style="color:#24292E">: </span><span style="color:#005CC5">16</span><span style="color:#D73A49">px</span><span style="color:#24292E">/</span><span style="color:#005CC5">1.6</span><span style="color:#005CC5"> system-ui</span><span style="color:#24292E">; </span><span style="color:#005CC5">margin</span><span style="color:#24292E">: </span><span style="color:#005CC5">24</span><span style="color:#D73A49">px</span><span style="color:#24292E">; </span><span style="color:#005CC5">color</span><span style="color:#24292E">: </span><span style="color:#005CC5">#16263c</span><span style="color:#24292E">; }</span></span>
<span class="line"><span style="color:#22863A"> section</span><span style="color:#24292E"> { </span><span style="color:#005CC5">margin-block</span><span style="color:#24292E">: </span><span style="color:#005CC5">24</span><span style="color:#D73A49">px</span><span style="color:#24292E">; } </span><span style="color:#22863A">button</span><span style="color:#24292E">, </span><span style="color:#22863A">input</span><span style="color:#24292E"> { </span><span style="color:#005CC5">font</span><span style="color:#24292E">: </span><span style="color:#005CC5">inherit</span><span style="color:#24292E">; </span><span style="color:#005CC5">padding</span><span style="color:#24292E">: </span><span style="color:#005CC5">8</span><span style="color:#D73A49">px</span><span style="color:#24292E">; }</span></span>
<span class="line"><span style="color:#22863A"> table</span><span style="color:#24292E"> { </span><span style="color:#005CC5">border-collapse</span><span style="color:#24292E">: </span><span style="color:#005CC5">collapse</span><span style="color:#24292E">; </span><span style="color:#005CC5">width</span><span style="color:#24292E">: </span><span style="color:#005CC5">100</span><span style="color:#D73A49">%</span><span style="color:#24292E">; }</span></span>
<span class="line"><span style="color:#22863A"> th</span><span style="color:#24292E">, </span><span style="color:#22863A">td</span><span style="color:#24292E"> { </span><span style="color:#005CC5">text-align</span><span style="color:#24292E">: </span><span style="color:#005CC5">left</span><span style="color:#24292E">; </span><span style="color:#005CC5">padding</span><span style="color:#24292E">: </span><span style="color:#005CC5">8</span><span style="color:#D73A49">px</span><span style="color:#24292E">; </span><span style="color:#005CC5">border-bottom</span><span style="color:#24292E">: </span><span style="color:#005CC5">1</span><span style="color:#D73A49">px</span><span style="color:#005CC5"> solid</span><span style="color:#005CC5"> #d9e1eb</span><span style="color:#24292E">; }</span></span>
<span class="line"><span style="color:#22863A"> pre</span><span style="color:#24292E"> { </span><span style="color:#005CC5">white-space</span><span style="color:#24292E">: </span><span style="color:#005CC5">pre-wrap</span><span style="color:#24292E">; </span><span style="color:#005CC5">overflow-wrap</span><span style="color:#24292E">: anywhere; }</span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">style</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">head</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">body</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">h1</span><span style="color:#24292E">>Equipment monitor</</span><span style="color:#22863A">h1</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">p</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"status"</span><span style="color:#6F42C1"> role</span><span style="color:#24292E">=</span><span style="color:#032F62">"status"</span><span style="color:#24292E">>Connecting to Mingcedar…</</span><span style="color:#22863A">p</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">section</span><span style="color:#24292E">><</span><span style="color:#22863A">h2</span><span style="color:#24292E">>Live value</</span><span style="color:#22863A">h2</span><span style="color:#24292E">><</span><span style="color:#22863A">output</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"value"</span><span style="color:#24292E">>Waiting for data</</span><span style="color:#22863A">output</span><span style="color:#24292E">></</span><span style="color:#22863A">section</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">section</span><span style="color:#24292E">><</span><span style="color:#22863A">h2</span><span style="color:#24292E">>Setpoint</</span><span style="color:#22863A">h2</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">label</span><span style="color:#24292E">>Target (0–100) <</span><span style="color:#22863A">input</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"setpoint"</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"number"</span><span style="color:#6F42C1"> min</span><span style="color:#24292E">=</span><span style="color:#032F62">"0"</span><span style="color:#6F42C1"> max</span><span style="color:#24292E">=</span><span style="color:#032F62">"100"</span><span style="color:#6F42C1"> step</span><span style="color:#24292E">=</span><span style="color:#032F62">"0.1"</span><span style="color:#24292E">></</span><span style="color:#22863A">label</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">button</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"write"</span><span style="color:#6F42C1"> disabled</span><span style="color:#24292E">>Write setpoint</</span><span style="color:#22863A">button</span><span style="color:#24292E">><</span><span style="color:#22863A">pre</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"write-result"</span><span style="color:#24292E">></</span><span style="color:#22863A">pre</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">section</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">section</span><span style="color:#24292E">><</span><span style="color:#22863A">h2</span><span style="color:#24292E">>Active alarms</</span><span style="color:#22863A">h2</span><span style="color:#24292E">><</span><span style="color:#22863A">button</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"alarms"</span><span style="color:#6F42C1"> disabled</span><span style="color:#24292E">>Refresh alarms</</span><span style="color:#22863A">button</span><span style="color:#24292E">><</span><span style="color:#22863A">div</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"alarm-list"</span><span style="color:#24292E">></</span><span style="color:#22863A">div</span><span style="color:#24292E">><</span><span style="color:#22863A">p</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"alarm-status"</span><span style="color:#24292E">></</span><span style="color:#22863A">p</span><span style="color:#24292E">></</span><span style="color:#22863A">section</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">section</span><span style="color:#24292E">><</span><span style="color:#22863A">h2</span><span style="color:#24292E">>Last hour</</span><span style="color:#22863A">h2</span><span style="color:#24292E">><</span><span style="color:#22863A">button</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"history"</span><span style="color:#6F42C1"> disabled</span><span style="color:#24292E">>Query historical trend</</span><span style="color:#22863A">button</span><span style="color:#24292E">><</span><span style="color:#22863A">div</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"trend"</span><span style="color:#24292E">></</span><span style="color:#22863A">div</span><span style="color:#24292E">><</span><span style="color:#22863A">p</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"trend-status"</span><span style="color:#24292E">></</span><span style="color:#22863A">p</span><span style="color:#24292E">></</span><span style="color:#22863A">section</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> (</span><span style="color:#D73A49">async</span><span style="color:#D73A49"> function</span><span style="color:#24292E"> () {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#6F42C1"> element</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> (</span><span style="color:#E36209">id</span><span style="color:#24292E">) </span><span style="color:#D73A49">=></span><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(id);</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> processTag</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "<device-code>.<process-tag-code>"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> setpointTag</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "<device-code>.<setpoint-tag-code>"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> api</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#005CC5"> HQVISU_CUSTOM_PAGE</span><span style="color:#24292E">.</span><span style="color:#6F42C1">ready</span><span style="color:#24292E">({timeoutMs: </span><span style="color:#005CC5">10000</span><span style="color:#24292E">});</span></span>
<span class="line"><span style="color:#D73A49"> let</span><span style="color:#6F42C1"> stopValues</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {};</span></span>
<span class="line"><span style="color:#D73A49"> let</span><span style="color:#6F42C1"> stopAlarms</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {};</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#032F62"> "Connected"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> window.</span><span style="color:#6F42C1">addEventListener</span><span style="color:#24292E">(</span><span style="color:#032F62">"pagehide"</span><span style="color:#24292E">, </span><span style="color:#E36209">event</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">event.persisted) { </span><span style="color:#6F42C1">stopValues</span><span style="color:#24292E">(); </span><span style="color:#6F42C1">stopAlarms</span><span style="color:#24292E">(); api.</span><span style="color:#6F42C1">destroy</span><span style="color:#24292E">(); }</span></span>
<span class="line"><span style="color:#24292E"> });</span></span>
<span class="line"><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> refreshValue</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> quality</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagQuality</span><span style="color:#24292E">(processTag);</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> value</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagDouble</span><span style="color:#24292E">(processTag, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"value"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> quality </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "good"</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> value </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> null</span></span>
<span class="line"><span style="color:#D73A49"> ?</span><span style="color:#6F42C1"> String</span><span style="color:#24292E">(value) </span><span style="color:#D73A49">:</span><span style="color:#032F62"> "Unavailable — quality: "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> quality;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E"> stopValues </span><span style="color:#D73A49">=</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">subscribe</span><span style="color:#24292E">([processTag, setpointTag], refreshValue);</span></span>
<span class="line"><span style="color:#6F42C1"> refreshValue</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#032F62"> "Connected. Reads remain subject to data quality."</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"write"</span><span style="color:#24292E">).disabled </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> !</span><span style="color:#24292E">(api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag.write"</span><span style="color:#24292E">) </span><span style="color:#D73A49">&&</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.permissions.</span><span style="color:#6F42C1">has</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag:write"</span><span style="color:#24292E">));</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"write"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> async</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> input</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"setpoint"</span><span style="color:#24292E">).value.</span><span style="color:#6F42C1">trim</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> value</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> Number</span><span style="color:#24292E">(input);</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">input </span><span style="color:#D73A49">||</span><span style="color:#D73A49"> !</span><span style="color:#24292E">Number.</span><span style="color:#6F42C1">isFinite</span><span style="color:#24292E">(value) </span><span style="color:#D73A49">||</span><span style="color:#24292E"> value </span><span style="color:#D73A49"><</span><span style="color:#005CC5"> 0</span><span style="color:#D73A49"> ||</span><span style="color:#24292E"> value </span><span style="color:#D73A49">></span><span style="color:#005CC5"> 100</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"write-result"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#032F62"> "Enter a number between 0 and 100."</span><span style="color:#24292E">; </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">api.</span><span style="color:#6F42C1">ReadTagQualityGood</span><span style="color:#24292E">(setpointTag)) {</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"write-result"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#032F62"> "Setpoint quality is not good."</span><span style="color:#24292E">; </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#6F42C1">confirm</span><span style="color:#24292E">(</span><span style="color:#032F62">"Write setpoint "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> value </span><span style="color:#D73A49">+</span><span style="color:#032F62"> "?"</span><span style="color:#24292E">)) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"write"</span><span style="color:#24292E">).disabled </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> result</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">WriteTagDouble</span><span style="color:#24292E">(setpointTag, value, {reason: </span><span style="color:#032F62">"Operator setpoint update"</span><span style="color:#24292E">});</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> done</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> result?.accepted </span><span style="color:#D73A49">===</span><span style="color:#005CC5"> true</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.status </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "done"</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.outcomeUncertain </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.operatorVerificationRequired </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> result?.transportOnly </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"write-result"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (done </span><span style="color:#D73A49">?</span><span style="color:#032F62"> "Command completed; verify normal collected state: "</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> "Verify before another write: "</span><span style="color:#24292E">) </span><span style="color:#D73A49">+</span><span style="color:#005CC5"> JSON</span><span style="color:#24292E">.</span><span style="color:#6F42C1">stringify</span><span style="color:#24292E">(result </span><span style="color:#D73A49">??</span><span style="color:#005CC5"> null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) {</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"write-result"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#032F62"> "Result not confirmed. Check actual value and command records. "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> error.message;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#6A737D"> // Keep the button disabled after submission; refresh only after result reconciliation.</span></span>
<span class="line"><span style="color:#24292E"> };</span></span>
<span class="line"><span style="color:#D73A49"> let</span><span style="color:#24292E"> alarmPending </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> async</span><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> refreshAlarms</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (alarmPending) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> alarmPending </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> result</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.alarms.</span><span style="color:#6F42C1">getActive</span><span style="color:#24292E">({limit: </span><span style="color:#005CC5">100</span><span style="color:#24292E">, offset: </span><span style="color:#005CC5">0</span><span style="color:#24292E">});</span></span>
<span class="line"><span style="color:#24292E"> api.</span><span style="color:#6F42C1">renderTable</span><span style="color:#24292E">(</span><span style="color:#6F42C1">element</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarm-list"</span><span style="color:#24292E">), result.items, {columns: [</span></span>
<span class="line"><span style="color:#24292E"> {key:</span><span style="color:#032F62">"fullCode"</span><span style="color:#24292E">,label:</span><span style="color:#032F62">"Tag"</span><span style="color:#24292E">}, {key:</span><span style="color:#032F62">"messageText"</span><span style="color:#24292E">,label:</span><span style="color:#032F62">"Message"</span><span style="color:#24292E">},</span></span>
<span class="line"><span style="color:#24292E"> {key:</span><span style="color:#032F62">"severity"</span><span style="color:#24292E">,label:</span><span style="color:#032F62">"Severity"</span><span style="color:#24292E">}, {key:</span><span style="color:#032F62">"state"</span><span style="color:#24292E">,label:</span><span style="color:#032F62">"State"</span><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E"> ]});</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarm-status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> result.hasMore </span><span style="color:#D73A49">?</span><span style="color:#032F62"> "Showing first 100 alarms; narrow the filter or request the next page."</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> "Current active alarms: "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> result.count;</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) { </span><span style="color:#6F42C1">element</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarm-status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> error.message; }</span></span>
<span class="line"><span style="color:#D73A49"> finally</span><span style="color:#24292E"> { alarmPending </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">; }</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarms"</span><span style="color:#24292E">).disabled </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> !</span><span style="color:#24292E">api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarm.read"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarms"</span><span style="color:#24292E">).onclick </span><span style="color:#D73A49">=</span><span style="color:#24292E"> refreshAlarms;</span></span>
<span class="line"><span style="color:#24292E"> stopAlarms </span><span style="color:#D73A49">=</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarm.read"</span><span style="color:#24292E">) </span><span style="color:#D73A49">?</span><span style="color:#24292E"> api.alarms.</span><span style="color:#6F42C1">subscribe</span><span style="color:#24292E">(refreshAlarms) </span><span style="color:#D73A49">:</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {};</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"alarm.read"</span><span style="color:#24292E">)) </span><span style="color:#D73A49">await</span><span style="color:#6F42C1"> refreshAlarms</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"history"</span><span style="color:#24292E">).disabled </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> !</span><span style="color:#24292E">api.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"history.read"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"history"</span><span style="color:#24292E">).</span><span style="color:#6F42C1">onclick</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> async</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"history"</span><span style="color:#24292E">).disabled </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> end</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> new</span><span style="color:#6F42C1"> Date</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> result</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.history.</span><span style="color:#6F42C1">query</span><span style="color:#24292E">({tags:[processTag],</span></span>
<span class="line"><span style="color:#24292E"> start:</span><span style="color:#D73A49">new</span><span style="color:#6F42C1"> Date</span><span style="color:#24292E">(end.</span><span style="color:#6F42C1">getTime</span><span style="color:#24292E">()</span><span style="color:#D73A49">-</span><span style="color:#005CC5">3600000</span><span style="color:#24292E">).</span><span style="color:#6F42C1">toISOString</span><span style="color:#24292E">(), end:end.</span><span style="color:#6F42C1">toISOString</span><span style="color:#24292E">(),</span></span>
<span class="line"><span style="color:#24292E"> interval:</span><span style="color:#032F62">"1m"</span><span style="color:#24292E">, aggregate:</span><span style="color:#032F62">"avg"</span><span style="color:#24292E">, points:</span><span style="color:#005CC5">1000</span><span style="color:#24292E">});</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> rows</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> (result.items </span><span style="color:#D73A49">||</span><span style="color:#24292E"> []).</span><span style="color:#6F42C1">flatMap</span><span style="color:#24292E">(</span><span style="color:#E36209">series</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> (series.points </span><span style="color:#D73A49">||</span><span style="color:#24292E"> []).</span><span style="color:#6F42C1">map</span><span style="color:#24292E">(</span><span style="color:#E36209">point</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> ({</span></span>
<span class="line"><span style="color:#24292E"> tag:series.tag, time:point.t, value:point.quality </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "good"</span><span style="color:#D73A49"> ?</span><span style="color:#24292E"> point.value </span><span style="color:#D73A49">:</span><span style="color:#005CC5"> null</span><span style="color:#24292E">, quality:point.quality</span></span>
<span class="line"><span style="color:#24292E"> })));</span></span>
<span class="line"><span style="color:#24292E"> api.</span><span style="color:#6F42C1">renderTable</span><span style="color:#24292E">(</span><span style="color:#6F42C1">element</span><span style="color:#24292E">(</span><span style="color:#032F62">"trend"</span><span style="color:#24292E">), rows);</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"trend-status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> rows.</span><span style="color:#005CC5">length</span></span>
<span class="line"><span style="color:#D73A49"> ?</span><span style="color:#032F62"> "Interval: "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> result.effectiveInterval </span><span style="color:#D73A49">+</span><span style="color:#24292E"> (result.truncated </span><span style="color:#D73A49">?</span><span style="color:#032F62"> " (adjusted to point budget)"</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> ""</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> :</span><span style="color:#032F62"> "No archived values in this time range."</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) { </span><span style="color:#6F42C1">element</span><span style="color:#24292E">(</span><span style="color:#032F62">"trend-status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> error.message; }</span></span>
<span class="line"><span style="color:#D73A49"> finally</span><span style="color:#24292E"> { </span><span style="color:#6F42C1">element</span><span style="color:#24292E">(</span><span style="color:#032F62">"history"</span><span style="color:#24292E">).disabled </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">; }</span></span>
<span class="line"><span style="color:#24292E"> };</span></span>
<span class="line"><span style="color:#24292E"> })().</span><span style="color:#6F42C1">catch</span><span style="color:#24292E">(</span><span style="color:#E36209">error</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> { document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> error.message; });</span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">body</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">html</span><span style="color:#24292E">></span></span></code></pre></div></details>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-23">renderTable and bindJsonTable</h3>
<p><code>api.renderTable</code> and global <code>HQVISU_CUSTOM_PAGE.renderTable</code> render arrays, <code>{ rows }</code>, or <code>{ items }</code> as HTML tables and return <code>{ rowCount, columnCount }</code>. <code>bindJsonTable</code> renders once, subscribes to a JSON Tag, and returns an unsubscribe function.</p>
<ul>
<li><code>target</code> may be a CSS selector or DOM element.</li>
<li><code>options.columns</code> can be an array of strings or <code>{ key, label }</code> objects.</li>
<li>When columns are omitted, the SDK infers columns from the first 25 rows.</li>
<li><code>options.className</code> sets the table class; the page owns the CSS.</li>
<li>Cells are written through <code>textContent</code>, not executed as HTML. A missing target returns <code>{ rowCount: 0, columnCount: 0 }</code>.</li>
<li><code>bindJsonTable(target, tagName, options)</code> accepts array JSON, <code>{ rows }</code>, or <code>{ items }</code>; empty or invalid JSON renders an empty table. It renders the raw snapshot without filtering Tag quality. For a quality-aware table, subscribe, check ReadTagQualityGood in the callback, then call renderTable.</li>
<li>Complex sorting, pagination, frozen columns, and Excel export should be implemented by the project page or a table component.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E">api.</span><span style="color:#6F42C1">renderTable</span><span style="color:#24292E">(</span><span style="color:#032F62">"#quality-table"</span><span style="color:#24292E">, {</span></span>
<span class="line"><span style="color:#24292E"> rows: [</span></span>
<span class="line"><span style="color:#24292E"> { tag: </span><span style="color:#032F62">"<device-code>.<temperature-tag-code>"</span><span style="color:#24292E">, value: </span><span style="color:#032F62">"72.4"</span><span style="color:#24292E">, quality: </span><span style="color:#032F62">"good"</span><span style="color:#24292E"> },</span></span>
<span class="line"><span style="color:#24292E"> { tag: </span><span style="color:#032F62">"<device-code>.<pressure-tag-code>"</span><span style="color:#24292E">, value: </span><span style="color:#032F62">"0.68"</span><span style="color:#24292E">, quality: </span><span style="color:#032F62">"good"</span><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E"> ]</span></span>
<span class="line"><span style="color:#24292E">}, {</span></span>
<span class="line"><span style="color:#24292E"> className: </span><span style="color:#032F62">"report-table"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> columns: [</span></span>
<span class="line"><span style="color:#24292E"> { key: </span><span style="color:#032F62">"tag"</span><span style="color:#24292E">, label: </span><span style="color:#032F62">"Tag"</span><span style="color:#24292E"> },</span></span>
<span class="line"><span style="color:#24292E"> { key: </span><span style="color:#032F62">"value"</span><span style="color:#24292E">, label: </span><span style="color:#032F62">"Current value"</span><span style="color:#24292E"> },</span></span>
<span class="line"><span style="color:#24292E"> { key: </span><span style="color:#032F62">"quality"</span><span style="color:#24292E">, label: </span><span style="color:#032F62">"Quality"</span><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E"> ]</span></span>
<span class="line"><span style="color:#24292E">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> stopTable</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">bindJsonTable</span><span style="color:#24292E">(</span><span style="color:#032F62">"#event-table"</span><span style="color:#24292E">, </span><span style="color:#032F62">"<device-code>.<event-rows-tag-code>"</span><span style="color:#24292E">, {</span></span>
<span class="line"><span style="color:#24292E"> columns: [</span><span style="color:#032F62">"time"</span><span style="color:#24292E">, </span><span style="color:#032F62">"type"</span><span style="color:#24292E">, </span><span style="color:#032F62">"message"</span><span style="color:#24292E">]</span></span>
<span class="line"><span style="color:#24292E">});</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-24">Language: follow the system LANG control</h3>
<p>Use the system LANG control without adding another selector. Render the current locale, then subscribe to changes. Translate display text; retain response data, raw Tag values, and command status codes.</p>
<ul>
<li><code>language.current()</code> / <code>getLocale()</code> → string.</li>
<li><code>language.translate(key, fallback?)</code> / <code>t(key, fallback?)</code>; root api.translate/api.t are equivalent. Missing keys return fallback or the key if no fallback is given. Maintain business translation keys in system language configuration.</li>
<li><code>language.onChange((locale, translations) => ...)</code> / <code>subscribe(...)</code> → cleanup function; no automatic initial callback.</li>
<li><code>language.setLocale(locale)</code> → string | Promise<string>; affects system language, not component-local state. Page language controls should stay consistent with the system header.</li>
<li>Breadcrumb names come from static navigation declarations; PageApi has no dynamic rename method. Do not repeat handshakes to refresh them.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D">// The system top-bar LANG is the language selector. Do not add a second one.</span></span>
<span class="line"><span style="color:#D73A49">function</span><span style="color:#6F42C1"> renderText</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> locale</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.language.</span><span style="color:#6F42C1">current</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#24292E"> document.documentElement.lang </span><span style="color:#D73A49">=</span><span style="color:#24292E"> locale;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> fallback</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> locale.</span><span style="color:#6F42C1">startsWith</span><span style="color:#24292E">(</span><span style="color:#032F62">"zh"</span><span style="color:#24292E">) </span><span style="color:#D73A49">?</span><span style="color:#032F62"> "设备总览"</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> "Equipment overview"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"title"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">t</span><span style="color:#24292E">(</span><span style="color:#032F62">"equipment.overview.title"</span><span style="color:#24292E">, fallback);</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#6F42C1">renderText</span><span style="color:#24292E">(); </span><span style="color:#6A737D">// onChange does not supply an initial callback.</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> stopLanguage</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.language.</span><span style="color:#6F42C1">onChange</span><span style="color:#24292E">(renderText);</span></span>
<span class="line"><span style="color:#6A737D">// Call stopLanguage() in this page/component's existing cleanup handler.</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-25">Local development with the Mock Host</h3>
<p>The versioned <code>runtime-page-sdk-mock-host-v1.js</code> provides development data without Mingcedar. Create the Mock Host before loading <code>hq-common.js</code> and <code>hq-custom-page.js</code>. It tests rendering, subscription, and error states; it is not a security, permission, audit, write-confirmation, history-store, or PLC simulator.</p>
<ul>
<li><code>createHQVisuMockHost(options)</code> returns a development Host.</li>
<li><code>options.values</code>, <code>meta</code>, <code>user</code>, <code>alarms</code>, and <code>locale</code> establish initial state.</li>
<li><code>queryHistory</code> and <code>queryAlarmHistory</code> can supply custom query results.</li>
<li><code>host.__mock.setTag(name, value, meta)</code> pushes a Tag change; <code>setAlarms(items)</code> pushes an alarm snapshot.</li>
<li>Delivery validation must return to Mingcedar Runtime and retest capability, ordinary-user permission, write confirmation, and real query limits.</li>
<li>Use the supplied development files for local Mock work. In production, load the SDK from the URL provided by Runtime; do not deploy the development copy of hq-common.js as a standalone package.</li>
</ul>
<div class="manual-code" data-code-language="html"><div class="manual-code-toolbar"><span>HTML</span><span class="manual-code-tools"><small>HTML</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: HTML">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> src</span><span style="color:#24292E">=</span><span style="color:#032F62">"./runtime-page-sdk-mock-host-v1.js"</span><span style="color:#24292E">></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E">window.</span><span style="color:#005CC5">HQVISU_HOST</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> createHQVisuMockHost</span><span style="color:#24292E">({</span></span>
<span class="line"><span style="color:#24292E"> values: { </span><span style="color:#032F62">"<device-code>.<process-tag-code>"</span><span style="color:#24292E">: </span><span style="color:#005CC5">72.4</span><span style="color:#24292E"> },</span></span>
<span class="line"><span style="color:#24292E"> meta: { </span><span style="color:#032F62">"<device-code>.<process-tag-code>"</span><span style="color:#24292E">: { quality: </span><span style="color:#032F62">"good"</span><span style="color:#24292E"> } },</span></span>
<span class="line"><span style="color:#24292E"> user: {</span></span>
<span class="line"><span style="color:#24292E"> username: </span><span style="color:#032F62">"<operator-name>"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> displayName: </span><span style="color:#032F62">"Shift Operator"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> permissions: []</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E">});</span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> src</span><span style="color:#24292E">=</span><span style="color:#032F62">"./hq-common.js"</span><span style="color:#24292E">></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> src</span><span style="color:#24292E">=</span><span style="color:#032F62">"./hq-custom-page.js"</span><span style="color:#24292E">></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-26">PPT and custom pages: one public read/write vocabulary</h3>
<p>Custom pages use api.ReadTagInteger / api.WriteTagInteger; PPT uses ReadTagInteger / WriteTagInteger. Case, parsing, quality fallbacks, dates, and result handling match. Replace old names when upgrading.</p>
<ul>
<li>Reads: ReadTag, ReadCalculatedTag, ReadTagBoolean/Integer/Double/String/Json/DateTime, ReadTagQuality, ReadTagQualityGood.</li>
<li>Writes: WriteTag, WriteTagBoolean/Integer/Double/String/Json/DateTime, WriteTagBit.</li>
<li>Reads return the current snapshot synchronously. Await writes and inspect the result. Business wrappers should preserve quality checks, permission checks, and command-result handling.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> api</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#005CC5"> HQVISU_CUSTOM_PAGE</span><span style="color:#24292E">.</span><span style="color:#6F42C1">ready</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> value</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagInteger</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<tag-code>"</span><span style="color:#24292E">, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6A737D">// PPT script reads the same name without the api prefix:</span></span>
<span class="line"><span style="color:#6A737D">// const value = ReadTagInteger("<device-code>.<tag-code>", null);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="custom-page-sdk-item-27">Error handling and delivery checks</h3>
<p>Use fallback and quality for unavailable reads, and await/try-catch for initialization, writes, history and alarms. Writes may have no return value; undefined is not success. Retain error.code, error.message and an available commandId, never capability secrets.</p>
<ul>
<li>Open the page as administrator and as an ordinary user to verify the permission boundary.</li>
<li>Try an undeclared Tag and verify a clear rejection or empty state without a blank page.</li>
<li>Simulate bad or stale quality and confirm the page does not display it as a normal value.</li>
<li>Verify history range, aggregate meaning, units, decimals, and chart explanations.</li>
<li>Verify writes include confirmation, failure messages, and an operation reason when required by the project.</li>
<li>Verify <code>outcome_uncertain</code> / <code>pending_verification</code> enters a blocking state and never retries automatically.</li>
<li>On component unmount, cancel its Tag, alarm, language and table subscriptions. Call api.destroy() only when the document permanently leaves (pagehide with !event.persisted), not during BFCache suspension or Vue unmount. Do not reuse subscriptions or pending requests after destruction.</li>
<li>Open the browser console and confirm there are no SDK initialization failures, 404s, JSON parse errors, or unhandled Promises.</li>
<li>Common errors: INVALID_ARGUMENT (input), CAPABILITY_DENIED (page scope/capability), PERMISSION_DENIED (user permission), NOT_FOUND, TIMEOUT, UNAVAILABLE, RUNTIME_WRITE_OUTCOME_UNCERTAIN and RUNTIME_PAGE_DESTROYED. Distinguish page readiness, service availability and Tag quality.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> history</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> api.history.</span><span style="color:#6F42C1">query</span><span style="color:#24292E">({</span></span>
<span class="line"><span style="color:#24292E"> tags: [</span><span style="color:#032F62">"<device-code>.<output-tag-code>"</span><span style="color:#24292E">],</span></span>
<span class="line"><span style="color:#24292E"> start,</span></span>
<span class="line"><span style="color:#24292E"> end,</span></span>
<span class="line"><span style="color:#24292E"> interval: </span><span style="color:#032F62">"1h"</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> aggregate: </span><span style="color:#032F62">"sum"</span></span>
<span class="line"><span style="color:#24292E"> });</span></span>
<span class="line"><span style="color:#6F42C1"> renderTrend</span><span style="color:#24292E">(history.items </span><span style="color:#D73A49">||</span><span style="color:#24292E"> []);</span></span>
<span class="line"><span style="color:#24292E">} </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (err) {</span></span>
<span class="line"><span style="color:#6F42C1"> renderEmptyState</span><span style="color:#24292E">(</span><span style="color:#032F62">"History query failed: "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> err.message);</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="sdk-native-module">Native HTML module: a complete page</h3>
<p>Save as equipment-overview.html and replace the Tag in both declaration and script. The mjs exports only ready and default {ready}; obtain methods such as ReadTagInteger from await ready(). The page is responsive and follows LANG.</p>
<details class="manual-code-disclosure"><summary>View complete source</summary><div class="manual-code" data-code-language="html"><div class="manual-code-toolbar"><span>HTML</span><span class="manual-code-tools"><small>HTML</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: HTML">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E"><!</span><span style="color:#22863A">doctype</span><span style="color:#6F42C1"> html</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">html</span><span style="color:#6F42C1"> lang</span><span style="color:#24292E">=</span><span style="color:#032F62">"en"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">head</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">meta</span><span style="color:#6F42C1"> charset</span><span style="color:#24292E">=</span><span style="color:#032F62">"utf-8"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">meta</span><span style="color:#6F42C1"> name</span><span style="color:#24292E">=</span><span style="color:#032F62">"viewport"</span><span style="color:#6F42C1"> content</span><span style="color:#24292E">=</span><span style="color:#032F62">"width=device-width, initial-scale=1"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">title</span><span style="color:#24292E">>Mingcedar equipment overview</</span><span style="color:#22863A">title</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#24292E">>window.</span><span style="color:#005CC5">HQVISU_PAGE_META</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> { layoutMode: </span><span style="color:#032F62">"native"</span><span style="color:#24292E"> };</</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"application/json"</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"hqvisu-custom-page"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> {"readableTags":["<device-code>.<process-tag-code>"],"writableTags":[]}</span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">head</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">body</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">h1</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"title"</span><span style="color:#24292E">>Equipment overview</</span><span style="color:#22863A">h1</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">output</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"value"</span><span style="color:#24292E">>—</</span><span style="color:#22863A">output</span><span style="color:#24292E">><</span><span style="color:#22863A">p</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"status"</span><span style="color:#6F42C1"> role</span><span style="color:#24292E">=</span><span style="color:#032F62">"status"</span><span style="color:#24292E">></</span><span style="color:#22863A">p</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"module"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#D73A49"> import</span><span style="color:#24292E"> { ready } </span><span style="color:#D73A49">from</span><span style="color:#032F62"> "/runtime/hqVisuDesigner/hq-custom-page.mjs"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> tag</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "<device-code>.<process-tag-code>"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#6F42C1"> element</span><span style="color:#D73A49"> =</span><span style="color:#E36209"> id</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(id);</span></span>
<span class="line"><span style="color:#D73A49"> let</span><span style="color:#24292E"> page, stopValue, stopLanguage, disposed </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> window.</span><span style="color:#6F42C1">addEventListener</span><span style="color:#24292E">(</span><span style="color:#032F62">"pagehide"</span><span style="color:#24292E">, </span><span style="color:#E36209">event</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (event.persisted) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> disposed </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1"> stopValue</span><span style="color:#24292E">?.(); </span><span style="color:#6F42C1">stopLanguage</span><span style="color:#24292E">?.(); page?.</span><span style="color:#6F42C1">destroy</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#24292E"> });</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E"> page </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> await</span><span style="color:#6F42C1"> ready</span><span style="color:#24292E">({ tags: [tag] });</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">disposed) {</span></span>
<span class="line"><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> render</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> zh</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> page.language.</span><span style="color:#6F42C1">current</span><span style="color:#24292E">().</span><span style="color:#6F42C1">startsWith</span><span style="color:#24292E">(</span><span style="color:#032F62">"zh"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E"> document.documentElement.lang </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.language.</span><span style="color:#6F42C1">current</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"title"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> zh </span><span style="color:#D73A49">?</span><span style="color:#032F62"> "设备总览"</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> "Equipment overview"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> quality</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">ReadTagQuality</span><span style="color:#24292E">(tag);</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> value</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">ReadTagDouble</span><span style="color:#24292E">(tag, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"value"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> quality </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "good"</span><span style="color:#D73A49"> &&</span><span style="color:#24292E"> value </span><span style="color:#D73A49">!==</span><span style="color:#005CC5"> null</span><span style="color:#D73A49"> ?</span><span style="color:#6F42C1"> String</span><span style="color:#24292E">(value) </span><span style="color:#D73A49">:</span><span style="color:#032F62"> "—"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1"> element</span><span style="color:#24292E">(</span><span style="color:#032F62">"status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (zh </span><span style="color:#D73A49">?</span><span style="color:#032F62"> "数据质量:"</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> "Quality: "</span><span style="color:#24292E">) </span><span style="color:#D73A49">+</span><span style="color:#24292E"> quality;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#6F42C1"> render</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#24292E"> stopValue </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">subscribe</span><span style="color:#24292E">([tag], render);</span></span>
<span class="line"><span style="color:#24292E"> stopLanguage </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.language.</span><span style="color:#6F42C1">onChange</span><span style="color:#24292E">(render);</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">disposed) </span><span style="color:#6F42C1">element</span><span style="color:#24292E">(</span><span style="color:#032F62">"status"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (error.code </span><span style="color:#D73A49">?</span><span style="color:#24292E"> error.code </span><span style="color:#D73A49">+</span><span style="color:#032F62"> ": "</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> ""</span><span style="color:#24292E">) </span><span style="color:#D73A49">+</span><span style="color:#24292E"> error.message;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">body</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">html</span><span style="color:#24292E">></span></span></code></pre></div></details>
</article>
<article class="doc-card">
<h3 id="sdk-vue-entry">Vue integration 1: declare scope in entry HTML</h3>
<p>Use this index.html in an existing Vue 3/Vite project. Bundle Vue from project dependencies; no CDN is needed. readableTags and writableTags must match the component prop. Call ready after onMounted in SSR, otherwise it rejects with RUNTIME_PAGE_BROWSER_REQUIRED.</p>
<div class="manual-code" data-code-language="html"><div class="manual-code-toolbar"><span>HTML</span><span class="manual-code-tools"><small>HTML</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: HTML">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D"><!-- Vite project root: index.html --></span></span>
<span class="line"><span style="color:#24292E"><!</span><span style="color:#22863A">doctype</span><span style="color:#6F42C1"> html</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">html</span><span style="color:#6F42C1"> lang</span><span style="color:#24292E">=</span><span style="color:#032F62">"en"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">head</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">meta</span><span style="color:#6F42C1"> charset</span><span style="color:#24292E">=</span><span style="color:#032F62">"utf-8"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">meta</span><span style="color:#6F42C1"> name</span><span style="color:#24292E">=</span><span style="color:#032F62">"viewport"</span><span style="color:#6F42C1"> content</span><span style="color:#24292E">=</span><span style="color:#032F62">"width=device-width, initial-scale=1"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">title</span><span style="color:#24292E">>Mingcedar equipment monitor</</span><span style="color:#22863A">title</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#24292E">>window.</span><span style="color:#005CC5">HQVISU_PAGE_META</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> { layoutMode: </span><span style="color:#032F62">"native"</span><span style="color:#24292E"> };</</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"application/json"</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"hqvisu-custom-page"</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> {"readableTags":["<device-code>.<setpoint-tag-code>"],</span></span>
<span class="line"><span style="color:#24292E"> "writableTags":["<device-code>.<setpoint-tag-code>"]}</span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">head</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">body</span><span style="color:#24292E">><</span><span style="color:#22863A">div</span><span style="color:#6F42C1"> id</span><span style="color:#24292E">=</span><span style="color:#032F62">"app"</span><span style="color:#24292E">></</span><span style="color:#22863A">div</span><span style="color:#24292E">><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"module"</span><span style="color:#6F42C1"> src</span><span style="color:#24292E">=</span><span style="color:#032F62">"/src/main.js"</span><span style="color:#24292E">></</span><span style="color:#22863A">script</span><span style="color:#24292E">></</span><span style="color:#22863A">body</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">html</span><span style="color:#24292E">></span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="sdk-vue-main">Vue integration 2: mount the component</h3>
<p>Save as src/main.js. Match the prop, entry declaration and component binding to the approved numeric setpoint Tag. Remove write declarations and controls for a read-only project.</p>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D">// src/main.js — use the Vue dependency installed in your project.</span></span>
<span class="line"><span style="color:#D73A49">import</span><span style="color:#24292E"> { createApp } </span><span style="color:#D73A49">from</span><span style="color:#032F62"> "vue"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">import</span><span style="color:#24292E"> EquipmentPanel </span><span style="color:#D73A49">from</span><span style="color:#032F62"> "./EquipmentPanel.vue"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">createApp</span><span style="color:#24292E">(EquipmentPanel, {</span></span>
<span class="line"><span style="color:#24292E"> tagName: </span><span style="color:#032F62">"<device-code>.<setpoint-tag-code>"</span></span>
<span class="line"><span style="color:#24292E">}).</span><span style="color:#6F42C1">mount</span><span style="color:#24292E">(</span><span style="color:#032F62">"#app"</span><span style="color:#24292E">);</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="sdk-vue-component">Vue integration 3: read, write once, LANG and unmount</h3>
<p>Save as src/EquipmentPanel.vue. On unmount, cancel only component subscriptions; stop setup if unmounted while awaiting. Submission disables the button, but reloads or remounts reset it. Reconcile the previous command and normal collected value before another write; persist and restore pending-verification state if blocking must survive reloads.</p>
<details class="manual-code-disclosure"><summary>View complete source</summary><div class="manual-code" data-code-language="vue"><div class="manual-code-toolbar"><span>Vue</span><span class="manual-code-tools"><small>Vue</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: Vue">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">script</span><span style="color:#6F42C1"> setup</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#D73A49">import</span><span style="color:#24292E"> { onMounted, onUnmounted, ref } </span><span style="color:#D73A49">from</span><span style="color:#032F62"> "vue"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> props</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> defineProps</span><span style="color:#24292E">({ tagName: { type: String, required: </span><span style="color:#005CC5">true</span><span style="color:#24292E"> } });</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> tag</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> props.tagName; </span><span style="color:#6A737D">// Remount with a new key when changing this Tag.</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> value</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> ref</span><span style="color:#24292E">(</span><span style="color:#005CC5">null</span><span style="color:#24292E">), </span><span style="color:#005CC5">quality</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> ref</span><span style="color:#24292E">(</span><span style="color:#032F62">"bad"</span><span style="color:#24292E">), </span><span style="color:#005CC5">locale</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> ref</span><span style="color:#24292E">(</span><span style="color:#032F62">"en-US"</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> target</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> ref</span><span style="color:#24292E">(</span><span style="color:#032F62">""</span><span style="color:#24292E">), </span><span style="color:#005CC5">message</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> ref</span><span style="color:#24292E">(</span><span style="color:#032F62">""</span><span style="color:#24292E">), </span><span style="color:#005CC5">canWrite</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> ref</span><span style="color:#24292E">(</span><span style="color:#005CC5">false</span><span style="color:#24292E">), </span><span style="color:#005CC5">submitted</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> ref</span><span style="color:#24292E">(</span><span style="color:#005CC5">false</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">let</span><span style="color:#24292E"> page, stopValue, stopLanguage, disposed </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">, allowed </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">const</span><span style="color:#6F42C1"> text</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> (</span><span style="color:#E36209">zh</span><span style="color:#24292E">, </span><span style="color:#E36209">en</span><span style="color:#24292E">) </span><span style="color:#D73A49">=></span><span style="color:#24292E"> locale.value.</span><span style="color:#6F42C1">startsWith</span><span style="color:#24292E">(</span><span style="color:#032F62">"zh"</span><span style="color:#24292E">) </span><span style="color:#D73A49">?</span><span style="color:#24292E"> zh </span><span style="color:#D73A49">:</span><span style="color:#24292E"> en;</span></span>
<span class="line"><span style="color:#D73A49">function</span><span style="color:#6F42C1"> render</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#24292E"> value.value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">ReadTagDouble</span><span style="color:#24292E">(tag, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E"> quality.value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">ReadTagQuality</span><span style="color:#24292E">(tag);</span></span>
<span class="line"><span style="color:#24292E"> canWrite.value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> allowed </span><span style="color:#D73A49">&&</span><span style="color:#24292E"> quality.value </span><span style="color:#D73A49">===</span><span style="color:#032F62"> "good"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#6F42C1">onMounted</span><span style="color:#24292E">(</span><span style="color:#D73A49">async</span><span style="color:#24292E"> () </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> sdkUrl</span><span style="color:#D73A49"> =</span><span style="color:#032F62"> "/runtime/hqVisuDesigner/hq-custom-page.mjs"</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#24292E"> { </span><span style="color:#005CC5">ready</span><span style="color:#24292E"> } </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> await</span><span style="color:#6F42C1"> import</span><span style="color:#24292E">(</span><span style="color:#6A737D">/* @vite-ignore */</span><span style="color:#24292E"> sdkUrl);</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (disposed) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> api</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#6F42C1"> ready</span><span style="color:#24292E">({ tags: [tag] });</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (disposed) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> page </span><span style="color:#D73A49">=</span><span style="color:#24292E"> api;</span></span>
<span class="line"><span style="color:#24292E"> locale.value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.language.</span><span style="color:#6F42C1">current</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#24292E"> stopLanguage </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.language.</span><span style="color:#6F42C1">onChange</span><span style="color:#24292E">(</span><span style="color:#E36209">next</span><span style="color:#D73A49"> =></span><span style="color:#24292E"> { </span><span style="color:#D73A49">if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">disposed) locale.value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> next; });</span></span>
<span class="line"><span style="color:#6F42C1"> render</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#24292E"> stopValue </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">subscribe</span><span style="color:#24292E">([tag], render);</span></span>
<span class="line"><span style="color:#24292E"> allowed </span><span style="color:#D73A49">=</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">supports</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag.write"</span><span style="color:#24292E">) </span><span style="color:#D73A49">&&</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> page.auth.</span><span style="color:#6F42C1">hasPermission</span><span style="color:#24292E">(</span><span style="color:#032F62">"tag:write"</span><span style="color:#24292E">) </span><span style="color:#D73A49">===</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">disposed) </span><span style="color:#6F42C1">render</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">disposed) message.value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> error.message;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#24292E">});</span></span>
<span class="line"><span style="color:#6F42C1">onUnmounted</span><span style="color:#24292E">(() </span><span style="color:#D73A49">=></span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E"> disposed </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1"> stopValue</span><span style="color:#24292E">?.(); </span><span style="color:#6F42C1">stopLanguage</span><span style="color:#24292E">?.();</span></span>
<span class="line"><span style="color:#6A737D"> // Do not destroy the shared document SDK on component unmount.</span></span>
<span class="line"><span style="color:#24292E">});</span></span>
<span class="line"><span style="color:#D73A49">async</span><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> write</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (submitted.value </span><span style="color:#D73A49">||</span><span style="color:#D73A49"> !</span><span style="color:#24292E">canWrite.value) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> input</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> String</span><span style="color:#24292E">(target.value).</span><span style="color:#6F42C1">trim</span><span style="color:#24292E">(), </span><span style="color:#005CC5">next</span><span style="color:#D73A49"> =</span><span style="color:#6F42C1"> Number</span><span style="color:#24292E">(input);</span></span>
<span class="line"><span style="color:#6A737D"> // Replace 0–100 with the approved engineering range for this Tag.</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">input </span><span style="color:#D73A49">||</span><span style="color:#D73A49"> !</span><span style="color:#24292E">Number.</span><span style="color:#6F42C1">isFinite</span><span style="color:#24292E">(next) </span><span style="color:#D73A49">||</span><span style="color:#24292E"> next </span><span style="color:#D73A49"><</span><span style="color:#005CC5"> 0</span><span style="color:#D73A49"> ||</span><span style="color:#24292E"> next </span><span style="color:#D73A49">></span><span style="color:#005CC5"> 100</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#24292E"> message.value </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> text</span><span style="color:#24292E">(</span><span style="color:#032F62">"请输入 0–100 的数值"</span><span style="color:#24292E">, </span><span style="color:#032F62">"Enter a number from 0 to 100"</span><span style="color:#24292E">); </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">page.</span><span style="color:#6F42C1">ReadTagQualityGood</span><span style="color:#24292E">(tag)) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">window.</span><span style="color:#6F42C1">confirm</span><span style="color:#24292E">(tag </span><span style="color:#D73A49">+</span><span style="color:#032F62"> ": "</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> next)) </span><span style="color:#D73A49">return</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E"> submitted.value </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49"> try</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> result</span><span style="color:#D73A49"> =</span><span style="color:#D73A49"> await</span><span style="color:#24292E"> page.</span><span style="color:#6F42C1">WriteTagDouble</span><span style="color:#24292E">(tag, next, { reason: </span><span style="color:#032F62">"Operator setpoint update"</span><span style="color:#24292E"> });</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">disposed) message.value </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> text</span><span style="color:#24292E">(</span><span style="color:#032F62">"命令回执(需核对正常采集值):"</span><span style="color:#24292E">, </span><span style="color:#032F62">"Command receipt (verify normal collected value): "</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> +</span><span style="color:#005CC5"> JSON</span><span style="color:#24292E">.</span><span style="color:#6F42C1">stringify</span><span style="color:#24292E">(result </span><span style="color:#D73A49">??</span><span style="color:#005CC5"> null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">catch</span><span style="color:#24292E"> (error) {</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">disposed) message.value </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> text</span><span style="color:#24292E">(</span><span style="color:#032F62">"结果未确认,核对后再操作:"</span><span style="color:#24292E">, </span><span style="color:#032F62">"Result unconfirmed; reconcile before another operation: "</span><span style="color:#24292E">) </span><span style="color:#D73A49">+</span><span style="color:#24292E"> error.message;</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#6A737D"> // Keep submission blocked until the operator reconciles command and collected state.</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">script</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"><</span><span style="color:#22863A">template</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">section</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">h1</span><span style="color:#24292E">>{{ text("设备设定值", "Equipment setpoint") }}</</span><span style="color:#22863A">h1</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">p</span><span style="color:#24292E">>{{ tag }}: {{ quality === "good" ? value : "—" }} · {{ quality }}</</span><span style="color:#22863A">p</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">label</span><span style="color:#24292E">>{{ text("目标值(0–100)", "Target (0–100)") }} <</span><span style="color:#22863A">input</span><span style="color:#6F42C1"> v-model</span><span style="color:#24292E">=</span><span style="color:#032F62">"target"</span><span style="color:#6F42C1"> type</span><span style="color:#24292E">=</span><span style="color:#032F62">"number"</span><span style="color:#6F42C1"> step</span><span style="color:#24292E">=</span><span style="color:#032F62">"0.1"</span><span style="color:#24292E">></</span><span style="color:#22863A">label</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">button</span><span style="color:#6F42C1"> :disabled</span><span style="color:#24292E">=</span><span style="color:#032F62">"!canWrite || submitted"</span><span style="color:#6F42C1"> @click</span><span style="color:#24292E">=</span><span style="color:#032F62">"write"</span><span style="color:#24292E">>{{ text("写入一次", "Write once") }}</</span><span style="color:#22863A">button</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> <</span><span style="color:#22863A">pre</span><span style="color:#6F42C1"> role</span><span style="color:#24292E">=</span><span style="color:#032F62">"status"</span><span style="color:#24292E">>{{ message }}</</span><span style="color:#22863A">pre</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"> </</span><span style="color:#22863A">section</span><span style="color:#24292E">></span></span>
<span class="line"><span style="color:#24292E"></</span><span style="color:#22863A">template</span><span style="color:#24292E">></span></span></code></pre></div></details>
</article>
<article class="doc-card">
<h3 id="sdk-vue-deploy">Vue integration 4: build and publish</h3>
<p>Keep the existing Vue plugin and set Vite base: "./" so output can live under any assets subfolder. The SDK uses an absolute /runtime/ service URL, independent of the relative application asset base.</p>
<ol>
<li>Run the project npm run build; confirm dist/index.html retains the hqvisu-custom-page static JSON declaration.</li>
<li>Upload dist contents with the original hierarchy into one Screen Files directory, including index.html and built assets/*.js/CSS. Uploading only .vue sources or only index.html is insufficient.</li>
<li>Point the runtime menu to that directory index.html. Check SDK readiness, GOOD values, LANG changes, remounting and actual operation results.</li>
</ol>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D">// In the existing vite.config.js:</span></span>
<span class="line"><span style="color:#D73A49">export</span><span style="color:#D73A49"> default</span><span style="color:#6F42C1"> defineConfig</span><span style="color:#24292E">({ plugins: [</span><span style="color:#6F42C1">vue</span><span style="color:#24292E">()], base: </span><span style="color:#032F62">"./"</span><span style="color:#24292E"> });</span></span></code></pre></div>
</article>
<article class="doc-card">
<h3 id="sdk-calendar-values">Dates and large integers: preserve meaning</h3>
<p>DATE_AND_TIME, DTL and LDT metadata denotes a timezone-free PLC calendar. ReadTagDateTime returns a Date carrying those calendar fields in UTC, not an actual UTC instant; read fields with getUTC*. WriteTagDateTime(Date) writes the same UTC fields without Z. Other instant-style Date values retain ISO instant semantics.</p>
<ul>
<li>A PLC calendar value 08:30 must remain 08:30 in Shanghai and other zones; do not interpret that calendar container with local getters or toLocaleString.</li>
<li>Date preserves milliseconds only; retain original text for DTL/LDT sub-millisecond precision. Date validity, year and encoding ranges remain device-type checks.</li>
<li>For integers outside JavaScript safe range, preserve text through ReadTag/ReadTagString and pass decimal text or BigInt to WriteTagInteger; do not first convert through Number.</li>
</ul>
<div class="manual-code" data-code-language="javascript"><div class="manual-code-toolbar"><span>JavaScript</span><span class="manual-code-tools"><small>JavaScript</small><button type="button" data-copy-code data-copied="Copied" data-failed="Select and copy the code" aria-label="Copy code: JavaScript">Copy code</button><span class="manual-code-status" role="status"></span></span></div><pre tabindex="0" class="docs-code-block shiki github-light" style="background-color:#fff;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> date</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> api.</span><span style="color:#6F42C1">ReadTagDateTime</span><span style="color:#24292E">(</span><span style="color:#032F62">"<device-code>.<datetime-tag-code>"</span><span style="color:#24292E">, </span><span style="color:#005CC5">null</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (date) {</span></span>
<span class="line"><span style="color:#D73A49"> const</span><span style="color:#005CC5"> fields</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> [date.</span><span style="color:#6F42C1">getUTCFullYear</span><span style="color:#24292E">(), date.</span><span style="color:#6F42C1">getUTCMonth</span><span style="color:#24292E">() </span><span style="color:#D73A49">+</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">, date.</span><span style="color:#6F42C1">getUTCDate</span><span style="color:#24292E">(),</span></span>
<span class="line"><span style="color:#24292E"> date.</span><span style="color:#6F42C1">getUTCHours</span><span style="color:#24292E">(), date.</span><span style="color:#6F42C1">getUTCMinutes</span><span style="color:#24292E">(), date.</span><span style="color:#6F42C1">getUTCSeconds</span><span style="color:#24292E">()];</span></span>
<span class="line"><span style="color:#24292E"> document.</span><span style="color:#6F42C1">getElementById</span><span style="color:#24292E">(</span><span style="color:#032F62">"calendar"</span><span style="color:#24292E">).textContent </span><span style="color:#D73A49">=</span><span style="color:#24292E"> fields.</span><span style="color:#6F42C1">join</span><span style="color:#24292E">(</span><span style="color:#032F62">" / "</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre></div>
</article>
</div>
</div>