@c8y/ngx-components 1024.21.3 → 1024.21.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs +82 -19
  2. package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs.map +1 -1
  3. package/fesm2022/{c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-1WsTSnO_.mjs → c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-B0EYpohd.mjs} +31 -17
  4. package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-B0EYpohd.mjs.map +1 -0
  5. package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-C3KZGqlc.mjs → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BqbzREl7.mjs} +2 -2
  6. package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-C3KZGqlc.mjs.map → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BqbzREl7.mjs.map} +1 -1
  7. package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-d3JeKEr4.mjs +108 -0
  8. package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-d3JeKEr4.mjs.map +1 -0
  9. package/fesm2022/c8y-ngx-components-computed-asset-properties.mjs +1 -1
  10. package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs +93 -9
  11. package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs.map +1 -1
  12. package/locales/de.po +0 -3
  13. package/locales/es.po +0 -3
  14. package/locales/fr.po +0 -3
  15. package/locales/ja_JP.po +0 -3
  16. package/locales/ko.po +0 -3
  17. package/locales/locales.pot +9 -0
  18. package/locales/nl.po +0 -3
  19. package/locales/pl.po +0 -3
  20. package/locales/pt_BR.po +0 -3
  21. package/locales/zh_CN.po +0 -3
  22. package/locales/zh_TW.po +0 -3
  23. package/package.json +1 -1
  24. package/types/c8y-ngx-components-ai-agents-scada.d.ts.map +1 -1
  25. package/types/c8y-ngx-components-widgets-implementations-scada.d.ts +28 -2
  26. package/types/c8y-ngx-components-widgets-implementations-scada.d.ts.map +1 -1
  27. package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-1WsTSnO_.mjs.map +0 -1
  28. package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-DOmwNU6m.mjs +0 -86
  29. package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-DOmwNU6m.mjs.map +0 -1
@@ -108,6 +108,41 @@ For direct mappings to device properties (like name, ID, status, or custom prope
108
108
  }
109
109
  \`\`\`
110
110
 
111
+ For a field nested inside a fragment (e.g. \`c8y_Address.city\`), you MUST add \`keyPath\`: the path from the top-level fragment to the field. \`name\` is the last segment only. Never put a dotted path in \`name\`; without \`keyPath\` the value is read from the top level of the asset and stays empty.
112
+
113
+ \`\`\`json
114
+ {
115
+ "city": {
116
+ "assetProperty": {
117
+ "isEditable": true,
118
+ "c8y_JsonSchema": {
119
+ "properties": {
120
+ "c8y_Address": {
121
+ "properties": {
122
+ "city": {
123
+ "label": "City",
124
+ "type": "string",
125
+ "key": "city",
126
+ "properties": {}
127
+ }
128
+ }
129
+ }
130
+ }
131
+ },
132
+ "name": "city",
133
+ "keyPath": ["c8y_Address", "city"],
134
+ "active": true,
135
+ "label": "City",
136
+ "type": "string",
137
+ "asset": {
138
+ "id": 94130201,
139
+ "name": "Temperature #1"
140
+ }
141
+ }
142
+ }
143
+ }
144
+ \`\`\`
145
+
111
146
  #### 2. Computed Property Mapping (e.g. Last Measurement)
112
147
 
113
148
  For measurements or computed properties (like last measurement, alarms count, etc.):
@@ -146,7 +181,8 @@ For measurements or computed properties (like last measurement, alarms count, et
146
181
  "yAxisType": null
147
182
  }
148
183
  ],
149
- "resultType": 4
184
+ "resultType": 4,
185
+ "rawValue": true
150
186
  },
151
187
  "computed": true,
152
188
  "isEditable": false,
@@ -399,6 +435,7 @@ Besides "Last measurement", Cumulocity supports several other computed propertie
399
435
  - **label**: A descriptive label
400
436
  - **__target**: Reference to the target device with name and id
401
437
  - **resultType**: MUST always be \`4\` — this is the only value that returns the complete measurement object, giving access to \`.value\` and \`.unit\` sub-properties
438
+ - **rawValue**: MUST always be \`true\`, so \`.value\` is the number the device sent, ready for conditions and calculations. Formatting for display happens in the SVG template (see "numeric formatting")
402
439
 
403
440
  5. **Generate Unique Instance IDs**: For computed properties, include a unique "instanceId" (use UUID format)
404
441
 
@@ -451,7 +488,8 @@ For an SVG that uses placeholders \${this.c8yScadaValues?.temperature?.value}, \
451
488
  }
452
489
  }
453
490
  ],
454
- "resultType": 4
491
+ "resultType": 4,
492
+ "rawValue": true
455
493
  },
456
494
  "asset": {
457
495
  "id": 94130201,
@@ -498,7 +536,8 @@ For an SVG that uses placeholders \${this.c8yScadaValues?.temperature?.value}, \
498
536
  }
499
537
  }
500
538
  ],
501
- "resultType": 4
539
+ "resultType": 4,
540
+ "rawValue": true
502
541
  },
503
542
  "asset": {
504
543
  "id": 94130201,
@@ -551,20 +590,20 @@ You can make your SVG dynamic by binding to data values using template literal s
551
590
  #### 1. For Device Measurements (with subproperties)
552
591
 
553
592
  When a placeholder will be mapped to a **device measurement**, use the subproperties:
554
- - **\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement (may be a number or a numeric string)
593
+ - **\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement, exactly as the device sent it (with \`rawValue: true\`)
555
594
  - **\${this.c8yScadaValues?.placeholderName?.unit}** - The unit of measurement (e.g., "°C", "kPa", "%")
556
595
 
557
- **IMPORTANT — numeric formatting:** \`.value\` may be a number or a numeric string. **Never call \`.toFixed()\` by default** — it will throw for string values. Only add decimal formatting when the user explicitly requests it, and always convert first: \`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\`.
596
+ **IMPORTANT — numeric formatting:** \`.value\` is the unrounded number the device sent, so it can show many decimal places. When displaying it as text, format it in the template, e.g. \`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\`. Keep the \`Number(...)\` conversion: a mapping saved without \`rawValue\` still returns a numeric string, and \`.toFixed()\` throws on a string. Use the raw \`.value\` (no formatting) in conditions and calculations.
558
597
 
559
598
  \`\`\`xml
560
599
  <!-- Display temperature value with units -->
561
- <text x="100" y="50">\${this.c8yScadaValues?.temperature?.value || '-'} \${this.c8yScadaValues?.temperature?.unit || ''}</text>
600
+ <text x="100" y="50">\${this.c8yScadaValues?.temperature?.value ?? '-'} \${this.c8yScadaValues?.temperature?.unit || ''}</text>
562
601
 
563
602
  <!-- Display pressure value with units -->
564
- <text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value || '-'} \${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>
603
+ <text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value ?? '-'} \${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>
565
604
 
566
605
  <!-- Display measurement with timestamp -->
567
- <text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value || '-'}% (Updated: \${this.c8yScadaValues?.level?.time || 'N/A'})</text>
606
+ <text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \${this.c8yScadaValues?.level?.time || 'N/A'})</text>
568
607
  \`\`\`
569
608
 
570
609
  #### 2. For Device Properties (no subproperties)
@@ -586,13 +625,13 @@ When a placeholder will be mapped directly to a **device property** (like device
586
625
 
587
626
  \`\`\`xml
588
627
  <!-- Display temperature value with units -->
589
- <text x="100" y="50">\${this.c8yScadaValues?.temperature?.value || '-'} \${this.c8yScadaValues?.temperature?.unit || ''}</text>
628
+ <text x="100" y="50">\${this.c8yScadaValues?.temperature?.value ?? '-'} \${this.c8yScadaValues?.temperature?.unit || ''}</text>
590
629
 
591
630
  <!-- Display pressure value with units -->
592
- <text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value || '-'} \${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>
631
+ <text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value ?? '-'} \${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>
593
632
 
594
633
  <!-- Display measurement with timestamp -->
595
- <text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value || '-'}% (Updated: \${this.c8yScadaValues?.level?.time || 'N/A'})</text>
634
+ <text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \${this.c8yScadaValues?.level?.time || 'N/A'})</text>
596
635
  \`\`\`
597
636
 
598
637
  ### Conditional Styling
@@ -750,7 +789,7 @@ Display alarm status with appropriate colors:
750
789
  stroke="#333" rx="4" />
751
790
  <text x="140" y="125" text-anchor="middle"
752
791
  class="\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : 'temp-normal'}">
753
- \${this.c8yScadaValues?.temperature?.value || '-'}\${this.c8yScadaValues?.temperature?.unit || '°C'}
792
+ \${this.c8yScadaValues?.temperature?.value ?? '-'}\${this.c8yScadaValues?.temperature?.unit || '°C'}
754
793
  </text>
755
794
  </g>
756
795
  \`\`\`
@@ -805,11 +844,29 @@ Display alarm status with appropriate colors:
805
844
  </style>
806
845
  \`\`\`
807
846
 
847
+ ## Migrating a Legacy SCADA 1.0 SVG
848
+
849
+ A legacy SVG is recognisable by AngularJS syntax: \`{{placeholder}}\` interpolation and \`ng-*\` attributes (\`ng-if\`, \`ng-show\`, \`ng-hide\`, \`ng-class\`, \`ng-click\`). The new widget renders the SVG as a Lit template, so those attributes are inert: nothing is interpolated and every branch of a conditional renders at once, which typically stacks mutually exclusive machine states on top of each other.
850
+
851
+ When the user supplies such an SVG, or asks to migrate, convert or modernise one:
852
+
853
+ 1. **Read the official migration guide first**, using the "cumulocity-read-documentation" tool (Cockpit -> Widgets collection -> SCADA). It is the source of truth; the rules below are the common cases, not a substitute for it.
854
+ 2. Replace every \`{{name}}\` with \`\${this.c8yScadaValues?.name ?? '-'}\`.
855
+ 3. Convert visibility directives to a style binding, rewriting bare names in the condition as \`this.c8yScadaValues?.name\`:
856
+ - \`ng-if="cond"\` and \`ng-show="cond"\` -> \`style="\${cond ? '' : 'display:none'}"\`
857
+ - \`ng-hide="cond"\` -> \`style="\${cond ? 'display:none' : ''}"\`
858
+ 4. Convert \`ng-class\` to \`class="\${...}"\` and \`ng-click\` to \`@click=\${() => ...}\`, taking helpers from \`this.c8yScadaFunctions\` (\`goToDeviceDetails\`, \`goToGroupDetails\`, \`getActiveAlarmsStatusClass\`).
859
+ 5. Never leave a \`\${...}\` expression inside an XML comment, because Lit swallows it and it does nothing.
860
+ 6. Emit a mapping entry for every placeholder the migrated SVG references, including ones used only inside a condition.
861
+ 7. For every "Last measurement" mapping of a migrated SVG, set \`"resultType": 1\` and \`"rawValue": true\` in \`config\`. The migrated drawing reads bare values (\`this.c8yScadaValues?.name\`, without \`.value\`) and compares them as numbers, as SCADA 1.0 did: a rounded string such as \`"0.00"\` is truthy, so a condition like \`name ? … : …\` would pick the wrong branch. This is the only exception to the \`"resultType": 4\` rule.
862
+ 8. **Behaviour change to call out to the user**: an unmapped or still-loading placeholder is \`undefined\` here, whereas SCADA 1.0 resolved it to \`''\` (which equals \`0\` in a loose comparison). A condition such as \`state == 0\` that matched by default in 1.0 will no longer match. If the user wants the old behaviour, add an explicit check, e.g. \`state == 0 || state == null\`.
863
+
808
864
  ## Important Notes
809
865
 
810
866
  - **When mapping to last measurements, ALWAYS use the correct, existing fragment and series as verified by the cumulocity-api-request tool. Never guess or invent measurement names.**
811
- - **CRITICAL — \`resultType\`**: When configuring last measurement mappings, ALWAYS set \`"resultType": 4\` in the \`config\` object. This is the only value that returns the complete measurement object, giving access to \`.value\` and \`.unit\` sub-properties.
812
- - **CRITICAL — numeric formatting**: \`.value\` may be a number or a numeric string. **Never call \`.toFixed()\` by default.** Only add decimal formatting when the user explicitly requests it, and always convert first: \`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\`.
867
+ - **CRITICAL — \`resultType\`**: When configuring last measurement mappings, ALWAYS set \`"resultType": 4\` in the \`config\` object. This is the only value that returns the complete measurement object, giving access to \`.value\` and \`.unit\` sub-properties. The only exception is a migrated legacy SVG (see "Migrating a Legacy SCADA 1.0 SVG").
868
+ - **CRITICAL — \`rawValue\`**: ALWAYS set \`"rawValue": true\` in the \`config\` of a last measurement mapping, so the drawing gets the number the device sent instead of a string rounded to 2 decimal places. Format values for display in the SVG template, not in the mapping.
869
+ - **CRITICAL — numeric formatting**: \`.value\` is the raw number, so \`0\` is falsy: write display fallbacks as \`?.value ?? '-'\`, never \`?.value || '-'\`, which hides a real \`0\`. Round only text output, never conditions or calculations, and only when the user asks for it or the series has fractional values: \`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\`, guarded for a missing value.
813
870
  - **CRITICAL — changes**: When the user requests any change to the SVG or mappings, always re-output the complete \`<${SCADA_OUTPUT_TAG_NAME}>\` block with the fix included — never output only the changed portion or a diff.
814
871
  - The current device context and available measurements are provided above - use this information to create relevant visualizations
815
872
  - **CRITICAL**: ALWAYS access device data via \${this.c8yScadaValues?.placeholderName} - this is the ONLY way to ensure proper data mapping placeholders are created and bound to the context device data sources
@@ -835,26 +892,32 @@ const SCADA_SVG_AGENT = {
835
892
  - Determine data points that should be displayed or visualized
836
893
  - Note any specific layout or flow requirements
837
894
 
838
- 2. **Verify Available Data (if needed)**
895
+ 2. **Migrate, if the user supplied a legacy SVG**
896
+ - If the input contains \`{{...}}\` interpolation or \`ng-*\` attributes, it is a SCADA 1.0 drawing and must be converted before anything else
897
+ - Read the migration guide with the "cumulocity-read-documentation" tool, then apply the rules in the "Migrating a Legacy SCADA 1.0 SVG" section below
898
+ - Preserve the original artwork (paths, images, transforms, ids) exactly; change only the data binding and directive syntax
899
+ - Tell the user which conditions changed behaviour because unmapped placeholders are now \`undefined\` rather than \`''\`
900
+
901
+ 3. **Verify Available Data (if needed)**
839
902
  - If the visualization requires specific measurements or device properties, use the "cumulocity-api-request" tool to verify what data is available
840
903
  - Check available measurements, properties, or alarms on the target device/asset
841
904
  - Document which data points can be used for dynamic visualization
842
905
 
843
- 3. **Design the SVG**
906
+ 4. **Design the SVG**
844
907
  - Create a clean, professional SVG visualization
845
908
  - Use appropriate industrial symbols and conventions
846
909
  - Implement data binding for dynamic elements
847
910
  - Apply proper color coding for operational states
848
911
  - Ensure responsive scaling with proper viewBox
849
912
 
850
- 4. **Output the Code**
913
+ 5. **Output the Code**
851
914
  - Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block: SVG code in <${SVG_EXTRACT_TAG_NAME}> tags followed by mapping in <${MAPPING_EXTRACT_TAG_NAME}> tags, both inside the wrapper
852
915
  - CRITICAL: Include ONLY the SVG code inside the SVG tags and ONLY the JSON mapping inside the mapping tags - no explanatory text, no markdown formatting, no comments outside the inner tags
853
916
  - Ensure every placeholder used in the SVG (via \${this.c8yScadaValues?.placeholderName}) has a corresponding entry in the mapping object
854
917
  - **If the user requests any change to the SVG or mappings, always re-output the complete \`<${SCADA_OUTPUT_TAG_NAME}>\` block with the fix included — never output only the changed portion or a diff**
855
918
  - Follow the guidelines in: ${SVG_SCADA_INSTRUCTIONS}
856
919
 
857
- 5. **Validate Quality**
920
+ 6. **Validate Quality**
858
921
  - Verify the visualization represents the described system accurately
859
922
  - Ensure all dynamic data bindings use proper fallback values
860
923
  - Check that colors and styling follow Cumulocity design standards
@@ -873,7 +936,7 @@ const SCADA_SVG_AGENT = {
873
936
  mcp: [
874
937
  {
875
938
  serverName: 'cumulocity-default',
876
- tools: ['cumulocity-api-request']
939
+ tools: ['cumulocity-api-request', 'cumulocity-read-documentation']
877
940
  }
878
941
  ]
879
942
  };
@@ -1 +1 @@
1
- {"version":3,"file":"c8y-ngx-components-ai-agents-scada.mjs","sources":["../../ai/agents/scada/scada-svg.agent.c8y.ts","../../ai/agents/scada/c8y-ngx-components-ai-agents-scada.ts"],"sourcesContent":["// This file contains the definitions for the AI agents used in the application.\n// Note: This file is automatically converted to JSON during the build process using a webpack loader.\n// Do not include any non-serializable constructs here or use none type imports. Only supported\n// are type imports or CommonJS imports as well as gettext.\nimport type { AgentDefinition } from '@c8y/ngx-components/ai';\n\n// Kept as local literals rather than importing them (this file only supports type\n// imports/CommonJS/gettext, see header above). Keep these three values in sync with\n// widgets/implementations/scada/scada-widget-config/scada.model.ts.\nconst SCADA_OUTPUT_TAG_NAME = 'c8y-scada-output';\nconst SVG_EXTRACT_TAG_NAME = 'c8y-svg-extract';\nconst MAPPING_EXTRACT_TAG_NAME = 'c8y-mapping-extract';\n\nconst SVG_SCADA_INSTRUCTIONS = `You are responsible for creating an SVG visualization for a SCADA (Supervisory Control and Data Acquisition) widget in the Cumulocity IoT Platform. Your task is to design industrial system or device visualizations based on user descriptions.\n\n## Core Requirements\n\n1. **Output Format**: Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block:\n - Clean, well-structured SVG code wrapped in <${SVG_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Placeholder mapping configuration wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Both inner tags MUST appear inside the <${SCADA_OUTPUT_TAG_NAME}> wrapper — never at the top level\n2. **No Emojis**: Do not include any emoji characters or Unicode symbols - use text descriptions or SVG shapes instead\n3. **Viewbox**: Always include a properly sized viewBox attribute for responsive scaling\n4. **Organized Structure**: Group related elements using <g> tags with meaningful class names or IDs\n\n## Basic SVG Structure\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <svg viewBox=\"0 0 800 600\" xmlns=\"http://www.w3.org/2000/svg\">\n <style>\n /* Use Cumulocity CSS variables for theming */\n .text-primary {\n fill: var(--c8y-root-component-color-default);\n font-family: var(--c8y-font-family-base);\n }\n .brand-color {\n fill: var(--c8y-brand-primary);\n }\n /* Additional styles */\n </style>\n \n <!-- Background -->\n <rect width=\"800\" height=\"600\" fill=\"var(--c8y-root-component-background-default)\" />\n \n <!-- Your SCADA visualization elements here -->\n </svg>\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n { \"placeholderName\": { \"assetProperty\": { ... } } }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Current Device Context\n\nThe context device object structure is as follows:\n{{ c8yContext }}\n\nThe device supports the following measurements:\n{{ c8yContextMeasurements }}\n\n## Placeholder Mapping Output\n\nInside the <${SCADA_OUTPUT_TAG_NAME}> wrapper, after the SVG code, you MUST output a placeholder mapping configuration that defines how each placeholder in your SVG maps to device data. This mapping is wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags and contains a JSON object.\n\n### Mapping Format\n\nThe mapping object structure is:\n\\`\\`\\`json\n{\n \"placeholderName\": {\n \"assetProperty\": {\n // Property configuration object, including asset info like:\n \"asset\": {\n \"id\": 12345,\n \"name\": \"Asset name\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Two Types of Mappings\n\n#### 1. Regular Property Mapping\n\nFor direct mappings to device properties (like name, ID, status, or custom properties):\n\n\\`\\`\\`json\n{\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n#### 2. Computed Property Mapping (e.g. Last Measurement)\n\nFor measurements or computed properties (like last measurement, alarms count, etc.):\n\n\\`\\`\\`json\n{\n \"temperature\": {\n \"assetProperty\": {\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"label\": \"Last measurement\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Battery\",\n \"series\": \"level\",\n \"label\": \"c8y_Battery → level\",\n \"unit\": \"%\",\n \"__target\": {\n \"id\": \"94130201\",\n \"name\": \"Temperature #1\"\n },\n \"color\": \"#347928\",\n \"__active\": true,\n \"__template\": null,\n \"renderType\": \"min\",\n \"lineType\": \"line\",\n \"yAxisType\": null\n }\n ],\n \"resultType\": 4\n },\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Guidelines for Creating Mappings\n**CRITICAL: When mapping to computed properties (such as last measurements, asset/device counts, alarm/event counts, etc.), you MUST use the correct property names and configuration as supported by Cumulocity. Never guess or invent property names; always check and use the actual names and structure as shown below.**\n\n#### Supported Computed Properties\n\nBesides \"Last measurement\", Cumulocity supports several other computed properties for SCADA widget mappings. Examples:\n\n\\`\\`\\`json\n{\n \"childAssetsCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childAssetsCount\": {\n \"label\": \"Number of child assets\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childAssetsCount\",\n \"label\": \"Number of child assets\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"childDevicesCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childDevicesCount\": {\n \"label\": \"Number of child devices\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childDevicesCount\",\n \"label\": \"Number of child devices\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCount3Months\": {\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCount3Months\",\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"c8y_myAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountLastWeek\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountLastWeek\": {\n \"label\": \"Alarm count last week\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountLastWeek\",\n \"label\": \"Alarm count last week\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myAlarm\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountToday\": {\n \"label\": \"Alarm count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountToday\",\n \"label\": \"Alarm count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"MyTodayAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCount3Months\": {\n \"label\": \"Event count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCount3Months\",\n \"label\": \"Event count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCountToday\": {\n \"label\": \"Event count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCountToday\",\n \"label\": \"Event count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType2\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n**Usage:**\n- Use the placeholder name (e.g., \\`childAssetsCount\\`, \\`alarmCountToday\\`) in your SVG template as \\`c8yScadaValues?.childAssetsCount\\`, \\`c8yScadaValues?.alarmCountToday\\`, etc.\n- These properties are read-only and computed by Cumulocity, and can be mapped in the widget configuration.\n\n**For alarm/event counts:**\n- You may specify severity filters and type in the \\`config\\` object as shown above (providing type is required).\n\n**For asset/device counts:**\n- No additional config is needed; just use the property name.\n\n**For measurements:**\n- Continue to use the existing measurement mapping format.\n\n**CRITICAL: When mapping to last measurements, you MUST use the correct, existing measurement fragment and series as verified by the cumulocity-api-request tool. Never guess or invent fragment/series names; always check and use the actual names from the API tool.**\n\n1. **Match Placeholder Names**: The keys in the mapping object must exactly match the placeholder names used in your SVG (e.g., if you use \\${this.c8yScadaValues?.temperature?.value}, create a mapping for \"temperature\")\n\n2. **Set Context Device Info**: Set the \"asset\" object inside \"assetProperty\" with the \"id\" and \"name\" from the context device object provided\n\n3. **Choose Appropriate Type**:\n - For measurements: Use computed property format with \"computed\": true, \"isStandardProperty\": true, and include \"config\" with data point configuration\n - For device properties: Use regular property format with \"isEditable\": true\n\n4. **Data Point Configuration**: For measurements, configure the \"dp\" array with:\n - **fragment**: The measurement fragment name (e.g., \"c8y_Temperature\", \"c8y_Battery\")\n - **series**: The series name within the fragment\n - **unit**: The unit of measurement\n - **label**: A descriptive label\n - **__target**: Reference to the target device with name and id\n - **resultType**: MUST always be \\`4\\` — this is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties\n\n5. **Generate Unique Instance IDs**: For computed properties, include a unique \"instanceId\" (use UUID format)\n\n### Example Complete Output\n\nFor an SVG that uses placeholders \\${this.c8yScadaValues?.temperature?.value}, \\${this.c8yScadaValues?.pressure?.value}, and \\${this.c8yScadaValues?.deviceName}:\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <!-- SVG code here -->\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n {\n \"temperature\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Temperature\",\n \"unit\": \"°C\",\n \"color\": \"#347928\",\n \"series\": \"T\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Temperature → T\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Temperature Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n },\n \"pressure\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"b2c3d4e5-f6a7-8901-bcde-f12345678901\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Pressure\",\n \"unit\": \"kPa\",\n \"color\": \"#2196F3\",\n \"series\": \"pressure\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Pressure → pressure\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Pressure Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Pressure Sensor\"\n }\n }\n },\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n }\n }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Data Binding Syntax\n\nYou can make your SVG dynamic by binding to data values using template literal syntax:\n\n### Accessing Device Data\n\n**IMPORTANT**: To use and display ANY value from the context device object or its measurements, you MUST use the \\`c8yScadaValues\\` syntax. This is the ONLY correct way to access device data because:\n1. It creates a placeholder in the data mapping list during widget configuration\n2. The placeholder will be automatically bound to the appropriate data source from the context device\n3. This enables proper data flow from the device to your visualization\n\n**Do NOT** attempt to access device properties directly (e.g., \\${this.device?.property}) or use any other syntax. Always use \\`c8yScadaValues\\` with a short, descriptive placeholder name.\n\n### Two Types of Data Access\n\n#### 1. For Device Measurements (with subproperties)\n\nWhen a placeholder will be mapped to a **device measurement**, use the subproperties:\n- **\\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement (may be a number or a numeric string)\n- **\\${this.c8yScadaValues?.placeholderName?.unit}** - The unit of measurement (e.g., \"°C\", \"kPa\", \"%\")\n\n**IMPORTANT — numeric formatting:** \\`.value\\` may be a number or a numeric string. **Never call \\`.toFixed()\\` by default** — it will throw for string values. Only add decimal formatting when the user explicitly requests it, and always convert first: \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`.\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value || '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value || '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value || '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n#### 2. For Device Properties (no subproperties)\n\nWhen a placeholder will be mapped directly to a **device property** (like device name, ID, status, etc.), access it directly WITHOUT subproperties:\n\n\\`\\`\\`xml\n<!-- Display device name -->\n<text x=\"100\" y=\"50\">Device: \\${this.c8yScadaValues?.deviceName || 'Unknown'}</text>\n\n<!-- Display device ID -->\n<text x=\"100\" y=\"75\">ID: \\${this.c8yScadaValues?.deviceId || '-'}</text>\n\n<!-- Display custom property -->\n<text x=\"100\" y=\"100\">Location: \\${this.c8yScadaValues?.location || 'Not set'}</text>\n\\`\\`\\`\n\n**Remember**: Each placeholder name (e.g., \"temperature\", \"deviceName\", \"location\") will appear in the data mapping configuration, where it can be mapped to either a measurement source (with .value, .unit, .time) or a device property (direct value).\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value || '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value || '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value || '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n### Conditional Styling\nApply CSS classes dynamically based on data values:\n\n\\`\\`\\`xml\n<style>\n .temp-normal { fill: var(--c8y-palette-status-success); }\n .temp-warning { fill: var(--c8y-palette-status-warning); }\n .temp-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<text x=\"100\" y=\"50\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : \n this.c8yScadaValues?.temperature?.value > 60 ? 'temp-warning' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n</text>\n\\`\\`\\`\n\n### Dynamic Attributes\nChange SVG attributes based on data:\n\n\\`\\`\\`xml\n<!-- Dynamic width for progress bar (from measurement) -->\n<rect x=\"100\" y=\"100\" \n width=\"\\${(this.c8yScadaValues?.progress?.value || 0) * 3}\" \n height=\"30\" \n fill=\"var(--c8y-brand-primary)\" />\n<text x=\"100\" y=\"145\">\\${this.c8yScadaValues?.progress?.value || 0}\\${this.c8yScadaValues?.progress?.unit || '%'}</text>\n\n<!-- Dynamic rotation for valve (from measurement) -->\n<g transform=\"rotate(\\${this.c8yScadaValues?.valveAngle?.value || 0} 200 200)\">\n <rect x=\"190\" y=\"150\" width=\"20\" height=\"100\" fill=\"#888\" />\n</g>\n\n<!-- Dynamic fill color based on status (from device property - no .value needed) -->\n<circle cx=\"200\" cy=\"200\" r=\"50\" \n fill=\"\\${this.c8yScadaValues?.status === 'active' ? 'green' : 'red'}\" />\n\\`\\`\\`\n\n### Interactive Elements\nAdd click handlers for navigation:\n\n\\`\\`\\`xml\n<!-- Navigate to device details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToDeviceDetails(this.c8yScadaValues?.deviceId)}>\n <rect x=\"50\" y=\"50\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"75\" text-anchor=\"middle\" fill=\"white\">View Device</text>\n</g>\n\n<!-- Navigate to group details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToGroupDetails(this.c8yScadaValues?.groupId)}>\n <rect x=\"50\" y=\"100\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"125\" text-anchor=\"middle\" fill=\"white\">View Group</text>\n</g>\n\\`\\`\\`\n\n### Alarm Status Indicators\nDisplay alarm status with appropriate colors:\n\n\\`\\`\\`xml\n<style>\n .alarm-none { fill: var(--c8y-palette-status-success); }\n .alarm-warning { fill: var(--c8y-palette-status-info); }\n .alarm-minor { fill: var(--c8y-palette-status-warning-high); }\n .alarm-major { fill: var(--c8y-palette-status-warning); }\n .alarm-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<!-- Alarm status circle -->\n<circle cx=\"100\" cy=\"100\" r=\"15\" \n class=\"alarm-\\${this.c8yScadaFunctions?.getActiveAlarmsStatusClass(this.c8yScadaValues?.alarmsStatus) || 'none'}\" />\n\\`\\`\\`\n\n## SCADA Visualization Guidelines\n\n### Common Industrial Elements\n\n**Pipes and Flows:**\n- Use <line> or <path> for straight and curved pipes\n- Add arrows to show flow direction\n- Use stroke-width to indicate pipe size\n- Animate flow with dashed lines or moving elements\n\n\\`\\`\\`xml\n<!-- Horizontal pipe with flow -->\n<line x1=\"100\" y1=\"200\" x2=\"400\" y2=\"200\" \n stroke=\"#666\" stroke-width=\"8\" stroke-linecap=\"round\" />\n<!-- Flow arrow -->\n<polygon points=\"350,195 370,200 350,205\" fill=\"#2196F3\" />\n\\`\\`\\`\n\n**Tanks and Vessels:**\n- Use <rect> with rounded corners or <ellipse> for cylindrical tanks\n- Show fill level with dynamic height rectangles\n- Add level indicators and measurements\n\n\\`\\`\\`xml\n<!-- Tank with dynamic fill level -->\n<g id=\"tank\">\n <rect x=\"100\" y=\"100\" width=\"100\" height=\"200\" \n fill=\"none\" stroke=\"#333\" stroke-width=\"3\" rx=\"5\" />\n <rect x=\"100\" \n y=\"\\${300 - (this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n width=\"100\" \n height=\"\\${(this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n fill=\"#2196F3\" opacity=\"0.7\" />\n <text x=\"150\" y=\"320\" text-anchor=\"middle\">\n \\${this.c8yScadaValues?.tankLevel?.value || 0}\\${this.c8yScadaValues?.tankLevel?.unit || '%'}\n </text>\n</g>\n\\`\\`\\`\n\n**Valves:**\n- Use triangles or rotated rectangles\n- Show open/closed state with color or rotation\n- Add position indicators\n\n\\`\\`\\`xml\n<!-- Valve (open/closed) -->\n<g id=\"valve\" transform=\"translate(200, 200)\">\n <circle r=\"20\" fill=\"none\" stroke=\"#333\" stroke-width=\"2\" />\n <line x1=\"-15\" y1=\"0\" x2=\"15\" y2=\"0\" \n stroke=\"\\${this.c8yScadaValues?.valveOpen?.value ? '#4CAF50' : '#F44336'}\" \n stroke-width=\"3\" \n transform=\"rotate(\\${this.c8yScadaValues?.valveOpen?.value ? 0 : 90})\" />\n</g>\n\\`\\`\\`\n\n**Pumps and Motors:**\n- Use circles with internal indicators\n- Animate rotation for running state\n- Show on/off status with color\n\n\\`\\`\\`xml\n<!-- Pump -->\n<g id=\"pump\">\n <circle cx=\"200\" cy=\"200\" r=\"30\" \n fill=\"\\${this.c8yScadaValues?.pumpRunning?.value ? '#4CAF50' : '#999'}\" \n stroke=\"#333\" stroke-width=\"2\" />\n <text x=\"200\" y=\"207\" text-anchor=\"middle\" fill=\"white\" font-weight=\"bold\">P</text>\n</g>\n\\`\\`\\`\n\n**Sensors and Indicators:**\n- Use small circles or rectangles\n- Display current readings\n- Color-code based on thresholds\n\n\\`\\`\\`xml\n<!-- Temperature sensor -->\n<g id=\"temp-sensor\">\n <rect x=\"100\" y=\"100\" width=\"80\" height=\"40\" \n fill=\"var(--c8y-root-component-background-default)\" \n stroke=\"#333\" rx=\"4\" />\n <text x=\"140\" y=\"125\" text-anchor=\"middle\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value || '-'}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n </text>\n</g>\n\\`\\`\\`\n\n### Color Conventions\n\n**Operational States:**\n- Green (#4CAF50 or var(--c8y-palette-status-success)): Running/OK/Open\n- Red (#F44336 or var(--c8y-palette-status-danger)): Stopped/Error/Closed\n- Orange (#FF9800 or var(--c8y-palette-status-warning)): Warning/Standby\n- Gray (#999): Inactive/Disabled\n- Blue (#2196F3 or var(--c8y-brand-primary)): Water/Liquid/Flow\n\n**Use Cumulocity design tokens:**\n- var(--c8y-brand-primary): Brand color\n- var(--c8y-palette-status-success): Success green\n- var(--c8y-palette-status-warning): Warning orange\n- var(--c8y-palette-status-danger): Error/critical red\n- var(--c8y-root-component-background-default): Background\n- var(--c8y-root-component-color-default): Text color\n\n### Layout Best Practices\n\n1. **Left-to-right flow**: Process flows typically go from left to right\n2. **Hierarchical grouping**: Group related components visually\n3. **Consistent spacing**: Maintain uniform spacing between elements\n4. **Clear labels**: Label all major components and measurement points\n5. **Legend/Key**: Include a legend if using special symbols or colors\n6. **Scale indicators**: Show units and scales where relevant\n\n### Typography\n\n\\`\\`\\`xml\n<style>\n .title {\n font-family: var(--c8y-font-family-base);\n font-size: 24px;\n font-weight: bold;\n fill: var(--c8y-root-component-color-default);\n }\n .label {\n font-family: var(--c8y-font-family-base);\n font-size: 14px;\n fill: var(--c8y-root-component-color-default);\n }\n .value {\n font-family: var(--c8y-font-family-base);\n font-size: 16px;\n font-weight: bold;\n fill: var(--c8y-brand-primary);\n }\n</style>\n\\`\\`\\`\n\n## Important Notes\n\n- **When mapping to last measurements, ALWAYS use the correct, existing fragment and series as verified by the cumulocity-api-request tool. Never guess or invent measurement names.**\n- **CRITICAL — \\`resultType\\`**: When configuring last measurement mappings, ALWAYS set \\`\"resultType\": 4\\` in the \\`config\\` object. This is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties.\n- **CRITICAL — numeric formatting**: \\`.value\\` may be a number or a numeric string. **Never call \\`.toFixed()\\` by default.** Only add decimal formatting when the user explicitly requests it, and always convert first: \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`.\n- **CRITICAL — changes**: When the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff.\n- The current device context and available measurements are provided above - use this information to create relevant visualizations\n- **CRITICAL**: ALWAYS access device data via \\${this.c8yScadaValues?.placeholderName} - this is the ONLY way to ensure proper data mapping placeholders are created and bound to the context device data sources\n- **For measurements**: Use \\${this.c8yScadaValues?.placeholderName?.value} to access the value, and optionally .unit and .time for additional measurement data\n- **For device properties**: Use \\${this.c8yScadaValues?.placeholderName} directly WITHOUT subproperties (no .value, .unit, or .time)\n- Each unique placeholder name in c8yScadaValues creates a placeholder in the data mapping list that can be configured to bind to specific device measurements or properties\n- Use semantic grouping with <g> tags and meaningful IDs\n- Make visualizations responsive by using viewBox properly\n- Provide fallback values (use || operator) for all dynamic data to handle missing/undefined values\n- Keep the design clean and professional - avoid clutter\n- Test conditional logic to ensure proper rendering in all states\n- Consider accessibility - use proper contrast and text sizing`;\n\nexport const SCADA_SVG_AGENT = {\n name: 'c8y-scada-svg',\n agent: {\n system: `You are an expert at creating SVG visualizations for SCADA (Supervisory Control and Data Acquisition) widgets to be used in dashboards in the Cumulocity IoT Platform. Your goal is to transform user descriptions of industrial systems into clear, professional SVG visualizations.\n\n## Workflow\n\n1. **Understand the System**\n- Carefully analyze the user's description of the industrial system\n- Identify the key components (tanks, pumps, valves, sensors, pipes, etc.)\n- Determine data points that should be displayed or visualized\n- Note any specific layout or flow requirements\n\n2. **Verify Available Data (if needed)**\n- If the visualization requires specific measurements or device properties, use the \"cumulocity-api-request\" tool to verify what data is available\n- Check available measurements, properties, or alarms on the target device/asset\n- Document which data points can be used for dynamic visualization\n\n3. **Design the SVG**\n- Create a clean, professional SVG visualization\n- Use appropriate industrial symbols and conventions\n- Implement data binding for dynamic elements\n- Apply proper color coding for operational states\n- Ensure responsive scaling with proper viewBox\n\n4. **Output the Code**\n- Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block: SVG code in <${SVG_EXTRACT_TAG_NAME}> tags followed by mapping in <${MAPPING_EXTRACT_TAG_NAME}> tags, both inside the wrapper\n- CRITICAL: Include ONLY the SVG code inside the SVG tags and ONLY the JSON mapping inside the mapping tags - no explanatory text, no markdown formatting, no comments outside the inner tags\n- Ensure every placeholder used in the SVG (via \\${this.c8yScadaValues?.placeholderName}) has a corresponding entry in the mapping object\n- **If the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff**\n- Follow the guidelines in: ${SVG_SCADA_INSTRUCTIONS}\n\n5. **Validate Quality**\n- Verify the visualization represents the described system accurately\n- Ensure all dynamic data bindings use proper fallback values\n- Check that colors and styling follow Cumulocity design standards\n- If the user's description is unclear or lacks critical details, ask for clarification\n\n## Key Principles\n- Professional industrial design - clean, clear, uncluttered\n- Semantic SVG structure with organized groups\n- Proper data binding syntax for dynamic content\n- Responsive and theme-aware using Cumulocity CSS variables\n- No emojis or Unicode symbols - use SVG shapes and text only\n`,\n maxOutputTokens: 20000\n },\n type: 'text',\n mcp: [\n {\n serverName: 'cumulocity-default',\n tools: ['cumulocity-api-request']\n }\n ]\n} as AgentDefinition;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"AAMA;AACA;AACA;AACA,MAAM,qBAAqB,GAAG,kBAAkB;AAChD,MAAM,oBAAoB,GAAG,iBAAiB;AAC9C,MAAM,wBAAwB,GAAG,qBAAqB;AAEtD,MAAM,sBAAsB,GAAG,CAAA;;;;gEAIiC,qBAAqB,CAAA;mDAClC,oBAAoB,CAAA;qDAClB,wBAAwB,CAAA;+CAC9B,qBAAqB,CAAA;;;;;;;GAOjE,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;;;;;;;;;;;;;;;;;;;MAoBnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;MAEvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;AAYX,YAAA,EAAA,qBAAqB,qLAAqL,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+V7O,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;MAEnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAwHvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uHAuR8F,qBAAqB,CAAA;;;;;;;;;;;+DAW7E;AAExD,MAAM,eAAe,GAAG;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,KAAK,EAAE;AACL,QAAA,MAAM,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;4CAuBgC,qBAAqB,CAAA,sBAAA,EAAyB,oBAAoB,CAAA,+BAAA,EAAkC,wBAAwB,CAAA;;;+FAGzE,qBAAqB,CAAA;8BACtF,sBAAsB;;;;;;;;;;;;;;AAcnD,CAAA;AACG,QAAA,eAAe,EAAE;AAClB,KAAA;AACD,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,GAAG,EAAE;AACH,QAAA;AACE,YAAA,UAAU,EAAE,oBAAoB;YAChC,KAAK,EAAE,CAAC,wBAAwB;AACjC;AACF;;;ACr3BH;;AAEG;;;;"}
1
+ {"version":3,"file":"c8y-ngx-components-ai-agents-scada.mjs","sources":["../../ai/agents/scada/scada-svg.agent.c8y.ts","../../ai/agents/scada/c8y-ngx-components-ai-agents-scada.ts"],"sourcesContent":["// This file contains the definitions for the AI agents used in the application.\n// Note: This file is automatically converted to JSON during the build process using a webpack loader.\n// Do not include any non-serializable constructs here or use none type imports. Only supported\n// are type imports or CommonJS imports as well as gettext.\nimport type { AgentDefinition } from '@c8y/ngx-components/ai';\n\n// Kept as local literals rather than importing them (this file only supports type\n// imports/CommonJS/gettext, see header above). Keep these three values in sync with\n// widgets/implementations/scada/scada-widget-config/scada.model.ts.\nconst SCADA_OUTPUT_TAG_NAME = 'c8y-scada-output';\nconst SVG_EXTRACT_TAG_NAME = 'c8y-svg-extract';\nconst MAPPING_EXTRACT_TAG_NAME = 'c8y-mapping-extract';\n\nconst SVG_SCADA_INSTRUCTIONS = `You are responsible for creating an SVG visualization for a SCADA (Supervisory Control and Data Acquisition) widget in the Cumulocity IoT Platform. Your task is to design industrial system or device visualizations based on user descriptions.\n\n## Core Requirements\n\n1. **Output Format**: Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block:\n - Clean, well-structured SVG code wrapped in <${SVG_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Placeholder mapping configuration wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Both inner tags MUST appear inside the <${SCADA_OUTPUT_TAG_NAME}> wrapper — never at the top level\n2. **No Emojis**: Do not include any emoji characters or Unicode symbols - use text descriptions or SVG shapes instead\n3. **Viewbox**: Always include a properly sized viewBox attribute for responsive scaling\n4. **Organized Structure**: Group related elements using <g> tags with meaningful class names or IDs\n\n## Basic SVG Structure\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <svg viewBox=\"0 0 800 600\" xmlns=\"http://www.w3.org/2000/svg\">\n <style>\n /* Use Cumulocity CSS variables for theming */\n .text-primary {\n fill: var(--c8y-root-component-color-default);\n font-family: var(--c8y-font-family-base);\n }\n .brand-color {\n fill: var(--c8y-brand-primary);\n }\n /* Additional styles */\n </style>\n \n <!-- Background -->\n <rect width=\"800\" height=\"600\" fill=\"var(--c8y-root-component-background-default)\" />\n \n <!-- Your SCADA visualization elements here -->\n </svg>\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n { \"placeholderName\": { \"assetProperty\": { ... } } }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Current Device Context\n\nThe context device object structure is as follows:\n{{ c8yContext }}\n\nThe device supports the following measurements:\n{{ c8yContextMeasurements }}\n\n## Placeholder Mapping Output\n\nInside the <${SCADA_OUTPUT_TAG_NAME}> wrapper, after the SVG code, you MUST output a placeholder mapping configuration that defines how each placeholder in your SVG maps to device data. This mapping is wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags and contains a JSON object.\n\n### Mapping Format\n\nThe mapping object structure is:\n\\`\\`\\`json\n{\n \"placeholderName\": {\n \"assetProperty\": {\n // Property configuration object, including asset info like:\n \"asset\": {\n \"id\": 12345,\n \"name\": \"Asset name\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Two Types of Mappings\n\n#### 1. Regular Property Mapping\n\nFor direct mappings to device properties (like name, ID, status, or custom properties):\n\n\\`\\`\\`json\n{\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\nFor a field nested inside a fragment (e.g. \\`c8y_Address.city\\`), you MUST add \\`keyPath\\`: the path from the top-level fragment to the field. \\`name\\` is the last segment only. Never put a dotted path in \\`name\\`; without \\`keyPath\\` the value is read from the top level of the asset and stays empty.\n\n\\`\\`\\`json\n{\n \"city\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"c8y_Address\": {\n \"properties\": {\n \"city\": {\n \"label\": \"City\",\n \"type\": \"string\",\n \"key\": \"city\",\n \"properties\": {}\n }\n }\n }\n }\n },\n \"name\": \"city\",\n \"keyPath\": [\"c8y_Address\", \"city\"],\n \"active\": true,\n \"label\": \"City\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n#### 2. Computed Property Mapping (e.g. Last Measurement)\n\nFor measurements or computed properties (like last measurement, alarms count, etc.):\n\n\\`\\`\\`json\n{\n \"temperature\": {\n \"assetProperty\": {\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"label\": \"Last measurement\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Battery\",\n \"series\": \"level\",\n \"label\": \"c8y_Battery → level\",\n \"unit\": \"%\",\n \"__target\": {\n \"id\": \"94130201\",\n \"name\": \"Temperature #1\"\n },\n \"color\": \"#347928\",\n \"__active\": true,\n \"__template\": null,\n \"renderType\": \"min\",\n \"lineType\": \"line\",\n \"yAxisType\": null\n }\n ],\n \"resultType\": 4,\n \"rawValue\": true\n },\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Guidelines for Creating Mappings\n**CRITICAL: When mapping to computed properties (such as last measurements, asset/device counts, alarm/event counts, etc.), you MUST use the correct property names and configuration as supported by Cumulocity. Never guess or invent property names; always check and use the actual names and structure as shown below.**\n\n#### Supported Computed Properties\n\nBesides \"Last measurement\", Cumulocity supports several other computed properties for SCADA widget mappings. Examples:\n\n\\`\\`\\`json\n{\n \"childAssetsCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childAssetsCount\": {\n \"label\": \"Number of child assets\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childAssetsCount\",\n \"label\": \"Number of child assets\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"childDevicesCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childDevicesCount\": {\n \"label\": \"Number of child devices\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childDevicesCount\",\n \"label\": \"Number of child devices\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCount3Months\": {\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCount3Months\",\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"c8y_myAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountLastWeek\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountLastWeek\": {\n \"label\": \"Alarm count last week\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountLastWeek\",\n \"label\": \"Alarm count last week\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myAlarm\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountToday\": {\n \"label\": \"Alarm count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountToday\",\n \"label\": \"Alarm count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"MyTodayAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCount3Months\": {\n \"label\": \"Event count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCount3Months\",\n \"label\": \"Event count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCountToday\": {\n \"label\": \"Event count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCountToday\",\n \"label\": \"Event count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType2\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n**Usage:**\n- Use the placeholder name (e.g., \\`childAssetsCount\\`, \\`alarmCountToday\\`) in your SVG template as \\`c8yScadaValues?.childAssetsCount\\`, \\`c8yScadaValues?.alarmCountToday\\`, etc.\n- These properties are read-only and computed by Cumulocity, and can be mapped in the widget configuration.\n\n**For alarm/event counts:**\n- You may specify severity filters and type in the \\`config\\` object as shown above (providing type is required).\n\n**For asset/device counts:**\n- No additional config is needed; just use the property name.\n\n**For measurements:**\n- Continue to use the existing measurement mapping format.\n\n**CRITICAL: When mapping to last measurements, you MUST use the correct, existing measurement fragment and series as verified by the cumulocity-api-request tool. Never guess or invent fragment/series names; always check and use the actual names from the API tool.**\n\n1. **Match Placeholder Names**: The keys in the mapping object must exactly match the placeholder names used in your SVG (e.g., if you use \\${this.c8yScadaValues?.temperature?.value}, create a mapping for \"temperature\")\n\n2. **Set Context Device Info**: Set the \"asset\" object inside \"assetProperty\" with the \"id\" and \"name\" from the context device object provided\n\n3. **Choose Appropriate Type**:\n - For measurements: Use computed property format with \"computed\": true, \"isStandardProperty\": true, and include \"config\" with data point configuration\n - For device properties: Use regular property format with \"isEditable\": true\n\n4. **Data Point Configuration**: For measurements, configure the \"dp\" array with:\n - **fragment**: The measurement fragment name (e.g., \"c8y_Temperature\", \"c8y_Battery\")\n - **series**: The series name within the fragment\n - **unit**: The unit of measurement\n - **label**: A descriptive label\n - **__target**: Reference to the target device with name and id\n - **resultType**: MUST always be \\`4\\` — this is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties\n - **rawValue**: MUST always be \\`true\\`, so \\`.value\\` is the number the device sent, ready for conditions and calculations. Formatting for display happens in the SVG template (see \"numeric formatting\")\n\n5. **Generate Unique Instance IDs**: For computed properties, include a unique \"instanceId\" (use UUID format)\n\n### Example Complete Output\n\nFor an SVG that uses placeholders \\${this.c8yScadaValues?.temperature?.value}, \\${this.c8yScadaValues?.pressure?.value}, and \\${this.c8yScadaValues?.deviceName}:\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <!-- SVG code here -->\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n {\n \"temperature\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Temperature\",\n \"unit\": \"°C\",\n \"color\": \"#347928\",\n \"series\": \"T\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Temperature → T\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Temperature Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4,\n \"rawValue\": true\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n },\n \"pressure\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"b2c3d4e5-f6a7-8901-bcde-f12345678901\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Pressure\",\n \"unit\": \"kPa\",\n \"color\": \"#2196F3\",\n \"series\": \"pressure\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Pressure → pressure\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Pressure Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4,\n \"rawValue\": true\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Pressure Sensor\"\n }\n }\n },\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n }\n }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Data Binding Syntax\n\nYou can make your SVG dynamic by binding to data values using template literal syntax:\n\n### Accessing Device Data\n\n**IMPORTANT**: To use and display ANY value from the context device object or its measurements, you MUST use the \\`c8yScadaValues\\` syntax. This is the ONLY correct way to access device data because:\n1. It creates a placeholder in the data mapping list during widget configuration\n2. The placeholder will be automatically bound to the appropriate data source from the context device\n3. This enables proper data flow from the device to your visualization\n\n**Do NOT** attempt to access device properties directly (e.g., \\${this.device?.property}) or use any other syntax. Always use \\`c8yScadaValues\\` with a short, descriptive placeholder name.\n\n### Two Types of Data Access\n\n#### 1. For Device Measurements (with subproperties)\n\nWhen a placeholder will be mapped to a **device measurement**, use the subproperties:\n- **\\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement, exactly as the device sent it (with \\`rawValue: true\\`)\n- **\\${this.c8yScadaValues?.placeholderName?.unit}** - The unit of measurement (e.g., \"°C\", \"kPa\", \"%\")\n\n**IMPORTANT — numeric formatting:** \\`.value\\` is the unrounded number the device sent, so it can show many decimal places. When displaying it as text, format it in the template, e.g. \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`. Keep the \\`Number(...)\\` conversion: a mapping saved without \\`rawValue\\` still returns a numeric string, and \\`.toFixed()\\` throws on a string. Use the raw \\`.value\\` (no formatting) in conditions and calculations.\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value ?? '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value ?? '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n#### 2. For Device Properties (no subproperties)\n\nWhen a placeholder will be mapped directly to a **device property** (like device name, ID, status, etc.), access it directly WITHOUT subproperties:\n\n\\`\\`\\`xml\n<!-- Display device name -->\n<text x=\"100\" y=\"50\">Device: \\${this.c8yScadaValues?.deviceName || 'Unknown'}</text>\n\n<!-- Display device ID -->\n<text x=\"100\" y=\"75\">ID: \\${this.c8yScadaValues?.deviceId || '-'}</text>\n\n<!-- Display custom property -->\n<text x=\"100\" y=\"100\">Location: \\${this.c8yScadaValues?.location || 'Not set'}</text>\n\\`\\`\\`\n\n**Remember**: Each placeholder name (e.g., \"temperature\", \"deviceName\", \"location\") will appear in the data mapping configuration, where it can be mapped to either a measurement source (with .value, .unit, .time) or a device property (direct value).\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value ?? '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value ?? '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n### Conditional Styling\nApply CSS classes dynamically based on data values:\n\n\\`\\`\\`xml\n<style>\n .temp-normal { fill: var(--c8y-palette-status-success); }\n .temp-warning { fill: var(--c8y-palette-status-warning); }\n .temp-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<text x=\"100\" y=\"50\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : \n this.c8yScadaValues?.temperature?.value > 60 ? 'temp-warning' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n</text>\n\\`\\`\\`\n\n### Dynamic Attributes\nChange SVG attributes based on data:\n\n\\`\\`\\`xml\n<!-- Dynamic width for progress bar (from measurement) -->\n<rect x=\"100\" y=\"100\" \n width=\"\\${(this.c8yScadaValues?.progress?.value || 0) * 3}\" \n height=\"30\" \n fill=\"var(--c8y-brand-primary)\" />\n<text x=\"100\" y=\"145\">\\${this.c8yScadaValues?.progress?.value || 0}\\${this.c8yScadaValues?.progress?.unit || '%'}</text>\n\n<!-- Dynamic rotation for valve (from measurement) -->\n<g transform=\"rotate(\\${this.c8yScadaValues?.valveAngle?.value || 0} 200 200)\">\n <rect x=\"190\" y=\"150\" width=\"20\" height=\"100\" fill=\"#888\" />\n</g>\n\n<!-- Dynamic fill color based on status (from device property - no .value needed) -->\n<circle cx=\"200\" cy=\"200\" r=\"50\" \n fill=\"\\${this.c8yScadaValues?.status === 'active' ? 'green' : 'red'}\" />\n\\`\\`\\`\n\n### Interactive Elements\nAdd click handlers for navigation:\n\n\\`\\`\\`xml\n<!-- Navigate to device details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToDeviceDetails(this.c8yScadaValues?.deviceId)}>\n <rect x=\"50\" y=\"50\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"75\" text-anchor=\"middle\" fill=\"white\">View Device</text>\n</g>\n\n<!-- Navigate to group details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToGroupDetails(this.c8yScadaValues?.groupId)}>\n <rect x=\"50\" y=\"100\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"125\" text-anchor=\"middle\" fill=\"white\">View Group</text>\n</g>\n\\`\\`\\`\n\n### Alarm Status Indicators\nDisplay alarm status with appropriate colors:\n\n\\`\\`\\`xml\n<style>\n .alarm-none { fill: var(--c8y-palette-status-success); }\n .alarm-warning { fill: var(--c8y-palette-status-info); }\n .alarm-minor { fill: var(--c8y-palette-status-warning-high); }\n .alarm-major { fill: var(--c8y-palette-status-warning); }\n .alarm-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<!-- Alarm status circle -->\n<circle cx=\"100\" cy=\"100\" r=\"15\" \n class=\"alarm-\\${this.c8yScadaFunctions?.getActiveAlarmsStatusClass(this.c8yScadaValues?.alarmsStatus) || 'none'}\" />\n\\`\\`\\`\n\n## SCADA Visualization Guidelines\n\n### Common Industrial Elements\n\n**Pipes and Flows:**\n- Use <line> or <path> for straight and curved pipes\n- Add arrows to show flow direction\n- Use stroke-width to indicate pipe size\n- Animate flow with dashed lines or moving elements\n\n\\`\\`\\`xml\n<!-- Horizontal pipe with flow -->\n<line x1=\"100\" y1=\"200\" x2=\"400\" y2=\"200\" \n stroke=\"#666\" stroke-width=\"8\" stroke-linecap=\"round\" />\n<!-- Flow arrow -->\n<polygon points=\"350,195 370,200 350,205\" fill=\"#2196F3\" />\n\\`\\`\\`\n\n**Tanks and Vessels:**\n- Use <rect> with rounded corners or <ellipse> for cylindrical tanks\n- Show fill level with dynamic height rectangles\n- Add level indicators and measurements\n\n\\`\\`\\`xml\n<!-- Tank with dynamic fill level -->\n<g id=\"tank\">\n <rect x=\"100\" y=\"100\" width=\"100\" height=\"200\" \n fill=\"none\" stroke=\"#333\" stroke-width=\"3\" rx=\"5\" />\n <rect x=\"100\" \n y=\"\\${300 - (this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n width=\"100\" \n height=\"\\${(this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n fill=\"#2196F3\" opacity=\"0.7\" />\n <text x=\"150\" y=\"320\" text-anchor=\"middle\">\n \\${this.c8yScadaValues?.tankLevel?.value || 0}\\${this.c8yScadaValues?.tankLevel?.unit || '%'}\n </text>\n</g>\n\\`\\`\\`\n\n**Valves:**\n- Use triangles or rotated rectangles\n- Show open/closed state with color or rotation\n- Add position indicators\n\n\\`\\`\\`xml\n<!-- Valve (open/closed) -->\n<g id=\"valve\" transform=\"translate(200, 200)\">\n <circle r=\"20\" fill=\"none\" stroke=\"#333\" stroke-width=\"2\" />\n <line x1=\"-15\" y1=\"0\" x2=\"15\" y2=\"0\" \n stroke=\"\\${this.c8yScadaValues?.valveOpen?.value ? '#4CAF50' : '#F44336'}\" \n stroke-width=\"3\" \n transform=\"rotate(\\${this.c8yScadaValues?.valveOpen?.value ? 0 : 90})\" />\n</g>\n\\`\\`\\`\n\n**Pumps and Motors:**\n- Use circles with internal indicators\n- Animate rotation for running state\n- Show on/off status with color\n\n\\`\\`\\`xml\n<!-- Pump -->\n<g id=\"pump\">\n <circle cx=\"200\" cy=\"200\" r=\"30\" \n fill=\"\\${this.c8yScadaValues?.pumpRunning?.value ? '#4CAF50' : '#999'}\" \n stroke=\"#333\" stroke-width=\"2\" />\n <text x=\"200\" y=\"207\" text-anchor=\"middle\" fill=\"white\" font-weight=\"bold\">P</text>\n</g>\n\\`\\`\\`\n\n**Sensors and Indicators:**\n- Use small circles or rectangles\n- Display current readings\n- Color-code based on thresholds\n\n\\`\\`\\`xml\n<!-- Temperature sensor -->\n<g id=\"temp-sensor\">\n <rect x=\"100\" y=\"100\" width=\"80\" height=\"40\" \n fill=\"var(--c8y-root-component-background-default)\" \n stroke=\"#333\" rx=\"4\" />\n <text x=\"140\" y=\"125\" text-anchor=\"middle\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value ?? '-'}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n </text>\n</g>\n\\`\\`\\`\n\n### Color Conventions\n\n**Operational States:**\n- Green (#4CAF50 or var(--c8y-palette-status-success)): Running/OK/Open\n- Red (#F44336 or var(--c8y-palette-status-danger)): Stopped/Error/Closed\n- Orange (#FF9800 or var(--c8y-palette-status-warning)): Warning/Standby\n- Gray (#999): Inactive/Disabled\n- Blue (#2196F3 or var(--c8y-brand-primary)): Water/Liquid/Flow\n\n**Use Cumulocity design tokens:**\n- var(--c8y-brand-primary): Brand color\n- var(--c8y-palette-status-success): Success green\n- var(--c8y-palette-status-warning): Warning orange\n- var(--c8y-palette-status-danger): Error/critical red\n- var(--c8y-root-component-background-default): Background\n- var(--c8y-root-component-color-default): Text color\n\n### Layout Best Practices\n\n1. **Left-to-right flow**: Process flows typically go from left to right\n2. **Hierarchical grouping**: Group related components visually\n3. **Consistent spacing**: Maintain uniform spacing between elements\n4. **Clear labels**: Label all major components and measurement points\n5. **Legend/Key**: Include a legend if using special symbols or colors\n6. **Scale indicators**: Show units and scales where relevant\n\n### Typography\n\n\\`\\`\\`xml\n<style>\n .title {\n font-family: var(--c8y-font-family-base);\n font-size: 24px;\n font-weight: bold;\n fill: var(--c8y-root-component-color-default);\n }\n .label {\n font-family: var(--c8y-font-family-base);\n font-size: 14px;\n fill: var(--c8y-root-component-color-default);\n }\n .value {\n font-family: var(--c8y-font-family-base);\n font-size: 16px;\n font-weight: bold;\n fill: var(--c8y-brand-primary);\n }\n</style>\n\\`\\`\\`\n\n## Migrating a Legacy SCADA 1.0 SVG\n\nA legacy SVG is recognisable by AngularJS syntax: \\`{{placeholder}}\\` interpolation and \\`ng-*\\` attributes (\\`ng-if\\`, \\`ng-show\\`, \\`ng-hide\\`, \\`ng-class\\`, \\`ng-click\\`). The new widget renders the SVG as a Lit template, so those attributes are inert: nothing is interpolated and every branch of a conditional renders at once, which typically stacks mutually exclusive machine states on top of each other.\n\nWhen the user supplies such an SVG, or asks to migrate, convert or modernise one:\n\n1. **Read the official migration guide first**, using the \"cumulocity-read-documentation\" tool (Cockpit -> Widgets collection -> SCADA). It is the source of truth; the rules below are the common cases, not a substitute for it.\n2. Replace every \\`{{name}}\\` with \\`\\${this.c8yScadaValues?.name ?? '-'}\\`.\n3. Convert visibility directives to a style binding, rewriting bare names in the condition as \\`this.c8yScadaValues?.name\\`:\n - \\`ng-if=\"cond\"\\` and \\`ng-show=\"cond\"\\` -> \\`style=\"\\${cond ? '' : 'display:none'}\"\\`\n - \\`ng-hide=\"cond\"\\` -> \\`style=\"\\${cond ? 'display:none' : ''}\"\\`\n4. Convert \\`ng-class\\` to \\`class=\"\\${...}\"\\` and \\`ng-click\\` to \\`@click=\\${() => ...}\\`, taking helpers from \\`this.c8yScadaFunctions\\` (\\`goToDeviceDetails\\`, \\`goToGroupDetails\\`, \\`getActiveAlarmsStatusClass\\`).\n5. Never leave a \\`\\${...}\\` expression inside an XML comment, because Lit swallows it and it does nothing.\n6. Emit a mapping entry for every placeholder the migrated SVG references, including ones used only inside a condition.\n7. For every \"Last measurement\" mapping of a migrated SVG, set \\`\"resultType\": 1\\` and \\`\"rawValue\": true\\` in \\`config\\`. The migrated drawing reads bare values (\\`this.c8yScadaValues?.name\\`, without \\`.value\\`) and compares them as numbers, as SCADA 1.0 did: a rounded string such as \\`\"0.00\"\\` is truthy, so a condition like \\`name ? … : …\\` would pick the wrong branch. This is the only exception to the \\`\"resultType\": 4\\` rule.\n8. **Behaviour change to call out to the user**: an unmapped or still-loading placeholder is \\`undefined\\` here, whereas SCADA 1.0 resolved it to \\`''\\` (which equals \\`0\\` in a loose comparison). A condition such as \\`state == 0\\` that matched by default in 1.0 will no longer match. If the user wants the old behaviour, add an explicit check, e.g. \\`state == 0 || state == null\\`.\n\n## Important Notes\n\n- **When mapping to last measurements, ALWAYS use the correct, existing fragment and series as verified by the cumulocity-api-request tool. Never guess or invent measurement names.**\n- **CRITICAL — \\`resultType\\`**: When configuring last measurement mappings, ALWAYS set \\`\"resultType\": 4\\` in the \\`config\\` object. This is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties. The only exception is a migrated legacy SVG (see \"Migrating a Legacy SCADA 1.0 SVG\").\n- **CRITICAL — \\`rawValue\\`**: ALWAYS set \\`\"rawValue\": true\\` in the \\`config\\` of a last measurement mapping, so the drawing gets the number the device sent instead of a string rounded to 2 decimal places. Format values for display in the SVG template, not in the mapping.\n- **CRITICAL — numeric formatting**: \\`.value\\` is the raw number, so \\`0\\` is falsy: write display fallbacks as \\`?.value ?? '-'\\`, never \\`?.value || '-'\\`, which hides a real \\`0\\`. Round only text output, never conditions or calculations, and only when the user asks for it or the series has fractional values: \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`, guarded for a missing value.\n- **CRITICAL — changes**: When the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff.\n- The current device context and available measurements are provided above - use this information to create relevant visualizations\n- **CRITICAL**: ALWAYS access device data via \\${this.c8yScadaValues?.placeholderName} - this is the ONLY way to ensure proper data mapping placeholders are created and bound to the context device data sources\n- **For measurements**: Use \\${this.c8yScadaValues?.placeholderName?.value} to access the value, and optionally .unit and .time for additional measurement data\n- **For device properties**: Use \\${this.c8yScadaValues?.placeholderName} directly WITHOUT subproperties (no .value, .unit, or .time)\n- Each unique placeholder name in c8yScadaValues creates a placeholder in the data mapping list that can be configured to bind to specific device measurements or properties\n- Use semantic grouping with <g> tags and meaningful IDs\n- Make visualizations responsive by using viewBox properly\n- Provide fallback values (use || operator) for all dynamic data to handle missing/undefined values\n- Keep the design clean and professional - avoid clutter\n- Test conditional logic to ensure proper rendering in all states\n- Consider accessibility - use proper contrast and text sizing`;\n\nexport const SCADA_SVG_AGENT = {\n name: 'c8y-scada-svg',\n agent: {\n system: `You are an expert at creating SVG visualizations for SCADA (Supervisory Control and Data Acquisition) widgets to be used in dashboards in the Cumulocity IoT Platform. Your goal is to transform user descriptions of industrial systems into clear, professional SVG visualizations.\n\n## Workflow\n\n1. **Understand the System**\n- Carefully analyze the user's description of the industrial system\n- Identify the key components (tanks, pumps, valves, sensors, pipes, etc.)\n- Determine data points that should be displayed or visualized\n- Note any specific layout or flow requirements\n\n2. **Migrate, if the user supplied a legacy SVG**\n- If the input contains \\`{{...}}\\` interpolation or \\`ng-*\\` attributes, it is a SCADA 1.0 drawing and must be converted before anything else\n- Read the migration guide with the \"cumulocity-read-documentation\" tool, then apply the rules in the \"Migrating a Legacy SCADA 1.0 SVG\" section below\n- Preserve the original artwork (paths, images, transforms, ids) exactly; change only the data binding and directive syntax\n- Tell the user which conditions changed behaviour because unmapped placeholders are now \\`undefined\\` rather than \\`''\\`\n\n3. **Verify Available Data (if needed)**\n- If the visualization requires specific measurements or device properties, use the \"cumulocity-api-request\" tool to verify what data is available\n- Check available measurements, properties, or alarms on the target device/asset\n- Document which data points can be used for dynamic visualization\n\n4. **Design the SVG**\n- Create a clean, professional SVG visualization\n- Use appropriate industrial symbols and conventions\n- Implement data binding for dynamic elements\n- Apply proper color coding for operational states\n- Ensure responsive scaling with proper viewBox\n\n5. **Output the Code**\n- Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block: SVG code in <${SVG_EXTRACT_TAG_NAME}> tags followed by mapping in <${MAPPING_EXTRACT_TAG_NAME}> tags, both inside the wrapper\n- CRITICAL: Include ONLY the SVG code inside the SVG tags and ONLY the JSON mapping inside the mapping tags - no explanatory text, no markdown formatting, no comments outside the inner tags\n- Ensure every placeholder used in the SVG (via \\${this.c8yScadaValues?.placeholderName}) has a corresponding entry in the mapping object\n- **If the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff**\n- Follow the guidelines in: ${SVG_SCADA_INSTRUCTIONS}\n\n6. **Validate Quality**\n- Verify the visualization represents the described system accurately\n- Ensure all dynamic data bindings use proper fallback values\n- Check that colors and styling follow Cumulocity design standards\n- If the user's description is unclear or lacks critical details, ask for clarification\n\n## Key Principles\n- Professional industrial design - clean, clear, uncluttered\n- Semantic SVG structure with organized groups\n- Proper data binding syntax for dynamic content\n- Responsive and theme-aware using Cumulocity CSS variables\n- No emojis or Unicode symbols - use SVG shapes and text only\n`,\n maxOutputTokens: 20000\n },\n type: 'text',\n mcp: [\n {\n serverName: 'cumulocity-default',\n tools: ['cumulocity-api-request', 'cumulocity-read-documentation']\n }\n ]\n} as AgentDefinition;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"AAMA;AACA;AACA;AACA,MAAM,qBAAqB,GAAG,kBAAkB;AAChD,MAAM,oBAAoB,GAAG,iBAAiB;AAC9C,MAAM,wBAAwB,GAAG,qBAAqB;AAEtD,MAAM,sBAAsB,GAAG,CAAA;;;;gEAIiC,qBAAqB,CAAA;mDAClC,oBAAoB,CAAA;qDAClB,wBAAwB,CAAA;+CAC9B,qBAAqB,CAAA;;;;;;;GAOjE,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;;;;;;;;;;;;;;;;;;;MAoBnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;MAEvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;AAYX,YAAA,EAAA,qBAAqB,qLAAqL,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoY7O,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;MAEnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA0HvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uHAyS8F,qBAAqB,CAAA;;;;;;;;;;;+DAW7E;AAExD,MAAM,eAAe,GAAG;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,KAAK,EAAE;AACL,QAAA,MAAM,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CA6BgC,qBAAqB,CAAA,sBAAA,EAAyB,oBAAoB,CAAA,+BAAA,EAAkC,wBAAwB,CAAA;;;+FAGzE,qBAAqB,CAAA;8BACtF,sBAAsB;;;;;;;;;;;;;;AAcnD,CAAA;AACG,QAAA,eAAe,EAAE;AAClB,KAAA;AACD,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,GAAG,EAAE;AACH,QAAA;AACE,YAAA,UAAU,EAAE,oBAAoB;AAChC,YAAA,KAAK,EAAE,CAAC,wBAAwB,EAAE,+BAA+B;AAClE;AACF;;;ACp7BH;;AAEG;;;;"}
@@ -4,7 +4,7 @@ import { inject, Injector, Injectable } from '@angular/core';
4
4
  import { TranslateService } from '@ngx-translate/core';
5
5
  import { MeasurementService, InventoryService, AlarmService, EventService, OperationService } from '@c8y/client';
6
6
  import { MeasurementRealtimeService, DatePipe, translateDatapointUnit, ManagedObjectRealtimeService, AlarmRealtimeService, EventRealtimeService, OperationRealtimeService } from '@c8y/ngx-components';
7
- import { from, map, filter, startWith, take, switchMap, distinctUntilChanged, pairwise, share, NEVER, scan, tap, of, catchError, combineLatest, merge } from 'rxjs';
7
+ import { from, map, filter, startWith, take, defaultIfEmpty, switchMap, distinctUntilChanged, pairwise, share, NEVER, scan, tap, of, catchError, combineLatest, merge } from 'rxjs';
8
8
  import { gettext } from '@c8y/ngx-components/gettext';
9
9
  import { get } from 'lodash-es';
10
10
 
@@ -37,19 +37,28 @@ class LastMeasurementStrategy {
37
37
  return from(this.measurementService.list(measurementFilter)).pipe(map(({ data }) => data[0]), filter(measurement => !!measurement), map(measurement => this.formatMeasurement(measurement)));
38
38
  }
39
39
  createRealtimeStream(initialValue) {
40
- return this.measurementRealtime
40
+ const measurementUpdates$ = this.measurementRealtime
41
41
  .onCreateOfSpecificMeasurement$(this.datapoint.fragment, this.datapoint.series, this.datapoint.__target)
42
- .pipe(map(measurement => this.formatMeasurement(measurement)), startWith(initialValue));
42
+ .pipe(map(measurement => this.formatMeasurement(measurement)));
43
+ // Without a measurement yet, consumers keep their own empty state until the first one arrives.
44
+ const hasInitialValue = initialValue != null;
45
+ return hasInitialValue
46
+ ? measurementUpdates$.pipe(startWith(initialValue))
47
+ : measurementUpdates$;
43
48
  }
44
49
  formatMeasurement(measurement) {
45
50
  const resultType = this.config?.resultType;
46
51
  const fragment = this.datapoint.fragment;
47
52
  const series = this.datapoint.series;
53
+ const seriesValue = measurement[fragment][series];
54
+ const keepRawValue = !!this.config?.rawValue;
48
55
  const decimalPlaces = this.config?.numberOfDecimalPlaces ?? DEFAULT_DECIMAL_PLACES;
49
- const value = this.formatValue(measurement[fragment][series].value, decimalPlaces);
56
+ const value = keepRawValue
57
+ ? seriesValue.value
58
+ : this.formatValue(seriesValue.value, decimalPlaces);
50
59
  // The data point carries the unit configured for it in the library, which is the one a tenant
51
60
  // registers a translation for. The measurement only reports whatever the device sent.
52
- const unit = translateDatapointUnit(this.datapoint.unit || measurement[fragment][series].unit, this.translate);
61
+ const unit = translateDatapointUnit(this.datapoint.unit || seriesValue.unit, this.translate);
53
62
  if (resultType === RESULT_TYPES$1.VALUE.value) {
54
63
  return value;
55
64
  }
@@ -61,7 +70,9 @@ class LastMeasurementStrategy {
61
70
  return `${date} | ${value} ${unit}`;
62
71
  }
63
72
  else if (resultType === RESULT_TYPES$1.OBJECT.value) {
64
- return { ...measurement[fragment][series], value: parseFloat(value).toFixed(decimalPlaces) };
73
+ return keepRawValue
74
+ ? { ...seriesValue }
75
+ : { ...seriesValue, value: parseFloat(value).toFixed(decimalPlaces) };
65
76
  }
66
77
  return '';
67
78
  }
@@ -104,9 +115,14 @@ class RealtimeValueHandler {
104
115
  }
105
116
  }
106
117
  handleUncontrolledRealtime() {
107
- return this.strategy
108
- .fetchCurrentValue()
109
- .pipe(switchMap(initialValue => this.strategy.createRealtimeStream(initialValue)));
118
+ return this.fetchCurrentValueThenStream();
119
+ }
120
+ /**
121
+ * A fetch that completes without a value, e.g. a series with no measurement yet, still starts
122
+ * the realtime stream, with `null` as the initial value.
123
+ */
124
+ fetchCurrentValueThenStream() {
125
+ return this.strategy.fetchCurrentValue().pipe(defaultIfEmpty(null), switchMap(currentValue => this.strategy.createRealtimeStream(currentValue)));
110
126
  }
111
127
  handleControlledRealtime(control$) {
112
128
  const controlWithPrevious$ = control$.pipe(distinctUntilChanged(), startWith(null), pairwise(), share());
@@ -130,9 +146,7 @@ class RealtimeValueHandler {
130
146
  // Realtime is enabled
131
147
  if (this.config.refetchOnResume || previous === null) {
132
148
  // Re-fetch current value and start streaming
133
- return this.strategy
134
- .fetchCurrentValue()
135
- .pipe(switchMap(currentValue => this.strategy.createRealtimeStream(currentValue)));
149
+ return this.fetchCurrentValueThenStream();
136
150
  }
137
151
  else {
138
152
  // Continue with realtime stream without re-fetching
@@ -149,7 +163,7 @@ class RealtimeValueHandler {
149
163
  *
150
164
  * @param config - Measurement configuration (datapoint, result type, etc.)
151
165
  * @param metadata - Configuration controlling the behavior of the function
152
- * @returns Observable<string> - Stream of measurement string values
166
+ * @returns Stream of measurement values, shaped by `config`
153
167
  */
154
168
  function lastMeasurementValue(config, metadata = { mode: 'realtime' }) {
155
169
  const injector = inject(Injector);
@@ -173,12 +187,12 @@ const lastMeasurement = {
173
187
  name: 'lastMeasurement',
174
188
  label: gettext('Last measurement'),
175
189
  type: 'string',
176
- config: { dp: [], resultType: 1 },
190
+ config: { dp: [], resultType: 1, rawValue: true },
177
191
  computed: true,
178
192
  isEditable: false,
179
193
  isStandardProperty: true
180
194
  },
181
- loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-last-measurement-config.component-DOmwNU6m.mjs').then(m => m.ComputedPropertyLastMeasurementConfigComponent),
195
+ loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-last-measurement-config.component-d3JeKEr4.mjs').then(m => m.ComputedPropertyLastMeasurementConfigComponent),
182
196
  value: ({ config }) => {
183
197
  return lastMeasurementValue(config);
184
198
  }
@@ -774,7 +788,7 @@ const fieldbusItemStatus = {
774
788
  isEditable: false,
775
789
  isStandardProperty: true
776
790
  },
777
- loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-C3KZGqlc.mjs').then(m => m.FieldbusItemStatusConfigComponent),
791
+ loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BqbzREl7.mjs').then(m => m.FieldbusItemStatusConfigComponent),
778
792
  value: ({ config, context }) => fieldbusItemStatusValue(context, config)
779
793
  };
780
794
  function fieldbusItemStatusValue(asset, config, metadata = { mode: 'realtime' }) {
@@ -809,4 +823,4 @@ const computedAssetPropertiesProviders = [
809
823
  */
810
824
 
811
825
  export { DEFAULT_DECIMAL_PLACES as D, FieldbusService as F, RESULT_TYPES$1 as R, RESULT_TYPES as a, computedAssetPropertiesProviders as c };
812
- //# sourceMappingURL=c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-1WsTSnO_.mjs.map
826
+ //# sourceMappingURL=c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-B0EYpohd.mjs.map