UNPKG

iobroker.nuki-extended

Version:
323 lines (270 loc) 10.1 kB
<!DOCTYPE html> <html lang="en"> <head> <title>ioBroker.nuki-extended</title> <script src="../../lib/js/jquery-3.2.1.min.js"></script> <script src="../../lib/js/socket.io.js"></script> <script src="conn.js"></script> <script src="modernizr-custom.js"></script> <script src="vertical-timeline.js"></script> <script src="LOCK.js"></script> <script src="ICONS.js"></script> <script src="LANGUAGE.js"></script> <script> <!-- var lang = (navigator.languages != undefined ? navigator.languages[0] : navigator.language).substr(0, 2); console.log('LANGUAGE: ' + lang); var STATES = ['logs', 'hex', 'name', 'type']; var logIDs = [], timeline = []; var timer = 0; var socketConnected; var tl; // error setTimeout(function() { $('#noNukiWebApi').text('Please activate Nuki Web API to retrieve log entries!'); }, 5*1000); // add entries function addTimeline(entries) { // compare payload to existing entries let added = false; let entry; for (let i = 0; i < entries.length; i++) { entry = entries[i]; if (logIDs.indexOf(entry.id) === -1) { added = true; logIDs.push(entry.id); // entry.date = new Date(entry.date); entry.dateFormatted = entry.date.toLocaleDateString(lang, {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}); entry.timeFormatted = entry.date.toLocaleTimeString(lang, {hour: '2-digit', minute:'2-digit', second:'2-digit'}); entry.action = ACTIONS[entry.action]; entry.deviceType = TYPES[entry.deviceType]; // add to timeline timeline.push(entry); } else break; } if (added) { // sort timeline.sort(function(a, b) { return b.date - a.date }); // show timeline showTimeline(timeline); } } function showTimeline(entries) { console.log("Show timeline with " + timeline.length + " entries."); //console.log(JSON.stringify(timeline)); $('.cd-timeline-block').not('.template').remove(); entries.forEach(function(entry) {addLog(entry)}); tl.hideBlocks(); $(window).trigger('scroll'); } // add log entry function addLog(entry) { var tpl = $('.template').clone().removeClass('template hidden').addClass('device device_' + entry.smartlockId); tpl.find('.cd-name').html(L(lang, entry.action).replace(/%person%/gi, entry.name ? L(lang, 'by') + ' ' + entry.name.replace('Nuki Web ()', 'Nuki Web API') : '<em>' + L(lang, 'manually') + '</em>')); tpl.find('.cd-icon').addClass('action-'+entry.action).css('background-image', 'url('+ICONS[entry.action]+')'); tpl.find('.cd-date').html(entry.dateFormatted + ', ' + entry.timeFormatted); tpl.find('.cd-description').removeClass('hidden').find('.nuki-device').html('<img style="vertical-align: middle" src="'+ICONS['DEVICE']+'" />' + ' ' + entry.deviceType); if (entry.trigger != 0) tpl.find('.cd-description').removeClass('hidden').find('.nuki-trigger').html('| <img style="vertical-align: middle" src="'+ICONS['TRIGGER']+'" />' + ' ' + L(lang, TRIGGER[entry.trigger])); if (entry.state != 0) tpl.find('.cd-description').removeClass('hidden').find('.nuki-state').html('| <img style="vertical-align: middle" src="'+ICONS['STATES']+'" />' + ' ' + L(lang, 'not executed') + ' ' + L(lang, STATES[entry.state])); if (entry.autoUnlock) tpl.find('.cd-description').removeClass('hidden').find('.nuki-autoUnlock').html('| <img style="vertical-align: middle" src="'+ICONS['AUTO_UNLOCK']+'" />' + ' ' + L(lang, 'AUTO_UNLOCK')); if (entry.openerLog && (entry.openerLog.activeCm || entry.openerLog.activeRto || entry.openerLog.flagGeoFence || entry.openerLog.flagForce || entry.openerLog.flagDoorbellSuppression)) { tpl.find('.cd-description').removeClass('hidden').find('.nuki-log').html( (entry.openerLog.activeCm ? '| CONTINUOUS MODE' : '') + (entry.openerLog.activeRto ? '| RING TO OPEN' : '') + (entry.openerLog.flagGeoFence ? '| GEO FENCE' : '') + (entry.openerLog.flagForce ? '| FORCED' : '') + (entry.openerLog.flagDoorbellSuppression ? '| DOORBELL SUPPRESSED' : '') ); } $('#cd-timeline .cd-timeline-block').last().after(tpl); $('#noNukiWebApi').remove(); } // add locks function addLock(lock) { var list = $('#cd-timeline .locks'); // add lock if (list.find('[data-node="' + lock.nuki + '"]').length == 0) list.append('<li class="menu" data-node="' + lock.nuki + '"><a class="filter">%name%</a></li>'); // show selection only with 2 devices or more if (list.find('li').length > 2) { list.css('width', $('#cd-timeline').css('width')); list.removeClass('hidden'); } // set data-id and data-name with its values if (!$('[data-node="' + lock.nuki + '"]').attr('data-' + lock.key)) $('[data-node="' + lock.nuki + '"]').attr('data-' + lock.key, lock.value); // replace %name% with acutal name if ($('[data-node="' + lock.nuki + '"]').html().indexOf('%') > -1) $('[data-node="' + lock.nuki + '"]').html($('[data-node="' + lock.nuki + '"]').html().replace(RegExp('%' + lock.key + '%', 'gi'), lock.value)); } // updater function updater(deviceType, instance) { console.log('Retrieving ' + deviceType + ' logs from instance ' + instance + '..'); servConn.getChildren('nuki-extended.' + instance + '.' + deviceType, function(err, _devices) { if (err !== null) { alert(err); return; } _devices.forEach(function(device) { servConn.getStates(STATES.map(state => device + '.' + state), function(err, _states) { if (err !== null) { alert(err); return; } if (_states !== null && _states !== undefined) { for (var key in _states) { var node = key.substr(key.lastIndexOf('.')+1); if (STATES.indexOf(node) === -1) continue; var content = _states[key].val; // add log entries if (node == 'logs') { let entries = JSON.parse(content); console.log('Found ' + entries.length + ' entries in ' + deviceType + ' log.'); addTimeline(entries); } // add other information else { addLock({'nuki': device, 'key': node.substr(node.lastIndexOf('.')+1), 'value': content}); $('[data-state="' + node + '"]').html(content); } } // updater setTimeout(updater, 10*1000, deviceType, instance); // every 10s } }); }); }); } // page loaded $(window).on('load', function() { // attach listener to filter $('body').on('click', '.menu .filter', function(e) { let id = $(this).parent().data('id') || parseInt($(this).parent().data('type') + $(this).parent().data('hex'), 16); // remove current selection $('.menu .filter').removeClass('active'); // add new selection $(this).addClass('active'); if (id != "ALL") { $('.device').addClass('hidden'); $('.device_' + id).removeClass('hidden'); } else $('.device').removeClass('hidden'); tl.hideBlocks(); $(window).trigger('scroll'); }); // // Vertical Timeline tl = new Timeline(); tl.bindScroll(); // // Connect Socket servConn.init({ connLink: window.location.href.substr(0, window.location.href.indexOf('/', 8)) }, { onConnChange: function(socketConnected) { console.log('Socket ' + (socketConnected ? 'connected' : 'disconnected') + '!'); // retrieve states if (socketConnected) { // get all instances of the adapter console.log('Retrieving all adapter instances..'); for (let instance = 0; instance < 9; instance++) { servConn.getObject('nuki-extended.' + instance + '.info.connection', function(err, _instance) { if (_instance !== null) { // get logs from devices ['smartlocks', 'openers'].forEach(function(deviceType) { timer = timer + 1; setTimeout(updater, timer*1000, deviceType, instance); }); } }); } } } }); }); // --> </script> <link rel="stylesheet" type="text/css" href="vertical-timeline.css"/> <link rel="stylesheet" type="text/css" href="nuki.css"/> <style type="text/css"> <!-- .action-UNLOCK {background-color: #B22222;} .action-LOCK {background-color: #22b322;} .action-UNLATCH {background-color: #b38e22;} .action-LOCK_N_GO {background-color: #b222b3;} .action-LOCK_N_GO_WITH_UNLATCH {background-color: #b222b3;} .action-DOORBELL_RECOGNITION {background-color: #b222b3;} .action-DOOR_OPENED {background-color: #b34622;} .action-DOOR_CLOSED {background-color: #6ab322;} .action-INITIALIZATION {background-color: #666;} // --> </style> </head> <body> <section id="cd-timeline" class="cd-container"> <ul class="locks hidden"> <li data-id="ALL" class="menu"><a class="filter active">Show all</a></li> </ul> <p id="noNukiWebApi">Loading log entries..</p> <div class="cd-timeline-block template hidden"> <div class="cd-timeline-img cd-icon"></div> <div class="cd-timeline-content"> <h2 class="cd-name"></h2> <p class="cd-description hidden"> <span class="nuki-device"></span> <span class="nuki-trigger"></span> <span class="nuki-state"></span> <span class="nuki-autoUnlock"></span> <span class="nuki-log"></span> </p> <span class="cd-date"></span> </div> </div> </section> <div class="copyright"> Icons made by <a href="https://www.flaticon.com/authors/smashicons" title="Smashicons">Smashicons</a> and <a href="https://www.freepik.com/" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a> </div> </body> </html>