- Jelina overview
- EMS Builder
- Integration and coordination
Real-Time Frontend Push and Direct Control
The execution step in the architecture overview includes not only point writes, alarms, and MQTT, but direct service to frontend interfaces. Edge-EMS Builder provides two WebSocket channels that let custom frontends receive real-time data pushes and send control commands directly to devices, without developing an extra intermediate layer.
1. Two WebSocket channels
Section titled “1. Two WebSocket channels”| Channel | Path | Authentication | Purpose |
|---|---|---|---|
| Frontend push channel | ws://<host>:<port>/front |
Login Token obtained with account/password authentication | Custom frontends: role-based authorization, subscriptions and direct writes |
| Data read/write channel | ws://<host>:<port>/data/rw |
Static data-rw-token configured in ems.yaml |
Backend integrations: low-latency reads/writes independent of login, suitable for system-to-system integration |
Both channels use identical message formats, differing only in authentication and permission-check granularity. This page focuses on /front; /data/rw follows the same usage pattern.
Pass connection parameters through the query string:
ws://<host>:<port>/front?socketId=<custom-connection-ID>&token=<login-Token>ws://<host>:<port>/data/rw?socketId=<custom-connection-ID>&token=<data-rw-token>2. Message format
Section titled “2. Message format”Requests (frontend → Edge-EMS Builder) share this structure:
{ "topic": "read | write | clear", "messageId": "Custom unique ID for matching the response", "keep": true, "content": [ { "dataKey": "pcsActivePower" } ]}Responses (Edge-EMS Builder → frontend) correspond to requests through messageId:
{ "code": "200", "message": "Processed successfully", "topic": "read", "messageId": "...", "time": "2026-07-23T10:00:00+08:00", "data": { "...": "..." }}3. Receiving real-time pushes (subscription = read + keep)
Section titled “3. Receiving real-time pushes (subscription = read + keep)”Edge-EMS Builder’s push model is not topic-based publish/subscribe. Instead, each connection declares its own push configuration:
- The frontend sends
topic: "read", lists requireddataKeyvalues incontent(or usesgroupCodefor an entire group), and setskeeptotrue. - Edge-EMS Builder stores
contentas that connection’s (socketId) push configuration and immediately returns current values. - A background task then visits all connected frontend sockets every 1 second, pushing the latest values from each connection’s configuration. The frontend need not repeatedly poll.
- If continuous pushing is unnecessary, set
keeptofalseor omit it; Edge-EMS Builder pushes for only 30s. - To cancel a connection’s push configuration, send
topic: "clear".
// Subscribe continuously to the latest pcsActivePower and bmsSOC values{ "topic": "read", "messageId": "sub-001", "keep": true, "content": [ { "dataKey": "pcsActivePower" }, { "dataKey": "bmsSOC" } ]}
// Or subscribe by group to all its points, including child groups{ "topic": "read", "messageId": "sub-002", "keep": true, "content": [ { "groupCode": "pcs-group-01" } ]}A connection can send
read+keep:truemultiple times to append or update its push configuration. At least one ofdataKeyorgroupCodeis required.
4. Directly controlling devices (write)
Section titled “4. Directly controlling devices (write)”The frontend sends topic: "write" with dataKey and dataValue in content. No data type is required: Edge-EMS Builder identifies Collection, Aggregation, or Customization from dataKey and routes to the appropriate write logic:
{ "topic": "write", "messageId": "ctrl-001", "content": [ { "dataKey": "pcsActivePower", "dataValue": "300" } ]}Writes to time-scheduled data (Customization) can include an effective interval:
{ "topic": "write", "messageId": "ctrl-002", "resetCustomValue": true, "content": [ { "dataKey": "import_price", "dataValue": "0.85", "availableDate": "2026-06-25T00:00:00+08:00", "expiredDate": "2026-06-25T08:00:00+08:00" } ]}resetCustomValue: trueclears existing values for that time-scheduled point before writing the new content. Use it for whole-schedule replacement to avoid overlapping old and new periods.- To write points on other shared devices, add
deviceCodeto the relevantcontentitem. Edge-EMS Builder recognizes a shared-data write and routes it to the target device.
Permission rules
Section titled “Permission rules”/data/rwconnections using the staticdata-rw-tokenare accepted directly without role checks, making them suitable for backend system-to-system integration./frontconnections using a login Token verify that the account has a super-administrator, administrator, or operator role. Observers cannot write. This matches page-level permissions; no write path bypasses frontend permissions.
Daily repeating schedules
Section titled “Daily repeating schedules”DAILY_REPEAT uses availableTime / expiredTime, such as 08:00:00–12:00:00, with the overall effective date range configured on the time-scheduled point. Only DAILY_CHANGE uses full date-and-time intervals. Do not mix the parameters; split intervals that cross midnight.
Connection and execution results
Section titled “Connection and execution results”/front checks connections with Ping/Pong heartbeats. Clients must handle close code/message values and reestablish subscriptions after reconnection. Do not automatically replay write requests along with subscriptions.
Device write results depend on CommBridge acknowledgments or timeouts; successful sending alone does not establish device execution. Device control logs show processing, success, and failure states. CommBridge performs inverse conversion of acquisition write coefficients; callers must not convert again.
Protected control writes return 200162 during maintenance freeze. Do not automatically exit freeze and retry.
5. Complete frontend integration example
Section titled “5. Complete frontend integration example”const ws = new WebSocket(`ws://<host>:<port>/front?socketId=web-ui-01&token=${loginToken}`);
ws.onopen = () => { // Subscribe to real-time PCS active power and SOC ws.send(JSON.stringify({ topic: 'read', messageId: 'sub-' + Date.now(), keep: true, content: [ { dataKey: 'pcsActivePower' }, { dataKey: 'bmsSOC' } ] }));};
ws.onmessage = (event) => { const resp = JSON.parse(event.data); if (resp.topic === 'read') { // Receive latest values every second and update interface dashboards/charts renderDashboard(resp.data); } if (resp.topic === 'write') { // Write-result feedback: resp.successContent / resp.errorContent showWriteResult(resp); }};
// When the user clicks "Set power to 300kW" in the interface:function setPower(value) { ws.send(JSON.stringify({ topic: 'write', messageId: 'ctrl-' + Date.now(), content: [{ dataKey: 'pcsActivePower', dataValue: String(value) }] }));}This code covers the typical frontend loop: subscribe when the page loads → refresh automatically every second → send control commands directly from user actions → receive write-result feedback. No additional backend relay service is required.
6. Relationship to other capabilities
Section titled “6. Relationship to other capabilities”- Data written through
/frontor/data/rwis the same data read by trigger schemes and the expression engine. After acquisition reads back the final device outcome, it can participate in aggregation expressions and trigger evaluation. See Data model. - To also push data to cloud dashboards, both paths can coexist: use
/frontfor your own interface and MQTT cloud-edge coordination to report the same data to the cloud. - The
aems-write-dataAI configuration Skill encapsulates/data/rwand/front, allowing natural-language requests for an AI Agent to help test write commands.
Next steps
Section titled “Next steps”- For the complete HTTP/WebSocket API list, request the Edge-EMS Builder HTTP API Documentation from technical support.
- See how written data is used by the expression engine and trigger schemes.
- For cross-device writes, learn about Device coordination and shared targets.