@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.
- package/dist/umd/index.js +593 -497
- package/dist/umd/index.orbundle.js +294 -195
- package/dist/umd/index.orbundle.js.LICENSE.txt +2 -2
- package/lib/index.js +4 -4
- package/lib/index.js.map +1 -1
- package/lib/or-dashboard-tree.js +7 -2
- package/lib/or-dashboard-tree.js.map +1 -1
- package/lib/or-dashboard-widgetcontainer.d.ts +4 -2
- package/lib/or-dashboard-widgetcontainer.js +2 -2
- package/lib/or-dashboard-widgetcontainer.js.map +1 -1
- package/lib/service/widget-service.js +1 -1
- package/lib/service/widget-service.js.map +1 -1
- package/lib/settings/gateway-settings.d.ts +19 -0
- package/lib/settings/gateway-settings.js +46 -0
- package/lib/settings/gateway-settings.js.map +1 -0
- package/lib/settings/gauge-settings.d.ts +0 -1
- package/lib/settings/gauge-settings.js +8 -8
- package/lib/settings/gauge-settings.js.map +1 -1
- package/lib/settings/image-settings.js +1 -1
- package/lib/settings/image-settings.js.map +1 -1
- package/lib/widgets/gateway-widget.d.ts +84 -0
- package/lib/widgets/gateway-widget.js +47 -0
- package/lib/widgets/gateway-widget.js.map +1 -0
- package/package.json +6 -6
- package/src/index.ts +33 -8
- package/src/or-dashboard-tree.ts +5 -0
- package/src/or-dashboard-widgetcontainer.ts +17 -6
- package/src/service/widget-service.ts +7 -1
- package/src/settings/gateway-settings.ts +144 -0
- package/src/settings/gauge-settings.ts +21 -5
- package/src/settings/image-settings.ts +1 -1
- package/src/widgets/gateway-widget.ts +386 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -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.
|
|
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.
|
|
20
|
-
"@openremote/model": "1.2.0-snapshot.
|
|
21
|
-
"@openremote/or-chart": "1.2.0-snapshot.
|
|
22
|
-
"@openremote/rest": "1.2.0-snapshot.
|
|
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.
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
this.
|
|
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
|
|
460
|
-
if(
|
|
461
|
-
this.dashboards[
|
|
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">
|
package/src/or-dashboard-tree.ts
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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.
|
|
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
|
-
|
|
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;
|
|
77
|
-
|
|
78
|
-
|
|
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="
|
|
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>
|