iobroker.fakeroku 0.2.0 → 0.2.2

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,115 @@
1
+ # This is a composition of lint and test scripts
2
+ # Make sure to update this file along with the others
3
+
4
+ name: Test and Release
5
+
6
+ # Run this job on all pushes and pull requests
7
+ # as well as tags with a semantic version
8
+ on:
9
+ push:
10
+ branches:
11
+ - '*'
12
+ tags:
13
+ # normal versions
14
+ - "v?[0-9]+.[0-9]+.[0-9]+"
15
+ # pre-releases
16
+ - "v?[0-9]+.[0-9]+.[0-9]+-**"
17
+ pull_request: {}
18
+
19
+ jobs:
20
+ # Performs quick checks before the expensive test runs
21
+ check-and-lint:
22
+ if: contains(github.event.head_commit.message, '[skip ci]') == false
23
+
24
+ runs-on: ubuntu-latest
25
+
26
+ strategy:
27
+ matrix:
28
+ node-version: [16.x]
29
+
30
+ steps:
31
+ - uses: actions/checkout@v1
32
+ - name: Use Node.js ${{ matrix.node-version }}
33
+ uses: actions/setup-node@v1
34
+ with:
35
+ node-version: ${{ matrix.node-version }}
36
+
37
+ - name: Install Dependencies
38
+ run: npm ci
39
+
40
+ - name: Test package files
41
+ run: npm run test:package
42
+
43
+ # Runs adapter tests on all supported node versions and OSes
44
+ adapter-tests:
45
+ if: contains(github.event.head_commit.message, '[skip ci]') == false
46
+
47
+ runs-on: ${{ matrix.os }}
48
+ strategy:
49
+ matrix:
50
+ node-version: [16.x, 18.x, 20.x]
51
+ os: [ubuntu-latest, windows-latest, macos-latest]
52
+
53
+ steps:
54
+ - uses: ioBroker/testing-action-adapter@v1
55
+ with:
56
+ node-version: ${{ matrix.node-version }}
57
+ os: ${{ matrix.os }}
58
+ # Uncomment the following line if your adapter cannot be installed using 'npm ci'
59
+ # install-command: 'npm install'
60
+ build: false
61
+
62
+ # Deploys the final package to NPM and GitHub Actions
63
+ deploy:
64
+ # Trigger this step only when a commit on master is tagged with a version number
65
+ if: |
66
+ contains(github.event.head_commit.message, '[skip ci]') == false &&
67
+ github.event_name == 'push' &&
68
+ startsWith(github.ref, 'refs/tags/v')
69
+ needs: [adapter-tests]
70
+
71
+ runs-on: ubuntu-latest
72
+ strategy:
73
+ matrix:
74
+ node-version: [14.x]
75
+
76
+ steps:
77
+ - name: Checkout code
78
+ uses: actions/checkout@v2
79
+
80
+ - name: Use Node.js ${{ matrix.node-version }}
81
+ uses: actions/setup-node@v1
82
+ with:
83
+ node-version: ${{ matrix.node-version }}
84
+
85
+ - name: Extract the version and commit body from the tag
86
+ id: extract_release
87
+ # The body may be multiline, therefore we need to escape some characters
88
+ run: |
89
+ VERSION="${{ github.ref }}"
90
+ VERSION=${VERSION##*/v}
91
+ echo "::set-output name=VERSION::$VERSION"
92
+ BODY=$(git show -s --format=%b)
93
+ BODY="${BODY//'%'/'%25'}"
94
+ BODY="${BODY//$'\n'/'%0A'}"
95
+ BODY="${BODY//$'\r'/'%0D'}"
96
+ echo "::set-output name=BODY::$BODY"
97
+ - name: Install dependencies
98
+ run: npm ci
99
+
100
+ - name: Publish package to npm
101
+ run: |
102
+ npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
103
+ npm whoami
104
+ npm publish
105
+ - name: Create Github Release
106
+ uses: actions/create-release@v1
107
+ env:
108
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
109
+ with:
110
+ tag_name: ${{ github.ref }}
111
+ release_name: Release v${{ steps.extract_release.outputs.VERSION }}
112
+ draft: false
113
+ # Prerelease versions create pre-releases on Github
114
+ prerelease: ${{ contains(steps.extract_release.outputs.VERSION, '-') }}
115
+ body: ${{ steps.extract_release.outputs.BODY }}
package/.mocharc.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "require": [
3
+ "./test/mocha.setup.js"
4
+ ]
5
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "plugins": ["iobroker"]
3
+ }
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2017 Pmant
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Pmant
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  THE SOFTWARE.
package/README.md CHANGED
@@ -1,57 +1,70 @@
1
- ![Logo](admin/fakeroku.png)
2
- # ioBroker.fakeroku
3
- =================
4
- This ioBroker Adapter emulates a Roku and it's only purpose is to connect ioBroker to Logitech Harmony Hubs.
5
- It may also work with other devices which can control a Roku.
6
-
7
- ## Installation
8
- Intall Adapter in ioBroker Admin
9
-
10
- ## Usage
11
-
12
- ### Configuration in ioBroker Admin:
13
- - ***LAN-IP*** needs to be the network IP of your ioBroker device
14
- - ***Multicast IP*** only change this if you know what you are doing
15
- - ***Roku devices*** add / change / delete devices to emulate
16
-
17
- ### Configuration in Harmony APP & Software
18
- Add Roku 3 device following this Guide:
19
- https://support.myharmony.com/en-us/harmony-experience-with-roku
20
- You can rename the device on your Harmony.
21
-
22
- ### States
23
- States are automatically created when fakeRoku receives a key for the first time.
24
-
25
- ## Changelog
26
-
27
- ### 0.2.0
28
- (Pmant) run multiple fakeroku's in one instance
29
-
30
- ### 0.1.1
31
- (Pmant) fix package.json
32
-
33
- ### 0.1.0
34
- (Pmant) initial release
35
-
36
- ## License
37
- The MIT License (MIT)
38
-
39
- Copyright (c) 2017 Pmant
40
-
41
- Permission is hereby granted, free of charge, to any person obtaining a copy
42
- of this software and associated documentation files (the "Software"), to deal
43
- in the Software without restriction, including without limitation the rights
44
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
45
- copies of the Software, and to permit persons to whom the Software is
46
- furnished to do so, subject to the following conditions:
47
-
48
- The above copyright notice and this permission notice shall be included in
49
- all copies or substantial portions of the Software.
50
-
51
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
56
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
57
- THE SOFTWARE.
1
+ ![Logo](admin/fakeroku.png)
2
+ # ioBroker.fakeroku
3
+
4
+ ![Number of Installations](http://iobroker.live/badges/fakeroku-installed.svg)
5
+ ![Number of Installations](http://iobroker.live/badges/fakeroku-stable.svg)
6
+
7
+ This ioBroker Adapter emulates a Roku and it's only purpose is to connect ioBroker to Logitech Harmony Hubs.
8
+ It may also work with other devices which can control a Roku.
9
+
10
+ ## Installation
11
+ Intall Adapter in ioBroker Admin
12
+
13
+ ## Usage
14
+
15
+ ### Configuration in ioBroker Admin:
16
+ - ***LAN-IP*** needs to be the network IP of your ioBroker device
17
+ - ***Multicast IP*** only change this if you know what you are doing
18
+ - ***Roku devices*** add / change / delete devices to emulate
19
+
20
+ ### Configuration in Harmony APP & Software
21
+ Add Roku 3 device following this Guide:
22
+ https://support.myharmony.com/en-us/harmony-experience-with-roku
23
+ You can rename the device on your Harmony.
24
+
25
+ ### States
26
+ States are automatically created when fakeRoku receives a key for the first time.
27
+
28
+ ## Changelog
29
+ <!--
30
+ Placeholder for the next version (at the beginning of the line):
31
+ ### __WORK IN PROGRESS__
32
+ -->
33
+ ### 0.2.2 (2023-07-24)
34
+ * (Appollon77) fixed issues with controller v5
35
+
36
+ ### 0.2.1
37
+ (Pmant) fix jQuery error in admin
38
+ (ykuendig) add translations
39
+
40
+ ### 0.2.0
41
+ (Pmant) run multiple fakeroku's in one instance
42
+
43
+ ### 0.1.1
44
+ (Pmant) fix package.json
45
+
46
+ ### 0.1.0
47
+ (Pmant) initial release
48
+
49
+ ## License
50
+ The MIT License (MIT)
51
+
52
+ Copyright (c) 2017 Pmant
53
+
54
+ Permission is hereby granted, free of charge, to any person obtaining a copy
55
+ of this software and associated documentation files (the "Software"), to deal
56
+ in the Software without restriction, including without limitation the rights
57
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58
+ copies of the Software, and to permit persons to whom the Software is
59
+ furnished to do so, subject to the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be included in
62
+ all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
67
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
69
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
70
+ THE SOFTWARE.
package/admin/index.html CHANGED
@@ -1,143 +1,142 @@
1
- <html>
2
- <link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
3
- <link rel="stylesheet" type="text/css" href="../../lib/css/jqGrid/ui.jqgrid-4.5.4.css"/>
4
- <link rel="stylesheet" type="text/css" href="../../lib/css/jquery.multiselect-1.13.css"/>
5
- <script type="text/javascript" src="../../lib/js/jqGrid/jquery.jqGrid-4.5.4.min.js"></script>
6
- <script type="text/javascript" src="../../lib/js/jqGrid/grid.locale-all.js"></script>
7
- <script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
8
- <script type="text/javascript" src="../../socket.io/socket.io.js"></script>
9
- <script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
10
- <link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
11
- <script type="text/javascript" src="../../js/translate.js"></script>
12
- <script type="text/javascript" src="../../js/adapter-settings.js"></script>
13
- <style>
14
- .number {
15
- width: 70px
16
- }
17
- </style>
18
- <style>
19
- .table_header {
20
- background-color: blue;
21
- color: white;
22
- }
23
- .ip {
24
- width: 150px;
25
- text-align: right;
26
- }
27
- </style>
28
- <script type="text/javascript">
29
- var devices = [];
30
- systemDictionary = {
31
- "Fakeroku adapter settings": {
32
- "de": "Fakeroku adapter settings",
33
- "ru": "Настройки драйвера Fakeroku"
34
- },
35
- "LAN IP (not 0.0.0.0):": {"de": "LAN IP (nicht 0.0.0.0):", "ru": "LAN IP (not 0.0.0.0):"},
36
- };
37
-
38
- function setValue(id, value, onChange) {
39
- // example: select elements with id=key and class=value and insert value
40
- if ($('#' + id + '.value').attr('type') == 'checkbox') {
41
- $('#' + id + '.value').prop('checked', value).change(function() {
42
- onChange();
43
- });
44
- } else {
45
- $('#' + id + '.value').val(value).change(function() {
46
- onChange();
47
- }).keyup(function() {
48
- // Chack that only numbers entered
49
- if ($(this).hasClass('number')) {
50
- var val = $(this).val();
51
- if (val) {
52
- var newVal = '';
53
- for (var i = 0; i < val.length; i++) {
54
- if (val[i] >= '0' && val[i] <= '9') {
55
- newVal += val[i];
56
- }
57
- }
58
- if (val != newVal) $(this).val(newVal);
59
- }
60
- }
61
- onChange();
62
- });
63
- }
64
- }
65
-
66
- // the function loadSettings has to exist ...
67
- function load(settings, onChange) {
68
- if (!settings) return;
69
-
70
- getIPs(function(ips) {
71
- for (var i = 0; i < ips.length; i++) {
72
- $('#BIND').append('<option value="' + ips[i].address + '">' + ips[i].name + '</option>');
73
- }
74
- $('#BIND.value').val(settings.BIND);
75
- });
76
-
77
- devices = settings.devices || [];
78
-
79
- for (var key in settings) {
80
- setValue(key, settings[key], onChange);
81
- }
82
-
83
- if (typeof values2table === 'function') {
84
- $('#devices').hide();
85
- $('#pager-devices').hide();
86
- values2table('values', devices, onChange);
87
- } else {
88
- editTable('devices', ['name', 'port'], devices, 350);
89
- $('#values').hide();
90
- }
91
-
92
- onChange(false);
93
- }
94
-
95
- function save(callback) {
96
- var obj = {};
97
- $('.value').each(function () {
98
- var $this = $(this);
99
- if ($this.attr('type') == 'checkbox') {
100
- obj[$this.attr('id')] = $this.prop('checked');
101
- } else {
102
- obj[$this.attr('id')] = $this.val();
103
- }
104
- });
105
-
106
- // Get edited table
107
- if (typeof table2values === 'function') {
108
- obj.devices = table2values('values');
109
- } else {
110
- obj.devices = getTableResult('devices', ['name', 'port']);
111
- }
112
-
113
- callback(obj);
114
- }
115
- </script>
116
-
117
-
118
- <div id="adapter-container">
119
- <table><tr><td><img src="fakeroku.png"></td><td><h3 class="translate">Fakeroku adapter settings</h3></td></tr></table>
120
- <table>
121
- <tr><td class="translate">LAN IP (not 0.0.0.0):</td><td><select class="value" id="BIND"></select></td></tr>
122
- <tr><td>Multicast IP:</td><td><input class="value" id="MULTICAST_IP" /></td></tr>
123
- </table>
124
-
125
- <h4 class="translate">Roku devices to emulate</h4>
126
- <table id="devices"></table><div id="pager-devices"></div>
127
- <div id="values" style="width: 100%; height: calc(100% - 195px)">
128
- <button class="table-button-add" style="margin-left: 10px; width: 1.5em; height: 1.5em"></button>
129
- <div style="width: 100%; height: calc(100% - 30px); overflow: auto;">
130
- <table class="table-values" style="width: 100%;">
131
- <thead>
132
- <tr>
133
- <th data-name="_index" style="width: 40px"></th>
134
- <th data-name="name">Name</th>
135
- <th data-name="port">Port</th>
136
- <th data-buttons="delete" style="width: 40px"></th>
137
- </tr>
138
- </thead>
139
- </table>
140
- </div>
141
- </div>
142
- </div>
143
- </html>
1
+ <html>
2
+ <link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
3
+ <link rel="stylesheet" type="text/css" href="../../lib/css/jqGrid/ui.jqgrid-4.5.4.css"/>
4
+ <link rel="stylesheet" type="text/css" href="../../lib/css/jquery.multiselect-1.13.css"/>
5
+ <script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
6
+ <script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
7
+ <script type="text/javascript" src="../../lib/js/jqGrid/jquery.jqGrid-4.5.4.min.js"></script>
8
+ <script type="text/javascript" src="../../lib/js/jqGrid/grid.locale-all.js"></script>
9
+ <script type="text/javascript" src="../../socket.io/socket.io.js"></script>
10
+ <link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
11
+ <script type="text/javascript" src="../../js/translate.js"></script>
12
+ <script type="text/javascript" src="../../js/adapter-settings.js"></script>
13
+ <style>
14
+ .number {
15
+ width: 70px
16
+ }
17
+ </style>
18
+ <style>
19
+ .table_header {
20
+ background-color: blue;
21
+ color: white;
22
+ }
23
+ .ip {
24
+ width: 150px;
25
+ text-align: right;
26
+ }
27
+ </style>
28
+ <script type="text/javascript">
29
+ var devices = [];
30
+ systemDictionary = {
31
+ "Fakeroku adapter settings": {"en": "Fakeroku adapter settings", "de": "Fakeroku-Adapter Einstellungen", "ru": "Настройки драйвера Fakeroku"},
32
+ "LAN IP (not 0.0.0.0):" : {"en": "LAN IP (not 0.0.0.0):", "de": "LAN IP (nicht 0.0.0.0):", "ru": "LAN IP (not 0.0.0.0):"},
33
+ "Listen on all IPs": {"en": "Listen on all IPs", "de": "Auf allen IP Adressen hören", "ru": "Слушать на всех адресах"},
34
+ "Roku devices to emulate": {"en": "Roku devices to emulate", "de": "Zu eemulierende Roku-Geräte", "ru": "Roku devices to emulate"},
35
+ };
36
+
37
+ function setValue(id, value, onChange) {
38
+ // example: select elements with id=key and class=value and insert value
39
+ if ($('#' + id + '.value').attr('type') == 'checkbox') {
40
+ $('#' + id + '.value').prop('checked', value).change(function() {
41
+ onChange();
42
+ });
43
+ } else {
44
+ $('#' + id + '.value').val(value).change(function() {
45
+ onChange();
46
+ }).keyup(function() {
47
+ // Chack that only numbers entered
48
+ if ($(this).hasClass('number')) {
49
+ var val = $(this).val();
50
+ if (val) {
51
+ var newVal = '';
52
+ for (var i = 0; i < val.length; i++) {
53
+ if (val[i] >= '0' && val[i] <= '9') {
54
+ newVal += val[i];
55
+ }
56
+ }
57
+ if (val != newVal) $(this).val(newVal);
58
+ }
59
+ }
60
+ onChange();
61
+ });
62
+ }
63
+ }
64
+
65
+ // the function loadSettings has to exist ...
66
+ function load(settings, onChange) {
67
+ if (!settings) return;
68
+
69
+ getIPs(function(ips) {
70
+ for (var i = 0; i < ips.length; i++) {
71
+ $('#BIND').append('<option value="' + ips[i].address + '">' + ips[i].name + '</option>');
72
+ }
73
+ $('#BIND.value').val(settings.BIND);
74
+ });
75
+
76
+ devices = settings.devices || [];
77
+
78
+ for (var key in settings) {
79
+ setValue(key, settings[key], onChange);
80
+ }
81
+
82
+ if (typeof values2table === 'function') {
83
+ $('#devices').hide();
84
+ $('#pager-devices').hide();
85
+ values2table('values', devices, onChange);
86
+ } else {
87
+ editTable('devices', ['name', 'port'], devices, 350);
88
+ $('#values').hide();
89
+ }
90
+
91
+ onChange(false);
92
+ }
93
+
94
+ function save(callback) {
95
+ var obj = {};
96
+ $('.value').each(function () {
97
+ var $this = $(this);
98
+ if ($this.attr('type') == 'checkbox') {
99
+ obj[$this.attr('id')] = $this.prop('checked');
100
+ } else {
101
+ obj[$this.attr('id')] = $this.val();
102
+ }
103
+ });
104
+
105
+ // Get edited table
106
+ if (typeof table2values === 'function') {
107
+ obj.devices = table2values('values');
108
+ } else {
109
+ obj.devices = getTableResult('devices', ['name', 'port']);
110
+ }
111
+
112
+ callback(obj);
113
+ }
114
+ </script>
115
+
116
+
117
+ <div id="adapter-container">
118
+ <table><tr><td><img src="fakeroku.png"></td><td><h3 class="translate">Fakeroku adapter settings</h3></td></tr></table>
119
+ <table>
120
+ <tr><td class="translate">LAN IP (not 0.0.0.0):</td><td><select class="value" id="BIND"></select></td></tr>
121
+ <tr><td>Multicast IP:</td><td><input class="value" id="MULTICAST_IP" /></td></tr>
122
+ </table>
123
+
124
+ <h4 class="translate">Roku devices to emulate</h4>
125
+ <table id="devices"></table><div id="pager-devices"></div>
126
+ <div id="values" style="width: 100%; height: calc(100% - 195px)">
127
+ <button class="table-button-add" style="margin-left: 10px; width: 1.5em; height: 1.5em"></button>
128
+ <div style="width: 100%; height: calc(100% - 30px); overflow: auto;">
129
+ <table class="table-values" style="width: 100%;">
130
+ <thead>
131
+ <tr>
132
+ <th data-name="_index" style="width: 40px"></th>
133
+ <th data-name="name">Name</th>
134
+ <th data-name="port">Port</th>
135
+ <th data-buttons="delete" style="width: 40px"></th>
136
+ </tr>
137
+ </thead>
138
+ </table>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </html>