Skip to content

Commit 59145e4

Browse files
authored
feature: Embed external HTML pages (#203)
1 parent e65c315 commit 59145e4

17 files changed

+287
-40
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Signal K Instrument Panel
33
Instrument panel implemented as a grid with draggable & resizable components. Components are added dynamically eg.
44
when the panel receives data it hasn't seen before a new cell is added to the grid.
55

6-
![main-page](public/help/main-page.png)
6+
![instrumentpanel-demo](instrumentpanel-demo.gif)
77

88
Online demo: [http://demo.signalk.org/@signalk/instrumentpanel](http://demo.signalk.org/@signalk/instrumentpanel)
99
InstrumentPanel help: [http://demo.signalk.org/@signalk/instrumentpanel#help](http://demo.signalk.org/@signalk/instrumentpanel#help)

instrumentpanel-demo.gif

2.38 MB
Loading

lib/help/help.md

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ ___
5151
- [2.3.1.5.2 Compass Widget](#2_3_1_5_2)
5252
- [2.3.1.5.3 Windmeter Widget](#2_3_1_5_3)
5353
- [2.3.1.5.4 Digital DateTime Widget](#2_3_1_5_4)
54+
- [2.3.1.5.5 Iframe Widget](#2_3_1_5_5)
5455
- [2.3.1.6 Widget managed paths](#2_3_1_6)
5556
- [2.3.1.7 Alarms Settings](#2_3_1_7)
5657
- [2.3.1.8 Add or Delete Pages](#2_3_1_8)
@@ -87,7 +88,7 @@ The cells are grouped into three columns:
8788
- everything else (electrical, propulsion etc)
8889

8990
>
90-
>![demo](./help/main-page.png#maxwidth)
91+
>![main-page](./help/main-page2.png#maxwidth)
9192
>
9293
<a id="0_help"></a>
9394
When the help page is displayed all buttons are disabled.
@@ -393,7 +394,7 @@ Changes there take effect after server restart.
393394
Using an editor that validates the format is a great help !
394395

395396
For more information on zones see [the Signal K Specification](https://signalk.org/specification/1.5.0/doc/data_model_metadata.html#metadata-for-a-data-value)
396-
and [Server FAQ](https://github.com/SignalK/signalk-server/wiki/FAQ:-Frequently-Asked-Questions#how-to-add-missing-units-in-instrumentpanel)
397+
and [Server FAQ](https://github.com/SignalK/signalk-server/wiki/FAQ:-Frequently-Asked-Questions#how-to-add-missing-units-and-static-data)
397398

398399
<a id="2_3_1_5_2"></a>
399400
**2.3.1.5.2. Compass Widget** [Back to up menu](#2_3_1_5)
@@ -426,6 +427,123 @@ In timezone list, **DST** means Daylight Saving Time and displays time automatic
426427
>![settings-digitaldatetime](./help/widget-settings-digitaldatetime.png#maxwidth)
427428
>
428429
430+
<a id="2_3_1_5_5"></a>
431+
**2.3.1.5.5. Iframe Widget:** [Back to up menu](#2_3_1_5)
432+
___
433+
This widget can embed an external HTML page like Grafana, Signal K Auto-pilot,...
434+
>
435+
>![widget-iframe-double](./help/widget-iframe-double.png#maxwidth)
436+
>
437+
The configuration is manual and must be done only once on the Signal K server.
438+
- Edit the `~/.signalk/baseDeltas.json` file on your Signal K server.
439+
You can also look at this
440+
[Server FAQ](https://github.com/SignalK/signalk-server/wiki/FAQ:-Frequently-Asked-Questions#if-you-have-a-signalkbasedeltasjson-file)
441+
on the format of this file.
442+
- Locate the `updates/value` section:
443+
```
444+
[
445+
{
446+
"context": "vessels.self",
447+
"updates": [
448+
{
449+
"values": [
450+
```
451+
- Insert the bloc below to set the URL parameters
452+
```
453+
{
454+
"path": "external.iframe.XXXXX",
455+
"value": {
456+
"src": "",
457+
"themeKey": "", // optional
458+
"darkValue": "", // optional
459+
"lightValue": "" // optional
460+
}
461+
}
462+
```
463+
- Fill the value with the data of the HTML page you want to expose in the widget.
464+
See the "Grafana example" below for details of the optional parameters and check the help
465+
of the software that produces the URL you want to embed in the widget
466+
to see if it supports dark mode via the URL parameters.
467+
- Locate the `updates/meta` section
468+
```
469+
{
470+
"context": "vessels.self",
471+
"updates": [
472+
{
473+
"meta": [
474+
```
475+
- Insert the block below to set the display name
476+
```
477+
{
478+
"path": "external.iframe.XXXXX",
479+
"value": {
480+
"displayName": "my external page"
481+
}
482+
}
483+
```
484+
- Fill the path value with the same value as in the `updates/value` section used above
485+
- Fill the displayName key with a value of your choice
486+
- Double check your json file with a json validator (if something wrong in your json file, your server will exclude the entire file)
487+
- Restart your Signal K server
488+
- Launch InstrumentPanel
489+
- Go in settings mode ![settings](./help/settings-icon.png)
490+
- Locate the new widget and select `Show on grid`
491+
>
492+
>![settings-iframe](./help/widget-settings-iframe.png#maxwidth)
493+
>
494+
- Return in main view ![view](./help/view-icon.png)
495+
- Unlock the grid ![lock](./help/button-lock.png) to adjust the widget size and position
496+
497+
**Grafana example:**
498+
- Edit you **grafana.ini**
499+
- Enable embedding query by `allow_embedding = true`
500+
- Optionally enable anonymous mode in section `[auth.anonymous]` to avoid authentication in the widget
501+
- Restart your Grafana server
502+
- Generate in Grafana GUI the iframe URL to embed in widget
503+
>
504+
>![grafana-share](./help/grafana-share.png#maxwidth)
505+
>
506+
>
507+
>![grafana-embed](./help/grafana-embed.png#maxwidth)
508+
>
509+
- Fill the src key with value generated in Grafana GUI, keep only src value **without** iframe, width, height, frameborder.
510+
- To follow the dark mode fill the key themeKey, darkValue and lightValue to inform Grafana when InstrumentPanel switch in dark mode.
511+
Like this, the URL will be updated by adding at the end `&theme=dark` for dark mode and `&theme=light` for light mode.
512+
```
513+
{
514+
"path": "external.iframe.grafana",
515+
"value": {
516+
"src": "http://barco.local:4000/d-solo/7v5TG3Wnk/demo-dashboard?orgId=1&refresh=10s&panelId=2",
517+
"themeKey": "theme",
518+
"darkValue": "dark",
519+
"lightValue": "light"
520+
}
521+
}
522+
```
523+
- The result in the Iframe widget
524+
>
525+
>![widget-iframe](./help/widget-iframe.png#maxwidth)
526+
>
527+
528+
**Signal K auto-pilot example:**
529+
```
530+
updates/value section
531+
{
532+
"path": "external.iframe.autopilot",
533+
"value": {
534+
"src": "http://my-signalk.local:3000/@signalk/signalk-autopilot"
535+
}
536+
}
537+
...
538+
updates/meta section
539+
{
540+
"path": "external.iframe.autopilot",
541+
"value": {
542+
"displayName": "autopilot"
543+
}
544+
}
545+
```
546+
429547
<a id="2_3_1_6"></a>
430548
**2.3.1.6. Widget managed paths:** [Back to up menu](#2_3_1)
431549
___

lib/streambundle.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ import Debug from 'debug';
2323
const debug = Debug('instrumentpanel:streambundle')
2424
import {Bus} from 'baconjs';
2525

26+
import {
27+
CS_BY_SKPATH
28+
} from './ui/settings/constants'
29+
2630
function getSourceId(source) {
2731
if (!source) {
2832
return 'no_source';
@@ -70,7 +74,7 @@ StreamBundle.prototype.handleDelta = function(delta, instrumentPanel) {
7074
that.push(sourceId, pathValue);
7175
}
7276
try {
73-
if ((pathValue.path === 'environment.mode') && instrumentPanel.isColorSchemeSetBySKPATH()) {
77+
if ((pathValue.path === 'environment.mode') && instrumentPanel.colorSchemeSetBy === CS_BY_SKPATH) {
7478
const nightOn = pathValue.value.toString() === 'night';
7579
if (instrumentPanel.getDarkMode() !== nightOn) {
7680
instrumentPanel.setDarkMode(! instrumentPanel.getDarkMode());

lib/ui/instrumentpanel.js

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import Digitaldatetime, {getIndexFromDateFormatLabel, getDateFormatLabels} from
3333
import Attitude from './widgets/attitude';
3434
import Notification from './widgets/notification';
3535
import Specifictext from './widgets/specifictext';
36+
import Iframe from './widgets/iframe';
3637
import {
3738
defaultLayoutName,
3839
CS_LIGHT,
@@ -60,7 +61,8 @@ var widgetModules = [
6061
Digitaldatetime,
6162
Attitude,
6263
Notification,
63-
Specifictext
64+
Specifictext,
65+
Iframe
6466
];
6567

6668
var defaultNotificationColors = {
@@ -131,10 +133,7 @@ export default function InstrumentPanel(streamBundle) {
131133
this.preferredUnits = {};
132134
this.layoutName = defaultLayoutName;
133135
this.colorSchemeTool = null;
134-
this.colorScheme = {
135-
colorSchemeCurrent: CS_LIGHT,
136-
colorSchemeSetBy: CS_BY_MAINBAR
137-
}
136+
this.colorSchemeSetBy = CS_BY_MAINBAR;
138137
this.widgetActiveMode = WA_BASE_DATA;
139138
this.signalkServer; // undefined if no signalkServer in URL
140139
this.loginStatus = {};
@@ -714,32 +713,15 @@ InstrumentPanel.prototype.getPages = function() {
714713
}
715714

716715
InstrumentPanel.prototype.getDarkMode = function() {
717-
return (this.colorScheme.colorSchemeCurrent === CS_DARK);
716+
return (this.colorSchemeTool.scheme === CS_DARK);
718717
}
719718

720719
InstrumentPanel.prototype.setDarkMode = function(darkModeOn) {
721720
this.colorSchemeTool.scheme = (darkModeOn) ? CS_DARK : CS_LIGHT;
722-
this.colorScheme.colorSchemeCurrent = this.colorSchemeTool.scheme;
723-
}
724-
725-
InstrumentPanel.prototype.getColorSchemeCurrent = function() {
726-
return this.colorScheme.colorSchemeCurrent;
727721
}
728722

729723
InstrumentPanel.prototype.setColorSchemeSetBy = function(newColorSchemeSetBy) {
730-
this.colorScheme.colorSchemeSetBy = newColorSchemeSetBy;
731-
}
732-
733-
InstrumentPanel.prototype.setColorSchemeSetByMAINBAR = function() {
734-
this.colorScheme.colorSchemeSetBy = CS_BY_MAINBAR;
735-
}
736-
737-
InstrumentPanel.prototype.isColorSchemeByOS = function() {
738-
return (this.colorScheme.colorSchemeSetBy === CS_BY_OS);
739-
}
740-
741-
InstrumentPanel.prototype.isColorSchemeSetBySKPATH = function() {
742-
return (this.colorScheme.colorSchemeSetBy === CS_BY_SKPATH);
724+
this.colorSchemeSetBy = newColorSchemeSetBy;
743725
}
744726

745727
InstrumentPanel.prototype.getReloadParams = function() {

lib/ui/main.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from '../util/localstorage';
2727
import {
2828
CS_BY_OS,
29+
CS_DARK,
2930
notificationPageId,
3031
backupSettingsKeyName
3132
} from './settings/constants';
@@ -57,6 +58,7 @@ class App extends React.Component {
5758
this.generateClickmeElement = this.generateClickmeElement.bind(this);
5859
this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
5960
this.toggleWidgetFullScreen = this.toggleWidgetFullScreen.bind(this);
61+
instrumentPanel.model = this.props.model;
6062
const initialColorScheme = retrieveColorScheme();
6163
if (initialColorScheme.colorSchemeSetBy !== CS_BY_OS) {
6264
instrumentPanel.colorSchemeTool.removeListener();
@@ -65,6 +67,13 @@ class App extends React.Component {
6567
}
6668
instrumentPanel.colorSchemeTool.scheme = initialColorScheme.colorSchemeCurrent;
6769
instrumentPanel.colorScheme = initialColorScheme;
70+
instrumentPanel.colorSchemeTool.onChange = () => {
71+
instrumentPanel.getActiveWidgets().forEach((widget)=>{
72+
if (widget.reactWidget.props.functions && typeof widget.reactWidget.props.functions.setDarkMode === 'function') {
73+
widget.reactWidget.props.functions.setDarkMode(instrumentPanel.colorSchemeTool.scheme === CS_DARK)
74+
}
75+
});
76+
}
6877
instrumentPanel.version = this.props.version;
6978
this.lastUsedVersion = retrieveVersion();
7079
if (this.lastUsedVersion !== instrumentPanel.version) {

lib/ui/navbar.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
retrieveToken,
2525
} from '../util/localstorage';
2626
import {
27-
notificationPageId
27+
notificationPageId,
28+
CS_BY_MAINBAR
2829
} from './settings/constants'
2930

3031
var buttonClass = "navbar-btn";
@@ -315,9 +316,9 @@ export default class IpNavBar extends React.Component {
315316
this.showIpNavBar(event);
316317
var newColorSchemeToSave = retrieveColorScheme();
317318
this.props.instrumentPanel.setDarkMode(!this.props.instrumentPanel.getDarkMode());
318-
newColorSchemeToSave.colorSchemeCurrent = this.props.instrumentPanel.getColorSchemeCurrent();
319+
newColorSchemeToSave.colorSchemeCurrent = this.props.instrumentPanel.colorSchemeTool.scheme;
319320
storeColorScheme(newColorSchemeToSave);
320-
this.props.instrumentPanel.setColorSchemeSetByMAINBAR();
321+
this.props.instrumentPanel.setColorSchemeSetBy(CS_BY_MAINBAR);
321322
}
322323

323324
toggleLocked(event) {

lib/ui/settings/colorschemesettings.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,20 @@ import {
1212
retrieveColorScheme,
1313
storeColorScheme
1414
} from '../../util/localstorage';
15+
import {
16+
CS_BY_MAINBAR,
17+
CS_BY_OS,
18+
CS_BY_SKPATH,
19+
CS_LIGHT,
20+
CS_DARK,
21+
} from './constants'
1522

1623
const menuSettings = {
1724
mainbar: {title: 'By main bar icon', help: 'The dark mode is set by icon in main bar'},
1825
os: {title: 'By OS settings', help: 'The dark mode follows your OS settings'},
1926
skpath: {title: 'By Signal K Mode', help: 'The dark mode follows the value of Signal K path: /vessels/self/environment/mode'}
2027
}
2128

22-
export const CS_BY_MAINBAR = "mainbar";
23-
export const CS_BY_OS = "os";
24-
export const CS_BY_SKPATH = "skpath";
25-
export const CS_LIGHT = "light";
26-
export const CS_DARK = "dark";
27-
2829
export default class ColorSchemeSettings extends React.Component {
2930
constructor(props) {
3031
super(props);
@@ -35,7 +36,7 @@ export default class ColorSchemeSettings extends React.Component {
3536
handleSelect(selectedKey) {
3637
var newColorSchemeToSave = this.state;
3738
newColorSchemeToSave.colorSchemeSetBy = selectedKey;
38-
newColorSchemeToSave.colorSchemeCurrent = this.props.instrumentPanel.getColorSchemeCurrent();
39+
newColorSchemeToSave.colorSchemeCurrent = this.props.instrumentPanel.colorSchemeTool.scheme;
3940
this.setState({colorSchemeSetBy: selectedKey});
4041
this.props.instrumentPanel.setColorSchemeSetBy(selectedKey);
4142
if (selectedKey === CS_BY_OS) this.props.instrumentPanel.setReloadRequired();

lib/ui/settings/widgetcell.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export default class WidgetCell extends React.Component {
114114
{(this.state.showAllPaths)?this.getAdditionalPathList():null}
115115
</div>
116116
<div className='widget'>
117-
{this.props.widget.getReactElement()}
117+
{this.props.widget.getReactElement()}
118118
</div>
119119
<div className='widgetSettings'>
120120
{this.props.widget.getSettingsElement(this.pushCellChange)}
@@ -131,7 +131,7 @@ export default class WidgetCell extends React.Component {
131131
<Button className='headerEdit' bsSize="xsmall" onClick={() => this.props.changeWidgetInEdit(this.props.widget.id)}>Edit</Button>
132132
</div>
133133
<div className='widget'>
134-
{this.props.widget.getReactElement()}
134+
{this.props.widget.getReactElement()}
135135
</div>
136136
</div>
137137
);

0 commit comments

Comments
 (0)