@openremote/or-dashboard-builder 1.2.0-snapshot.20240616202404 → 1.2.0-snapshot.20240819101332

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.
@@ -0,0 +1,84 @@
1
+ import { OrWidget, WidgetManifest } from "../util/or-widget";
2
+ import { PropertyValues, TemplateResult } from "lit";
3
+ import { WidgetConfig } from "../util/widget-config";
4
+ import { OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
5
+ import { GatewayTunnelInfo, GatewayTunnelInfoType } from "@openremote/model";
6
+ export interface GatewayWidgetConfig extends WidgetConfig {
7
+ gatewayId?: string;
8
+ type: GatewayTunnelInfoType;
9
+ target: string;
10
+ targetPort: number;
11
+ }
12
+ export declare class GatewayWidget extends OrWidget {
13
+ protected widgetConfig: GatewayWidgetConfig;
14
+ protected _loading: boolean;
15
+ protected _activeTunnel?: GatewayTunnelInfo;
16
+ protected _startedByUser: boolean;
17
+ static getManifest(): WidgetManifest;
18
+ refreshContent(force: boolean): void;
19
+ static get styles(): import("lit").CSSResult[];
20
+ disconnectedCallback(): void;
21
+ protected firstUpdated(_changedProps: PropertyValues): void;
22
+ protected render(): TemplateResult;
23
+ /**
24
+ * HTML callback function when 'copy address' button is pressed for a TCP tunnel.
25
+ */
26
+ protected _onCopyTunnelAddressClick(ev: OrInputChangedEvent): void;
27
+ /**
28
+ * HTML callback function when 'start' button is pressed, meant to create / start a new tunnel.
29
+ */
30
+ protected _onStartTunnelClick(ev: OrInputChangedEvent): void;
31
+ /**
32
+ * HTML callback function when 'stop' button is pressed, meant to destroy the active tunnel.
33
+ */
34
+ protected _onStopTunnelClick(ev: OrInputChangedEvent): void;
35
+ /**
36
+ * HTML callback function when 'open' button is pressed, meant to start using the tunnel.
37
+ */
38
+ protected _onTunnelNavigateClick(ev: OrInputChangedEvent): void;
39
+ /**
40
+ * Internal function to set the active tunnel.
41
+ * Navigates the user to the tunnel after updating. This can be disabled using the {@link silent} parameter.
42
+ */
43
+ protected _setActiveTunnel(tunnelInfo?: GatewayTunnelInfo, silent?: boolean): void;
44
+ /**
45
+ * Function that tries to start the tunnel. It checks the configuration beforehand,
46
+ * and acts as a controller to call the correct functions throughout the starting process.
47
+ */
48
+ protected _tryStartTunnel(widgetConfig: GatewayWidgetConfig): void;
49
+ /**
50
+ * Internal function that starts the tunnel by communicating with the Manager API.
51
+ */
52
+ protected _startTunnel(info: GatewayTunnelInfo): Promise<GatewayTunnelInfo | undefined>;
53
+ /**
54
+ * Internal function that requests the Manager API for the active tunnel based on the {@link GatewayTunnelInfo} parameter.
55
+ * Returns `undefined` if no tunnel could be found on the Manager instance.
56
+ */
57
+ protected _getActiveTunnel(info: GatewayTunnelInfo): Promise<GatewayTunnelInfo | undefined>;
58
+ /**
59
+ * Function that tries to destroy the currently active tunnel.
60
+ */
61
+ protected _tryStopTunnel(config: GatewayWidgetConfig): void;
62
+ /**
63
+ * Internal function that requests the Manager API to destroy a tunnel that is in line with the {@link GatewayTunnelInfo} parameter.
64
+ */
65
+ protected _stopTunnel(info: GatewayTunnelInfo): Promise<void>;
66
+ /**
67
+ * Function that navigates the user to an HTTP web page that interacts with a service through the tunnel.
68
+ * It will open in a new browser tab automatically.
69
+ */
70
+ protected _navigateToTunnel(info: GatewayTunnelInfo): void;
71
+ /**
72
+ * Internal function to get the tunnel address based on {@link GatewayTunnelInfo}
73
+ */
74
+ protected _getTunnelAddress(info: GatewayTunnelInfo): string | undefined;
75
+ /**
76
+ * Internal function to check whether the {@link GatewayWidgetConfig} includes the necessary information to control the tunnel.
77
+ * Uses several `undefined` or empty checks. Useful for checking the object before interacting with Manager APIs.
78
+ */
79
+ protected _isConfigComplete(widgetConfig: GatewayWidgetConfig): boolean;
80
+ /**
81
+ * Internal function that parses a {@link GatewayWidgetConfig} into a new {@link GatewayTunnelInfo}.
82
+ */
83
+ protected _getTunnelInfoByConfig(config: GatewayWidgetConfig): GatewayTunnelInfo;
84
+ }
@@ -0,0 +1,47 @@
1
+ var GatewayWidget_1,__decorate=this&&this.__decorate||function(t,e,n,o){var i,a=arguments.length,r=a<3?e:null===o?o=Object.getOwnPropertyDescriptor(e,n):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,e,n,o);else for(var s=t.length-1;s>=0;s--)(i=t[s])&&(r=(a<3?i(r):a>3?i(e,n,r):i(e,n))||r);return a>3&&r&&Object.defineProperty(e,n,r),r},__awaiter=this&&this.__awaiter||function(t,e,n,o){return new(n||(n=Promise))((function(i,a){function r(t){try{l(o.next(t))}catch(t){a(t)}}function s(t){try{l(o.throw(t))}catch(t){a(t)}}function l(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(r,s)}l((o=o.apply(t,e||[])).next())}))};import{customElement,state}from"lit/decorators.js";import{OrWidget}from"../util/or-widget";import{css,html}from"lit";import{GatewaySettings}from"../settings/gateway-settings";import{InputType}from"@openremote/or-mwc-components/or-mwc-input";import manager from"@openremote/core";import{when}from"lit/directives/when.js";import{i18next}from"@openremote/or-translate";import{showSnackbar}from"@openremote/or-mwc-components/or-mwc-snackbar";const styling=css`
2
+ #gateway-widget-wrapper {
3
+ height: 100%;
4
+ display: flex;
5
+ justify-content: center;
6
+ align-items: center;
7
+ overflow: hidden;
8
+ }
9
+
10
+ #gateway-widget-container {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ gap: 8px;
15
+ max-height: 48px;
16
+ flex-wrap: wrap;
17
+ position: relative;
18
+ }
19
+ `;function getDefaultWidgetConfig(){return{type:"HTTPS",target:"localhost",targetPort:443}}let GatewayWidget=GatewayWidget_1=class extends OrWidget{constructor(){super(...arguments),this._loading=!0,this._startedByUser=!1}static getManifest(){return{displayName:"Gateway",displayIcon:"lan-connect",minColumnWidth:1,minColumnHeight:1,getContentHtml:t=>new GatewayWidget_1(t),getSettingsHtml:t=>new GatewaySettings(t),getDefaultConfig:()=>getDefaultWidgetConfig()}}refreshContent(t){this.widgetConfig=JSON.parse(JSON.stringify(this.widgetConfig))}static get styles(){return[...super.styles,styling]}disconnectedCallback(){this._activeTunnel&&(this._startedByUser?this._stopTunnel(this._activeTunnel).then((()=>{console.warn("Stopped the active tunnel, as it was created through the widget.")})):console.warn("Keeping the active tunnel open, as it is not started through the widget.")),super.disconnectedCallback()}firstUpdated(t){this.widgetConfig&&setTimeout((()=>{const t=this._getTunnelInfoByConfig(this.widgetConfig);this._getActiveTunnel(t).then((t=>{t&&(console.log("Existing tunnel found!",t),this._setActiveTunnel(t,!0))})).catch((t=>{console.error(t)})).finally((()=>{this._loading=!1}))}),500)}render(){var t;const e=(null===(t=this.getEditMode)||void 0===t?void 0:t.call(this))||!this._isConfigComplete(this.widgetConfig);return html`
20
+ <div id="gateway-widget-wrapper">
21
+ <div id="gateway-widget-container">
22
+ ${when(this._loading,(()=>html`
23
+ <or-loading-indicator></or-loading-indicator>
24
+ `),(()=>this._activeTunnel?html`
25
+
26
+ <or-mwc-input .type="${InputType.BUTTON}" icon="stop" label="${i18next.t("gatewayTunnels.stop")}" .disabled="${e}"
27
+ @or-mwc-input-changed="${t=>this._onStopTunnelClick(t)}"
28
+ ></or-mwc-input>
29
+
30
+ ${when("TCP"===this.widgetConfig.type,(()=>html`
31
+ <or-mwc-input .type="${InputType.BUTTON}" icon="content-copy" label="${i18next.t("gatewayTunnels.copyAddress")}" outlined .disabled="${e}"
32
+ @or-mwc-input-changed="${t=>this._onCopyTunnelAddressClick(t)}"
33
+ ></or-mwc-input>
34
+ `),(()=>html`
35
+ <or-mwc-input .type="${InputType.BUTTON}" icon="open-in-new" label="${i18next.t("gatewayTunnels.open")}" outlined .disabled="${e}"
36
+ @or-mwc-input-changed="${t=>this._onTunnelNavigateClick(t)}"
37
+ ></or-mwc-input>
38
+ `))}
39
+
40
+ `:html`
41
+ <or-mwc-input .type="${InputType.BUTTON}" label="${i18next.t("gatewayTunnels.start")}" outlined .disabled="${e}"
42
+ @or-mwc-input-changed="${t=>this._onStartTunnelClick(t)}"
43
+ ></or-mwc-input>
44
+ `))}
45
+ </div>
46
+ </div>
47
+ `}_onCopyTunnelAddressClick(t){if(this._isConfigComplete(this.widgetConfig)){const t=this._getTunnelInfoByConfig(this.widgetConfig),e=this._getTunnelAddress(t);e?navigator.clipboard.writeText(e).finally((()=>{showSnackbar(void 0,i18next.t("gatewayTunnels.copySuccess"))})):(console.warn("Could not copy tunnel address as it could not be found."),showSnackbar(void 0,i18next.t("errorOccurred")))}else console.warn("Could not copy tunnel address as configuration is not complete."),showSnackbar(void 0,i18next.t("errorOccurred"))}_onStartTunnelClick(t){this._tryStartTunnel(this.widgetConfig)}_onStopTunnelClick(t){this._tryStopTunnel(this.widgetConfig)}_onTunnelNavigateClick(t){this._isConfigComplete(this.widgetConfig)?this._navigateToTunnel(this._getTunnelInfoByConfig(this.widgetConfig)):console.warn("Could not navigate to tunnel as configuration is not complete.")}_setActiveTunnel(t,e=!1){this._activeTunnel=t,t&&!e&&this._navigateToTunnel(t)}_tryStartTunnel(t){if(this._isConfigComplete(t)){const e=this._getTunnelInfoByConfig(t);this._loading=!0,this._getActiveTunnel(e).then((t=>{t?(console.log("Found an active tunnel!",t),this._setActiveTunnel(t),this._loading=!1):this._startTunnel(e).then((t=>{t?(console.log("Started a new tunnel!",t),this._setActiveTunnel(t),this._startedByUser=!0):console.warn("No new tunnel!")})).catch((t=>{console.error(t),showSnackbar(void 0,i18next.t("errorOccurred"))})).finally((()=>{this._loading=!1}))})).catch((t=>{console.error(t),showSnackbar(void 0,i18next.t("errorOccurred")),this._loading=!1}))}}_startTunnel(t){return __awaiter(this,void 0,void 0,(function*(){if(!(t.realm&&t.gatewayId&&t.target&&t.targetPort))throw new Error("Could not start tunnel, as some provided information was not set.");return(yield manager.rest.api.GatewayServiceResource.startTunnel(t)).data}))}_getActiveTunnel(t){return __awaiter(this,void 0,void 0,(function*(){if(!(t.realm&&t.gatewayId&&t.target&&t.targetPort))throw new Error("Could not get active tunnel, as some provided information was not set.");const e=yield manager.rest.api.GatewayServiceResource.getActiveTunnelInfo(t.realm,t.gatewayId,t.target,t.targetPort);return 204===e.status?void 0:e.data}))}_tryStopTunnel(t){const e=this._getTunnelInfoByConfig(t);this._loading=!0,this._stopTunnel(e).then((()=>{this._setActiveTunnel(void 0),this._startedByUser=!1})).catch((t=>{console.warn(t)})).finally((()=>{this._loading=!1}))}_stopTunnel(t){return __awaiter(this,void 0,void 0,(function*(){return(yield manager.rest.api.GatewayServiceResource.stopTunnel(t)).data}))}_navigateToTunnel(t){var e;t.realm&&t.gatewayId&&t.target&&t.targetPort||console.warn("Could not navigate to tunnel, as some provided information was not set.");const n=this._getTunnelAddress(t);switch(t.type){case"HTTPS":case"HTTP":null===(e=window.open(n))||void 0===e||e.focus();break;default:console.error("Unknown error when navigating to tunnel.")}}_getTunnelAddress(t){switch(t.type){case"HTTPS":case"HTTP":return"//"+t.id+"."+window.location.host;case"TCP":return t.id+"."+window.location.hostname+":"+t.assignedPort}}_isConfigComplete(t){return t.gatewayId?t.type?t.target?!!t.targetPort||(console.error("Could not start tunnel, since no gateway target port was configured."),!1):(console.error("Could not start tunnel, since no gateway target was configured."),!1):(console.error("Could not start tunnel, since no gateway type / protocol was configured."),!1):(console.error("Could not start tunnel, since no gateway asset was configured."),!1)}_getTunnelInfoByConfig(t){return{realm:manager.displayRealm,gatewayId:t.gatewayId,type:t.type,target:t.target,targetPort:t.targetPort}}};__decorate([state()],GatewayWidget.prototype,"_loading",void 0),__decorate([state()],GatewayWidget.prototype,"_activeTunnel",void 0),GatewayWidget=GatewayWidget_1=__decorate([customElement("gateway-widget")],GatewayWidget);export{GatewayWidget};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-widget.js","sourceRoot":"","sources":["../../src/widgets/gateway-widget.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openremote/or-dashboard-builder",
3
- "version": "1.2.0-snapshot.20240616202404",
3
+ "version": "1.2.0-snapshot.20240819101332",
4
4
  "description": "OpenRemote Dashboard Builder",
5
5
  "main": "dist/umd/index.bundle.js",
6
6
  "module": "lib/index.js",
@@ -16,15 +16,15 @@
16
16
  "author": "OpenRemote",
17
17
  "license": "AGPL-3.0-or-later",
18
18
  "dependencies": {
19
- "@openremote/core": "1.2.0-snapshot.20240616202404",
20
- "@openremote/model": "1.2.0-snapshot.20240616202404",
21
- "@openremote/or-chart": "1.2.0-snapshot.20240616202404",
22
- "@openremote/rest": "1.2.0-snapshot.20240616202404",
19
+ "@openremote/core": "1.2.0-snapshot.20240819101332",
20
+ "@openremote/model": "1.2.0-snapshot.20240819101332",
21
+ "@openremote/or-chart": "1.2.0-snapshot.20240819101332",
22
+ "@openremote/rest": "1.2.0-snapshot.20240819101332",
23
23
  "gridstack": "^7.2.0",
24
24
  "lit": "^2.0.2"
25
25
  },
26
26
  "devDependencies": {
27
- "@openremote/util": "1.2.0-snapshot.20240616202404"
27
+ "@openremote/util": "1.2.0-snapshot.20240819101332"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"
package/src/index.ts CHANGED
@@ -30,6 +30,7 @@ import {KpiWidget} from "./widgets/kpi-widget";
30
30
  import {MapWidget} from "./widgets/map-widget";
31
31
  import {AttributeInputWidget} from "./widgets/attribute-input-widget";
32
32
  import {TableWidget} from "./widgets/table-widget";
33
+ import {GatewayWidget} from "./widgets/gateway-widget";
33
34
 
34
35
  // language=CSS
35
36
  const styling = css`
@@ -216,6 +217,7 @@ export function registerWidgetTypes() {
216
217
  widgetTypes.set("map", MapWidget.getManifest());
217
218
  widgetTypes.set("attributeinput", AttributeInputWidget.getManifest());
218
219
  widgetTypes.set("table", TableWidget.getManifest());
220
+ widgetTypes.set("gateway", GatewayWidget.getManifest());
219
221
  }
220
222
 
221
223
  @customElement("or-dashboard-builder")
@@ -327,9 +329,28 @@ export class OrDashboardBuilder extends LitElement {
327
329
 
328
330
  // On any update (except widget selection), check whether hasChanged should be updated.
329
331
  if(!(changedProps.size === 1 && changedProps.has('selectedWidget'))) {
330
- const dashboardEqual = Util.objectsEqual(this.selectedDashboard, this.initialDashboardJSON ? JSON.parse(this.initialDashboardJSON) : undefined);
331
- const templateEqual = Util.objectsEqual(this.currentTemplate, this.initialTemplateJSON ? JSON.parse(this.initialTemplateJSON) : undefined);
332
- this.hasChanged = (!dashboardEqual || !templateEqual);
332
+
333
+ // Check for dashboard changes
334
+ const initialDashboard = this.initialDashboardJSON ? JSON.parse(this.initialDashboardJSON) : undefined;
335
+ const dashboardDiff = Util.difference(this.selectedDashboard, initialDashboard);
336
+ if(Object.keys(dashboardDiff).length > 0) {
337
+ this.hasChanged = true;
338
+ console.debug("Dashboard has detected changes! Diff:", dashboardDiff);
339
+ }
340
+
341
+ // Check for template changes
342
+ else {
343
+ const initialTemplate = this.initialTemplateJSON ? JSON.parse(this.initialTemplateJSON) : undefined;
344
+ const templateDiff = Util.difference(this.currentTemplate, initialTemplate);
345
+ if(Object.keys(templateDiff).length > 0) {
346
+ this.hasChanged = true;
347
+ console.debug("Template has detected changes! Diff:", templateDiff);
348
+ } else {
349
+
350
+ // If no changes detected between dashboard nor template
351
+ this.hasChanged = false;
352
+ }
353
+ }
333
354
  }
334
355
 
335
356
  // Support for realm switching
@@ -455,12 +476,16 @@ export class OrDashboardBuilder extends LitElement {
455
476
 
456
477
  selectDashboard(dashboard: Dashboard | undefined) {
457
478
  if(this.dashboards != null) {
479
+
480
+ // If switching between dashboards, check if there were changes, and reset to 'initialDashboardJSON' if needed.
458
481
  if(this.selectedDashboard && this.initialDashboardJSON) {
459
- const indexOf = this.dashboards.indexOf(this.selectedDashboard);
460
- if(indexOf) {
461
- this.dashboards[indexOf] = JSON.parse(this.initialDashboardJSON) as Dashboard;
482
+ const index = this.dashboards.indexOf(this.selectedDashboard);
483
+ if(index && JSON.stringify(this.selectedDashboard) !== this.initialDashboardJSON) {
484
+ this.dashboards[index] = JSON.parse(this.initialDashboardJSON) as Dashboard;
462
485
  }
463
486
  }
487
+
488
+ // Update selected dashboard
464
489
  this.selectedDashboard = (dashboard ? this.dashboards.find((x) => { return x.id == dashboard.id; }) : undefined);
465
490
  this.initialDashboardJSON = JSON.stringify(this.selectedDashboard);
466
491
  this.initialTemplateJSON = JSON.stringify(this.selectedDashboard?.template);
@@ -606,8 +631,8 @@ export class OrDashboardBuilder extends LitElement {
606
631
  <div id="fullscreen-header">
607
632
  <div id="fullscreen-header-wrapper">
608
633
  <div id="fullscreen-header-title" style="display: flex; align-items: center;">
609
- <or-icon class="showMobile" style="margin-right: 10px;" icon="chevron-left" @click="${() => { this.selectedDashboard = undefined; }}"></or-icon>
610
- <or-icon class="hideMobile" style="margin-right: 10px;" icon="menu" @click="${() => { this.showDashboardTree = !this.showDashboardTree; }}"></or-icon>
634
+ <or-icon class="showMobile" style="margin-right: 10px; cursor: pointer;" icon="chevron-left" @click="${() => { this.selectedDashboard = undefined; }}"></or-icon>
635
+ <or-icon class="hideMobile" style="margin-right: 10px; cursor: pointer;" icon="menu" @click="${() => { this.showDashboardTree = !this.showDashboardTree; }}"></or-icon>
611
636
  <span>${this.selectedDashboard?.displayName}</span>
612
637
  </div>
613
638
  <div id="fullscreen-header-actions">
@@ -21,6 +21,11 @@ const treeStyling = css`
21
21
  flex-direction: row;
22
22
  padding-right: 5px;
23
23
  }
24
+
25
+ #content {
26
+ flex: 1;
27
+ overflow: hidden auto;
28
+ }
24
29
 
25
30
  .node-container {
26
31
  align-items: center;
@@ -1,4 +1,3 @@
1
- import {i18next} from "@openremote/or-translate";
2
1
  import {html, LitElement, PropertyValues} from "lit";
3
2
  import {customElement, property, query, state} from "lit/decorators.js";
4
3
  import {when} from "lit/directives/when.js";
@@ -8,6 +7,7 @@ import {DashboardWidget} from "@openremote/model";
8
7
  import {OrWidget, WidgetManifest} from "./util/or-widget";
9
8
  import {WidgetService} from "./service/widget-service";
10
9
  import {WidgetConfig} from "./util/widget-config";
10
+ import {Util} from "@openremote/core";
11
11
 
12
12
  /* ------------------------------------ */
13
13
 
@@ -21,6 +21,9 @@ export class OrDashboardWidgetContainer extends LitElement {
21
21
  @property()
22
22
  protected readonly widget!: DashboardWidget;
23
23
 
24
+ @property() // Optional property, that overrides widget.widgetConfig. Useful for view-only mode.
25
+ protected widgetConfig?: WidgetConfig;
26
+
24
27
  @property()
25
28
  protected readonly editMode!: boolean;
26
29
 
@@ -49,7 +52,7 @@ export class OrDashboardWidgetContainer extends LitElement {
49
52
  this.resizeObserver?.disconnect();
50
53
  }
51
54
 
52
- shouldUpdate(changedProps: Map<PropertyKey, unknown>): boolean {
55
+ shouldUpdate(changedProps: PropertyValues): boolean {
53
56
  const changed = changedProps;
54
57
 
55
58
  // Update config if some values in the spec are not set.
@@ -57,7 +60,11 @@ export class OrDashboardWidgetContainer extends LitElement {
57
60
  if (this.widget) {
58
61
  const manifest = WidgetService.getManifest(this.widget.widgetTypeId!);
59
62
  if (manifest) {
60
- this.widget.widgetConfig = WidgetService.correctToConfigSpec(manifest, this.widget.widgetConfig);
63
+ if(this.editMode) {
64
+ this.widget.widgetConfig = WidgetService.correctToConfigSpec(manifest, this.widget.widgetConfig);
65
+ } else {
66
+ this.widgetConfig = WidgetService.correctToConfigSpec(manifest, this.widget.widgetConfig);
67
+ }
61
68
  }
62
69
  }
63
70
 
@@ -67,7 +74,7 @@ export class OrDashboardWidgetContainer extends LitElement {
67
74
  const oldVal = changedProps.get('widget') as DashboardWidget | undefined;
68
75
  const idChanged = oldVal?.id !== this.widget?.id;
69
76
  const nameChanged = oldVal?.displayName !== this.widget?.displayName;
70
- const configChanged = JSON.stringify(oldVal?.widgetConfig) !== JSON.stringify(this.widget?.widgetConfig);
77
+ const configChanged = !Util.objectsEqual(oldVal?.widgetConfig, this.getWidgetConfig());
71
78
  if (!(idChanged || nameChanged || configChanged)) {
72
79
  changed.delete('widget');
73
80
  }
@@ -76,7 +83,7 @@ export class OrDashboardWidgetContainer extends LitElement {
76
83
  return (changed.size === 0 ? false : super.shouldUpdate(changedProps));
77
84
  }
78
85
 
79
- willUpdate(changedProps: Map<string, any>) {
86
+ willUpdate(changedProps: PropertyValues) {
80
87
  super.willUpdate(changedProps);
81
88
 
82
89
  if (!this.manifest && this.widget) {
@@ -85,7 +92,7 @@ export class OrDashboardWidgetContainer extends LitElement {
85
92
 
86
93
  // Create widget
87
94
  if (changedProps.has("widget") && this.widget) {
88
- this.initializeWidgetElem(this.manifest!, this.widget.widgetConfig);
95
+ this.initializeWidgetElem(this.manifest!, this.getWidgetConfig()!);
89
96
  }
90
97
  }
91
98
 
@@ -152,4 +159,8 @@ export class OrDashboardWidgetContainer extends LitElement {
152
159
  public refreshContent(force: boolean) {
153
160
  this.orWidget?.refreshContent(force);
154
161
  }
162
+
163
+ public getWidgetConfig(): WidgetConfig | undefined {
164
+ return this.widgetConfig || this.widget?.widgetConfig;
165
+ }
155
166
  }
@@ -42,7 +42,13 @@ export class WidgetService {
42
42
 
43
43
  // Method used to correct the OrWidgetConfig specification
44
44
  // So, if certain fields are removed or invalid, it will be corrected by merging the object with the default OrWidgetConfig.
45
+ // Will only return a different object if it actually changes. This prevents child objects being returned in a different order.
45
46
  public static correctToConfigSpec(manifest: WidgetManifest, widgetConfig: WidgetConfig) {
46
- return Util.mergeObjects(manifest.getDefaultConfig(), widgetConfig, false) as WidgetConfig;
47
+ const newConfig = Util.mergeObjects(manifest.getDefaultConfig(), widgetConfig, false) as WidgetConfig;
48
+ if(Util.objectsEqual(newConfig, widgetConfig)) {
49
+ return widgetConfig;
50
+ } else {
51
+ return newConfig;
52
+ }
47
53
  }
48
54
  }
@@ -0,0 +1,144 @@
1
+ import { customElement } from "lit/decorators.js";
2
+ import {WidgetSettings} from "../util/widget-settings";
3
+ import {TemplateResult, html} from "lit";
4
+ import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
5
+ import {i18next} from "@openremote/or-translate";
6
+ import {Asset, AssetQuery, GatewayTunnelInfoType} from "@openremote/model";
7
+ import {GatewayWidgetConfig} from "../widgets/gateway-widget";
8
+ import {Task} from "@lit/task";
9
+ import manager from "@openremote/core";
10
+
11
+ @customElement("gateway-settings")
12
+ export class GatewaySettings extends WidgetSettings {
13
+
14
+ protected GATEWAY_ASSET_TYPES: string[] = ["GatewayAsset"];
15
+ protected GATEWAY_TUNNEL_TYPES: GatewayTunnelInfoType[] = [GatewayTunnelInfoType.HTTPS, GatewayTunnelInfoType.HTTP, GatewayTunnelInfoType.TCP];
16
+
17
+ // Override of widgetConfig with extended type
18
+ protected readonly widgetConfig!: GatewayWidgetConfig;
19
+
20
+ connectedCallback() {
21
+ super.connectedCallback();
22
+ }
23
+
24
+ protected render(): TemplateResult {
25
+ const disabled = !this.widgetConfig.gatewayId;
26
+ return html`
27
+ <div>
28
+ <settings-panel displayName="gatewayTunnels.settings" expanded="${true}">
29
+ <div style="display: flex; flex-direction: column; gap: 8px;">
30
+ <div>
31
+ ${this._fetchAssetsTask.render({
32
+ pending: () => html`
33
+ <or-mwc-input .type="${InputType.SELECT}" compact style="width: 100%;" disabled
34
+ label="${i18next.t('gatewayTunnels.selectAsset')}"
35
+ ></or-mwc-input>
36
+ `,
37
+ complete: (gatewayAssets) => {
38
+ const options: [string, string][] = gatewayAssets.map(a => [a.id, a.name]);
39
+ const selected = gatewayAssets.find(a => a.id === this.widgetConfig.gatewayId);
40
+ const value = selected ? [selected.id, selected.name] as [string, string] : undefined;
41
+ return html`
42
+ <or-mwc-input .type="${InputType.SELECT}" compact style="width: 100%;"
43
+ .options="${options}" .value="${value}"
44
+ label="${i18next.t('gatewayTunnels.selectAsset')}"
45
+ @or-mwc-input-changed="${(ev: OrInputChangedEvent) => this._onAssetSelect(ev)}"
46
+ ></or-mwc-input>
47
+ `;
48
+ },
49
+ error: () => html`
50
+ <or-mwc-input .type="${InputType.TEXT}" compact style="width: 100%;" disabled
51
+ label="${i18next.t('gatewayTunnels.selectAsset')}" .value="${i18next.t('errorOccurred')}"
52
+ ></or-mwc-input>
53
+ `
54
+ })}
55
+ </div>
56
+ <div>
57
+ <or-mwc-input .type="${InputType.SELECT}" compact style="width: 100%;"
58
+ .disabled="${disabled}" .options="${this.GATEWAY_TUNNEL_TYPES}" .value="${this.widgetConfig.type}"
59
+ label="${i18next.t('gatewayTunnels.protocol')}"
60
+ @or-mwc-input-changed="${(ev: OrInputChangedEvent) => this._onTunnelTypeSelect(ev)}"
61
+ ></or-mwc-input>
62
+ </div>
63
+ <div>
64
+ <or-mwc-input .type="${InputType.TEXT}" style="width: 100%;"
65
+ .disabled="${disabled}" .value="${this.widgetConfig.target}"
66
+ label="${i18next.t('gatewayTunnels.target')}"
67
+ @or-mwc-input-changed="${(ev: OrInputChangedEvent) => this._onTunnelTargetSelect(ev)}"
68
+ ></or-mwc-input>
69
+ </div>
70
+ <div>
71
+ <or-mwc-input .type="${InputType.NUMBER}" style="width: 100%;"
72
+ .disabled="${disabled}" .value="${this.widgetConfig.targetPort}"
73
+ label="${i18next.t('gatewayTunnels.targetPort')}"
74
+ @or-mwc-input-changed="${(ev: OrInputChangedEvent) => this._onTunnelTargetPortSelect(ev)}"
75
+ ></or-mwc-input>
76
+ </div>
77
+ </div>
78
+ </settings-panel>
79
+ </div>
80
+ `;
81
+ }
82
+
83
+ protected _onAssetSelect(ev: OrInputChangedEvent): void {
84
+ this.widgetConfig.gatewayId = ev.detail.value;
85
+ this.notifyConfigUpdate();
86
+ }
87
+
88
+ protected _onTunnelTypeSelect(ev: OrInputChangedEvent) {
89
+ this.widgetConfig.type = ev.detail.value;
90
+ this.notifyConfigUpdate();
91
+ }
92
+
93
+ protected _onTunnelTargetSelect(ev: OrInputChangedEvent) {
94
+ this.widgetConfig.target = ev.detail.value;
95
+ this.notifyConfigUpdate();
96
+ }
97
+
98
+ protected _onTunnelTargetPortSelect(ev: OrInputChangedEvent) {
99
+ this.widgetConfig.targetPort = ev.detail.value;
100
+ this.notifyConfigUpdate();
101
+ }
102
+
103
+
104
+ protected _fetchAssetsTask = new Task(this, {
105
+ task: async ([], {signal}) => {
106
+ return await this._fetchAssets(true, signal);
107
+ },
108
+ args: () => []
109
+ });
110
+
111
+ protected async _fetchAssets(requireTunnelSupport = true, signal?: AbortSignal): Promise<Asset[]> {
112
+ const query = {
113
+ realm: {name: manager.displayRealm},
114
+ select: {attributes: ["tunnelingSupported"]},
115
+ types: this.GATEWAY_ASSET_TYPES,
116
+ attributes: requireTunnelSupport ? {
117
+ items: [
118
+ {
119
+ name: {
120
+ predicateType: "string",
121
+ value: "tunnelingSupported"
122
+ },
123
+ value: {
124
+ predicateType: "boolean",
125
+ value: true
126
+ },
127
+ },
128
+ {
129
+ name: {
130
+ predicateType: "string",
131
+ value: "gatewayStatus"
132
+ },
133
+ value: {
134
+ predicateType: "string",
135
+ value: "CONNECTED"
136
+ },
137
+ }
138
+ ]
139
+ } : undefined
140
+ } as AssetQuery;
141
+ return (await manager.rest.api.AssetResource.queryAssets(query)).data;
142
+ }
143
+
144
+ }
@@ -7,7 +7,6 @@ import {AttributesSelectEvent} from "../panels/attributes-panel";
7
7
  import {Attribute} from "@openremote/model";
8
8
  import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
9
9
  import {ThresholdChangeEvent} from "../panels/thresholds-panel";
10
- import "../panels/thresholds-panel";
11
10
 
12
11
  @customElement("gauge-settings")
13
12
  export class GaugeSettings extends WidgetSettings {
@@ -72,10 +71,27 @@ export class GaugeSettings extends WidgetSettings {
72
71
 
73
72
  protected onMinMaxValueChange(type: 'min' | 'max', ev: OrInputChangedEvent) {
74
73
  switch (type) {
75
- case "max":
76
- this.widgetConfig.max = ev.detail.value; break;
77
- case "min":
78
- this.widgetConfig.min = ev.detail.value; break;
74
+ case "max": {
75
+ this.widgetConfig.max = ev.detail.value;
76
+
77
+ // Make sure every threshold value is not higher than what is allowed.
78
+ const sortedThresholds = this.widgetConfig.thresholds.sort((x, y) => y[0] - x[0]);
79
+ sortedThresholds.forEach((t, index) => {
80
+ t[0] = Math.max(Math.min(t[0], (ev.detail.value - index - 1)), this.widgetConfig.min);
81
+ });
82
+ break;
83
+ }
84
+ case "min": {
85
+ this.widgetConfig.min = ev.detail.value;
86
+
87
+ // Update the lowest (locked) threshold value to minimum value, and make sure every threshold value is not lower than what is allowed.
88
+ const sortedThresholds = this.widgetConfig.thresholds.sort((x, y) => (x[0] < y[0]) ? -1 : 1);
89
+ sortedThresholds[0][0] = ev.detail.value;
90
+ sortedThresholds.forEach((t, index) => {
91
+ t[0] = Math.min(Math.max(t[0], (ev.detail.value + index)), this.widgetConfig.max);
92
+ })
93
+ break;
94
+ }
79
95
  }
80
96
  this.notifyConfigUpdate();
81
97
  }
@@ -53,7 +53,7 @@ export class ImageSettings extends AssetWidgetSettings {
53
53
  return html`
54
54
  <div>
55
55
  <!-- Attributes selector -->
56
- <settings-panel displayName="'attributes" expanded="${true}">
56
+ <settings-panel displayName="attributes" expanded="${true}">
57
57
  <attributes-panel .attributeRefs="${this.widgetConfig.attributeRefs}" onlyDataAttrs="${false}" multi="${true}"
58
58
  @attribute-select="${(ev: AttributesSelectEvent) => this.onAttributesSelect(ev)}"
59
59
  ></attributes-panel>