@openremote/or-dashboard-builder 1.2.0-snapshot.20240512160221
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/README.md +156 -0
- package/build.gradle +15 -0
- package/lib/controls/dashboard-refresh-controls.d.ts +17 -0
- package/lib/controls/dashboard-refresh-controls.js +17 -0
- package/lib/controls/dashboard-refresh-controls.js.map +1 -0
- package/lib/index.d.ts +71 -0
- package/lib/index.js +285 -0
- package/lib/index.js.map +1 -0
- package/lib/or-dashboard-boardsettings.d.ts +19 -0
- package/lib/or-dashboard-boardsettings.js +121 -0
- package/lib/or-dashboard-boardsettings.js.map +1 -0
- package/lib/or-dashboard-browser.d.ts +9 -0
- package/lib/or-dashboard-browser.js +35 -0
- package/lib/or-dashboard-browser.js.map +1 -0
- package/lib/or-dashboard-engine.d.ts +4 -0
- package/lib/or-dashboard-engine.js +1 -0
- package/lib/or-dashboard-engine.js.map +1 -0
- package/lib/or-dashboard-keyhandler.d.ts +6 -0
- package/lib/or-dashboard-keyhandler.js +1 -0
- package/lib/or-dashboard-keyhandler.js.map +1 -0
- package/lib/or-dashboard-preview.d.ts +70 -0
- package/lib/or-dashboard-preview.js +155 -0
- package/lib/or-dashboard-preview.js.map +1 -0
- package/lib/or-dashboard-settingspanel.d.ts +44 -0
- package/lib/or-dashboard-settingspanel.js +216 -0
- package/lib/or-dashboard-settingspanel.js.map +1 -0
- package/lib/or-dashboard-tree.d.ts +25 -0
- package/lib/or-dashboard-tree.js +62 -0
- package/lib/or-dashboard-tree.js.map +1 -0
- package/lib/or-dashboard-widget.d.ts +18 -0
- package/lib/or-dashboard-widget.js +15 -0
- package/lib/or-dashboard-widget.js.map +1 -0
- package/lib/or-dashboard-widgetcontainer.d.ts +23 -0
- package/lib/or-dashboard-widgetcontainer.js +20 -0
- package/lib/or-dashboard-widgetcontainer.js.map +1 -0
- package/lib/or-dashboard-widgetsettings.d.ts +15 -0
- package/lib/or-dashboard-widgetsettings.js +16 -0
- package/lib/or-dashboard-widgetsettings.js.map +1 -0
- package/lib/panels/assettypes-panel.d.ts +44 -0
- package/lib/panels/assettypes-panel.js +51 -0
- package/lib/panels/assettypes-panel.js.map +1 -0
- package/lib/panels/attributes-panel.d.ts +41 -0
- package/lib/panels/attributes-panel.js +126 -0
- package/lib/panels/attributes-panel.js.map +1 -0
- package/lib/panels/thresholds-panel.d.ts +39 -0
- package/lib/panels/thresholds-panel.js +129 -0
- package/lib/panels/thresholds-panel.js.map +1 -0
- package/lib/service/dashboard-service.d.ts +13 -0
- package/lib/service/dashboard-service.js +1 -0
- package/lib/service/dashboard-service.js.map +1 -0
- package/lib/service/widget-service.d.ts +8 -0
- package/lib/service/widget-service.js +1 -0
- package/lib/service/widget-service.js.map +1 -0
- package/lib/settings/attribute-input-settings.d.ts +13 -0
- package/lib/settings/attribute-input-settings.js +36 -0
- package/lib/settings/attribute-input-settings.js.map +1 -0
- package/lib/settings/chart-settings.d.ts +30 -0
- package/lib/settings/chart-settings.js +144 -0
- package/lib/settings/chart-settings.js.map +1 -0
- package/lib/settings/gauge-settings.d.ts +15 -0
- package/lib/settings/gauge-settings.js +37 -0
- package/lib/settings/gauge-settings.js.map +1 -0
- package/lib/settings/image-settings.d.ts +17 -0
- package/lib/settings/image-settings.js +52 -0
- package/lib/settings/image-settings.js.map +1 -0
- package/lib/settings/kpi-settings.d.ts +15 -0
- package/lib/settings/kpi-settings.js +47 -0
- package/lib/settings/kpi-settings.js.map +1 -0
- package/lib/settings/map-settings.d.ts +21 -0
- package/lib/settings/map-settings.js +72 -0
- package/lib/settings/map-settings.js.map +1 -0
- package/lib/settings/table-settings.d.ts +14 -0
- package/lib/settings/table-settings.js +33 -0
- package/lib/settings/table-settings.js.map +1 -0
- package/lib/style.d.ts +1 -0
- package/lib/style.js +99 -0
- package/lib/style.js.map +1 -0
- package/lib/util/or-asset-widget.d.ts +20 -0
- package/lib/util/or-asset-widget.js +1 -0
- package/lib/util/or-asset-widget.js.map +1 -0
- package/lib/util/or-widget.d.ts +31 -0
- package/lib/util/or-widget.js +1 -0
- package/lib/util/or-widget.js.map +1 -0
- package/lib/util/settings-panel.d.ts +9 -0
- package/lib/util/settings-panel.js +56 -0
- package/lib/util/settings-panel.js.map +1 -0
- package/lib/util/widget-config.d.ts +2 -0
- package/lib/util/widget-config.js +1 -0
- package/lib/util/widget-config.js.map +1 -0
- package/lib/util/widget-settings.d.ts +23 -0
- package/lib/util/widget-settings.js +1 -0
- package/lib/util/widget-settings.js.map +1 -0
- package/lib/widgets/attribute-input-widget.d.ts +24 -0
- package/lib/widgets/attribute-input-widget.js +31 -0
- package/lib/widgets/attribute-input-widget.js.map +1 -0
- package/lib/widgets/chart-widget.d.ts +25 -0
- package/lib/widgets/chart-widget.js +15 -0
- package/lib/widgets/chart-widget.js.map +1 -0
- package/lib/widgets/gauge-widget.d.ts +22 -0
- package/lib/widgets/gauge-widget.js +12 -0
- package/lib/widgets/gauge-widget.js.map +1 -0
- package/lib/widgets/image-widget.d.ts +25 -0
- package/lib/widgets/image-widget.js +54 -0
- package/lib/widgets/image-widget.js.map +1 -0
- package/lib/widgets/kpi-widget.d.ts +21 -0
- package/lib/widgets/kpi-widget.js +8 -0
- package/lib/widgets/kpi-widget.js.map +1 -0
- package/lib/widgets/map-widget.d.ts +39 -0
- package/lib/widgets/map-widget.js +9 -0
- package/lib/widgets/map-widget.js.map +1 -0
- package/lib/widgets/or-base-widget.d.ts +15 -0
- package/lib/widgets/or-base-widget.js +1 -0
- package/lib/widgets/or-base-widget.js.map +1 -0
- package/lib/widgets/or-chart-widget.d.ts +36 -0
- package/lib/widgets/or-chart-widget.js +112 -0
- package/lib/widgets/or-chart-widget.js.map +1 -0
- package/lib/widgets/or-gauge-widget.d.ts +46 -0
- package/lib/widgets/or-gauge-widget.js +60 -0
- package/lib/widgets/or-gauge-widget.js.map +1 -0
- package/lib/widgets/or-kpi-widget.d.ts +44 -0
- package/lib/widgets/or-kpi-widget.js +63 -0
- package/lib/widgets/or-kpi-widget.js.map +1 -0
- package/lib/widgets/or-map-widget.d.ts +49 -0
- package/lib/widgets/or-map-widget.js +75 -0
- package/lib/widgets/or-map-widget.js.map +1 -0
- package/lib/widgets/table-widget.d.ts +25 -0
- package/lib/widgets/table-widget.js +12 -0
- package/lib/widgets/table-widget.js.map +1 -0
- package/package.json +32 -0
- package/src/controls/dashboard-refresh-controls.ts +100 -0
- package/src/index.ts +731 -0
- package/src/or-dashboard-boardsettings.ts +249 -0
- package/src/or-dashboard-browser.ts +160 -0
- package/src/or-dashboard-engine.ts +17 -0
- package/src/or-dashboard-keyhandler.ts +25 -0
- package/src/or-dashboard-preview.ts +713 -0
- package/src/or-dashboard-tree.ts +304 -0
- package/src/or-dashboard-widgetcontainer.ts +155 -0
- package/src/or-dashboard-widgetsettings.ts +91 -0
- package/src/panels/assettypes-panel.ts +311 -0
- package/src/panels/attributes-panel.ts +304 -0
- package/src/panels/thresholds-panel.ts +285 -0
- package/src/service/dashboard-service.ts +89 -0
- package/src/service/widget-service.ts +48 -0
- package/src/settings/attribute-input-settings.ts +79 -0
- package/src/settings/chart-settings.ts +306 -0
- package/src/settings/gauge-settings.ts +93 -0
- package/src/settings/image-settings.ts +175 -0
- package/src/settings/kpi-settings.ts +106 -0
- package/src/settings/map-settings.ts +185 -0
- package/src/settings/table-settings.ts +92 -0
- package/src/style.ts +104 -0
- package/src/util/or-asset-widget.ts +110 -0
- package/src/util/or-widget.ts +60 -0
- package/src/util/settings-panel.ts +93 -0
- package/src/util/widget-config.ts +2 -0
- package/src/util/widget-settings.ts +58 -0
- package/src/widgets/attribute-input-widget.ts +143 -0
- package/src/widgets/chart-widget.ts +203 -0
- package/src/widgets/gauge-widget.ts +111 -0
- package/src/widgets/image-widget.ts +180 -0
- package/src/widgets/kpi-widget.ts +97 -0
- package/src/widgets/map-widget.ts +187 -0
- package/src/widgets/table-widget.ts +157 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/webpack.config.js +10 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {WidgetConfig} from "../util/widget-config";
|
|
2
|
+
import {AssetModelUtil, Attribute, AttributeRef, WellknownValueTypes} from "@openremote/model";
|
|
3
|
+
import {OrWidget, WidgetManifest} from "../util/or-widget";
|
|
4
|
+
import { customElement } from "lit/decorators.js";
|
|
5
|
+
import {WidgetSettings} from "../util/widget-settings";
|
|
6
|
+
import {css, CSSResult, html, PropertyValues, TemplateResult, unsafeCSS } from "lit";
|
|
7
|
+
import {OrAssetWidget} from "../util/or-asset-widget";
|
|
8
|
+
import {ImageSettings} from "../settings/image-settings";
|
|
9
|
+
import { when } from "lit/directives/when.js";
|
|
10
|
+
import {DefaultColor2, DefaultColor3, Util} from "@openremote/core";
|
|
11
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
12
|
+
|
|
13
|
+
const styling = css`
|
|
14
|
+
#img-wrapper {
|
|
15
|
+
height: 100%;
|
|
16
|
+
width: 100%;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
align-items: center;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
z-index: 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#img-container {
|
|
26
|
+
position: relative;
|
|
27
|
+
max-height: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#img-content {
|
|
31
|
+
height: 100%;
|
|
32
|
+
max-height: 100%;
|
|
33
|
+
max-width: 100%;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#overlay {
|
|
37
|
+
position: absolute;
|
|
38
|
+
z-index: 3;
|
|
39
|
+
|
|
40
|
+
/* additional marker styling */
|
|
41
|
+
color: var(--or-app-color2, ${unsafeCSS(DefaultColor2)});
|
|
42
|
+
background-color: var(--or-app-color3, ${unsafeCSS(DefaultColor3)});
|
|
43
|
+
border-radius: 15px;
|
|
44
|
+
padding: 3px 8px 5px 8px;
|
|
45
|
+
object-fit: contain;
|
|
46
|
+
text-overflow: ellipsis;
|
|
47
|
+
white-space: nowrap;
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
export interface ImageAssetMarker {
|
|
52
|
+
attributeRef: AttributeRef,
|
|
53
|
+
coordinates: [number, number]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ImageWidgetConfig extends WidgetConfig {
|
|
57
|
+
attributeRefs: AttributeRef[];
|
|
58
|
+
markers: ImageAssetMarker[];
|
|
59
|
+
showTimestampControls: boolean;
|
|
60
|
+
imagePath: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getDefaultWidgetConfig(): ImageWidgetConfig {
|
|
64
|
+
return {
|
|
65
|
+
attributeRefs: [],
|
|
66
|
+
showTimestampControls: false,
|
|
67
|
+
imagePath: '',
|
|
68
|
+
markers: [],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@customElement("image-widget")
|
|
73
|
+
export class ImageWidget extends OrAssetWidget {
|
|
74
|
+
|
|
75
|
+
// Override of widgetConfig with extended type
|
|
76
|
+
protected readonly widgetConfig!: ImageWidgetConfig;
|
|
77
|
+
|
|
78
|
+
static getManifest(): WidgetManifest {
|
|
79
|
+
return {
|
|
80
|
+
displayName: "Image",
|
|
81
|
+
displayIcon: "file-image-marker",
|
|
82
|
+
minColumnWidth: 1,
|
|
83
|
+
minColumnHeight: 1,
|
|
84
|
+
getContentHtml(config: ImageWidgetConfig): OrWidget {
|
|
85
|
+
return new ImageWidget(config);
|
|
86
|
+
},
|
|
87
|
+
getSettingsHtml(config: ImageWidgetConfig): WidgetSettings {
|
|
88
|
+
return new ImageSettings(config);
|
|
89
|
+
},
|
|
90
|
+
getDefaultConfig(): ImageWidgetConfig {
|
|
91
|
+
return getDefaultWidgetConfig();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public refreshContent(force: boolean) {
|
|
97
|
+
this.loadAssets();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static get styles(): CSSResult[] {
|
|
101
|
+
return [...super.styles, styling];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
updated(changedProps: PropertyValues) {
|
|
105
|
+
|
|
106
|
+
if(changedProps.has('widgetConfig') && this.widgetConfig) {
|
|
107
|
+
const attributeRefs = this.widgetConfig.attributeRefs;
|
|
108
|
+
const missingAssets = attributeRefs?.filter((attrRef: AttributeRef) => !this.isAttributeRefLoaded(attrRef));
|
|
109
|
+
if (missingAssets.length > 0) {
|
|
110
|
+
this.loadAssets();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected loadAssets() {
|
|
116
|
+
this.fetchAssets(this.widgetConfig.attributeRefs).then(assets => {
|
|
117
|
+
this.loadedAssets = assets!;
|
|
118
|
+
this.assetAttributes = this.widgetConfig.attributeRefs.map((attrRef: AttributeRef) => {
|
|
119
|
+
const assetIndex = assets!.findIndex(asset => asset.id === attrRef.id);
|
|
120
|
+
const foundAsset = assetIndex >= 0 ? assets![assetIndex] : undefined;
|
|
121
|
+
return foundAsset && foundAsset.attributes ? [assetIndex, foundAsset.attributes[attrRef.name!]] : undefined;
|
|
122
|
+
}).filter((indexAndAttr: any) => !!indexAndAttr) as [number, Attribute<any>][];
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// method to render and update the markers on the image
|
|
127
|
+
protected handleMarkerPlacement(config: ImageWidgetConfig) {
|
|
128
|
+
if (this.assetAttributes.length && config.attributeRefs.length > 0) {
|
|
129
|
+
|
|
130
|
+
if(config.markers.length === 0) {
|
|
131
|
+
console.error("No markers found!");
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
return config.attributeRefs.map((attributeRef, index) => {
|
|
135
|
+
const marker = config.markers.find(m => m.attributeRef.id === attributeRef.id && m.attributeRef.name === attributeRef.name);
|
|
136
|
+
const asset = this.loadedAssets.find(a => a.id === attributeRef.id);
|
|
137
|
+
let value: string | undefined;
|
|
138
|
+
const styles: any = {
|
|
139
|
+
"left": `${marker!.coordinates[0]}%`,
|
|
140
|
+
"top": `${marker!.coordinates[1]}%`
|
|
141
|
+
};
|
|
142
|
+
if(asset) {
|
|
143
|
+
const attribute = asset.attributes![attributeRef.name!];
|
|
144
|
+
const descriptors = AssetModelUtil.getAttributeAndValueDescriptors(asset.type, attributeRef.name, attribute);
|
|
145
|
+
value = Util.getAttributeValueAsString(attribute, descriptors[0], asset.type, true, "-");
|
|
146
|
+
if(attribute?.type === WellknownValueTypes.COLOURRGB && value !== "-") {
|
|
147
|
+
styles.backgroundColor = value;
|
|
148
|
+
styles.minHeight = "21px";
|
|
149
|
+
styles.minWidth = "13px";
|
|
150
|
+
value = undefined;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return html`
|
|
154
|
+
<span id="overlay" style="${styleMap(styles)}">
|
|
155
|
+
${value}
|
|
156
|
+
</span>
|
|
157
|
+
`;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
protected render(): TemplateResult {
|
|
163
|
+
const imagePath = this.widgetConfig.imagePath;
|
|
164
|
+
return html`
|
|
165
|
+
<div id="img-wrapper">
|
|
166
|
+
${when(imagePath, () => html`
|
|
167
|
+
<div id="img-container">
|
|
168
|
+
<img id="img-content" src="${imagePath}" alt=""/>
|
|
169
|
+
<div>
|
|
170
|
+
${this.handleMarkerPlacement(this.widgetConfig)}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
`, () => html`
|
|
174
|
+
<span><or-translate value="dashboard.noImageSelected"></or-translate></span>
|
|
175
|
+
`)}
|
|
176
|
+
</div>
|
|
177
|
+
`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { customElement } from "lit/decorators.js";
|
|
2
|
+
import {OrAssetWidget} from "../util/or-asset-widget";
|
|
3
|
+
import {OrWidget, WidgetManifest} from "../util/or-widget";
|
|
4
|
+
import {WidgetSettings} from "../util/widget-settings";
|
|
5
|
+
import {WidgetConfig} from "../util/widget-config";
|
|
6
|
+
import {Attribute, AttributeRef} from "@openremote/model";
|
|
7
|
+
import {html, TemplateResult } from "lit";
|
|
8
|
+
import {KpiSettings} from "../settings/kpi-settings";
|
|
9
|
+
import "@openremote/or-attribute-card";
|
|
10
|
+
|
|
11
|
+
export interface KpiWidgetConfig extends WidgetConfig {
|
|
12
|
+
attributeRefs: AttributeRef[];
|
|
13
|
+
period?: 'year' | 'month' | 'week' | 'day' | 'hour';
|
|
14
|
+
decimals: number;
|
|
15
|
+
deltaFormat: "absolute" | "percentage";
|
|
16
|
+
showTimestampControls: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getDefaultWidgetConfig(): KpiWidgetConfig {
|
|
20
|
+
return {
|
|
21
|
+
attributeRefs: [],
|
|
22
|
+
period: "day",
|
|
23
|
+
decimals: 0,
|
|
24
|
+
deltaFormat: "absolute",
|
|
25
|
+
showTimestampControls: false
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@customElement("kpi-widget")
|
|
30
|
+
export class KpiWidget extends OrAssetWidget {
|
|
31
|
+
|
|
32
|
+
protected widgetConfig!: KpiWidgetConfig;
|
|
33
|
+
|
|
34
|
+
static getManifest(): WidgetManifest {
|
|
35
|
+
return {
|
|
36
|
+
displayName: "KPI",
|
|
37
|
+
displayIcon: "label",
|
|
38
|
+
minColumnWidth: 1,
|
|
39
|
+
minColumnHeight: 1,
|
|
40
|
+
getContentHtml(config: KpiWidgetConfig): OrWidget {
|
|
41
|
+
return new KpiWidget(config);
|
|
42
|
+
},
|
|
43
|
+
getSettingsHtml(config: KpiWidgetConfig): WidgetSettings {
|
|
44
|
+
return new KpiSettings(config);
|
|
45
|
+
},
|
|
46
|
+
getDefaultConfig(): KpiWidgetConfig {
|
|
47
|
+
return getDefaultWidgetConfig();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
refreshContent(force: boolean): void {
|
|
53
|
+
this.loadAssets(this.widgetConfig.attributeRefs);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected updated(changedProps: Map<string, any>) {
|
|
57
|
+
super.updated(changedProps);
|
|
58
|
+
|
|
59
|
+
// If widgetConfig, and the attributeRefs of them have changed...
|
|
60
|
+
if(changedProps.has("widgetConfig") && this.widgetConfig) {
|
|
61
|
+
const attributeRefs = this.widgetConfig.attributeRefs;
|
|
62
|
+
|
|
63
|
+
// Check if list of attributes has changed, based on the cached assets
|
|
64
|
+
const loadedRefs: AttributeRef[] = attributeRefs?.filter((attrRef: AttributeRef) => this.isAttributeRefLoaded(attrRef));
|
|
65
|
+
if (loadedRefs?.length !== (attributeRefs ? attributeRefs.length : 0)) {
|
|
66
|
+
|
|
67
|
+
// Fetch the new list of assets
|
|
68
|
+
this.loadAssets(attributeRefs);
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return super.updated(changedProps);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected loadAssets(attributeRefs: AttributeRef[]) {
|
|
76
|
+
this.fetchAssets(attributeRefs).then((assets) => {
|
|
77
|
+
this.loadedAssets = assets;
|
|
78
|
+
this.assetAttributes = attributeRefs?.map((attrRef: AttributeRef) => {
|
|
79
|
+
const assetIndex = assets.findIndex((asset) => asset.id === attrRef.id);
|
|
80
|
+
const foundAsset = assetIndex >= 0 ? assets[assetIndex] : undefined;
|
|
81
|
+
return foundAsset && foundAsset.attributes ? [assetIndex, foundAsset.attributes[attrRef.name!]] : undefined;
|
|
82
|
+
}).filter((indexAndAttr: any) => !!indexAndAttr) as [number, Attribute<any>][];
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected render(): TemplateResult {
|
|
87
|
+
return html`
|
|
88
|
+
<div style="height: 100%; overflow: hidden;">
|
|
89
|
+
<or-attribute-card .assets="${this.loadedAssets}" .assetAttributes="${this.assetAttributes}" .period="${this.widgetConfig.period}"
|
|
90
|
+
.deltaFormat="${this.widgetConfig.deltaFormat}" .mainValueDecimals="${this.widgetConfig.decimals}"
|
|
91
|
+
showControls="${this.widgetConfig?.showTimestampControls}" showTitle="${false}" style="height: 100%;">
|
|
92
|
+
</or-attribute-card>
|
|
93
|
+
</div>
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {html, PropertyValues, TemplateResult} from "lit";
|
|
2
|
+
import {customElement} from "lit/decorators.js";
|
|
3
|
+
import {OrAssetWidget} from "../util/or-asset-widget";
|
|
4
|
+
import {OrWidget, WidgetManifest} from "../util/or-widget";
|
|
5
|
+
import {WidgetSettings} from "../util/widget-settings";
|
|
6
|
+
import {MapSettings} from "../settings/map-settings";
|
|
7
|
+
import {WidgetConfig} from "../util/widget-config";
|
|
8
|
+
import {Asset, AssetDescriptor, Attribute, AttributeRef, GeoJSONPoint, WellknownAttributes, WellknownMetaItems} from "@openremote/model";
|
|
9
|
+
import {
|
|
10
|
+
LngLatLike,
|
|
11
|
+
AttributeMarkerColours,
|
|
12
|
+
RangeAttributeMarkerColours,
|
|
13
|
+
AttributeMarkerColoursRange,
|
|
14
|
+
MapMarkerColours, MapMarkerAssetConfig,
|
|
15
|
+
} from "@openremote/or-map";
|
|
16
|
+
import {when} from "lit/directives/when.js";
|
|
17
|
+
import manager, {Util} from "@openremote/core";
|
|
18
|
+
import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar";
|
|
19
|
+
import "@openremote/or-map";
|
|
20
|
+
|
|
21
|
+
export interface MapWidgetConfig extends WidgetConfig {
|
|
22
|
+
// General values
|
|
23
|
+
attributeRefs: AttributeRef[];
|
|
24
|
+
// Map related values
|
|
25
|
+
zoom?: number,
|
|
26
|
+
center?: LngLatLike,
|
|
27
|
+
lat?: number,
|
|
28
|
+
lng?: number,
|
|
29
|
+
// Map marker related values
|
|
30
|
+
showLabels: boolean,
|
|
31
|
+
showUnits: boolean,
|
|
32
|
+
showGeoJson: boolean,
|
|
33
|
+
boolColors: MapMarkerColours,
|
|
34
|
+
textColors: [string, string][],
|
|
35
|
+
// Threshold related values
|
|
36
|
+
thresholds: [number, string][],
|
|
37
|
+
min?: number,
|
|
38
|
+
max?: number,
|
|
39
|
+
// Asset type related values
|
|
40
|
+
assetType?: string,
|
|
41
|
+
valueType?: string,
|
|
42
|
+
attributeName?: string,
|
|
43
|
+
assetTypes: AssetDescriptor[],
|
|
44
|
+
assetIds: string[],
|
|
45
|
+
attributes: string[],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getDefaultWidgetConfig(): MapWidgetConfig {
|
|
49
|
+
return {
|
|
50
|
+
attributeRefs: [],
|
|
51
|
+
showLabels: false,
|
|
52
|
+
showUnits: false,
|
|
53
|
+
showGeoJson: true,
|
|
54
|
+
boolColors: {type: 'boolean', 'false': '#ef5350', 'true': '#4caf50'},
|
|
55
|
+
textColors: [['example', '4caf50'], ['example2', 'ef5350']],
|
|
56
|
+
thresholds: [[0, "#4caf50"], [75, "#ff9800"], [90, "#ef5350"]],
|
|
57
|
+
assetTypes: [],
|
|
58
|
+
assetType: undefined,
|
|
59
|
+
assetIds: [],
|
|
60
|
+
attributes: [],
|
|
61
|
+
} as MapWidgetConfig;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@customElement("map-widget")
|
|
65
|
+
export class MapWidget extends OrAssetWidget {
|
|
66
|
+
|
|
67
|
+
protected widgetConfig!: MapWidgetConfig
|
|
68
|
+
|
|
69
|
+
private markers: MapMarkerAssetConfig = {};
|
|
70
|
+
|
|
71
|
+
static getManifest(): WidgetManifest {
|
|
72
|
+
return {
|
|
73
|
+
displayName: "Map",
|
|
74
|
+
displayIcon: "map",
|
|
75
|
+
minColumnWidth: 2,
|
|
76
|
+
minColumnHeight: 2,
|
|
77
|
+
getContentHtml(config: MapWidgetConfig): OrWidget {
|
|
78
|
+
return new MapWidget(config);
|
|
79
|
+
},
|
|
80
|
+
getSettingsHtml(config: MapWidgetConfig): WidgetSettings {
|
|
81
|
+
return new MapSettings(config);
|
|
82
|
+
},
|
|
83
|
+
getDefaultConfig(): MapWidgetConfig {
|
|
84
|
+
return getDefaultWidgetConfig();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
refreshContent(force: boolean): void {
|
|
90
|
+
this.loadAssets();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected updated(changedProps: PropertyValues) {
|
|
94
|
+
if(changedProps.has('widgetConfig') && this.widgetConfig) {
|
|
95
|
+
this.loadAssets();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
protected async loadAssets() {
|
|
100
|
+
if(this.widgetConfig.assetType && this.widgetConfig.attributeName) {
|
|
101
|
+
this.fetchAssetsByType([this.widgetConfig.assetType], this.widgetConfig.attributeName).then((assets) => {
|
|
102
|
+
this.loadedAssets = assets;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
protected async fetchAssetsByType(assetTypes: string[], attributeName: string) {
|
|
108
|
+
let assets: Asset[] = [];
|
|
109
|
+
await manager.rest.api.AssetResource.queryAssets({
|
|
110
|
+
realm: {
|
|
111
|
+
name: manager.displayRealm
|
|
112
|
+
},
|
|
113
|
+
select: {
|
|
114
|
+
attributes: [attributeName, 'location']
|
|
115
|
+
},
|
|
116
|
+
types: assetTypes,
|
|
117
|
+
|
|
118
|
+
}).then(response => {
|
|
119
|
+
assets = response.data;
|
|
120
|
+
this.markers = {};
|
|
121
|
+
}).catch((reason) => {
|
|
122
|
+
console.error(reason);
|
|
123
|
+
showSnackbar(undefined, "errorOccurred");
|
|
124
|
+
});
|
|
125
|
+
return assets;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
protected render(): TemplateResult {
|
|
130
|
+
return html`
|
|
131
|
+
<div style="height: 100%; display: flex; flex-direction: column; overflow: hidden;">
|
|
132
|
+
<or-map id="miniMap" class="or-map" .zoom="${this.widgetConfig.zoom}" .center="${this.widgetConfig.center}" .showGeoJson="${this.widgetConfig.showGeoJson}" style="flex: 1;">
|
|
133
|
+
${when(this.loadedAssets, () => {
|
|
134
|
+
return this.getMarkerTemplates();
|
|
135
|
+
})}
|
|
136
|
+
</or-map>
|
|
137
|
+
</div>
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected getMarkerTemplates(): TemplateResult[] {
|
|
142
|
+
return this.loadedAssets.filter((asset: Asset) => {
|
|
143
|
+
if (!asset.attributes) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const attr = asset.attributes[WellknownAttributes.LOCATION] as Attribute<GeoJSONPoint>;
|
|
147
|
+
return !attr || !attr.meta || !attr.meta.hasOwnProperty(WellknownMetaItems.SHOWONDASHBOARD) || !!Util.getMetaValue(WellknownMetaItems.SHOWONDASHBOARD, attr);
|
|
148
|
+
}).map(asset => {
|
|
149
|
+
if (this.markers) {
|
|
150
|
+
// Configure map marker asset settings
|
|
151
|
+
this.markers[asset.type!] = {attributeName: this.widgetConfig.attributeName!};
|
|
152
|
+
this.markers[asset.type!].showUnits = this.widgetConfig.showUnits;
|
|
153
|
+
this.markers[asset.type!].showLabel = this.widgetConfig.showLabels;
|
|
154
|
+
if (this.widgetConfig.valueType == 'boolean') {
|
|
155
|
+
(this.widgetConfig.boolColors as any).true = (this.widgetConfig.boolColors as any).true.replace("#", "");
|
|
156
|
+
(this.widgetConfig.boolColors as any).false = (this.widgetConfig.boolColors as any).false.replace("#", "");
|
|
157
|
+
this.markers[asset.type!].colours = this.widgetConfig.boolColors;
|
|
158
|
+
} else if (this.widgetConfig.valueType == 'text') {
|
|
159
|
+
var colors: AttributeMarkerColours = {type: 'string',};
|
|
160
|
+
(this.widgetConfig.textColors as [string, string][]).map((threshold) => {
|
|
161
|
+
colors[threshold[0] as string] = (threshold[1] as string).replace('#', '');
|
|
162
|
+
})
|
|
163
|
+
this.markers[asset.type!].colours = colors;
|
|
164
|
+
} else {
|
|
165
|
+
var ranges: AttributeMarkerColoursRange[] = [];
|
|
166
|
+
(this.widgetConfig.thresholds as [number, string][]).sort((x, y) => (x[0] > y[0]) ? -1 : 1).map((threshold, index) => {
|
|
167
|
+
var range: AttributeMarkerColoursRange = {
|
|
168
|
+
min: threshold[0],
|
|
169
|
+
colour: threshold[1].replace('#', '')
|
|
170
|
+
}
|
|
171
|
+
ranges.push(range);
|
|
172
|
+
})
|
|
173
|
+
var colorsNum: RangeAttributeMarkerColours = {
|
|
174
|
+
type: 'range',
|
|
175
|
+
ranges: ranges
|
|
176
|
+
};
|
|
177
|
+
this.markers[asset.type!].colours = colorsNum;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return html`
|
|
181
|
+
<or-map-marker-asset .asset="${asset}" .config="${this.markers}"></or-map-marker-asset>
|
|
182
|
+
`
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {css, html, PropertyValues, TemplateResult } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
import {OrAssetWidget} from "../util/or-asset-widget";
|
|
4
|
+
import {WidgetManifest} from "../util/or-widget";
|
|
5
|
+
import {WidgetConfig} from "../util/widget-config";
|
|
6
|
+
import {WidgetSettings} from "../util/widget-settings";
|
|
7
|
+
import {TableSettings} from "../settings/table-settings";
|
|
8
|
+
import {OrMwcTableRowClickEvent, TableColumn, TableRow, TableConfig} from "@openremote/or-mwc-components/or-mwc-table";
|
|
9
|
+
import {i18next} from "@openremote/or-translate";
|
|
10
|
+
import {Util} from "@openremote/core";
|
|
11
|
+
import {Asset, AssetModelUtil} from "@openremote/model";
|
|
12
|
+
import "@openremote/or-mwc-components/or-mwc-table";
|
|
13
|
+
|
|
14
|
+
export interface TableWidgetConfig extends WidgetConfig {
|
|
15
|
+
assetType?: string
|
|
16
|
+
assetIds: string[]
|
|
17
|
+
attributeNames: string[],
|
|
18
|
+
tableSize: number,
|
|
19
|
+
tableOptions: number[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getDefaultConfig(): TableWidgetConfig {
|
|
23
|
+
return {
|
|
24
|
+
assetType: undefined,
|
|
25
|
+
assetIds: [],
|
|
26
|
+
attributeNames: [],
|
|
27
|
+
tableSize: 10,
|
|
28
|
+
tableOptions: [10, 25, 100]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const styling = css`
|
|
33
|
+
#widget-wrapper {
|
|
34
|
+
height: 100%;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
}
|
|
37
|
+
`
|
|
38
|
+
|
|
39
|
+
@customElement("table-widget")
|
|
40
|
+
export class TableWidget extends OrAssetWidget {
|
|
41
|
+
|
|
42
|
+
protected widgetConfig!: TableWidgetConfig;
|
|
43
|
+
|
|
44
|
+
static getManifest(): WidgetManifest {
|
|
45
|
+
return {
|
|
46
|
+
displayName: "Table",
|
|
47
|
+
displayIcon: "table",
|
|
48
|
+
getContentHtml(config: WidgetConfig): OrAssetWidget {
|
|
49
|
+
return new TableWidget(config);
|
|
50
|
+
},
|
|
51
|
+
getDefaultConfig(): WidgetConfig {
|
|
52
|
+
return getDefaultConfig();
|
|
53
|
+
},
|
|
54
|
+
getSettingsHtml(config: WidgetConfig): WidgetSettings {
|
|
55
|
+
return new TableSettings(config);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static get styles() {
|
|
62
|
+
return [...super.styles, styling];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// TODO: Improve this to be more efficient
|
|
66
|
+
refreshContent(force: boolean): void {
|
|
67
|
+
this.widgetConfig = JSON.parse(JSON.stringify(this.widgetConfig)) as TableWidgetConfig;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Lit Lifecycle
|
|
71
|
+
protected willUpdate(changedProps: PropertyValues) {
|
|
72
|
+
if(changedProps.has('widgetConfig') && this.widgetConfig) {
|
|
73
|
+
this.loadAssets();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return super.willUpdate(changedProps);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/* --------------------------------------- */
|
|
81
|
+
|
|
82
|
+
protected loadAssets() {
|
|
83
|
+
if(this.widgetConfig.assetIds.find(id => !this.isAssetLoaded(id))) {
|
|
84
|
+
this.queryAssets({
|
|
85
|
+
ids: this.widgetConfig.assetIds,
|
|
86
|
+
select: {
|
|
87
|
+
attributes: this.widgetConfig.attributeNames
|
|
88
|
+
}
|
|
89
|
+
}).then((assets) => {
|
|
90
|
+
this.loadedAssets = assets;
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected getColumns(attributeNames: string[]): TableColumn[] {
|
|
96
|
+
const referenceAsset: Asset | undefined = this.loadedAssets[0];
|
|
97
|
+
const attrColumns = attributeNames.map(attrName => {
|
|
98
|
+
let text = attrName;
|
|
99
|
+
let numeric = false;
|
|
100
|
+
if(this.widgetConfig.assetType && referenceAsset && referenceAsset.attributes && referenceAsset.attributes[attrName]) {
|
|
101
|
+
const attributeDescriptor = AssetModelUtil.getAttributeDescriptor(attrName, this.widgetConfig.assetType);
|
|
102
|
+
text = Util.getAttributeLabel(referenceAsset.attributes[attrName], attributeDescriptor, this.widgetConfig.assetType, true);
|
|
103
|
+
numeric = attributeDescriptor?.format?.asNumber || attributeDescriptor?.format?.asSlider || false;
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
title: text,
|
|
107
|
+
isSortable: true,
|
|
108
|
+
isNumeric: numeric
|
|
109
|
+
} as TableColumn;
|
|
110
|
+
});
|
|
111
|
+
return Array.of({ title: i18next.t('assetName'), isSortable: true }, ...attrColumns);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected getRows(attributeNames: string[]): TableRow[] {
|
|
115
|
+
return this.loadedAssets.map(asset => {
|
|
116
|
+
const attrEntries = attributeNames.map(attrName => {
|
|
117
|
+
if(asset.attributes && asset.attributes[attrName]) {
|
|
118
|
+
const attributeDescriptor = AssetModelUtil.getAttributeDescriptor(attrName, asset.type!);
|
|
119
|
+
const strValue = Util.getAttributeValueAsString(asset.attributes[attrName], attributeDescriptor, asset.type, false);
|
|
120
|
+
const numValue = parseFloat(strValue);
|
|
121
|
+
return isNaN(numValue) ? strValue : numValue;
|
|
122
|
+
} else {
|
|
123
|
+
return 'N.A.'
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
const content: any[] = Array.of(asset.name!, ...attrEntries);
|
|
127
|
+
return {
|
|
128
|
+
content: content
|
|
129
|
+
};
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected render(): TemplateResult {
|
|
134
|
+
const tableConfig: any = {
|
|
135
|
+
fullHeight: true,
|
|
136
|
+
pagination: {
|
|
137
|
+
enable: true,
|
|
138
|
+
options: this.widgetConfig.tableOptions,
|
|
139
|
+
}
|
|
140
|
+
} as TableConfig
|
|
141
|
+
const columns: TableColumn[] = this.getColumns(this.widgetConfig.attributeNames);
|
|
142
|
+
const rows: TableRow[] = this.getRows(this.widgetConfig.attributeNames);
|
|
143
|
+
return html`
|
|
144
|
+
<div id="widget-wrapper">
|
|
145
|
+
<or-mwc-table .columns="${columns}" .rows="${rows}" .config="${tableConfig}" .paginationSize="${this.widgetConfig.tableSize}"
|
|
146
|
+
@or-mwc-table-row-click="${(ev: OrMwcTableRowClickEvent) => this.onTableRowClick(ev)}"
|
|
147
|
+
></or-mwc-table>
|
|
148
|
+
</div>
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
protected onTableRowClick(ev: OrMwcTableRowClickEvent) {
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
}
|
package/tsconfig.json
ADDED