Use IO State API or WebSocket when a third party needs live state. Use Integration Script for local computation, Tag bridging, signed inbound triggers, or declared external connections. Both paths preserve HQControl authentication, scope, quality, and audit boundaries.
IO State API: start with a dedicated key and one tag
- Confirm the full code
<device-code>.<tag-code> in Tag Management.
- Create a dedicated client key under IO State API → Key Management, with an expiry and minimum scope.
- Query one
fullCodes target first and verify value, quality, source, and timestamp.
- Expand to a device or group only after that check; use WebSocket for continuous updates and test reconnection.
X-IO-State-Key: <access-key>
GET /api/v1/integration/io-state?fullCodes=<device-code>.<tag-code>
GET /api/v1/integration/io-state?deviceCodes=<device-code>&qualities=good
WebSocket /api/v1/integration/io-state/subscribe
IO State scope and network boundary
- Use
fullCodes for exact targets. Device, tag, area, object, group, and quality filters cover broader reads.
- Scope function, equipment, and signal groups as
<device-code>.<group-code> so a same-named group on another device is not included.
- Third parties access only the HQControl main service. Database, Redis, and Integration Script internal services are not public endpoints.
- Use VPN, a private link, or an HTTPS reverse proxy across networks, and enable WebSocket Upgrade on the proxy.
- Phase 1 calculated-tag values are not exposed through the IO State API.
What Integration Script officially supports
Integration Script is a HQControl-managed integration runtime. Python code performs local calculations, JSON and time handling, Tag operations, logging, and exception handling inside an isolated environment. Host-managed HTTP and PostgreSQL connectors perform declared external access. Scripts do not receive direct PLC, host database, Redis, or raw network access.
- Triggers: manual / diagnostics, schedule, signed HTTP inbound, and IO change events.
- Tag methods:
ReadTag, ReadTags, typed ReadTag*, quality helpers, WriteTag, and typed WriteTag*.
- Managed external methods:
HTTPRequest, SQLQuery, and SQLExecute.
- The formal module name for reusable helpers is
hqcontrol.
Create and validate a script
- Create a meaningfully named script in Integration Script and set timeout, concurrency, and cooldown.
- Start with read-only logic and inspect manual or diagnostic output and recent executions.
- Add a schedule, signed HTTP inbound trigger, or IO change trigger with explicit input and quality rules.
- Test Tag writes or external side effects against controlled targets before enabling automatic triggers.
Declare a managed HTTP connector
Open Connectors on the right, add HTTP, and use a meaningful name such as production-reporting-api. Enter only scheme, host, and port in Origin; remote targets require HTTPS and only loopback may use HTTP. Configure allowed methods, timeout, concurrency, and body limits. Map sensitive headers to HQCONTROL_CONNECTOR_... host environment variables.
The example below assumes a signed HTTP inbound trigger and uses its stable ctx["requestId"] as the idempotency key.
tag_ref = "hq://io/<io-domain-id>/<device-uid>/<tag-uid>"
tag = ReadTag(tag_ref)
if tag and tag.get("quality") == "good":
response = HTTPRequest(
"production-reporting-api",
"POST",
"/v1/measurements",
body={
"tagRef": tag_ref,
"value": tag.get("valueNum"),
"sourceTs": tag.get("sourceTs"),
},
idempotency_key=ctx["requestId"],
)
Declare a managed PostgreSQL connector
Add PostgreSQL with a meaningful name such as production-reporting-database. The DSN field contains only an HQCONTROL_CONNECTOR_... environment variable name. Set query timeout, maximum rows, response size, and concurrency. Explicitly enable writes before using SQLExecute.
This example also assumes signed HTTP inbound: it queries a plan by inbound planId, then records a Good-quality measurement.
tag_ref = "hq://io/<io-domain-id>/<device-uid>/<tag-uid>"
tag = ReadTag(tag_ref)
rows = SQLQuery(
"production-reporting-database",
"select shift_code, target_quantity from production_plan where plan_id = $1",
[ctx["payload"]["planId"]],
)
if tag and tag.get("quality") == "good":
result = SQLExecute(
"production-reporting-database",
"insert into measurement_log(tag_ref, value_num) values ($1, $2)",
[tag_ref, tag.get("valueNum")],
idempotency_key=ctx["requestId"],
)
Dry-run and Debug have different effects
- Dry-run blocks
WriteTag*, state-changing HTTP, and SQLExecute.
ReadTag*, HTTP GET / HEAD, and SQLQuery still perform real reads during Dry-run.
- Debug uses real execution semantics and can write Tags, call external HTTP, and modify a database. Confirm every target first.
- Dry-run, Debug, and automatic triggers use the same production sandbox and connector policy.
Production boundaries
- The sandbox blocks raw Python network access. Use declared managed connectors; the formal connector types are HTTP and PostgreSQL.
- Every non-GET / HEAD HTTP request and every
SQLExecute requires a stable idempotency key.
- When a dispatched side effect has an uncertain outcome, HQControl does not retry it automatically. Verify the target using the execution identity.
- Production execution cannot install dependencies dynamically. The interpreter and dependencies come from the audited, locked, and verified release package.
- Per-script Python, venv, or custom working-directory settings do not change the formal interpreter; custom working directories are rejected.