@c8y/ngx-components 1024.15.13 → 1024.16.10
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.
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +303 -63
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agents-html.mjs +9 -0
- package/fesm2022/c8y-ngx-components-ai-agents-html.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs +4 -0
- package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +394 -52
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai.mjs +329 -19
- package/fesm2022/c8y-ngx-components-ai.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-bookmarks.mjs +1 -1
- package/fesm2022/c8y-ngx-components-bookmarks.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs +8 -2
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +96 -77
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-selector.mjs +92 -47
- package/fesm2022/c8y-ngx-components-datapoint-selector.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-device-profile.mjs +1 -1
- package/fesm2022/c8y-ngx-components-device-profile.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart.mjs +39 -12
- package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ecosystem.mjs +1 -1
- package/fesm2022/c8y-ngx-components-ecosystem.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-protocol-opcua.mjs +1 -1
- package/fesm2022/c8y-ngx-components-protocol-opcua.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-register-device.mjs +5 -6
- package/fesm2022/c8y-ngx-components-register-device.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-repository-firmware.mjs +1 -1
- package/fesm2022/c8y-ngx-components-repository-firmware.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-repository-software.mjs +1 -1
- package/fesm2022/c8y-ngx-components-repository-software.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-sub-assets.mjs +30 -10
- package/fesm2022/c8y-ngx-components-sub-assets.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-time-context.mjs +13 -9
- package/fesm2022/c8y-ngx-components-time-context.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-trusted-certificates.mjs +1 -1
- package/fesm2022/c8y-ngx-components-trusted-certificates.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs +3 -2
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs +4 -6
- package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs +3 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components.mjs +122 -24
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/locales/de.po +147 -16
- package/locales/es.po +143 -11
- package/locales/fr.po +459 -324
- package/locales/ja_JP.po +143 -11
- package/locales/ko.po +143 -11
- package/locales/locales.pot +139 -10
- package/locales/nl.po +143 -11
- package/locales/pl.po +143 -11
- package/locales/pt_BR.po +143 -11
- package/locales/zh_CN.po +143 -11
- package/locales/zh_TW.po +143 -11
- package/package.json +1 -1
- package/types/c8y-ngx-components-ai-agent-chat.d.ts +111 -4
- package/types/c8y-ngx-components-ai-agent-chat.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai-agents-html.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai-agents-scada.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai-ai-chat.d.ts +224 -10
- package/types/c8y-ngx-components-ai-ai-chat.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai.d.ts +176 -6
- package/types/c8y-ngx-components-ai.d.ts.map +1 -1
- package/types/c8y-ngx-components-context-dashboard.d.ts.map +1 -1
- package/types/c8y-ngx-components-datapoint-explorer-view.d.ts +7 -2
- package/types/c8y-ngx-components-datapoint-explorer-view.d.ts.map +1 -1
- package/types/c8y-ngx-components-datapoint-selector.d.ts +63 -50
- package/types/c8y-ngx-components-datapoint-selector.d.ts.map +1 -1
- package/types/c8y-ngx-components-echart.d.ts +12 -0
- package/types/c8y-ngx-components-echart.d.ts.map +1 -1
- package/types/c8y-ngx-components-register-device.d.ts.map +1 -1
- package/types/c8y-ngx-components-sub-assets.d.ts +10 -1
- package/types/c8y-ngx-components-sub-assets.d.ts.map +1 -1
- package/types/c8y-ngx-components-time-context.d.ts +2 -0
- package/types/c8y-ngx-components-time-context.d.ts.map +1 -1
- package/types/c8y-ngx-components-widgets-implementations-scada.d.ts.map +1 -1
- package/types/c8y-ngx-components.d.ts +37 -5
- package/types/c8y-ngx-components.d.ts.map +1 -1
|
@@ -1664,6 +1664,15 @@ Currently the following Code is used:
|
|
|
1664
1664
|
{
|
|
1665
1665
|
serverName: 'cumulocity-default',
|
|
1666
1666
|
tools: ['cumulocity-api-request']
|
|
1667
|
+
// TO DO: Uncomment the below to enable the HITL prompts. This can be done when c8y-ai-agents version 2.3.0 is released.
|
|
1668
|
+
// tools: [
|
|
1669
|
+
// {
|
|
1670
|
+
// name: 'cumulocity-api-request',
|
|
1671
|
+
// destructive: true,
|
|
1672
|
+
// openWorld: true,
|
|
1673
|
+
// nonReadOnly: true
|
|
1674
|
+
// }
|
|
1675
|
+
// ]
|
|
1667
1676
|
}
|
|
1668
1677
|
]
|
|
1669
1678
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"c8y-ngx-components-ai-agents-html.mjs","sources":["../../ai/agents/html/html-widget.agent.c8y.ts","../../ai/agents/html/c8y-ngx-components-ai-agents-html.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\nconst EXTRACT_TAG_NAME = 'c8y-code-extract';\nconst WIDGET_CODE_INSTRUCTIONS = `You are responsible for creating a Web component that is rendered on the Dashboard of the Cumulocity IoT Platform. It is written as a lit-element. The following is a very basic example:\n\n<${EXTRACT_TAG_NAME}>\nimport { LitElement, html, css} from 'lit';\nimport { styleImports } from 'styles';\n\nexport default class DefaultWebComponent extends LitElement {\n static styles = css\\`\n\n:host > div {\n padding-left: var(--c8y-root-component-padding);\n padding-right: var(--c8y-root-component-padding);\n}\nspan.branded {\n color: var(--c8y-brand-primary);\n}\n \\`;\n\n static properties = {\n // The managed object this widget is assigned to. Can be null.\n c8yContext: { type: Object },\n };\n\n constructor() {\n super();\n }\n\n render() {\n return html\\`\n <style>\n \\${styleImports}\n </style>\n <div>\n <h1>Hello from HTML widget</h1>\n <p>\n You can use HTML and Javascript template literals here:\n \\${this.c8yContext ? this.c8yContext.name : 'No device selected'}\n </p>\n\n <a class=\"btn btn-primary\" href=\"#/group\">Go to groups</a>\n\n <p>\n Use the CSS editor to add CSS. You can use <span class=\"branded\">any design-token CSS variable</span> in there.\n </p>\n</div>\n \\`;\n }\n}\n</${EXTRACT_TAG_NAME}>\n\nYou are allowed to use the following ESM imports and libs:\n\nimport * as echarts from 'echarts'\nimport { fetch } from 'fetch'\n\n## Leaflet Map Integration Guidelines\n\nWhen building a widget that displays a map, you must use the Leaflet library. Follow the implementation pattern below as your foundation,\nwith flexibility to make improvements as needed.\n\n **IMPORTANT: Remember to not use any Leaflet plugin. You are only allowed to use pure Leaflet.**\n\n### Reference Implementation\n\nUse this complete example as your guide for implementing map functionality:\n\n\\`\\`\\`javascript\nimport { LitElement, html, css } from 'lit';\nimport { styleImports } from 'styles';\n\nexport default class DeviceLocationMapWidget extends LitElement {\n static styles = css\\`\n :host > div {\n padding: var(--c8y-root-component-padding);\n height: 100%;\n display: flex;\n flex-direction: column;\n }\n\n .map-container {\n flex: 1;\n min-height: 400px;\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-root-component-border-radius-base);\n position: relative;\n }\n\n .no-position {\n text-align: center;\n padding: 40px;\n color: var(--text-muted);\n }\n \\`;\n\n static properties = {\n error: { type: String },\n map: { type: Object },\n c8yContext: { type: Object },\n };\n\n constructor() {\n super();\n this.error = null;\n this.map = null;\n }\n\n async connectedCallback() {\n super.connectedCallback();\n await this.updateComplete;\n this.initializeMap();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this.map) {\n this.map.remove();\n this.map = null;\n }\n }\n\n getStatus(device) {\n if (!device.c8y_ActiveAlarmsStatus) {\n return 'text-muted';\n }\n if (device.c8y_ActiveAlarmsStatus.critical) {\n return 'status critical';\n }\n if (device.c8y_ActiveAlarmsStatus.major) {\n return 'status major';\n }\n if (device.c8y_ActiveAlarmsStatus.minor) {\n return 'status minor';\n }\n if (device.c8y_ActiveAlarmsStatus.warning) {\n return 'status warning';\n }\n return 'text-muted';\n }\n\n initializeMap() {\n if (!window.L) {\n console.error('Leaflet (L) is not available');\n this.error = 'Map library not loaded';\n return;\n }\n\n const mapContainer = this.shadowRoot.querySelector('#map');\n if (!mapContainer || !this.c8yContext?.c8y_Position) {\n return;\n }\n\n const { lat, lng } = this.c8yContext.c8y_Position;\n\n try {\n this.map = window.L.map(mapContainer).setView([lat, lng], 15);\n\n window.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n attribution: '© OpenStreetMap contributors'\n }).addTo(this.map);\n\n const status = this.getStatus(this.c8yContext);\n\n const deviceIcon = window.L.divIcon({\n html: '<div class=\"dlt-c8y-icon-marker icon-3x ' + status + '\"></div>',\n className: 'c8y-map-marker-icon',\n iconAnchor: [8, 8]\n });\n\n const marker = window.L.marker([lat, lng], { icon: deviceIcon }).addTo(this.map);\n\n const popupContent = '<div style=\"min-width: 200px;\">' +\n '<h4 style=\"margin: 0 0 8px 0;\">' + (this.c8yContext?.name || '') + '</h4>' +\n '<p style=\"margin: 4px 0; font-size: 12px;\">' +\n '<strong>Latitude:</strong> ' + lat.toFixed(6) + '<br>' +\n '<strong>Longitude:</strong> ' + lng.toFixed(6) +\n (this.c8yContext.c8y_Position.alt ? '<br><strong>Altitude:</strong> ' + this.c8yContext.c8y_Position.alt + 'm' : '') +\n '</p>' +\n '<p style=\"margin: 4px 0 0 0; font-size: 11px;\">' +\n 'Last updated: ' + new Date(this.c8yContext?.lastUpdated || Date.now()).toLocaleString() +\n '</p>' +\n '</div>';\n\n marker.bindPopup(popupContent);\n\n this.map.setView([lat, lng], 15);\n\n setTimeout(() => {\n if (this.map) {\n this.map.invalidateSize();\n }\n }, 100);\n } catch (error) {\n console.error('Error initializing map:', error);\n this.error = 'Failed to initialize map: ' + error.message;\n }\n }\n\n render() {\n if (this.error) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"alert alert-danger\" role=\"alert\">\n <strong>Error:</strong> \\${this.error}\n </div>\n </div>\n \\`;\n }\n\n if (!this.c8yContext?.c8y_Position) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"no-position\">\n <h4>No Position Data Available</h4>\n <p>The device \"\\${this.c8yContext?.name || 'Unknown'}\" does not have position information.</p>\n </div>\n </div>\n \\`;\n }\n\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"map-container\">\n <div id=\"map\" style=\"width: 100%; height: 100%;\"></div>\n </div>\n </div>\n \\`;\n }\n}\n\\`\\`\\`\n\n## ECharts Integration Example\nThis example provides a generic implementation pattern for creating ECharts visualizations using Lit elements.\nUse this as a reference for implementing any ECharts chart type (line, bar, pie, scatter, gauge, etc.).\n\n\\`\\`\\`typescript\nimport { LitElement, html, css } from 'lit';\nimport { styleImports } from 'styles';\nimport * as echarts from 'echarts';\nimport { fetch } from 'fetch';\n\nexport default class EnergyConsumptionPieChart extends LitElement {\n static styles = css\\`\n :host > div {\n padding: var(--c8y-root-component-padding);\n height: 100%;\n display: flex;\n flex-direction: column;\n }\n\n .chart-container {\n flex: 1;\n min-height: 400px;\n position: relative;\n }\n\n .chart-header {\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid var(--c8y-root-component-separator-color);\n }\n\n .chart-title {\n font-size: 18px;\n font-weight: 600;\n color: var(--text-color);\n margin: 0 0 4px 0;\n }\n\n .chart-subtitle {\n font-size: 14px;\n color: var(--text-muted);\n margin: 0;\n }\n\n .loading-container {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 300px;\n }\n\n .error-container {\n text-align: center;\n padding: 40px 20px;\n color: var(--text-muted);\n }\n\n .error-icon {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--c8y-palette-status-warning);\n }\n\n .energy-summary {\n display: flex;\n justify-content: space-around;\n margin-top: 16px;\n padding: 16px;\n background-color: var(--body-background-color);\n border-radius: var(--c8y-root-component-border-radius-base);\n border: 1px solid var(--c8y-root-component-border-color);\n }\n\n .energy-item {\n text-align: center;\n flex: 1;\n }\n\n .energy-value {\n font-size: 20px;\n font-weight: 600;\n margin-bottom: 4px;\n }\n\n .energy-label {\n font-size: 12px;\n color: var(--text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .solar { color: #ffa726; }\n .wind { color: #42a5f5; }\n .grid { color: #66bb6a; }\n .battery { color: #ab47bc; }\n \\`;\n\n static properties = {\n c8yContext: { type: Object },\n chartInstance: { type: Object },\n energyData: { type: Object },\n loading: { type: Boolean },\n error: { type: String }\n };\n\n constructor() {\n super();\n this.chartInstance = null;\n this.energyData = null;\n this.loading = false;\n this.error = null;\n }\n\n async connectedCallback() {\n super.connectedCallback();\n await this.updateComplete;\n await this.loadEnergyData();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this.chartInstance) {\n this.chartInstance.dispose();\n this.chartInstance = null;\n }\n }\n\n async loadEnergyData() {\n if (!this.c8yContext?.id) {\n this.error = 'No device selected';\n return;\n }\n\n this.loading = true;\n this.error = null;\n\n try {\n // Try to fetch energy measurements from the device\n const response = await fetch(\\`/measurement/measurements?source=\\${this.c8yContext.id}&type=c8y_EnergyMeasurement&pageSize=100&revert=true\\`);\n const data = await response.json();\n\n if (data.measurements && data.measurements.length > 0) {\n // Process real energy measurements\n this.processRealEnergyData(data.measurements);\n } else {\n // If no energy measurements found, try other energy-related types\n const alternativeResponse = await fetch(\\`/measurement/measurements?source=\\${this.c8yContext.id}&pageSize=100&revert=true\\`);\n const alternativeData = await alternativeResponse.json();\n\n const energyMeasurements = alternativeData.measurements?.filter(m =>\n m.type.toLowerCase().includes('energy') ||\n m.type.toLowerCase().includes('power') ||\n Object.keys(m).some(key => key.toLowerCase().includes('energy') || key.toLowerCase().includes('power'))\n );\n\n if (energyMeasurements && energyMeasurements.length > 0) {\n this.processRealEnergyData(energyMeasurements);\n }\n }\n } catch (error) {\n console.error('Error loading energy data:', error);\n this.error = 'Failed to load energy data';\n } finally {\n this.loading = false;\n await this.updateComplete;\n this.initializeChart();\n }\n }\n\n processRealEnergyData(measurements) {\n // Process real energy measurements and categorize by source\n const energyBySource = {\n solar: 0,\n wind: 0,\n grid: 0,\n battery: 0\n };\n\n measurements.forEach(measurement => {\n // Look for energy values in the measurement\n Object.keys(measurement).forEach(key => {\n if (key.startsWith('c8y_') && typeof measurement[key] === 'object') {\n const fragment = measurement[key];\n Object.keys(fragment).forEach(subKey => {\n if (fragment[subKey] && typeof fragment[subKey] === 'object' && fragment[subKey].value !== undefined) {\n const value = parseFloat(fragment[subKey].value) || 0;\n\n // Categorize based on measurement type or fragment name\n if (key.toLowerCase().includes('solar') || subKey.toLowerCase().includes('solar')) {\n energyBySource.solar += value;\n } else if (key.toLowerCase().includes('wind') || subKey.toLowerCase().includes('wind')) {\n energyBySource.wind += value;\n } else if (key.toLowerCase().includes('battery') || subKey.toLowerCase().includes('battery')) {\n energyBySource.battery += value;\n } else {\n energyBySource.grid += value;\n }\n }\n });\n }\n });\n });\n\n // If no categorized data found, distribute evenly for demo\n const totalEnergy = Object.values(energyBySource).reduce((sum, val) => sum + val, 0);\n this.energyData = energyBySource;\n }\n\n initializeChart() {\n const chartContainer = this.shadowRoot.querySelector('#energyChart');\n if (!chartContainer || !this.energyData) return;\n\n if (this.chartInstance) {\n this.chartInstance.dispose();\n }\n\n this.chartInstance = echarts.init(chartContainer);\n\n const chartData = [\n { value: this.energyData.solar, name: 'Solar', itemStyle: { color: '#ffa726' } },\n { value: this.energyData.wind, name: 'Wind', itemStyle: { color: '#42a5f5' } },\n { value: this.energyData.grid, name: 'Grid', itemStyle: { color: '#66bb6a' } },\n { value: this.energyData.battery, name: 'Battery', itemStyle: { color: '#ab47bc' } }\n ];\n\n const totalConsumption = Object.values(this.energyData).reduce((sum, val) => sum + val, 0);\n\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: function(params) {\n const percentage = ((params.value / totalConsumption) * 100).toFixed(1);\n return \\`<strong>\\${params.name}</strong><br/>\n \\${params.value.toFixed(1)} kWh (\\${percentage}%)\\`;\n }\n },\n legend: {\n orient: 'horizontal',\n bottom: '10%',\n left: 'center',\n textStyle: {\n color: getComputedStyle(document.documentElement).getPropertyValue('--text-color') || '#333'\n }\n },\n series: [\n {\n name: 'Energy Consumption',\n type: 'pie',\n radius: ['40%', '70%'],\n center: ['50%', '40%'],\n avoidLabelOverlap: false,\n itemStyle: {\n borderRadius: 8,\n borderColor: getComputedStyle(document.documentElement).getPropertyValue('--body-background-color') || '#fff',\n borderWidth: 2\n },\n label: {\n show: true,\n position: 'outside',\n formatter: function(params) {\n const percentage = ((params.value / totalConsumption) * 100).toFixed(1);\n return \\`\\${params.name}\\\\n\\${percentage}%\\`;\n },\n color: getComputedStyle(document.documentElement).getPropertyValue('--text-color') || '#333'\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 14,\n fontWeight: 'bold'\n },\n itemStyle: {\n shadowBlur: 10,\n shadowOffsetX: 0,\n shadowColor: 'rgba(0, 0, 0, 0.5)'\n }\n },\n data: chartData\n }\n ]\n };\n\n this.chartInstance.setOption(option);\n\n // Handle resize\n const resizeObserver = new ResizeObserver(() => {\n if (this.chartInstance) {\n this.chartInstance.resize();\n }\n });\n resizeObserver.observe(chartContainer);\n }\n\n render() {\n if (this.loading) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"chart-header\">\n <h3 class=\"chart-title\">Energy Consumption Breakdown</h3>\n <p class=\"chart-subtitle\">Loading energy data...</p>\n </div>\n <div class=\"loading-container\">\n <div class=\"spinner\">\n <div class=\"rect1\"></div>\n <div class=\"rect2\"></div>\n <div class=\"rect3\"></div>\n <div class=\"rect4\"></div>\n <div class=\"rect5\"></div>\n </div>\n </div>\n </div>\n \\`;\n }\n\n if (this.error && !this.energyData) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"chart-header\">\n <h3 class=\"chart-title\">Energy Consumption Breakdown</h3>\n <p class=\"chart-subtitle\">Unable to load data</p>\n </div>\n <div class=\"error-container\">\n <div class=\"error-icon\">⚠</div>\n <h4>No Energy Data Available</h4>\n <p>The device \"\\${this.c8yContext?.name || 'Unknown'}\" does not have energy consumption measurements.</p>\n <p class=\"text-muted\">This widget requires energy measurements with types like c8y_EnergyMeasurement.</p>\n </div>\n </div>\n \\`;\n }\n\n const totalConsumption = this.energyData ? Object.values(this.energyData).reduce((sum, val) => sum + val, 0) : 0;\n const deviceName = this.c8yContext?.name || 'Unknown Device';\n\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"chart-header\">\n <h3 class=\"chart-title\">Energy Consumption Breakdown</h3>\n <p class=\"chart-subtitle\">\\${deviceName} - Total: \\${totalConsumption.toFixed(1)} kWh</p>\n </div>\n\n <div class=\"chart-container\">\n <div id=\"energyChart\" style=\"width: 100%; height: 100%;\"></div>\n </div>\n\n \\${\n this.energyData\n ? html\\`\n <div class=\"energy-summary\">\n <div class=\"energy-item\">\n <div class=\"energy-value solar\">\\${this.energyData.solar.toFixed(1)}</div>\n <div class=\"energy-label\">Solar kWh</div>\n </div>\n <div class=\"energy-item\">\n <div class=\"energy-value wind\">\\${this.energyData.wind.toFixed(1)}</div>\n <div class=\"energy-label\">Wind kWh</div>\n </div>\n <div class=\"energy-item\">\n <div class=\"energy-value grid\">\\${this.energyData.grid.toFixed(1)}</div>\n <div class=\"energy-label\">Grid kWh</div>\n </div>\n <div class=\"energy-item\">\n <div class=\"energy-value battery\">\\${this.energyData.battery.toFixed(1)}</div>\n <div class=\"energy-label\">Battery kWh</div>\n </div>\n </div>\n \\`\n : ''\n }\n \\</div>\n \\`;\n }\n}\n\\`\\`\\`\n\n\nAlways use the imported fetch function to make API calls authenticated to the Cumulocity instance.\n\nDo not use mocking or fake data. Always use real, actual data whenever possible.\nDo not include any emoji characters or Unicode symbols in the output - replace any decorative icons with plain text descriptions.\n\nAmong the UI elements that you are allowed to build on your own using HTML and CSS, here are components that you are encouraged to use in your answer:\n\n## Buttons\nYou can apply button classes to any <a>, <input>, or <button> element to style them as buttons. When using a button inside a <form> without a defined type, it defaults to type=\"submit\". To prevent accidental form submissions, always explicitly define the button's type as type=\"button\".\n\nAvailable button variants:\n- .btn-default (standard button)\n- .btn-primary (primary action)\n- .btn-success (positive action)\n- .btn-warning (caution action)\n- .btn-danger (destructive action)\n- .btn-link (text-only button)\n\nButton sizes:\n- .btn-lg (large)\n- .btn-sm (small)\n- .btn-xs (extra small)\n\n\\`\\`\\`html\n<button class=\"btn btn-primary\" type=\"button\">Primary Button</button>\n<button class=\"btn btn-default\" type=\"button\">Default Button</button>\n<button class=\"btn btn-success\" type=\"button\">Success Button</button>\n<button class=\"btn btn-warning\" type=\"button\">Warning Button</button>\n<button class=\"btn btn-danger\" type=\"button\">Danger Button</button>\n<a class=\"btn btn-link\" href=\"javascript:void(0);\">Link Button</a>\n\\`\\`\\`\n\n### Pending/Loading Buttons\nAdd .btn-pending to display an active process. The pointer-events are set to none, making the button unclickable.\n\n\\`\\`\\`html\n<button type=\"button\" aria-busy=\"true\" class=\"btn btn-primary btn-pending\">\n Processing...\n</button>\n\\`\\`\\`\n\n### Button Groups\nGroup a series of buttons together with .btn-group. Can be used for toolbars or action groups.\n\n\\`\\`\\`html\n<div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-default\">Left</button>\n <button type=\"button\" class=\"btn btn-default\">Middle</button>\n <button type=\"button\" class=\"btn btn-default\">Right</button>\n</div>\n\\`\\`\\`\n\n## Alerts\nWrap your message in a <div> with .alert class and a modifier class. Always add appropriate ARIA roles:\n\n- Use role=\"status\" for informational messages\n- Use role=\"alert\" for errors or messages\nrequiring immediate attention\n\n\\`\\`\\`html\n<div class=\"alert alert-success\" role=\"status\">\n <strong>Success!</strong> Operation completed successfully.\n</div>\n<div class=\"alert alert-warning\" role=\"alert\">\n <strong>Warning!</strong> Please review your settings.\n</div>\n<div class=\"alert alert-danger\" role=\"alert\">\n <strong>Error!</strong> Failed to save data.\n</div>\n<div class=\"alert alert-info\" role=\"status\">\n <strong>Info:</strong> New updates available.\n</div>\n<div class=\"alert alert-system\" role=\"alert\">\n <strong>System:</strong> Maintenance scheduled.\n</div>\n\\`\\`\\`\n\n### Dismissible Alerts\nFor dismissible alerts, add a close button with proper accessibility:\n\n\\`\\`\\`html\n<div class=\"alert alert-danger alert-dismissible\" role=\"alert\">\n <button class=\"close\" type=\"button\" @click=\"\\${() => this.dismissAlert()}\" aria-label=\"Close alert\">\n <span aria-hidden=\"true\">×</span>\n </button>\n <strong>Error:</strong> Invalid credentials provided.\n</div>\n\\`\\`\\`\n\n## Badges\nBadges indicate states with specific color schemes. Add .badge with a modifier class:\n\n\\`\\`\\`html\n<span class=\"badge badge-default\">3</span>\n<span class=\"badge badge-success\">Active</span>\n<span class=\"badge badge-warning\">Pending</span>\n<span class=\"badge badge-danger\">Critical</span>\n<span class=\"badge badge-system\">System</span>\n<span class=\"badge badge-info\">72</span>\n\\`\\`\\`\n### Icon Badges\nCombine badges with icons using .c8y-icon-badge wrapper:\n\n\\`\\`\\`html\n<span class=\"c8y-icon-badge\" title=\"14 Critical alarms\">\n <i class=\"dlt-c8y-icon-exclamation-circle status critical\"></i>\n <span class=\"badge badge-danger\" aria-live=\"assertive\">14</span>\n</span>\n\\`\\`\\`\n\n## Loading Spinner\nShows content is being loaded or processed:\n\nWhen content is loading, show only a visual loading indicator (spinner/progress bar) and suppress any 'Loading...' text messages and show it in the middle of the widget.\n\n\\`\\`\\`html\n<div class=\"spinner\">\n <div class=\"rect1\"></div>\n <div class=\"rect2\"></div>\n <div class=\"rect3\"></div>\n <div class=\"rect4\"></div>\n <div class=\"rect5\"></div>\n</div>\n\\`\\`\\`\n\n## Tag\nTags highlight small pieces of information inline. They are commonly used to display categories, filters, or selected options in a visually appealing and compact manner.\n\nAdd the .tag class to any <span> or <div> together with any of the modifier classes mentioned below to change the appearance of an inline label. Add the .font-size-inherit class to inherit the font size.\n\n\\`\\`\\`html\n<h1>heading 1\n<span class=\"tag tag--default\">Default</span>\n</h1>\n<br>\n<h2>heading 2\n<span class=\"tag tag--primary\">Primary</span>\n</h2>\n<br>\n<h3>heading 3\n<span class=\"tag tag--danger\">Danger</span>\n</h3>\n<br>\n<h4>heading 4\n<span class=\"tag tag--success font-size-inherit\">Success</span>\n</h4>\n<br>\n<h5>heading 5\n<span class=\"tag tag--warning\">Warning</span>\n</h5>\n<br>\n<h6>heading 6\n<span class=\"tag tag--info\">Info</span>\n</h6>\n\\`\\`\\`\n\n## Table\nTables help you see and process great amounts of data in a tabular form. Designed for simplicity and clarity, they are an efficient way to organize and present information.\n\n\n### Default table\n\nFor basic styling—light padding and only horizontal dividers—add the base class .table to any table tag. It may seem redundant, but given the widespread use of tables for other plugins like calendars and date pickers, we have opted to isolate our custom table styles.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table\" role=\"table\" aria-label=\"Basic data table\">\n <caption>Optional table caption.</caption>\n <colgroup>\n <col width=\"20px\">\n <col width=\"33%\">\n <col width=\"33%\">\n <col width=\"33%\">\n </colgroup>\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n### Striped rows\nUse .table-striped to add zebra-striping to any table row within the tbody tag.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-striped\" role=\"table\" aria-label=\"Striped data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n### Bordered tables\nAdd .table-bordered for borders on all sides of the table and cells.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-bordered\" role=\"table\" aria-label=\"Bordered data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n\n### Hover rows\nAdd .table-hover to enable a hover state on table rows within a tbody tag.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-hover\" role=\"table\" aria-label=\"Hoverable data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n\n### Condensed tables\nWhen in need of showing tables in a more compact way, add .table-condensed to reduce font size and cut cell padding in half.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-condensed\" role=\"table\" aria-label=\"Condensed data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td colspan=\"2\">Larry the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n## Alarms\nAlarms indicate device or system issues with varying levels of severity. Each alarm has both a severity level and a status to track its lifecycle.\n\n### Alarm Severities\n\nAlarms are classified into four severity levels, each with its own icon and use case:\n\n- **CRITICAL**: Device is out of service and requires immediate attention\n- **MAJOR**: Device has a significant problem that should be fixed\n- **MINOR**: Device has a problem that may need attention\n- **WARNING**: Informational warning that should be noted\n\n\\`\\`\\`html\n<!-- Critical Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-exclamation-circle critical\"></i>\n<span>Critical: System failure detected</span>\n\n<!-- Major Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-warning major\"></i>\n<span>Major: Service degradation</span>\n\n<!-- Minor Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-high-priority minor\"></i>\n<span>Minor: Performance threshold exceeded</span>\n\n<!-- Warning Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-circle warning\"></i>\n<span>Warning: Maintenance required soon</span>\n\n### Alarm Statuses\nAlarms progress through three possible statuses during their lifecycle:\n\n- Active: Alarm has been raised and no one is currently addressing it\n- Acknowledged: Someone has acknowledged the alarm and is working on resolution\n- Cleared: The issue has been resolved (either manually cleared or auto-resolved by the device)\n\n\\`\\`\\`html\n<!-- Active Status -->\n<i class=\"c8y-icon dlt-c8y-icon-bell\"></i>\n<span>Active alarm</span>\n\n<!-- Acknowledged Status -->\n<i class=\"c8y-icon dlt-c8y-icon-bell-slash\"></i>\n<span>Acknowledged by technician</span>\n\n<!-- Cleared Status -->\n<i class=\"c8y-icon c8y-icon-alert-idle\"></i>\n<span>Alarm cleared</span>\n\\`\\`\\`\n\n## Forms\n\n### Input fields\nInput fields play a crucial role in forms, enabling users to input diverse data types like text, numbers, dates, and more.\n\n\\`\\`\\`html\n <input class=\"form-control input-sm\" type=\"text\" placeholder=\"Text input\" autocomplete=\"off\" aria-label=\"Text input\">\n <input class=\"form-control\" type=\"number\" placeholder=\"Number input\" step=\"5\" aria-label=\"Number input\">\n <input class=\"form-control input-lg\" type=\"email\" placeholder=\"Email input\" aria-label=\"Email input\">\n\\`\\`\\`\n\n\n### Textarea\nThe textarea is used for multiline text input, often for extensive descriptions.\n\nConsistent styling with other input fields is achieved by adding the form-control class. This ensures a 100% width and prevents horizontal resizing. You can still customize the rows attribute as necessary.\n\n\\`\\`\\`html\n <textarea class=\"form-control\" rows=\"6\" placeholder=\"Add multiline text here…\" aria-label=\"Multiline text input\"></textarea>\n\\`\\`\\`\\`\n\n### Select\nThe Select displays a list of options with a filter field. It enhances user interaction by providing a searchable list of options and supporting multiple selections, making the selection process efficient and user-friendly.\n\n\\`\\`\\`html\n <div class=\"c8y-select-wrapper\">\n <select id=\"selectExample\" class=\"form-control\" aria-label=\"Native select example\">\n <option>Pick one option…</option>\n <option>Option 1</option>\n <option>Option 2</option>\n <option>Option 3</option>\n <option>Option 4</option>\n <option>Option 5</option>\n </select>\n </div>\n\\`\\`\\`\n\n### Checkboxes and radio buttons\nCheckboxes allow users to select one or multiple options from a list, while radio buttons enable the selection of a single option from several choices.\n\nFor consistent styling across different browsers, it is essential to override the default appearance. To achieve this, enclose the input element within a label element and apply the appropriate class: c8y-checkbox for checkboxes or c8y-radio for radio buttons. Then, add an empty span, followed by another span containing the label text. Ensure proper styling by declaring the type attribute of the input element as either checkbox or radio\n\n\\`\\`\\`html\n <label class=\"c8y-checkbox\" title=\"Checkbox One\" aria-label=\"Checkbox One\">\n <input type=\"checkbox\" checked=\"checked\" aria-checked=\"true\">\n <span></span>\n <span>Checkbox One</span>\n </label>\n\n <label class=\"c8y-checkbox\" title=\"Checkbox Two\" aria-label=\"Checkbox Two\">\n <input type=\"checkbox\" aria-checked=\"false\">\n <span></span>\n <span>Checkbox Two</span>\n </label>\n\n <label title=\"Checkbox Three\" class=\"c8y-checkbox\" aria-label=\"Checkbox Three\">\n <input type=\"checkbox\" [indeterminate]=\"true\" aria-checked=\"mixed\">\n <span></span>\n <span>Checkbox Three</span>\n </label>\n\n <label class=\"c8y-radio\" title=\"Radio One\" aria-label=\"Radio One\">\n <input type=\"radio\" name=\"rgroup1\" checked=\"checked\" aria-checked=\"true\">\n <span></span>\n <span>Radio One</span>\n </label>\n\n <label class=\"c8y-radio\" title=\"Radio Two\" aria-label=\"Radio Two\">\n <input type=\"radio\" name=\"rgroup1\" aria-checked=\"false\">\n <span></span>\n <span>Radio Two</span>\n </label>\n\n <label title=\"Radio Three\" class=\"c8y-radio\" aria-label=\"Radio Three\">\n <input type=\"radio\" name=\"rgroup1\" aria-checked=\"false\">\n <span></span>\n <span>Radio Three</span>\n </label>\n\\`\\`\\`\n\n### Toggle switch\nThe toggle switch is a UI element that provides a simple and intuitive way to toggle between two states, typically representing \"On\" and \"Off\" options\n\n\\`\\`\\`html\n <label class=\"c8y-switch\" aria-label=\"Toggle switch label\">\n <input type=\"checkbox\" checked=\"checked\" aria-checked=\"true\">\n <span></span> Toggle switch label\n </label>\n\n <label class=\"c8y-switch\" title=\"Switch on/off\" aria-label=\"Switch on/off\">\n <input type=\"checkbox\" aria-checked=\"false\">\n <span></span>\n </label>\n <p>\n Some text with the toggle switch aligned\n <label class=\"c8y-switch c8y-switch--inline\" aria-label=\"Toggle switch label inline\">\n <input type=\"checkbox\" aria-checked=\"false\">\n <span></span> Toggle switch label\n </label>\n </p>\n\\`\\`\\`\n\n### Input groups\nElevate text-based inputs with input groups. Easily add text, icons, or buttons before and/or after input fields for enhanced functionality and design.\n\nUse .input-group with an .input-group-addon or .input-group-btn to prepend or append elements to a single input field. Place one add-on or button on either side of an input. You may also place them on both sides of an input.\n\\`\\`\\`html\n <div class=\"input-group\">\n <span class=\"input-group-addon\" id=\"basic-addon1\"> @ </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. johndoe\" [attr.aria-describedby]=\"'basic-addon1'\" aria-label=\"Username input with @ prefix\">\n </div>\n\n <div class=\"input-group\">\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. www.example\" [attr.aria-describedby]=\"'basic-addon2'\" aria-label=\"Website input\">\n <span class=\"input-group-addon\" id=\"basic-addon2\">.com</span>\n </div>\n\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"euro\"></i>\n </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. 1000\" aria-label=\"Amount input\">\n <span class=\"input-group-addon\">.00</span>\n </div>\n\n <div class=\"input-group\">\n <span class=\"input-group-addon\" id=\"basic-addon3\">https://example.com/users/</span>\n <input class=\"form-control\" type=\"text\" id=\"basic-url\" [attr.aria-describedby]=\"'basic-addon3'\" placeholder=\"e.g. johndoe\" aria-label=\"User URL input\">\n <div class=\"input-group-btn\">\n <button class=\"btn btn-primary\" type=\"button\" aria-label=\"Submit username\">Submit</button>\n </div>\n </div>\n\\`\\`\\`\n\nSearch input-group\nSearch input groups have specific styling, append .input-group-search to the .input-group to place the button inside the input field and add round corners.\n\n\\`\\`\\`html\n <div class=\"input-group input-group-search\" id=\"example\">\n <input class=\"form-control\" type=\"search\" placeholder=\"Search…\" aria-label=\"Search input\">\n <span class=\"input-group-btn\">\n <button class=\"btn btn-clean\" type=\"button\" title=\"Search\" aria-label=\"Search\">\n <i class=\"c8y-icon dlt-c8y-icon-search\"></i>\n </button>\n <button class=\"btn btn-clean\" type=\"button\" title=\"Clear search\" aria-label=\"Clear search\">\n <i class=\"c8y-icon dlt-c8y-icon-times\"></i>\n </button>\n </span>\n </div>\n\\`\\`\\`\n\nInput group sizing\nAdd the relative form sizing classes to the .input-group itself and contents within will automatically resize—no need for repeating the form control size classes on each element.\n\n\\`\\`\\`html\n <!-- Small size -->\n <div class=\"input-group input-group-sm\">\n <span class=\"input-group-addon\" id=\"sizing-addon3\"> @ </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"Username\" aria-label=\"Small username input\">\n </div>\n\n <!-- Regular size -->\n <div class=\"input-group\">\n <span class=\"input-group-addon\" id=\"sizing-addon2\"> @ </span>\n <input type=\"text\" class=\"form-control\" placeholder=\"Username\" aria-label=\"Regular username input\">\n </div>\n\n <!-- Large size -->\n <div class=\"input-group input-group-lg\">\n <span class=\"input-group-addon\" id=\"sizing-addon1\"> @ </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"Username\" aria-label=\"Large username input\">\n </div>\n\\`\\`\\`\n\nButton addons\nButtons in input groups are a bit different and require one extra level of nesting. Instead of .input-group-addon, you have to use .input-group-btn to wrap the buttons. This is required due to default browser styles that cannot be overridden.\n\n\\`\\`\\`html\n\n <div class=\"input-group\">\n <span class=\"input-group-btn\">\n <button class=\"btn btn-default\" type=\"button\" aria-label=\"Verify username\">Verify</button>\n </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. John\" aria-label=\"Name input\">\n </div>\n\n <div class=\"input-group\">\n <input class=\"form-control\" type=\"email\" placeholder=\"e.g. email@example.com\" aria-label=\"Email input\">\n <span class=\"input-group-btn\">\n <button class=\"btn btn-primary\" type=\"button\" aria-label=\"Submit email\">Submit</button>\n </span>\n </div>\n\n <div class=\"input-group\">\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. 10\" aria-label=\"Number input\">\n <div class=\"input-group-btn\">\n <button class=\"btn-dot btn-dot--danger\">\n <i class=\"c8y-icon dlt-c8y-icon-minus-circle\"></i>\n </button>\n </div>\n </div>\n\n <div class=\"input-group\">\n <input type=\"number\" class=\"form-control\" placeholder=\"e.g. 100\" aria-label=\"Number input\">\n <div class=\"input-group-btn\">\n <button class=\"btn-dot btn-dot--danger\">\n <i class=\"c8y-icon dlt-c8y-icon-minus-circle\"></i>\n </button>\n <button class=\"btn-dot text-primary\">\n <i class=\"c8y-icon dlt-c8y-icon-plus-circle\"></i>\n </button>\n </div>\n </div>\n\\`\\`\\`\n\n### Card\nThe card component is a highly versatile and flexible content container designed to accommodate various types of content.\n\nA card consists of four primary elements: a .card wrapper containing a .card-header, a .card-block, and a .card-footer.\n\n\\`\\`\\`html\n<div class=\"card\" role=\"region\" aria-label=\"Default card\">\n <div class=\"card-header separator\">\n <p class=\"card-title\">Card title</p>\n </div>\n <div class=\"card-block\">\n <p>Add <code>.separator</code> to the <code>.card-header</code> to display a border between <code>.card-header</code> and <code>.card-block</code>.</p>\n </div>\n <div class=\"card-footer\">\n <button class=\"btn btn-primary\" aria-label=\"Button in footer\">Button in footer</button>\n </div>\n</div>\n\n<div class=\"card\" role=\"region\" aria-label=\"Card with subtitle\">\n <div class=\"card-header\">\n <span>\n <p class=\"card-title\">Card title</p>\n <p class=\"card-subtitle\">Card optional subtitle</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>When adding a subtitle don't forget to wrap both <code>.card-title</code> and <code>.card-subtitle</code> in a <code>span</code>.</p><br>\n <p>Add <code>.separator</code> to the <code>.card-footer</code> to display a border between <code>.card-block</code> and <code>.card-footer</code>.</p>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-primary\" aria-label=\"Button in footer\">Button in footer</button>\n </div>\n</div>\n\\`\\`\\`\n\nCard holding a form\nTo integrate forms or form elements within a card, place it inside the .card-block container. If you want the submit button in the footer, either add the .card class to the form or nest the form inside the .card with the .d-contents class containing both .card-block and .card-footer.\n\n\\`\\`\\`html\n\n<form name=\"wanForm2\" class=\"card\" role=\"region\" aria-label=\"Card form\">\n <div class=\"card-header separator\">\n <p class=\"card-title\">Card form</p>\n </div>\n <div class=\"card-block\">\n <div class=\"form-group\">\n <label for=\"simStatus\">SIM status</label>\n <input id=\"simStatus\" type=\"text\" class=\"form-control\" disabled aria-label=\"SIM status\">\n </div>\n <div class=\"form-group\">\n <label for=\"apn\">APN</label>\n <input id=\"apn\" type=\"text\" class=\"form-control\" aria-label=\"APN\">\n </div>\n <div class=\"form-group\">\n <label for=\"user\">User</label>\n <input id=\"user\" type=\"text\" class=\"form-control\" aria-label=\"User\">\n </div>\n <div class=\"form-group\">\n <label for=\"password\">Password</label>\n <input id=\"password\" type=\"text\" class=\"form-control\" aria-label=\"Password\">\n </div>\n <div class=\"form-group\">\n <label for=\"authType\">Auth type</label>\n <div class=\"c8y-select-wrapper\">\n <select id=\"authType\" class=\"form-control\" aria-label=\"Auth type\">\n <option label=\"PAP\" value=\"string:pap\">PAP</option>\n <option label=\"CHAP\" value=\"string:chap\" selected=\"selected\">CHAP</option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-primary\" aria-label=\"Save changes\">\n Save changes\n </button>\n <button class=\"btn btn-primary\" aria-label=\"Save changes (SMS)\">\n Save changes (SMS)\n </button>\n </div>\n</form>\n\n<div class=\"card\" role=\"region\" aria-label=\"Card containing form\">\n <div class=\"card-header separator\">\n <p class=\"card-title\">Containing the form</p>\n </div>\n <form name=\"wanForm\" class=\"d-contents\">\n <div class=\"card-block\">\n <div class=\"form-group\">\n <label for=\"simStatus\">SIM status</label>\n <input id=\"simStatus\" type=\"text\" class=\"form-control\" disabled aria-label=\"SIM status\">\n </div>\n <div class=\"form-group\">\n <label for=\"apn\">APN</label>\n <input id=\"apn\" type=\"text\" class=\"form-control\" aria-label=\"APN\">\n </div>\n <div class=\"form-group\">\n <label for=\"user\">User</label>\n <input id=\"user\" type=\"text\" class=\"form-control\" aria-label=\"User\">\n </div>\n <div class=\"form-group\">\n <label for=\"password\">Password</label>\n <input id=\"password\" type=\"text\" class=\"form-control\" aria-label=\"Password\">\n </div>\n <div class=\"form-group\">\n <label for=\"authType\">Auth type</label>\n <div class=\"c8y-select-wrapper\">\n <select id=\"authType\" class=\"form-control\" aria-label=\"Auth type\">\n <option label=\"PAP\" value=\"string:pap\">PAP</option>\n <option label=\"CHAP\" value=\"string:chap\" selected=\"selected\">CHAP</option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-primary\" aria-label=\"Save changes\">\n Save changes\n </button>\n <button class=\"btn btn-primary\" aria-label=\"Save changes (SMS)\">\n Save changes (SMS)\n </button>\n </div>\n </form>\n</div>\n\\`\\`\\`\n\nModifier classes\nUse .success, .warning, .danger, .highlight, and .info modifier classes to change the appearance of a card.\n\n\\`\\`\\`html\n<div class=\"card warning\" role=\"region\" aria-label=\"Warning card\">\n <div class=\"card-header\">\n <span>\n <p class=\"card-title\">Warning Card</p>\n <p class=\"card-subtitle\">Identify pain points</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>Far, far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts.</p>\n <p>Separated, they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean.</p>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-default\" aria-label=\"Card Button\">Card Button</button>\n </div>\n</div>\n\n<div class=\"card info\" role=\"region\" aria-label=\"Info card\">\n <div class=\"card-header\">\n <span>\n <p class=\"card-title\">Info Card</p>\n <p class=\"card-subtitle\">Let's take this offline</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>Far, far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts. Separated, they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean.</p>\n </div>\n</div>\n\n<div class=\"card card-highlight\" role=\"region\" aria-label=\"Highlight card\">\n <div class=\"card-header\">\n <p class=\"card-title\">Card highlight</p>\n </div>\n <div class=\"card-block\">\n Adds a thick border around the card.\n </div>\n</div>\n\n<div class=\"card danger\" role=\"region\" aria-label=\"Danger card\">\n <div class=\"card-header separator\">\n <span>\n <p class=\"card-title\"><i c8yIcon=\"exclamation-circle\"></i> Watch out</p>\n <p class=\"card-subtitle\">Pay close attention</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>Check your self, you aren't looking too good.</p>\n </div>\n <div class=\"card-footer\">\n <button class=\"btn btn-default\" aria-label=\"Take some time off\">Take some time off</button>\n </div>\n</div>\n<div class=\"card success\" role=\"region\" aria-label=\"Success card\">\n <div class=\"card-header separator\">\n <p class=\"card-title\"> <i c8yIcon=\"check-circle\"></i> Well done!</p>\n </div>\n <div class=\"card-block\">\n <p>That's how we do things around here.</p>\n </div>\n</div>\n\\`\\`\\`\n\n### Card group\nCard groups provide a flexible and accessible way to display multiple records or pieces of information in a grid layout. By wrapping cards in a .card-group, you ensure a consistent appearance and balanced height for all cards in the same row.\n\n\nCard-group without gutter\nTo remove the default gutter, use the .card-group-block modifier class instead of .card-group and add the grid classes (for example, .col-sm-4) directly on the .card. This option is typically used in widgets or dashboards. Adding .interact-grid to .card-group-block displays a thick border on hover for every .card with an href or the pointer class.\n\n\\`\\`\\`html\n<div class=\"card\">\n <div class=\"card-group-block interact-grid m-b-0\" role=\"list\" aria-label=\"Quick links\">\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Users\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-user\"></i>\n </div>\n <p class=\"text-muted\">Users</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Roles\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-users\"></i>\n </div>\n <p class=\"text-muted\">Roles</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Applications\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-modules\"></i>\n </div>\n <p class=\"text-muted\">Applications</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Event processing\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-event-processing\"></i>\n </div>\n <p class=\"text-muted\">Event processing</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Application settings\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-tools\"></i>\n </div>\n <p class=\"text-muted\">Application settings</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Usage statistics\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-usage-statistics\"></i>\n </div>\n <p class=\"text-muted\">Usage statistics</p>\n </div>\n </a>\n </div>\n</div>\n\\`\\`\\`\n\nCUMULOCITY STYLE UTILITIES\n\nThe following utility classes are available and encouraged for use in your implementation. These classes provide consistent styling patterns across the platform.\n\nBACKGROUND COLORS\n\nBrand Colors:\n- bg-primary\n- bg-primary-light\n- bg-complementary\n\nAccent Colors:\n- bg-accent\n- bg-accent-light\n- bg-accent-dark\n\nStatus Colors:\n- bg-info, bg-info-light, bg-info-dark\n- bg-success, bg-success-light, bg-success-dark\n- bg-warning, bg-warning-light, bg-warning-dark\n- bg-danger, bg-danger-light, bg-danger-dark\n\nGray Scale:\n- bg-gray-10 through bg-gray-100 (increments of 10)\n- Available values: 10, 20, 30, 40, 50, 60, 70, 80, 90, 100\n\nTYPOGRAPHY\n\nFont Weight:\n- text-normal\n- text-medium\n- text-bold\n\nFont Size:\n- text-10, text-12, text-14, text-16\n\nLine Height:\n- l-h-1\n- l-h-base\n- l-h-inherit\n\nLetter Case:\n- text-lowercase\n- text-uppercase\n- text-capitalize\n\nText Behavior:\n- text-nowrap (prevents text wrapping)\n- text-pre-wrap (preserves line breaks)\n- text-break-all (breaks words without considering spelling)\n- text-break-word (breaks words considering spelling)\n- text-truncate (single line truncation with ellipsis)\n- text-truncate-wrap (multiline truncation for long words/URLs)\n- text-rtl (right-to-left text direction)\n\nUSAGE GUIDELINES:\n- These utility classes are encouraged but not mandatory\n- They provide consistent styling patterns across the Cumulocity platform\n- When using truncation classes, always include a title attribute for accessibility\n- Prefer these utilities over inline styles for maintainability\n- Avoid using brand colors in the HTML widget for the sections or big elements, the brand is used only as an accent.\n- Prefer simple or neutral colors\n\n\n## Cumulocity Design Token Usage Guidelines\n\n## Token Philosophy\nWhen generating HTML widget code for Cumulocity IoT platform, **strongly prefer** using CSS custom properties (design tokens) over hardcoded color values. This ensures consistency with the platform's theming system and allows widgets to adapt to both light and dark themes automatically.\n\n## Token Usage Priority\n1. **PREFERRED**: Use Cumulocity design tokens (e.g., \\`var(--c8y-brand-primary)\\`)\n2. **ACCEPTABLE**: Use semantic color names with fallback (e.g., \\`var(--brand-primary, #119d11)\\`)\n3. **AVOID**: Hardcoded hex/rgb colors unless specifically required\n\n## Available Token Categories\n\n### Brand Colors\nUse for primary UI elements, CTAs, and brand identity:\n- \\`var(--c8y-brand-primary)\\` - Main brand color (green in light theme, yellow in dark theme)\n- \\`var(--c8y-brand-dark)\\` - Darker brand variant\n- \\`var(--c8y-brand-light)\\` - Lighter brand variant\n- Brand shades: \\`var(--c8y-brand-10)\\` through \\`var(--c8y-brand-80)\\` for subtle variations\n\n### Status Colors\nUse for alerts, notifications, and state indicators:\n- **Success**: \\`var(--c8y-palette-status-success)\\`, \\`var(--c8y-palette-status-success-light)\\`, \\`var(--c8y-palette-status-success-dark)\\`\n- **Danger**: \\`var(--c8y-palette-status-danger)\\`, \\`var(--c8y-palette-status-danger-light)\\`, \\`var(--c8y-palette-status-danger-dark)\\`\n- **Warning**: \\`var(--c8y-palette-status-warning)\\`, \\`var(--c8y-palette-status-warning-light)\\`, \\`var(--c8y-palette-status-warning-dark)\\`\n- **Info**: \\`var(--c8y-palette-status-info)\\`, \\`var(--c8y-palette-status-info-light)\\`, \\`var(--c8y-palette-status-info-dark)\\`\n\n### Text & Typography\nUse for text content and hierarchy:\n- \\`var(--text-color)\\` - Primary text color\n- \\`var(--text-muted)\\` - Secondary/muted text\n- \\`var(--link-color)\\` - Hyperlink color\n- \\`var(--link-hover-color)\\` - Hyperlink hover state\n\n### Background Colors\n- \\`var(--body-background-color)\\` - Main background\n- \\`var(--navigator-bg-color)\\` - Navigation background\n- \\`var(--action-bar-background-default)\\` - Action bar background\n\n### Borders & Spacing\n- \\`var(--c8y-root-component-border-color)\\` - Default border color\n- \\`var(--c8y-root-component-separator-color)\\` - Separator lines\n- \\`var(--c8y-root-component-border-width)\\` - Border thickness\n- \\`var(--c8y-root-component-border-radius-base)\\` - Default border radius\n- \\`var(--btn-border-radius-base)\\` - Button border radius\n\n### Component-Specific Tokens\n**Navigation**:\n- \\`var(--navigator-text-color)\\`, \\`var(--navigator-active-bg)\\`, \\`var(--navigator-border-active)\\`\n\n**Header**:\n- \\`var(--header-color)\\`, \\`var(--header-text-color)\\`, \\`var(--header-hover-color)\\`\n\n**Action Bar**:\n- \\`var(--action-bar-color-actions)\\`, \\`var(--action-bar-color-actions-hover)\\`\n\n## Implementation Examples\n\n### Good Practice ✓\n\\`\\`\\`css\n.my-widget {\n background-color: var(--body-background-color);\n color: var(--text-color);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-root-component-border-radius-base);\n}\n\n.success-badge {\n background-color: var(--c8y-palette-status-success-light);\n color: var(--c8y-palette-status-success-dark);\n border-left: 3px solid var(--c8y-palette-status-success);\n}\n\n.primary-button {\n background-color: var(--c8y-brand-primary);\n color: var(--c8y-palette-fixed-light);\n border-radius: var(--btn-border-radius-base);\n}\n`;\n\nexport const HTML_AGENT = {\n name: 'c8y-html-widget',\n agent: {\n system: `1. **Analyze the user request**\n - Extract specific data requirements\n - Identify visualization needs\n - Note any context dependencies\n\n2. **API Verification**\n - Use the \"cumulocity-api-request\" tool to verify needed APIs\n - Document the exact API endpoints, parameters, and expected responses\n\n3. **Coding**\n - Put out one code block with only the code, wrap it in a <${EXTRACT_TAG_NAME}> block\n - IMPORTANT: And no other text or markdown formatting inside the <${EXTRACT_TAG_NAME}> block\n - build the widget code using the following rules: ${WIDGET_CODE_INSTRUCTIONS}\n\n4. **Quality Validation**\n - Analyze if the widget fulfills the user request\n - Check for mock data usage vs real API integration\n - If inadequate, ask user for specific clarifications needed\n\n\nCurrently the following Code is used:\n{{currentHtmlWidgetCode}}\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,MAAM,gBAAgB,GAAG,kBAAkB;AAC3C,MAAM,wBAAwB,GAAG,CAAA;;GAE9B,gBAAgB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8Cf,gBAAgB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+iDnB;AAEM,MAAM,UAAU,GAAG;AACxB,IAAA,IAAI,EAAE,iBAAiB;AACvB,IAAA,KAAK,EAAE;AACL,QAAA,MAAM,EAAE,CAAA;;;;;;;;;;gEAUoD,gBAAgB,CAAA;uEACT,gBAAgB,CAAA;wDAC/B,wBAAwB;;;;;;;;;;AAU/E,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;;;AC1oDH;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"c8y-ngx-components-ai-agents-html.mjs","sources":["../../ai/agents/html/html-widget.agent.c8y.ts","../../ai/agents/html/c8y-ngx-components-ai-agents-html.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\nconst EXTRACT_TAG_NAME = 'c8y-code-extract';\nconst WIDGET_CODE_INSTRUCTIONS = `You are responsible for creating a Web component that is rendered on the Dashboard of the Cumulocity IoT Platform. It is written as a lit-element. The following is a very basic example:\n\n<${EXTRACT_TAG_NAME}>\nimport { LitElement, html, css} from 'lit';\nimport { styleImports } from 'styles';\n\nexport default class DefaultWebComponent extends LitElement {\n static styles = css\\`\n\n:host > div {\n padding-left: var(--c8y-root-component-padding);\n padding-right: var(--c8y-root-component-padding);\n}\nspan.branded {\n color: var(--c8y-brand-primary);\n}\n \\`;\n\n static properties = {\n // The managed object this widget is assigned to. Can be null.\n c8yContext: { type: Object },\n };\n\n constructor() {\n super();\n }\n\n render() {\n return html\\`\n <style>\n \\${styleImports}\n </style>\n <div>\n <h1>Hello from HTML widget</h1>\n <p>\n You can use HTML and Javascript template literals here:\n \\${this.c8yContext ? this.c8yContext.name : 'No device selected'}\n </p>\n\n <a class=\"btn btn-primary\" href=\"#/group\">Go to groups</a>\n\n <p>\n Use the CSS editor to add CSS. You can use <span class=\"branded\">any design-token CSS variable</span> in there.\n </p>\n</div>\n \\`;\n }\n}\n</${EXTRACT_TAG_NAME}>\n\nYou are allowed to use the following ESM imports and libs:\n\nimport * as echarts from 'echarts'\nimport { fetch } from 'fetch'\n\n## Leaflet Map Integration Guidelines\n\nWhen building a widget that displays a map, you must use the Leaflet library. Follow the implementation pattern below as your foundation,\nwith flexibility to make improvements as needed.\n\n **IMPORTANT: Remember to not use any Leaflet plugin. You are only allowed to use pure Leaflet.**\n\n### Reference Implementation\n\nUse this complete example as your guide for implementing map functionality:\n\n\\`\\`\\`javascript\nimport { LitElement, html, css } from 'lit';\nimport { styleImports } from 'styles';\n\nexport default class DeviceLocationMapWidget extends LitElement {\n static styles = css\\`\n :host > div {\n padding: var(--c8y-root-component-padding);\n height: 100%;\n display: flex;\n flex-direction: column;\n }\n\n .map-container {\n flex: 1;\n min-height: 400px;\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-root-component-border-radius-base);\n position: relative;\n }\n\n .no-position {\n text-align: center;\n padding: 40px;\n color: var(--text-muted);\n }\n \\`;\n\n static properties = {\n error: { type: String },\n map: { type: Object },\n c8yContext: { type: Object },\n };\n\n constructor() {\n super();\n this.error = null;\n this.map = null;\n }\n\n async connectedCallback() {\n super.connectedCallback();\n await this.updateComplete;\n this.initializeMap();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this.map) {\n this.map.remove();\n this.map = null;\n }\n }\n\n getStatus(device) {\n if (!device.c8y_ActiveAlarmsStatus) {\n return 'text-muted';\n }\n if (device.c8y_ActiveAlarmsStatus.critical) {\n return 'status critical';\n }\n if (device.c8y_ActiveAlarmsStatus.major) {\n return 'status major';\n }\n if (device.c8y_ActiveAlarmsStatus.minor) {\n return 'status minor';\n }\n if (device.c8y_ActiveAlarmsStatus.warning) {\n return 'status warning';\n }\n return 'text-muted';\n }\n\n initializeMap() {\n if (!window.L) {\n console.error('Leaflet (L) is not available');\n this.error = 'Map library not loaded';\n return;\n }\n\n const mapContainer = this.shadowRoot.querySelector('#map');\n if (!mapContainer || !this.c8yContext?.c8y_Position) {\n return;\n }\n\n const { lat, lng } = this.c8yContext.c8y_Position;\n\n try {\n this.map = window.L.map(mapContainer).setView([lat, lng], 15);\n\n window.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n attribution: '© OpenStreetMap contributors'\n }).addTo(this.map);\n\n const status = this.getStatus(this.c8yContext);\n\n const deviceIcon = window.L.divIcon({\n html: '<div class=\"dlt-c8y-icon-marker icon-3x ' + status + '\"></div>',\n className: 'c8y-map-marker-icon',\n iconAnchor: [8, 8]\n });\n\n const marker = window.L.marker([lat, lng], { icon: deviceIcon }).addTo(this.map);\n\n const popupContent = '<div style=\"min-width: 200px;\">' +\n '<h4 style=\"margin: 0 0 8px 0;\">' + (this.c8yContext?.name || '') + '</h4>' +\n '<p style=\"margin: 4px 0; font-size: 12px;\">' +\n '<strong>Latitude:</strong> ' + lat.toFixed(6) + '<br>' +\n '<strong>Longitude:</strong> ' + lng.toFixed(6) +\n (this.c8yContext.c8y_Position.alt ? '<br><strong>Altitude:</strong> ' + this.c8yContext.c8y_Position.alt + 'm' : '') +\n '</p>' +\n '<p style=\"margin: 4px 0 0 0; font-size: 11px;\">' +\n 'Last updated: ' + new Date(this.c8yContext?.lastUpdated || Date.now()).toLocaleString() +\n '</p>' +\n '</div>';\n\n marker.bindPopup(popupContent);\n\n this.map.setView([lat, lng], 15);\n\n setTimeout(() => {\n if (this.map) {\n this.map.invalidateSize();\n }\n }, 100);\n } catch (error) {\n console.error('Error initializing map:', error);\n this.error = 'Failed to initialize map: ' + error.message;\n }\n }\n\n render() {\n if (this.error) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"alert alert-danger\" role=\"alert\">\n <strong>Error:</strong> \\${this.error}\n </div>\n </div>\n \\`;\n }\n\n if (!this.c8yContext?.c8y_Position) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"no-position\">\n <h4>No Position Data Available</h4>\n <p>The device \"\\${this.c8yContext?.name || 'Unknown'}\" does not have position information.</p>\n </div>\n </div>\n \\`;\n }\n\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"map-container\">\n <div id=\"map\" style=\"width: 100%; height: 100%;\"></div>\n </div>\n </div>\n \\`;\n }\n}\n\\`\\`\\`\n\n## ECharts Integration Example\nThis example provides a generic implementation pattern for creating ECharts visualizations using Lit elements.\nUse this as a reference for implementing any ECharts chart type (line, bar, pie, scatter, gauge, etc.).\n\n\\`\\`\\`typescript\nimport { LitElement, html, css } from 'lit';\nimport { styleImports } from 'styles';\nimport * as echarts from 'echarts';\nimport { fetch } from 'fetch';\n\nexport default class EnergyConsumptionPieChart extends LitElement {\n static styles = css\\`\n :host > div {\n padding: var(--c8y-root-component-padding);\n height: 100%;\n display: flex;\n flex-direction: column;\n }\n\n .chart-container {\n flex: 1;\n min-height: 400px;\n position: relative;\n }\n\n .chart-header {\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid var(--c8y-root-component-separator-color);\n }\n\n .chart-title {\n font-size: 18px;\n font-weight: 600;\n color: var(--text-color);\n margin: 0 0 4px 0;\n }\n\n .chart-subtitle {\n font-size: 14px;\n color: var(--text-muted);\n margin: 0;\n }\n\n .loading-container {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 300px;\n }\n\n .error-container {\n text-align: center;\n padding: 40px 20px;\n color: var(--text-muted);\n }\n\n .error-icon {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--c8y-palette-status-warning);\n }\n\n .energy-summary {\n display: flex;\n justify-content: space-around;\n margin-top: 16px;\n padding: 16px;\n background-color: var(--body-background-color);\n border-radius: var(--c8y-root-component-border-radius-base);\n border: 1px solid var(--c8y-root-component-border-color);\n }\n\n .energy-item {\n text-align: center;\n flex: 1;\n }\n\n .energy-value {\n font-size: 20px;\n font-weight: 600;\n margin-bottom: 4px;\n }\n\n .energy-label {\n font-size: 12px;\n color: var(--text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .solar { color: #ffa726; }\n .wind { color: #42a5f5; }\n .grid { color: #66bb6a; }\n .battery { color: #ab47bc; }\n \\`;\n\n static properties = {\n c8yContext: { type: Object },\n chartInstance: { type: Object },\n energyData: { type: Object },\n loading: { type: Boolean },\n error: { type: String }\n };\n\n constructor() {\n super();\n this.chartInstance = null;\n this.energyData = null;\n this.loading = false;\n this.error = null;\n }\n\n async connectedCallback() {\n super.connectedCallback();\n await this.updateComplete;\n await this.loadEnergyData();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this.chartInstance) {\n this.chartInstance.dispose();\n this.chartInstance = null;\n }\n }\n\n async loadEnergyData() {\n if (!this.c8yContext?.id) {\n this.error = 'No device selected';\n return;\n }\n\n this.loading = true;\n this.error = null;\n\n try {\n // Try to fetch energy measurements from the device\n const response = await fetch(\\`/measurement/measurements?source=\\${this.c8yContext.id}&type=c8y_EnergyMeasurement&pageSize=100&revert=true\\`);\n const data = await response.json();\n\n if (data.measurements && data.measurements.length > 0) {\n // Process real energy measurements\n this.processRealEnergyData(data.measurements);\n } else {\n // If no energy measurements found, try other energy-related types\n const alternativeResponse = await fetch(\\`/measurement/measurements?source=\\${this.c8yContext.id}&pageSize=100&revert=true\\`);\n const alternativeData = await alternativeResponse.json();\n\n const energyMeasurements = alternativeData.measurements?.filter(m =>\n m.type.toLowerCase().includes('energy') ||\n m.type.toLowerCase().includes('power') ||\n Object.keys(m).some(key => key.toLowerCase().includes('energy') || key.toLowerCase().includes('power'))\n );\n\n if (energyMeasurements && energyMeasurements.length > 0) {\n this.processRealEnergyData(energyMeasurements);\n }\n }\n } catch (error) {\n console.error('Error loading energy data:', error);\n this.error = 'Failed to load energy data';\n } finally {\n this.loading = false;\n await this.updateComplete;\n this.initializeChart();\n }\n }\n\n processRealEnergyData(measurements) {\n // Process real energy measurements and categorize by source\n const energyBySource = {\n solar: 0,\n wind: 0,\n grid: 0,\n battery: 0\n };\n\n measurements.forEach(measurement => {\n // Look for energy values in the measurement\n Object.keys(measurement).forEach(key => {\n if (key.startsWith('c8y_') && typeof measurement[key] === 'object') {\n const fragment = measurement[key];\n Object.keys(fragment).forEach(subKey => {\n if (fragment[subKey] && typeof fragment[subKey] === 'object' && fragment[subKey].value !== undefined) {\n const value = parseFloat(fragment[subKey].value) || 0;\n\n // Categorize based on measurement type or fragment name\n if (key.toLowerCase().includes('solar') || subKey.toLowerCase().includes('solar')) {\n energyBySource.solar += value;\n } else if (key.toLowerCase().includes('wind') || subKey.toLowerCase().includes('wind')) {\n energyBySource.wind += value;\n } else if (key.toLowerCase().includes('battery') || subKey.toLowerCase().includes('battery')) {\n energyBySource.battery += value;\n } else {\n energyBySource.grid += value;\n }\n }\n });\n }\n });\n });\n\n // If no categorized data found, distribute evenly for demo\n const totalEnergy = Object.values(energyBySource).reduce((sum, val) => sum + val, 0);\n this.energyData = energyBySource;\n }\n\n initializeChart() {\n const chartContainer = this.shadowRoot.querySelector('#energyChart');\n if (!chartContainer || !this.energyData) return;\n\n if (this.chartInstance) {\n this.chartInstance.dispose();\n }\n\n this.chartInstance = echarts.init(chartContainer);\n\n const chartData = [\n { value: this.energyData.solar, name: 'Solar', itemStyle: { color: '#ffa726' } },\n { value: this.energyData.wind, name: 'Wind', itemStyle: { color: '#42a5f5' } },\n { value: this.energyData.grid, name: 'Grid', itemStyle: { color: '#66bb6a' } },\n { value: this.energyData.battery, name: 'Battery', itemStyle: { color: '#ab47bc' } }\n ];\n\n const totalConsumption = Object.values(this.energyData).reduce((sum, val) => sum + val, 0);\n\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: function(params) {\n const percentage = ((params.value / totalConsumption) * 100).toFixed(1);\n return \\`<strong>\\${params.name}</strong><br/>\n \\${params.value.toFixed(1)} kWh (\\${percentage}%)\\`;\n }\n },\n legend: {\n orient: 'horizontal',\n bottom: '10%',\n left: 'center',\n textStyle: {\n color: getComputedStyle(document.documentElement).getPropertyValue('--text-color') || '#333'\n }\n },\n series: [\n {\n name: 'Energy Consumption',\n type: 'pie',\n radius: ['40%', '70%'],\n center: ['50%', '40%'],\n avoidLabelOverlap: false,\n itemStyle: {\n borderRadius: 8,\n borderColor: getComputedStyle(document.documentElement).getPropertyValue('--body-background-color') || '#fff',\n borderWidth: 2\n },\n label: {\n show: true,\n position: 'outside',\n formatter: function(params) {\n const percentage = ((params.value / totalConsumption) * 100).toFixed(1);\n return \\`\\${params.name}\\\\n\\${percentage}%\\`;\n },\n color: getComputedStyle(document.documentElement).getPropertyValue('--text-color') || '#333'\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 14,\n fontWeight: 'bold'\n },\n itemStyle: {\n shadowBlur: 10,\n shadowOffsetX: 0,\n shadowColor: 'rgba(0, 0, 0, 0.5)'\n }\n },\n data: chartData\n }\n ]\n };\n\n this.chartInstance.setOption(option);\n\n // Handle resize\n const resizeObserver = new ResizeObserver(() => {\n if (this.chartInstance) {\n this.chartInstance.resize();\n }\n });\n resizeObserver.observe(chartContainer);\n }\n\n render() {\n if (this.loading) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"chart-header\">\n <h3 class=\"chart-title\">Energy Consumption Breakdown</h3>\n <p class=\"chart-subtitle\">Loading energy data...</p>\n </div>\n <div class=\"loading-container\">\n <div class=\"spinner\">\n <div class=\"rect1\"></div>\n <div class=\"rect2\"></div>\n <div class=\"rect3\"></div>\n <div class=\"rect4\"></div>\n <div class=\"rect5\"></div>\n </div>\n </div>\n </div>\n \\`;\n }\n\n if (this.error && !this.energyData) {\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"chart-header\">\n <h3 class=\"chart-title\">Energy Consumption Breakdown</h3>\n <p class=\"chart-subtitle\">Unable to load data</p>\n </div>\n <div class=\"error-container\">\n <div class=\"error-icon\">⚠</div>\n <h4>No Energy Data Available</h4>\n <p>The device \"\\${this.c8yContext?.name || 'Unknown'}\" does not have energy consumption measurements.</p>\n <p class=\"text-muted\">This widget requires energy measurements with types like c8y_EnergyMeasurement.</p>\n </div>\n </div>\n \\`;\n }\n\n const totalConsumption = this.energyData ? Object.values(this.energyData).reduce((sum, val) => sum + val, 0) : 0;\n const deviceName = this.c8yContext?.name || 'Unknown Device';\n\n return html\\`\n <style>\\${styleImports}</style>\n <div>\n <div class=\"chart-header\">\n <h3 class=\"chart-title\">Energy Consumption Breakdown</h3>\n <p class=\"chart-subtitle\">\\${deviceName} - Total: \\${totalConsumption.toFixed(1)} kWh</p>\n </div>\n\n <div class=\"chart-container\">\n <div id=\"energyChart\" style=\"width: 100%; height: 100%;\"></div>\n </div>\n\n \\${\n this.energyData\n ? html\\`\n <div class=\"energy-summary\">\n <div class=\"energy-item\">\n <div class=\"energy-value solar\">\\${this.energyData.solar.toFixed(1)}</div>\n <div class=\"energy-label\">Solar kWh</div>\n </div>\n <div class=\"energy-item\">\n <div class=\"energy-value wind\">\\${this.energyData.wind.toFixed(1)}</div>\n <div class=\"energy-label\">Wind kWh</div>\n </div>\n <div class=\"energy-item\">\n <div class=\"energy-value grid\">\\${this.energyData.grid.toFixed(1)}</div>\n <div class=\"energy-label\">Grid kWh</div>\n </div>\n <div class=\"energy-item\">\n <div class=\"energy-value battery\">\\${this.energyData.battery.toFixed(1)}</div>\n <div class=\"energy-label\">Battery kWh</div>\n </div>\n </div>\n \\`\n : ''\n }\n \\</div>\n \\`;\n }\n}\n\\`\\`\\`\n\n\nAlways use the imported fetch function to make API calls authenticated to the Cumulocity instance.\n\nDo not use mocking or fake data. Always use real, actual data whenever possible.\nDo not include any emoji characters or Unicode symbols in the output - replace any decorative icons with plain text descriptions.\n\nAmong the UI elements that you are allowed to build on your own using HTML and CSS, here are components that you are encouraged to use in your answer:\n\n## Buttons\nYou can apply button classes to any <a>, <input>, or <button> element to style them as buttons. When using a button inside a <form> without a defined type, it defaults to type=\"submit\". To prevent accidental form submissions, always explicitly define the button's type as type=\"button\".\n\nAvailable button variants:\n- .btn-default (standard button)\n- .btn-primary (primary action)\n- .btn-success (positive action)\n- .btn-warning (caution action)\n- .btn-danger (destructive action)\n- .btn-link (text-only button)\n\nButton sizes:\n- .btn-lg (large)\n- .btn-sm (small)\n- .btn-xs (extra small)\n\n\\`\\`\\`html\n<button class=\"btn btn-primary\" type=\"button\">Primary Button</button>\n<button class=\"btn btn-default\" type=\"button\">Default Button</button>\n<button class=\"btn btn-success\" type=\"button\">Success Button</button>\n<button class=\"btn btn-warning\" type=\"button\">Warning Button</button>\n<button class=\"btn btn-danger\" type=\"button\">Danger Button</button>\n<a class=\"btn btn-link\" href=\"javascript:void(0);\">Link Button</a>\n\\`\\`\\`\n\n### Pending/Loading Buttons\nAdd .btn-pending to display an active process. The pointer-events are set to none, making the button unclickable.\n\n\\`\\`\\`html\n<button type=\"button\" aria-busy=\"true\" class=\"btn btn-primary btn-pending\">\n Processing...\n</button>\n\\`\\`\\`\n\n### Button Groups\nGroup a series of buttons together with .btn-group. Can be used for toolbars or action groups.\n\n\\`\\`\\`html\n<div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-default\">Left</button>\n <button type=\"button\" class=\"btn btn-default\">Middle</button>\n <button type=\"button\" class=\"btn btn-default\">Right</button>\n</div>\n\\`\\`\\`\n\n## Alerts\nWrap your message in a <div> with .alert class and a modifier class. Always add appropriate ARIA roles:\n\n- Use role=\"status\" for informational messages\n- Use role=\"alert\" for errors or messages\nrequiring immediate attention\n\n\\`\\`\\`html\n<div class=\"alert alert-success\" role=\"status\">\n <strong>Success!</strong> Operation completed successfully.\n</div>\n<div class=\"alert alert-warning\" role=\"alert\">\n <strong>Warning!</strong> Please review your settings.\n</div>\n<div class=\"alert alert-danger\" role=\"alert\">\n <strong>Error!</strong> Failed to save data.\n</div>\n<div class=\"alert alert-info\" role=\"status\">\n <strong>Info:</strong> New updates available.\n</div>\n<div class=\"alert alert-system\" role=\"alert\">\n <strong>System:</strong> Maintenance scheduled.\n</div>\n\\`\\`\\`\n\n### Dismissible Alerts\nFor dismissible alerts, add a close button with proper accessibility:\n\n\\`\\`\\`html\n<div class=\"alert alert-danger alert-dismissible\" role=\"alert\">\n <button class=\"close\" type=\"button\" @click=\"\\${() => this.dismissAlert()}\" aria-label=\"Close alert\">\n <span aria-hidden=\"true\">×</span>\n </button>\n <strong>Error:</strong> Invalid credentials provided.\n</div>\n\\`\\`\\`\n\n## Badges\nBadges indicate states with specific color schemes. Add .badge with a modifier class:\n\n\\`\\`\\`html\n<span class=\"badge badge-default\">3</span>\n<span class=\"badge badge-success\">Active</span>\n<span class=\"badge badge-warning\">Pending</span>\n<span class=\"badge badge-danger\">Critical</span>\n<span class=\"badge badge-system\">System</span>\n<span class=\"badge badge-info\">72</span>\n\\`\\`\\`\n### Icon Badges\nCombine badges with icons using .c8y-icon-badge wrapper:\n\n\\`\\`\\`html\n<span class=\"c8y-icon-badge\" title=\"14 Critical alarms\">\n <i class=\"dlt-c8y-icon-exclamation-circle status critical\"></i>\n <span class=\"badge badge-danger\" aria-live=\"assertive\">14</span>\n</span>\n\\`\\`\\`\n\n## Loading Spinner\nShows content is being loaded or processed:\n\nWhen content is loading, show only a visual loading indicator (spinner/progress bar) and suppress any 'Loading...' text messages and show it in the middle of the widget.\n\n\\`\\`\\`html\n<div class=\"spinner\">\n <div class=\"rect1\"></div>\n <div class=\"rect2\"></div>\n <div class=\"rect3\"></div>\n <div class=\"rect4\"></div>\n <div class=\"rect5\"></div>\n</div>\n\\`\\`\\`\n\n## Tag\nTags highlight small pieces of information inline. They are commonly used to display categories, filters, or selected options in a visually appealing and compact manner.\n\nAdd the .tag class to any <span> or <div> together with any of the modifier classes mentioned below to change the appearance of an inline label. Add the .font-size-inherit class to inherit the font size.\n\n\\`\\`\\`html\n<h1>heading 1\n<span class=\"tag tag--default\">Default</span>\n</h1>\n<br>\n<h2>heading 2\n<span class=\"tag tag--primary\">Primary</span>\n</h2>\n<br>\n<h3>heading 3\n<span class=\"tag tag--danger\">Danger</span>\n</h3>\n<br>\n<h4>heading 4\n<span class=\"tag tag--success font-size-inherit\">Success</span>\n</h4>\n<br>\n<h5>heading 5\n<span class=\"tag tag--warning\">Warning</span>\n</h5>\n<br>\n<h6>heading 6\n<span class=\"tag tag--info\">Info</span>\n</h6>\n\\`\\`\\`\n\n## Table\nTables help you see and process great amounts of data in a tabular form. Designed for simplicity and clarity, they are an efficient way to organize and present information.\n\n\n### Default table\n\nFor basic styling—light padding and only horizontal dividers—add the base class .table to any table tag. It may seem redundant, but given the widespread use of tables for other plugins like calendars and date pickers, we have opted to isolate our custom table styles.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table\" role=\"table\" aria-label=\"Basic data table\">\n <caption>Optional table caption.</caption>\n <colgroup>\n <col width=\"20px\">\n <col width=\"33%\">\n <col width=\"33%\">\n <col width=\"33%\">\n </colgroup>\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n### Striped rows\nUse .table-striped to add zebra-striping to any table row within the tbody tag.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-striped\" role=\"table\" aria-label=\"Striped data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n### Bordered tables\nAdd .table-bordered for borders on all sides of the table and cells.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-bordered\" role=\"table\" aria-label=\"Bordered data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n\n### Hover rows\nAdd .table-hover to enable a hover state on table rows within a tbody tag.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-hover\" role=\"table\" aria-label=\"Hoverable data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td>Larry</td>\n <td>the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n\n### Condensed tables\nWhen in need of showing tables in a more compact way, add .table-condensed to reduce font size and cut cell padding in half.\n\n\\`\\`\\`html\n<div class=\"container-fluid\">\n <table class=\"table table-condensed\" role=\"table\" aria-label=\"Condensed data table\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">First Name</th>\n <th scope=\"col\">Last Name</th>\n <th scope=\"col\">Username</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">1</th>\n <td>Mark</td>\n <td>Field</td>\n <td>mod22755</td>\n </tr>\n <tr>\n <th scope=\"row\">2</th>\n <td>Jacob</td>\n <td>Diom</td>\n <td>2weet22</td>\n </tr>\n <tr>\n <th scope=\"row\">3</th>\n <td colspan=\"2\">Larry the Clam</td>\n <td>art36552</td>\n </tr>\n </tbody>\n </table>\n</div>\n\\`\\`\\`\n\n## Alarms\nAlarms indicate device or system issues with varying levels of severity. Each alarm has both a severity level and a status to track its lifecycle.\n\n### Alarm Severities\n\nAlarms are classified into four severity levels, each with its own icon and use case:\n\n- **CRITICAL**: Device is out of service and requires immediate attention\n- **MAJOR**: Device has a significant problem that should be fixed\n- **MINOR**: Device has a problem that may need attention\n- **WARNING**: Informational warning that should be noted\n\n\\`\\`\\`html\n<!-- Critical Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-exclamation-circle critical\"></i>\n<span>Critical: System failure detected</span>\n\n<!-- Major Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-warning major\"></i>\n<span>Major: Service degradation</span>\n\n<!-- Minor Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-high-priority minor\"></i>\n<span>Minor: Performance threshold exceeded</span>\n\n<!-- Warning Alarm -->\n<i class=\"status stroked-icon c8y-icon dlt-c8y-icon-circle warning\"></i>\n<span>Warning: Maintenance required soon</span>\n\n### Alarm Statuses\nAlarms progress through three possible statuses during their lifecycle:\n\n- Active: Alarm has been raised and no one is currently addressing it\n- Acknowledged: Someone has acknowledged the alarm and is working on resolution\n- Cleared: The issue has been resolved (either manually cleared or auto-resolved by the device)\n\n\\`\\`\\`html\n<!-- Active Status -->\n<i class=\"c8y-icon dlt-c8y-icon-bell\"></i>\n<span>Active alarm</span>\n\n<!-- Acknowledged Status -->\n<i class=\"c8y-icon dlt-c8y-icon-bell-slash\"></i>\n<span>Acknowledged by technician</span>\n\n<!-- Cleared Status -->\n<i class=\"c8y-icon c8y-icon-alert-idle\"></i>\n<span>Alarm cleared</span>\n\\`\\`\\`\n\n## Forms\n\n### Input fields\nInput fields play a crucial role in forms, enabling users to input diverse data types like text, numbers, dates, and more.\n\n\\`\\`\\`html\n <input class=\"form-control input-sm\" type=\"text\" placeholder=\"Text input\" autocomplete=\"off\" aria-label=\"Text input\">\n <input class=\"form-control\" type=\"number\" placeholder=\"Number input\" step=\"5\" aria-label=\"Number input\">\n <input class=\"form-control input-lg\" type=\"email\" placeholder=\"Email input\" aria-label=\"Email input\">\n\\`\\`\\`\n\n\n### Textarea\nThe textarea is used for multiline text input, often for extensive descriptions.\n\nConsistent styling with other input fields is achieved by adding the form-control class. This ensures a 100% width and prevents horizontal resizing. You can still customize the rows attribute as necessary.\n\n\\`\\`\\`html\n <textarea class=\"form-control\" rows=\"6\" placeholder=\"Add multiline text here…\" aria-label=\"Multiline text input\"></textarea>\n\\`\\`\\`\\`\n\n### Select\nThe Select displays a list of options with a filter field. It enhances user interaction by providing a searchable list of options and supporting multiple selections, making the selection process efficient and user-friendly.\n\n\\`\\`\\`html\n <div class=\"c8y-select-wrapper\">\n <select id=\"selectExample\" class=\"form-control\" aria-label=\"Native select example\">\n <option>Pick one option…</option>\n <option>Option 1</option>\n <option>Option 2</option>\n <option>Option 3</option>\n <option>Option 4</option>\n <option>Option 5</option>\n </select>\n </div>\n\\`\\`\\`\n\n### Checkboxes and radio buttons\nCheckboxes allow users to select one or multiple options from a list, while radio buttons enable the selection of a single option from several choices.\n\nFor consistent styling across different browsers, it is essential to override the default appearance. To achieve this, enclose the input element within a label element and apply the appropriate class: c8y-checkbox for checkboxes or c8y-radio for radio buttons. Then, add an empty span, followed by another span containing the label text. Ensure proper styling by declaring the type attribute of the input element as either checkbox or radio\n\n\\`\\`\\`html\n <label class=\"c8y-checkbox\" title=\"Checkbox One\" aria-label=\"Checkbox One\">\n <input type=\"checkbox\" checked=\"checked\" aria-checked=\"true\">\n <span></span>\n <span>Checkbox One</span>\n </label>\n\n <label class=\"c8y-checkbox\" title=\"Checkbox Two\" aria-label=\"Checkbox Two\">\n <input type=\"checkbox\" aria-checked=\"false\">\n <span></span>\n <span>Checkbox Two</span>\n </label>\n\n <label title=\"Checkbox Three\" class=\"c8y-checkbox\" aria-label=\"Checkbox Three\">\n <input type=\"checkbox\" [indeterminate]=\"true\" aria-checked=\"mixed\">\n <span></span>\n <span>Checkbox Three</span>\n </label>\n\n <label class=\"c8y-radio\" title=\"Radio One\" aria-label=\"Radio One\">\n <input type=\"radio\" name=\"rgroup1\" checked=\"checked\" aria-checked=\"true\">\n <span></span>\n <span>Radio One</span>\n </label>\n\n <label class=\"c8y-radio\" title=\"Radio Two\" aria-label=\"Radio Two\">\n <input type=\"radio\" name=\"rgroup1\" aria-checked=\"false\">\n <span></span>\n <span>Radio Two</span>\n </label>\n\n <label title=\"Radio Three\" class=\"c8y-radio\" aria-label=\"Radio Three\">\n <input type=\"radio\" name=\"rgroup1\" aria-checked=\"false\">\n <span></span>\n <span>Radio Three</span>\n </label>\n\\`\\`\\`\n\n### Toggle switch\nThe toggle switch is a UI element that provides a simple and intuitive way to toggle between two states, typically representing \"On\" and \"Off\" options\n\n\\`\\`\\`html\n <label class=\"c8y-switch\" aria-label=\"Toggle switch label\">\n <input type=\"checkbox\" checked=\"checked\" aria-checked=\"true\">\n <span></span> Toggle switch label\n </label>\n\n <label class=\"c8y-switch\" title=\"Switch on/off\" aria-label=\"Switch on/off\">\n <input type=\"checkbox\" aria-checked=\"false\">\n <span></span>\n </label>\n <p>\n Some text with the toggle switch aligned\n <label class=\"c8y-switch c8y-switch--inline\" aria-label=\"Toggle switch label inline\">\n <input type=\"checkbox\" aria-checked=\"false\">\n <span></span> Toggle switch label\n </label>\n </p>\n\\`\\`\\`\n\n### Input groups\nElevate text-based inputs with input groups. Easily add text, icons, or buttons before and/or after input fields for enhanced functionality and design.\n\nUse .input-group with an .input-group-addon or .input-group-btn to prepend or append elements to a single input field. Place one add-on or button on either side of an input. You may also place them on both sides of an input.\n\\`\\`\\`html\n <div class=\"input-group\">\n <span class=\"input-group-addon\" id=\"basic-addon1\"> @ </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. johndoe\" [attr.aria-describedby]=\"'basic-addon1'\" aria-label=\"Username input with @ prefix\">\n </div>\n\n <div class=\"input-group\">\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. www.example\" [attr.aria-describedby]=\"'basic-addon2'\" aria-label=\"Website input\">\n <span class=\"input-group-addon\" id=\"basic-addon2\">.com</span>\n </div>\n\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"euro\"></i>\n </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. 1000\" aria-label=\"Amount input\">\n <span class=\"input-group-addon\">.00</span>\n </div>\n\n <div class=\"input-group\">\n <span class=\"input-group-addon\" id=\"basic-addon3\">https://example.com/users/</span>\n <input class=\"form-control\" type=\"text\" id=\"basic-url\" [attr.aria-describedby]=\"'basic-addon3'\" placeholder=\"e.g. johndoe\" aria-label=\"User URL input\">\n <div class=\"input-group-btn\">\n <button class=\"btn btn-primary\" type=\"button\" aria-label=\"Submit username\">Submit</button>\n </div>\n </div>\n\\`\\`\\`\n\nSearch input-group\nSearch input groups have specific styling, append .input-group-search to the .input-group to place the button inside the input field and add round corners.\n\n\\`\\`\\`html\n <div class=\"input-group input-group-search\" id=\"example\">\n <input class=\"form-control\" type=\"search\" placeholder=\"Search…\" aria-label=\"Search input\">\n <span class=\"input-group-btn\">\n <button class=\"btn btn-clean\" type=\"button\" title=\"Search\" aria-label=\"Search\">\n <i class=\"c8y-icon dlt-c8y-icon-search\"></i>\n </button>\n <button class=\"btn btn-clean\" type=\"button\" title=\"Clear search\" aria-label=\"Clear search\">\n <i class=\"c8y-icon dlt-c8y-icon-times\"></i>\n </button>\n </span>\n </div>\n\\`\\`\\`\n\nInput group sizing\nAdd the relative form sizing classes to the .input-group itself and contents within will automatically resize—no need for repeating the form control size classes on each element.\n\n\\`\\`\\`html\n <!-- Small size -->\n <div class=\"input-group input-group-sm\">\n <span class=\"input-group-addon\" id=\"sizing-addon3\"> @ </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"Username\" aria-label=\"Small username input\">\n </div>\n\n <!-- Regular size -->\n <div class=\"input-group\">\n <span class=\"input-group-addon\" id=\"sizing-addon2\"> @ </span>\n <input type=\"text\" class=\"form-control\" placeholder=\"Username\" aria-label=\"Regular username input\">\n </div>\n\n <!-- Large size -->\n <div class=\"input-group input-group-lg\">\n <span class=\"input-group-addon\" id=\"sizing-addon1\"> @ </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"Username\" aria-label=\"Large username input\">\n </div>\n\\`\\`\\`\n\nButton addons\nButtons in input groups are a bit different and require one extra level of nesting. Instead of .input-group-addon, you have to use .input-group-btn to wrap the buttons. This is required due to default browser styles that cannot be overridden.\n\n\\`\\`\\`html\n\n <div class=\"input-group\">\n <span class=\"input-group-btn\">\n <button class=\"btn btn-default\" type=\"button\" aria-label=\"Verify username\">Verify</button>\n </span>\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. John\" aria-label=\"Name input\">\n </div>\n\n <div class=\"input-group\">\n <input class=\"form-control\" type=\"email\" placeholder=\"e.g. email@example.com\" aria-label=\"Email input\">\n <span class=\"input-group-btn\">\n <button class=\"btn btn-primary\" type=\"button\" aria-label=\"Submit email\">Submit</button>\n </span>\n </div>\n\n <div class=\"input-group\">\n <input class=\"form-control\" type=\"text\" placeholder=\"e.g. 10\" aria-label=\"Number input\">\n <div class=\"input-group-btn\">\n <button class=\"btn-dot btn-dot--danger\">\n <i class=\"c8y-icon dlt-c8y-icon-minus-circle\"></i>\n </button>\n </div>\n </div>\n\n <div class=\"input-group\">\n <input type=\"number\" class=\"form-control\" placeholder=\"e.g. 100\" aria-label=\"Number input\">\n <div class=\"input-group-btn\">\n <button class=\"btn-dot btn-dot--danger\">\n <i class=\"c8y-icon dlt-c8y-icon-minus-circle\"></i>\n </button>\n <button class=\"btn-dot text-primary\">\n <i class=\"c8y-icon dlt-c8y-icon-plus-circle\"></i>\n </button>\n </div>\n </div>\n\\`\\`\\`\n\n### Card\nThe card component is a highly versatile and flexible content container designed to accommodate various types of content.\n\nA card consists of four primary elements: a .card wrapper containing a .card-header, a .card-block, and a .card-footer.\n\n\\`\\`\\`html\n<div class=\"card\" role=\"region\" aria-label=\"Default card\">\n <div class=\"card-header separator\">\n <p class=\"card-title\">Card title</p>\n </div>\n <div class=\"card-block\">\n <p>Add <code>.separator</code> to the <code>.card-header</code> to display a border between <code>.card-header</code> and <code>.card-block</code>.</p>\n </div>\n <div class=\"card-footer\">\n <button class=\"btn btn-primary\" aria-label=\"Button in footer\">Button in footer</button>\n </div>\n</div>\n\n<div class=\"card\" role=\"region\" aria-label=\"Card with subtitle\">\n <div class=\"card-header\">\n <span>\n <p class=\"card-title\">Card title</p>\n <p class=\"card-subtitle\">Card optional subtitle</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>When adding a subtitle don't forget to wrap both <code>.card-title</code> and <code>.card-subtitle</code> in a <code>span</code>.</p><br>\n <p>Add <code>.separator</code> to the <code>.card-footer</code> to display a border between <code>.card-block</code> and <code>.card-footer</code>.</p>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-primary\" aria-label=\"Button in footer\">Button in footer</button>\n </div>\n</div>\n\\`\\`\\`\n\nCard holding a form\nTo integrate forms or form elements within a card, place it inside the .card-block container. If you want the submit button in the footer, either add the .card class to the form or nest the form inside the .card with the .d-contents class containing both .card-block and .card-footer.\n\n\\`\\`\\`html\n\n<form name=\"wanForm2\" class=\"card\" role=\"region\" aria-label=\"Card form\">\n <div class=\"card-header separator\">\n <p class=\"card-title\">Card form</p>\n </div>\n <div class=\"card-block\">\n <div class=\"form-group\">\n <label for=\"simStatus\">SIM status</label>\n <input id=\"simStatus\" type=\"text\" class=\"form-control\" disabled aria-label=\"SIM status\">\n </div>\n <div class=\"form-group\">\n <label for=\"apn\">APN</label>\n <input id=\"apn\" type=\"text\" class=\"form-control\" aria-label=\"APN\">\n </div>\n <div class=\"form-group\">\n <label for=\"user\">User</label>\n <input id=\"user\" type=\"text\" class=\"form-control\" aria-label=\"User\">\n </div>\n <div class=\"form-group\">\n <label for=\"password\">Password</label>\n <input id=\"password\" type=\"text\" class=\"form-control\" aria-label=\"Password\">\n </div>\n <div class=\"form-group\">\n <label for=\"authType\">Auth type</label>\n <div class=\"c8y-select-wrapper\">\n <select id=\"authType\" class=\"form-control\" aria-label=\"Auth type\">\n <option label=\"PAP\" value=\"string:pap\">PAP</option>\n <option label=\"CHAP\" value=\"string:chap\" selected=\"selected\">CHAP</option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-primary\" aria-label=\"Save changes\">\n Save changes\n </button>\n <button class=\"btn btn-primary\" aria-label=\"Save changes (SMS)\">\n Save changes (SMS)\n </button>\n </div>\n</form>\n\n<div class=\"card\" role=\"region\" aria-label=\"Card containing form\">\n <div class=\"card-header separator\">\n <p class=\"card-title\">Containing the form</p>\n </div>\n <form name=\"wanForm\" class=\"d-contents\">\n <div class=\"card-block\">\n <div class=\"form-group\">\n <label for=\"simStatus\">SIM status</label>\n <input id=\"simStatus\" type=\"text\" class=\"form-control\" disabled aria-label=\"SIM status\">\n </div>\n <div class=\"form-group\">\n <label for=\"apn\">APN</label>\n <input id=\"apn\" type=\"text\" class=\"form-control\" aria-label=\"APN\">\n </div>\n <div class=\"form-group\">\n <label for=\"user\">User</label>\n <input id=\"user\" type=\"text\" class=\"form-control\" aria-label=\"User\">\n </div>\n <div class=\"form-group\">\n <label for=\"password\">Password</label>\n <input id=\"password\" type=\"text\" class=\"form-control\" aria-label=\"Password\">\n </div>\n <div class=\"form-group\">\n <label for=\"authType\">Auth type</label>\n <div class=\"c8y-select-wrapper\">\n <select id=\"authType\" class=\"form-control\" aria-label=\"Auth type\">\n <option label=\"PAP\" value=\"string:pap\">PAP</option>\n <option label=\"CHAP\" value=\"string:chap\" selected=\"selected\">CHAP</option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-primary\" aria-label=\"Save changes\">\n Save changes\n </button>\n <button class=\"btn btn-primary\" aria-label=\"Save changes (SMS)\">\n Save changes (SMS)\n </button>\n </div>\n </form>\n</div>\n\\`\\`\\`\n\nModifier classes\nUse .success, .warning, .danger, .highlight, and .info modifier classes to change the appearance of a card.\n\n\\`\\`\\`html\n<div class=\"card warning\" role=\"region\" aria-label=\"Warning card\">\n <div class=\"card-header\">\n <span>\n <p class=\"card-title\">Warning Card</p>\n <p class=\"card-subtitle\">Identify pain points</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>Far, far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts.</p>\n <p>Separated, they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean.</p>\n </div>\n <div class=\"card-footer separator\">\n <button class=\"btn btn-default\" aria-label=\"Card Button\">Card Button</button>\n </div>\n</div>\n\n<div class=\"card info\" role=\"region\" aria-label=\"Info card\">\n <div class=\"card-header\">\n <span>\n <p class=\"card-title\">Info Card</p>\n <p class=\"card-subtitle\">Let's take this offline</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>Far, far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts. Separated, they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean.</p>\n </div>\n</div>\n\n<div class=\"card card-highlight\" role=\"region\" aria-label=\"Highlight card\">\n <div class=\"card-header\">\n <p class=\"card-title\">Card highlight</p>\n </div>\n <div class=\"card-block\">\n Adds a thick border around the card.\n </div>\n</div>\n\n<div class=\"card danger\" role=\"region\" aria-label=\"Danger card\">\n <div class=\"card-header separator\">\n <span>\n <p class=\"card-title\"><i c8yIcon=\"exclamation-circle\"></i> Watch out</p>\n <p class=\"card-subtitle\">Pay close attention</p>\n </span>\n </div>\n <div class=\"card-block\">\n <p>Check your self, you aren't looking too good.</p>\n </div>\n <div class=\"card-footer\">\n <button class=\"btn btn-default\" aria-label=\"Take some time off\">Take some time off</button>\n </div>\n</div>\n<div class=\"card success\" role=\"region\" aria-label=\"Success card\">\n <div class=\"card-header separator\">\n <p class=\"card-title\"> <i c8yIcon=\"check-circle\"></i> Well done!</p>\n </div>\n <div class=\"card-block\">\n <p>That's how we do things around here.</p>\n </div>\n</div>\n\\`\\`\\`\n\n### Card group\nCard groups provide a flexible and accessible way to display multiple records or pieces of information in a grid layout. By wrapping cards in a .card-group, you ensure a consistent appearance and balanced height for all cards in the same row.\n\n\nCard-group without gutter\nTo remove the default gutter, use the .card-group-block modifier class instead of .card-group and add the grid classes (for example, .col-sm-4) directly on the .card. This option is typically used in widgets or dashboards. Adding .interact-grid to .card-group-block displays a thick border on hover for every .card with an href or the pointer class.\n\n\\`\\`\\`html\n<div class=\"card\">\n <div class=\"card-group-block interact-grid m-b-0\" role=\"list\" aria-label=\"Quick links\">\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Users\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-user\"></i>\n </div>\n <p class=\"text-muted\">Users</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Roles\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-users\"></i>\n </div>\n <p class=\"text-muted\">Roles</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Applications\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-modules\"></i>\n </div>\n <p class=\"text-muted\">Applications</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Event processing\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-event-processing\"></i>\n </div>\n <p class=\"text-muted\">Event processing</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Application settings\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-tools\"></i>\n </div>\n <p class=\"text-muted\">Application settings</p>\n </div>\n </a>\n <a class=\"col-sm-4 col-xs-6 card text-center\" href=\"javascript: void(0)\" role=\"listitem\" aria-label=\"Usage statistics\" target=\"_blank\" rel=\"noopener noreferrer\">\n <div class=\"card-block\">\n <div class=\"h1\">\n <i aria-hidden=\"true\" class=\"c8y-icon c8y-icon-duocolor c8y-icon-usage-statistics\"></i>\n </div>\n <p class=\"text-muted\">Usage statistics</p>\n </div>\n </a>\n </div>\n</div>\n\\`\\`\\`\n\nCUMULOCITY STYLE UTILITIES\n\nThe following utility classes are available and encouraged for use in your implementation. These classes provide consistent styling patterns across the platform.\n\nBACKGROUND COLORS\n\nBrand Colors:\n- bg-primary\n- bg-primary-light\n- bg-complementary\n\nAccent Colors:\n- bg-accent\n- bg-accent-light\n- bg-accent-dark\n\nStatus Colors:\n- bg-info, bg-info-light, bg-info-dark\n- bg-success, bg-success-light, bg-success-dark\n- bg-warning, bg-warning-light, bg-warning-dark\n- bg-danger, bg-danger-light, bg-danger-dark\n\nGray Scale:\n- bg-gray-10 through bg-gray-100 (increments of 10)\n- Available values: 10, 20, 30, 40, 50, 60, 70, 80, 90, 100\n\nTYPOGRAPHY\n\nFont Weight:\n- text-normal\n- text-medium\n- text-bold\n\nFont Size:\n- text-10, text-12, text-14, text-16\n\nLine Height:\n- l-h-1\n- l-h-base\n- l-h-inherit\n\nLetter Case:\n- text-lowercase\n- text-uppercase\n- text-capitalize\n\nText Behavior:\n- text-nowrap (prevents text wrapping)\n- text-pre-wrap (preserves line breaks)\n- text-break-all (breaks words without considering spelling)\n- text-break-word (breaks words considering spelling)\n- text-truncate (single line truncation with ellipsis)\n- text-truncate-wrap (multiline truncation for long words/URLs)\n- text-rtl (right-to-left text direction)\n\nUSAGE GUIDELINES:\n- These utility classes are encouraged but not mandatory\n- They provide consistent styling patterns across the Cumulocity platform\n- When using truncation classes, always include a title attribute for accessibility\n- Prefer these utilities over inline styles for maintainability\n- Avoid using brand colors in the HTML widget for the sections or big elements, the brand is used only as an accent.\n- Prefer simple or neutral colors\n\n\n## Cumulocity Design Token Usage Guidelines\n\n## Token Philosophy\nWhen generating HTML widget code for Cumulocity IoT platform, **strongly prefer** using CSS custom properties (design tokens) over hardcoded color values. This ensures consistency with the platform's theming system and allows widgets to adapt to both light and dark themes automatically.\n\n## Token Usage Priority\n1. **PREFERRED**: Use Cumulocity design tokens (e.g., \\`var(--c8y-brand-primary)\\`)\n2. **ACCEPTABLE**: Use semantic color names with fallback (e.g., \\`var(--brand-primary, #119d11)\\`)\n3. **AVOID**: Hardcoded hex/rgb colors unless specifically required\n\n## Available Token Categories\n\n### Brand Colors\nUse for primary UI elements, CTAs, and brand identity:\n- \\`var(--c8y-brand-primary)\\` - Main brand color (green in light theme, yellow in dark theme)\n- \\`var(--c8y-brand-dark)\\` - Darker brand variant\n- \\`var(--c8y-brand-light)\\` - Lighter brand variant\n- Brand shades: \\`var(--c8y-brand-10)\\` through \\`var(--c8y-brand-80)\\` for subtle variations\n\n### Status Colors\nUse for alerts, notifications, and state indicators:\n- **Success**: \\`var(--c8y-palette-status-success)\\`, \\`var(--c8y-palette-status-success-light)\\`, \\`var(--c8y-palette-status-success-dark)\\`\n- **Danger**: \\`var(--c8y-palette-status-danger)\\`, \\`var(--c8y-palette-status-danger-light)\\`, \\`var(--c8y-palette-status-danger-dark)\\`\n- **Warning**: \\`var(--c8y-palette-status-warning)\\`, \\`var(--c8y-palette-status-warning-light)\\`, \\`var(--c8y-palette-status-warning-dark)\\`\n- **Info**: \\`var(--c8y-palette-status-info)\\`, \\`var(--c8y-palette-status-info-light)\\`, \\`var(--c8y-palette-status-info-dark)\\`\n\n### Text & Typography\nUse for text content and hierarchy:\n- \\`var(--text-color)\\` - Primary text color\n- \\`var(--text-muted)\\` - Secondary/muted text\n- \\`var(--link-color)\\` - Hyperlink color\n- \\`var(--link-hover-color)\\` - Hyperlink hover state\n\n### Background Colors\n- \\`var(--body-background-color)\\` - Main background\n- \\`var(--navigator-bg-color)\\` - Navigation background\n- \\`var(--action-bar-background-default)\\` - Action bar background\n\n### Borders & Spacing\n- \\`var(--c8y-root-component-border-color)\\` - Default border color\n- \\`var(--c8y-root-component-separator-color)\\` - Separator lines\n- \\`var(--c8y-root-component-border-width)\\` - Border thickness\n- \\`var(--c8y-root-component-border-radius-base)\\` - Default border radius\n- \\`var(--btn-border-radius-base)\\` - Button border radius\n\n### Component-Specific Tokens\n**Navigation**:\n- \\`var(--navigator-text-color)\\`, \\`var(--navigator-active-bg)\\`, \\`var(--navigator-border-active)\\`\n\n**Header**:\n- \\`var(--header-color)\\`, \\`var(--header-text-color)\\`, \\`var(--header-hover-color)\\`\n\n**Action Bar**:\n- \\`var(--action-bar-color-actions)\\`, \\`var(--action-bar-color-actions-hover)\\`\n\n## Implementation Examples\n\n### Good Practice ✓\n\\`\\`\\`css\n.my-widget {\n background-color: var(--body-background-color);\n color: var(--text-color);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-root-component-border-radius-base);\n}\n\n.success-badge {\n background-color: var(--c8y-palette-status-success-light);\n color: var(--c8y-palette-status-success-dark);\n border-left: 3px solid var(--c8y-palette-status-success);\n}\n\n.primary-button {\n background-color: var(--c8y-brand-primary);\n color: var(--c8y-palette-fixed-light);\n border-radius: var(--btn-border-radius-base);\n}\n`;\n\nexport const HTML_AGENT = {\n name: 'c8y-html-widget',\n agent: {\n system: `1. **Analyze the user request**\n - Extract specific data requirements\n - Identify visualization needs\n - Note any context dependencies\n\n2. **API Verification**\n - Use the \"cumulocity-api-request\" tool to verify needed APIs\n - Document the exact API endpoints, parameters, and expected responses\n\n3. **Coding**\n - Put out one code block with only the code, wrap it in a <${EXTRACT_TAG_NAME}> block\n - IMPORTANT: And no other text or markdown formatting inside the <${EXTRACT_TAG_NAME}> block\n - build the widget code using the following rules: ${WIDGET_CODE_INSTRUCTIONS}\n\n4. **Quality Validation**\n - Analyze if the widget fulfills the user request\n - Check for mock data usage vs real API integration\n - If inadequate, ask user for specific clarifications needed\n\n\nCurrently the following Code is used:\n{{currentHtmlWidgetCode}}\n`,\n maxOutputTokens: 20000\n },\n type: 'text',\n mcp: [\n {\n serverName: 'cumulocity-default',\n tools: ['cumulocity-api-request']\n // TO DO: Uncomment the below to enable the HITL prompts. This can be done when c8y-ai-agents version 2.3.0 is released.\n // tools: [\n // {\n // name: 'cumulocity-api-request',\n // destructive: true,\n // openWorld: true,\n // nonReadOnly: true\n // }\n // ]\n }\n ]\n} as AgentDefinition;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"AAMA,MAAM,gBAAgB,GAAG,kBAAkB;AAC3C,MAAM,wBAAwB,GAAG,CAAA;;GAE9B,gBAAgB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8Cf,gBAAgB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+iDnB;AAEM,MAAM,UAAU,GAAG;AACxB,IAAA,IAAI,EAAE,iBAAiB;AACvB,IAAA,KAAK,EAAE;AACL,QAAA,MAAM,EAAE,CAAA;;;;;;;;;;gEAUoD,gBAAgB,CAAA;uEACT,gBAAgB,CAAA;wDAC/B,wBAAwB;;;;;;;;;;AAU/E,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;;;;;;;;;;AAUjC;AACF;;;ACnpDH;;AAEG;;;;"}
|
|
@@ -874,6 +874,10 @@ const SCADA_SVG_AGENT = {
|
|
|
874
874
|
{
|
|
875
875
|
serverName: 'cumulocity-default',
|
|
876
876
|
tools: ['cumulocity-api-request']
|
|
877
|
+
// TO DO: Uncomment the below to enable the HITL prompts. This can be done when c8y-ai-agents version 2.3.0 is released.
|
|
878
|
+
// tools: [
|
|
879
|
+
// { name: 'cumulocity-api-request', destructive: true, openWorld: true, nonReadOnly: true }
|
|
880
|
+
// ]
|
|
877
881
|
}
|
|
878
882
|
]
|
|
879
883
|
};
|
|
@@ -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\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 // TO DO: Uncomment the below to enable the HITL prompts. This can be done when c8y-ai-agents version 2.3.0 is released.\n // tools: [\n // { name: 'cumulocity-api-request', destructive: true, openWorld: true, nonReadOnly: true }\n // ]\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;;;;;AAKjC;AACF;;;ACz3BH;;AAEG;;;;"}
|