iobroker.zigbee 1.5.5 → 1.6.6

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.
@@ -1,3 +1,3 @@
1
1
  # These are supported funding model platforms
2
2
 
3
- custom: ['https://www.paypal.me/goofyk', 'https://paypal.me/pools/c/8gWlKqAfIF']
3
+ custom: ['https://www.paypal.me/goofyk', 'https://paypal.me/ArthurRupp']
@@ -50,8 +50,8 @@ jobs:
50
50
  runs-on: ${{ matrix.os }}
51
51
  strategy:
52
52
  matrix:
53
- node-version: [10.x, 12.x, 14.x]
54
- os: [ubuntu-latest, windows-latest, macos-latest]
53
+ node-version: [12.x, 14.x, 16.x]
54
+ os: [ubuntu-latest, macos-latest]
55
55
 
56
56
  steps:
57
57
  - name: Checkout code
@@ -115,13 +115,13 @@ jobs:
115
115
 
116
116
  # If you like, activate automated version publish to NPM
117
117
  # Only if commit contains a version, see : https://github.com/AlCalzone/release-script
118
- #
119
- # - name: Publish package to npm
120
- # run: |
121
- # npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
122
- # npm whoami
123
- # npm publish
124
- #
118
+
119
+ - name: Publish package to npm
120
+ run: |
121
+ npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
122
+ npm whoami
123
+ npm publish
124
+
125
125
  # - name: Create Github Release
126
126
  # uses: actions/create-release@v1
127
127
  # env:
@@ -148,4 +148,4 @@ jobs:
148
148
  # # sentry-cli releases set-commits $SENTRY_VERSION --auto
149
149
  #
150
150
  # # Add the following line BEFORE finalize if sourcemap uploads are needed
151
- # # sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/
151
+ # # sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018-2020 Kirov Ilya <kirovilya@gmail.com>
3
+ Copyright (c) 2018-2021 Kirov Ilya <kirovilya@gmail.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  ![Logo](admin/zigbee.png)
2
2
  # ioBroker.zigbee
3
3
 
4
- ![Number of Installations](http://iobroker.live/badges/zigbee-installed.svg) ![Number of Installations](http://iobroker.live/badges/zigbee-stable.svg) [![NPM version](http://img.shields.io/npm/v/iobroker.zigbee.svg)](https://www.npmjs.com/package/iobroker.zigbee)
5
- [![Downloads](https://img.shields.io/npm/dm/iobroker.zigbee.svg)](https://www.npmjs.com/package/iobroker.zigbee)
6
- [![Tests](https://travis-ci.org/ioBroker/ioBroker.zigbee.svg?branch=master)](https://travis-ci.org/ioBroker/ioBroker.zigbee)
4
+ ![Number of Installations](http://iobroker.live/badges/zigbee-installed.svg)
5
+ ![Number of Installations](http://iobroker.live/badges/zigbee-stable.svg)
6
+ [![NPM version](http://img.shields.io/npm/v/iobroker.zigbee.svg)](https://www.npmjs.com/package/iobroker.zigbee)
7
7
 
8
- [![NPM](https://nodei.co/npm/iobroker.zigbee.png?downloads=true)](https://nodei.co/npm/iobroker.zigbee/)
8
+ ![Test and Release](https://github.com/ioBroker/iobroker.zigbee/workflows/Test%20and%20Release/badge.svg)
9
+ [![Translation status](https://weblate.iobroker.net/widgets/adapters/-/zigbee/svg-badge.svg)](https://weblate.iobroker.net/engage/adapters/?utm_source=widget)
10
+ [![Downloads](https://img.shields.io/npm/dm/iobroker.zigbee.svg)](https://www.npmjs.com/package/iobroker.zigbee)
9
11
 
10
12
  ## ioBroker adapter for Zigbee devices via TI cc2531/cc2530/cc26x2r/cc2538 and deCONZ ConBee/RaspBee.
11
13
 
@@ -15,7 +17,7 @@ With the Zigbee-coordinator based on Texas Instruments SoC cc253x (and others mo
15
17
 
16
18
  ### Texas Instruments SoC
17
19
  ..
18
- For work, you need one of the following devices, flashed with a special ZNP firmware: [cc2531, cc2530, cc26x2r, cc2538](https://github.com/Koenkk/Z-Stack-firmware)
20
+ For work, you need one of the following Adapters [all are listed here](https://www.zigbee2mqtt.io/information/supported_adapters.html) , flashed with a special ZNP firmware: [cc2531, cc2530, cc26x2r, cc2538](https://github.com/Koenkk/Z-Stack-firmware)
19
21
 
20
22
  <span><img src="https://ae01.alicdn.com/kf/HTB1Httue3vD8KJjSsplq6yIEFXaJ/Wireless-Zigbee-CC2531-Sniffer-Bare-Board-Packet-Protocol-Analyzer-Module-USB-Interface-Dongle-Capture-Packet.jpg_640x640.jpg" width="100"></span>
21
23
  <span><img src="http://img.dxcdn.com/productimages/sku_429478_2.jpg" width="100"></span>
@@ -95,15 +97,45 @@ Works with devices from this list https://github.com/ioBroker/ioBroker.zigbee/wi
95
97
 
96
98
  You can thank the authors by these links:
97
99
  * to Kirov Ilya https://www.paypal.me/goofyk
98
- * to Arthur Rupp https://paypal.me/pools/c/8gWlKqAfIF
100
+ * to Arthur Rupp https://paypal.me/ArthurRupp
99
101
 
100
102
  <!--
101
103
  Placeholder for the next version (at the beginning of the line):
102
- ### __WORK IN PROGRESS__
104
+
105
+ https://github.com/AlCalzone/release-script#usage
106
+ npm run release minor -- --all 0.9.8 -> 0.10.0
107
+ npm run release patch -- --all 0.9.8 -> 0.9.9
108
+ npm run release prerelease beta -- --all v0.2.1 -> v0.2.2-beta.0
109
+ Placeholder for the next version (at the beginning of the line):
110
+ ### **WORK IN PROGRESS**
103
111
  -->
104
112
 
105
113
  ## Changelog
106
114
 
115
+ ### 1.6.1 (2021-08)
116
+ * (kirovilya) herdsman compatibility
117
+
118
+
119
+ ### 1.6.0 (2021-08-09)
120
+ ## Attention! Attention! Attention! Attention! Attention! Attention! Attention!
121
+
122
+ After introducing a new z-stack startup procedure into zigbee-herdsman, we got some problems with our adapter in version 1.5.6.
123
+ This was discussed [here](https://github.com/ioBroker/ioBroker.zigbee/issues/1110) and [here](https://github.com/Koenkk/zigbee-herdsman/issues/376)
124
+
125
+ Unfortunately, not all user sticks were able to work successfully with this new init routine. This mainly affected the TI cc2538 based sticks with the 2019 year's firmware. All subsequent firmwares and sticks have no problems when starting the adapter or they can be fixed.
126
+
127
+ At the moment, there is no working solution to this problem, except for updating the firmware of such sticks to a newer one.
128
+ As a fallback, it is proposed to use the old version of zigbee-herdsman 0.13.93, but some of the devices and functionality may not work.
129
+ Therefore, we made a separate version 1.6.0o especially for this case. It can be installed from github at https://github.com/ioBroker/ioBroker.zigbee/tarball/old_herdsman
130
+
131
+ * Update to latest zigbee-herdsman and zigbee-herdsman-converters
132
+ * (PeterVoronov) Update support composite exposes
133
+ * (kirovilya) UI fixes
134
+ * (kirovilya) Sentry support
135
+
136
+ ### 1.5.6 (2021-05-26)
137
+ * (kirovilya) new UI add
138
+
107
139
  ### 1.5.5 (2021-05-05)
108
140
  * Fixes for new zigbee-herdsman-converters
109
141
  * UI fixes
@@ -332,7 +364,7 @@ new Zigbee-herdsman features:
332
364
  ## License
333
365
  The MIT License (MIT)
334
366
 
335
- Copyright (c) 2018-2020 Kirov Ilya <kirovilya@gmail.com>
367
+ Copyright (c) 2018-2021 Kirov Ilya <kirovilya@gmail.com>
336
368
 
337
369
  Permission is hereby granted, free of charge, to any person obtaining a copy
338
370
  of this software and associated documentation files (the "Software"), to deal
@@ -3,8 +3,33 @@ const path = location.pathname;
3
3
  const parts = path.split('/');
4
4
  parts.splice(-3);
5
5
 
6
- const socket = io.connect('/', {path: parts.join('/') + '/socket.io'});
7
- const instance = window.location.search.slice(-1) || 0;
6
+ const socket = io.connect('/', { path: parts.join('/') + '/socket.io' });
7
+ var query = (window.location.search || '').replace(/^\?/, '').replace(/#.*$/, '');
8
+ var args = {};
9
+ let theme = null;
10
+
11
+ // parse parameters
12
+ query.trim().split('&').filter(function (t) { return t.trim(); }).forEach(function (b, i) {
13
+ const parts = b.split('=');
14
+ if (!i && parts.length === 1 && !isNaN(parseInt(b, 10))) {
15
+ args.instance = parseInt(b, 10);
16
+ }
17
+ var name = parts[0];
18
+ args[name] = parts.length === 2 ? parts[1] : true;
19
+
20
+ if (name === 'instance') {
21
+ args.instance = parseInt(args.instance, 10) || 0;
22
+ }
23
+
24
+ if (args[name] === 'true') {
25
+ args[name] = true;
26
+ } else if (args[name] === 'false') {
27
+ args[name] = false;
28
+ }
29
+ });
30
+
31
+ var instance = args.instance;
32
+
8
33
  let common = null; // common information of adapter
9
34
  const host = null; // host object on which the adapter runs
10
35
  const changed = false;
@@ -71,6 +96,17 @@ function loadSettings(callback) {
71
96
  if (typeof load === 'undefined') {
72
97
  alert('Please implement save function in your admin/index.html');
73
98
  } else {
99
+ // detect, that we are now in react container (themeNames = ['dark', 'blue', 'colored', 'light'])
100
+
101
+ const _query = query.split('&');
102
+
103
+ for (var q = 0; q < _query.length; q++) {
104
+ if (_query[q].indexOf('react=') !== -1) {
105
+ $('.adapter-container').addClass('react-' + _query[q].substring(6));
106
+ theme = 'react-' + _query[q].substring(6);
107
+ }
108
+ }
109
+
74
110
  load(res.native, onChange);
75
111
  }
76
112
  if (typeof callback === 'function') {
@@ -205,4 +241,4 @@ function showMessage(message, title, icon) {
205
241
  }
206
242
  $dialogMessage.find('.dialog-text').html(message);
207
243
  $dialogMessage.modal().modal('open');
208
- }
244
+ }
package/admin/admin.js CHANGED
@@ -31,7 +31,7 @@ const updateCardInterval = setInterval(updateCardTimer, 6000);
31
31
 
32
32
  const savedSettings = [
33
33
  'port', 'panID', 'channel', 'disableLed', 'countDown', 'groups', 'extPanID', 'precfgkey', 'transmitPower',
34
- 'adapterType', 'debugHerdsman', 'disablePing', 'external',
34
+ 'adapterType', 'debugHerdsman', 'disablePing', 'external', 'startWithInconsistent',
35
35
  ];
36
36
 
37
37
  function getDeviceByID(ID) {
@@ -154,10 +154,10 @@ function getGroupCard(dev) {
154
154
  </div>`);
155
155
  const image = `<img src="img/group_${memberCount}.png" width="80px" onerror="this.onerror=null;this.src='img/unavailable.png';">`;
156
156
  const dashCard = getDashCard(dev,`img/group_${memberCount}.png` );
157
- const card = `<div id="${id}" class="device">
158
- <div class="card hoverable">
157
+ const card = `<div id="${id}" class="device group">
158
+ <div class="card hoverable flipable">
159
159
  <div class="front face">${dashCard}</div>
160
- <div class="back face hide">
160
+ <div class="back face">
161
161
  <div class="card-content zcard">
162
162
  <div class="flip" style="cursor: pointer">
163
163
  <span class="top right small" style="border-radius: 50%">
@@ -187,12 +187,16 @@ function getGroupCard(dev) {
187
187
  return card;
188
188
  }
189
189
 
190
-
190
+ function sanitizeModelParameter(parameter) {
191
+ const replaceByUnderscore = /[\s/]/g;
192
+ return parameter.replace(replaceByUnderscore, '_');
193
+ }
191
194
 
192
195
  function getCard(dev) {
193
196
  const title = dev.common.name,
194
197
  id = dev._id,
195
- type = (dev.common.type ? dev.common.type.replace('/','_'):'unknown'),
198
+ type = (dev.common.type ? dev.common.type : 'unknown'),
199
+ type_url = (dev.common.type ? sanitizeModelParameter(dev.common.type) : 'unknown'),
196
200
  img_src = dev.icon || dev.common.icon,
197
201
  rooms = [],
198
202
  lang = systemLang || 'en';
@@ -206,7 +210,7 @@ function getCard(dev) {
206
210
  const room = rooms.join(',') || '&nbsp';
207
211
  const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
208
212
  const rid = id.split('.').join('_');
209
- const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
213
+ const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type_url}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
210
214
  const image = `<img src="${img_src}" width="80px" onerror="this.onerror=null;this.src='img/unavailable.png';">`,
211
215
  nwk = (dev.info && dev.info.device) ? dev.info.device._networkAddress : undefined,
212
216
  battery_cls = getBatteryCls(dev.battery),
@@ -226,9 +230,9 @@ function getCard(dev) {
226
230
  infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '';
227
231
  const dashCard = getDashCard(dev);
228
232
  const card = `<div id="${id}" class="device">
229
- <div class="card hoverable">
233
+ <div class="card hoverable flipable">
230
234
  <div class="front face">${dashCard}</div>
231
- <div class="back face hide">
235
+ <div class="back face">
232
236
  <div class="card-content zcard">
233
237
  <div class="flip" style="cursor: pointer">
234
238
  <span class="top right small" style="border-radius: 50%">
@@ -442,14 +446,13 @@ function showDevices() {
442
446
  {
443
447
  const card = getGroupCard(d);
444
448
  html += card;
449
+ continue;
445
450
  }
446
- continue;
447
451
  };
448
- if (d.info.device._type == 'Coordinator') {
452
+ if (d.info && d.info.device._type == 'Coordinator') {
449
453
  const card = getCoordinatorCard(d);
450
454
  html += card;
451
- }
452
- else {
455
+ } else {
453
456
  //if (d.groups && d.info && d.info.device._type == "Router") {
454
457
  if (d.groups) {
455
458
  devGroups[d._id] = d.groups;
@@ -489,23 +492,10 @@ function showDevices() {
489
492
  });
490
493
  $(".flip").click(function(){
491
494
  const card = $(this).parents(".card");
492
- const flipped = card.hasClass("flipped");
493
495
  card.toggleClass("flipped");
494
- if (flipped) {
495
- card.children(".front").removeClass("hide");
496
- } else {
497
- card.children(".back").removeClass("hide");
498
- }
499
- setTimeout(function() {
500
- const flipped = card.hasClass("flipped");
501
- if (flipped) {
502
- card.children(".back").removeClass("hide");
503
- card.children(".front").addClass("hide");
504
- } else {
505
- card.children(".front").removeClass("hide");
506
- card.children(".back").addClass("hide");
507
- }
508
- }, 500);
496
+ });
497
+ $('#rotate_btn').click(function () {
498
+ $('.card.flipable').toggleClass("flipped");
509
499
  });
510
500
 
511
501
  shuffleInstance = new Shuffle($("#devices"), {
@@ -670,7 +660,7 @@ function getDevices() {
670
660
  }
671
661
 
672
662
  function getDeviceCards() {
673
- return $('#devices .device');
663
+ return $('#devices .device').not(".group");
674
664
  }
675
665
 
676
666
  function getDeviceCard(devId) {
@@ -2199,7 +2189,7 @@ function genDevInfo(device) {
2199
2189
  }).join('');
2200
2190
  }
2201
2191
  };
2202
- const modelUrl = (!mapped) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${mapped.model}.html" target="_blank" rel="noopener noreferrer">${mapped.model}</a>`;
2192
+ const modelUrl = (!mapped) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${sanitizeModelParameter(mapped.model)}.html" target="_blank" rel="noopener noreferrer">${mapped.model}</a>`;
2203
2193
  const mappedInfo = (!mapped) ? '' :
2204
2194
  `<div style="font-size: 0.9em">
2205
2195
  <ul>
@@ -2441,10 +2431,10 @@ function showExclude() {
2441
2431
  const exclude_dev = devices.find((d) => d.common.type == exclude_id) || {common: {name: exclude_id}};
2442
2432
  // exclude_icon = (exclude_dev.icon) ? `<img src="${exclude_dev.icon}" width="64px">` : '';
2443
2433
 
2444
- const modelUrl = (!exclude_id) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${exclude_id}.html" target="_blank" rel="noopener noreferrer">${exclude_id}</a>`;
2434
+ const modelUrl = (!exclude_id) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${sanitizeModelParameter(exclude_id)}.html" target="_blank" rel="noopener noreferrer">${exclude_id}</a>`;
2445
2435
 
2446
2436
  const card = `
2447
- <div id="${exclude_id}" class="exclude col s12 m6 l4 xl3">
2437
+ <div id="${exclude_id}" class="exclude col s12 m6 l4 xl3" style="height: 135px;padding-bottom: 10px;">
2448
2438
  <div class="card hoverable">
2449
2439
  <div class="card-content zcard">
2450
2440
  <i class="left"><img src="${exclude_dev.icon}" width="64px" onerror="this.onerror=null;this.src='img/unavailable.png';"></i>
Binary file
@@ -105,19 +105,34 @@
105
105
  width: 100%;
106
106
  height: 100%;
107
107
  position: relative;
108
- transition: transform .5s;
109
- -webkit-transition: -webkit-transform .5s;
110
- transform-style: preserve-3d;
108
+ -webkit-transition: -webkit-transform 0.5s;
109
+ -moz-transition: -moz-transform 0.5s;
110
+ -o-transition: -o-transform 0.5s;
111
+ transition: transform 0.5s;
111
112
  -webkit-transform-style: preserve-3d;
113
+ -moz-transform-style: preserve-3d;
114
+ -o-transform-style: preserve-3d;
115
+ transform-style: preserve-3d;
116
+ -webkit-transform-origin: 50% 50%;
117
+ transform-origin: 50% 50%;
112
118
  }
113
119
  .m .card .zcard {
114
120
  padding: 10px;
115
121
  }
122
+ .m .card div {
123
+ -webkit-backface-visibility: hidden;
124
+ -moz-backface-visibility: hidden;
125
+ -o-backface-visibility: hidden;
126
+ backface-visibility: hidden;
127
+ }
116
128
  .device {
117
129
  padding: 5px;
118
130
  width: 330px;
119
131
  height: 200px;
120
- perspective: 600px;
132
+ -webkit-perspective: 800px;
133
+ -moz-perspective: 800px;
134
+ -o-perspective: 800px;
135
+ perspective: 800px;
121
136
  }
122
137
  .m .zcard .card-title {
123
138
  font-size: 16px;
@@ -129,7 +144,7 @@
129
144
  border: none;
130
145
  bottom: 0;
131
146
  height: 50px;
132
- background: #fff;
147
+ /*background: #fff;*/
133
148
  right: 0;
134
149
  /*text-align: right;*/
135
150
  padding: 10px;
@@ -228,19 +243,35 @@
228
243
  margin-bottom: 0;
229
244
  }
230
245
  .face {
231
- position: absolute;
246
+ overflow:hidden;
247
+ position:absolute;
232
248
  height: 100%;
233
249
  width: 100%;
234
- backface-visibility: hidden;
235
- -webkit-backface-visibility: hidden;
250
+ /*background: #fff;*/
251
+ z-index: 2;
252
+ -webkit-transform: rotateY( 0deg );
253
+ -moz-transform: rotateY( 0deg );
254
+ -o-transform: rotateY( 0deg );
255
+ transform: rotateY( 0deg );
236
256
  }
237
257
  .back {
238
- transform: rotateY( 180deg );
239
- -webkit-transform: rotateY(180deg);
258
+ overflow:hidden;
259
+ position:absolute;
260
+ display: block;
261
+ box-sizing: border-box;
262
+ height: 100%;
263
+ width: 100%;
264
+ /*background: #fff;*/
265
+ -webkit-transform: rotateY( -180deg );
266
+ -moz-transform: rotateY( -180deg );
267
+ -o-transform: rotateY( -180deg );
268
+ transform: rotateY( -180deg );
240
269
  }
241
270
  .card.flipped {
242
- transform: rotateY(180deg);
243
- -webkit-transform: rotateY(180deg);
271
+ -webkit-transform: rotateY( 180deg );
272
+ -moz-transform: rotateY( 180deg );
273
+ -o-transform: rotateY( 180deg );
274
+ transform: rotateY( 180deg );
244
275
  }
245
276
  .m .switch label .lever {
246
277
  width: 30px;
@@ -332,29 +363,41 @@
332
363
  </div>
333
364
  <div id="tabs" class="tabs-content row">
334
365
  <div id="tab-main" class="col s12 page active">
335
- <div class="navbar-fixed" style="height: 36px">
336
- <nav style="height: 36px; margin-left: -12px">
337
- <div class="nav-wrapper main-toolbar-table">
338
- <div class="col s4 m4 l4 xl3 main-toolbar-table-types-tools main-toolbar-table-filter input-field" style="line-height: 24px;">
339
- <i class="material-icons prefix" style="line-height: 24px; height: 30px;">search</i>
340
- <input id="device-search" class="filter-input translateP" placeholder="Искать" autocomplete="new-password" readonly="readonly" onfocus="if (this.hasAttribute('readonly')) {this.removeAttribute('readonly'); this.blur(); this.focus();}" data-lang-placeholder="Filter">
341
- <a class="filter-clear btn-floating btn-very-small translateT red lighten-3" title="Очистить" data-lang-title="clear" style="display: none;"><i class="material-icons">clear</i></a>
342
- </div>
343
- <div class="col s4 m4 l4 xl3 input-field" style="line-height: 24px;">
344
- <i class="material-icons left" style="line-height: 24px; margin-top: 5px;">sort</i>
345
- <a id="device-order-btn" class="dropdown-trigger btn" href="#" data-target="device-order">Default</a>
346
- <ul id="device-order" class="dropdown-content" tabindex="0">
347
- <li class="device-order-item" data-type="a-z" tabindex="0"><a class="translate" data-lang="A-Z">A-Z</a></li>
348
- <li class="device-order-item" data-type="default" tabindex="0"><a class="translate" data-lang="Default">Default</a></li>
349
- </ul>
350
- </div>
351
- <div class="col s4 m4 l4 xl3 input-field" style="line-height: 24px;">
352
- <i class="material-icons left" style="line-height: 24px; margin-top: 5px;">art_track</i>
353
- <a id="room-filter-btn" class="dropdown-trigger btn" href="#" data-target="room-filter">All</a>
354
- <ul id="room-filter" class="dropdown-content" tabindex="0">
355
- </ul>
356
- </div>
357
- </div>
366
+ <div class="navbar-fixed" style="height: 36px;">
367
+ <nav style="height: 36px; margin-left: -12px; line-height: 24px;">
368
+ <ul id="nav-mobile" class="left">
369
+ <li>
370
+ <a id="rotate_btn" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Rotate cards">
371
+ <i class="material-icons large icon-blue">3d_rotation</i>
372
+ </a>
373
+ </li>
374
+ <li>
375
+ <div class="col main-toolbar-table-types-tools main-toolbar-table-filter input-field" style="line-height: 24px;">
376
+ <i class="material-icons prefix" style="line-height: 24px;">search</i>
377
+ <input id="device-search" class="filter-input translateP" placeholder="Искать" autocomplete="new-password" readonly="readonly" onfocus="if (this.hasAttribute('readonly')) {this.removeAttribute('readonly'); this.blur(); this.focus();}" data-lang-placeholder="Filter">
378
+ <a class="filter-clear btn-floating btn-very-small translateT red lighten-3" title="Очистить" data-lang-title="clear" style="display: none;"><i class="material-icons">clear</i></a>
379
+ </div>
380
+
381
+ </li>
382
+ <li>
383
+ <div class="col input-field" style="line-height: 24px;">
384
+ <i class="material-icons left" style="line-height: 24px; margin-top: 5px;">sort</i>
385
+ <a id="device-order-btn" class="dropdown-trigger btn" href="#" data-target="device-order">Default</a>
386
+ <ul id="device-order" class="dropdown-content" tabindex="0">
387
+ <li class="device-order-item" data-type="a-z" tabindex="0"><a class="translate" data-lang="A-Z">A-Z</a></li>
388
+ <li class="device-order-item" data-type="default" tabindex="0"><a class="translate" data-lang="Default">Default</a></li>
389
+ </ul>
390
+ </div>
391
+ </li>
392
+ <li>
393
+ <div class="col input-field" style="line-height: 24px;">
394
+ <i class="material-icons left" style="line-height: 24px; margin-top: 5px;">art_track</i>
395
+ <a id="room-filter-btn" class="dropdown-trigger btn" href="#" data-target="room-filter">All</a>
396
+ <ul id="room-filter" class="dropdown-content" tabindex="0">
397
+ </ul>
398
+ </div>
399
+ </li>
400
+ </ul>
358
401
  </nav>
359
402
  </div>
360
403
  <div id="devices" class="row">
@@ -491,6 +534,10 @@
491
534
  <label class="translate" for="external">Paths to files, semicolon (;) splitted</label>
492
535
  </div>
493
536
  </div>
537
+ <div class="input-field col s12 m12 l12 col-disableLed">
538
+ <input id="startWithInconsistent" type="checkbox" class="value" />
539
+ <label class="translate" for="startWithInconsistent">Force start adapter with inconsistent configuration (not recommended). Please update the adapter to compatible firmware and recreate your network as soon as possible.</label>
540
+ </div>
494
541
  </div>
495
542
 
496
543
  <div id="tab-expos" class="col s12 page">
@@ -705,8 +752,8 @@
705
752
  </div>
706
753
  </div>
707
754
  <div id="tab-exclude" class="col s12 page">
708
- <div class="col s4">
709
- <p class="translate">ExcludeTextTranslation</p>&nbsp;
755
+ <div class="row">
756
+ <p class="translate">ExcludeTextTranslation</p>
710
757
  </div>
711
758
  <div class="fixed-action-btn" style="margin-bottom: 100px">
712
759
  <a id="add_exclude" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Add exlude"><i class="material-icons large">add</i></a>