UNPKG

iobroker.javascript

Version:
657 lines (606 loc) 26.7 kB
<html> <head> <!-- Materialze style --> <link rel="stylesheet" type="text/css" href="../../css/adapter.css"/> <link rel="stylesheet" type="text/css" href="../../lib/css/materialize.css"> <script type="text/javascript" src="../../lib/js/jquery-3.2.1.min.js"></script> <script type="text/javascript" src="../../socket.io/socket.io.js"></script> <script type="text/javascript" src="../../js/translate.js"></script> <script type="text/javascript" src="../../lib/js/materialize.js"></script> <script type="text/javascript" src="../../js/adapter-settings.js"></script> <script type="text/javascript" src="adminWords.js"></script> <!-- you have to define 2 functions in the global scope: --> <script type="text/javascript"> var gSettings; var mapTimer; var mapLoaded; var useOpenLayers = true; var OSM = null; /** * List of forbidden Locations for a mirror directory * relative to the default data directory * @type {*[]} */ var forbiddenMirrorLocations = [ 'backup-objects', 'files', 'backitup', '../backups', '../node_modules', '../log' ]; var ioBrokerDataDir = '/opt/iobroker'; // Use as default because most systems are linux var pathSep = '/'; // Use as default because most systems are linux function setValue(id, value, onChange) { var $value = $('#' + id + '.value'); if ($value.attr('type') === 'checkbox') { $value.prop('checked', value).on('change', function () { var id = $(this).attr('id'); if (id === 'useSystemGPS') { showHideSettings(); updateMap(); } gSettings[id] = $(this).prop('checked'); onChange(); }); } else { $value.val(value).on('change', function () { var id = $(this).attr('id'); gSettings[id] = $(this).val(); if (id === 'longitude' || id === 'latitude') { updateMap(); } onChange(); }).on('keyup', function() { $(this).trigger('change'); }); } } function initMap() { mapLoaded = true; updateMap(true); } function positionReady(position) { $('#latitude').val(position.coords.latitude.toFixed(8)); $('#longitude').val(position.coords.longitude.toFixed(8)).trigger('change'); M.updateTextFields(); updateMap(); } function updateMap(immediately) { var lng; var lat; if ($('#useSystemGPS').prop('checked')) { if (systemConfig && systemConfig.common && (systemConfig.common.longitude || systemConfig.common.latitude)) { lng = systemConfig.common.longitude; lat = systemConfig.common.latitude; } } else { lng = $('#longitude').val(); lat = $('#latitude').val(); if (!lng || !lat) { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(positionReady); } } } lng = lng || 0; lat = lat || 0; if (useOpenLayers) { // OPEN STREET MAPS if (typeof ol === 'undefined') { return setTimeout(updateMap, 200); } var point = ol.proj.fromLonLat([parseFloat(lng), parseFloat(lat)]); if (!OSM) { OSM = {}; OSM.markerSource = new ol.source.Vector(); OSM.markerStyle = new ol.style.Style({ image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({ anchor: [0.5, 49], anchorXUnits: 'fraction', anchorYUnits: 'pixels', opacity: 0.75, src: './pin.png' })) }); OSM.oMap = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({source: new ol.source.OSM()}), new ol.layer.Vector({ source: OSM.markerSource, style: OSM.markerStyle, }) ], view: new ol.View({center: point, zoom: 17}) }); OSM.marker = new ol.Feature({ geometry: new ol.geom.Point(point), name: _('Your home') }); OSM.markerSource.addFeature(OSM.marker); OSM.oMap.on('singleclick', function (event){ if (!$('#useSystemGPS').prop('checked')) { var lonLat = ol.proj.toLonLat(event.coordinate); $('#longitude').val(Math.round(lonLat[0] * 1000000) / 1000000); $('#latitude').val(Math.round(lonLat[1] * 1000000) / 1000000).trigger('change'); M.updateTextFields(); } }); } var zoom = OSM.oMap.getView().getZoom(); OSM.marker.setGeometry(new ol.geom.Point(point)); OSM.oMap.setView(new ol.View({center: point, zoom: zoom})); } else { // GOOGLE MAPS if (!mapLoaded) return; if (!immediately) { clearTimeout(mapTimer); mapTimer = setTimeout(function () { updateMap(true); }, 1000); return; } if (mapTimer) { clearTimeout(mapTimer); mapTimer = null; } if (lat || lng) { var map = new google.maps.Map(document.getElementById('map'), { zoom: 14, center: {lat: parseFloat(lat), lng: parseFloat(lng)} }); var marker = new google.maps.Marker({ position: {lat: parseFloat(lat), lng: parseFloat(lng)}, map: map, title: _('Your home') }); } } } function showHideSettings() { if ($('#useSystemGPS').prop('checked')) { $('.local-coords').hide(); } else { $('.local-coords').show(); } } function load(settings, onChange) { if (!useOpenLayers) { var key1 = 'AIzaSyCIrBRZfZAE'; var key2 = '_0C1OplAUy7OXhiWLoZc3eY'; var key = key1 + key2; // load google API $.ajax({ // please do not misuse this api key! url: 'https://maps.googleapis.com/maps/api/js?key=' + key + '&signed_in=true&callback=initMap', dataType: 'script', cache: true }); } else { mapLoaded = true; // load OSM $.ajax({ url: '../../lib/js/ol.js', dataType: 'script', cache: true }).done(function () { //setTimeout(updateMap, 500); }); $.ajax({ url: '../../lib/css/ol.css', success: function (data) { $('head').append('<style>' + data + '</style>'); } }); } if (settings.sunriseEvent === undefined) { settings.sunriseEvent = 'nightEnd'; } if (settings.sunriseOffset === undefined) { settings.sunriseOffset = 0; } if (settings.sunriseLimitStart === undefined) { settings.sunriseLimitStart = '06:00'; } if (settings.sunriseLimitEnd === undefined) { settings.sunriseLimitEnd = '12:00'; } if (settings.sunsetEvent === undefined) { settings.sunsetEvent = 'dusk'; } if (settings.sunsetOffset === undefined) { settings.sunsetOffset = 0; } if (settings.sunsetLimitStart === undefined) { settings.sunsetLimitStart = '18:00'; } if (settings.sunsetLimitEnd === undefined) { settings.sunsetLimitEnd = '23:00'; } if (settings.useSystemGPS === undefined) { settings.useSystemGPS = (systemConfig.common.longitude && systemConfig.common.latitude); } if (settings.enableSendToHost === undefined) { settings.enableSendToHost = false; } if (settings.enableExec === undefined) { settings.enableExec = false; } if (settings.mirrorInstance === undefined) { settings.mirrorInstance = 0; } if (settings.maxSetStatePerMinute === undefined) { settings.maxSetStatePerMinute = 1000; } $('.astro-calc').on('change', function () { updateAstroTimes(); }).on('keyup', function () { $(this).attr('type') === 'text' && $(this).trigger('change'); }); getAdapterInstances('javascript', function (res) { if (!res) return; var $mirrorInstance = $('#mirrorInstance'); for (var t = 0; t < res.length; t++) { $mirrorInstance.append('<option value="' + res[t]._id.split('.').pop() + '">' + res[t]._id.replace('system.adapter.', '') + '</option>'); } $mirrorInstance.val(settings.mirrorInstance); $mirrorInstance.select(); }); // get the iobroker data dir and path separator sendTo(null, 'getIoBrokerDataDir', null, function (response) { if (!response) { return; } if (response.dataDir) { ioBrokerDataDir = response.dataDir; } if (response.sep) { pathSep = response.sep; } console.log('Update ioBroker Directory to ' + ioBrokerDataDir + ' with pathSeparator ' + pathSep); }); gSettings = settings; $('.value').each(function () { var id = $(this).attr('id'); setValue(id, settings[id] || '', onChange); }); showHideSettings(); list2chips('.libraries', settings.libraries || '', onChange); list2chips('.libraryTypings', settings.libraryTypings || '', onChange); $('.tab-astro').on('click', function () { updateMap(); setTimeout(function() { OSM && OSM.oMap && OSM.oMap.updateSize(); }, 400); }); updateAstroTimes(); onChange(false); M.updateTextFields(); } function list2chips(selector, list, onChange) { var chips = list.split(/[,;\s]+/); var data = []; for (var c = 0; c < chips.length; c++) { if (chips[c] && chips[c].trim()) { data.push({tag: chips[c].trim()}); } } $(selector).chips({ data: data, placeholder: _('Module names'), secondaryPlaceholder: _('Add module'), onChipAdd: onChange, onChipDelete: onChange }); } function chips2list(selector) { var data = $(selector).chips('getData'); var text = []; for (var lib = 0; lib < data.length; lib++) { text.push(data[lib].tag); } return text.join(' '); } function convertGrad(obj, attr) { // Extract ° " and ' var m = obj[attr].match(/([0-9]+)°(\s*)(([0-9]+)')?(\s*)(([0-9]+)")?(\s*)([A-Z]+)?/); if (m) { var grad = m[1]; var min = m[4]; var sec = m[7]; var dir = m[9]; if (grad !== undefined && grad !== null) { // Graddez = Grad + ( Minuten * 60 + Sekunden ) / 3600 var result = parseInt(grad, 10); if (min !== undefined && min !== null) { result += parseInt(min, 10) / 60; if (sec !== undefined && sec !== null) { result += parseInt(sec, 10) / 3600; } } if (dir) { dir = dir.toLowerCase(); if (dir === 's' || dir === 'w') { result = (-1) * result; } } obj[attr] = result.toFixed(6); return true; } else { return false; } } else { showMessage(_('Invalid format. Use A°B\'C"D')); return false; } } function formatTime(date) { const h = String(date.getHours()); const m = String(date.getMinutes()); const s = String(date.getSeconds()); return `${h.padStart(2, '0')}:${m.padStart(2, '0')}:${s.padStart(2, '0')}`; } function updateAstroTimes() { const obj = {}; const useSystem = $('#useSystemGPS').prop('checked'); $('.astro-calc').each(function () { var id = $(this).attr('id'); var val = $(this).val(); if (!useSystem || (id !== 'longitude' && id !== 'latitude')) { obj[id] = val; } }); sendTo(null, 'calcAstro', obj, function (times) { const nextSunrise = formatTime(new Date(times.nextSunrise.date)); const nextSunset = formatTime(new Date(times.nextSunset.date)); if (nextSunrise !== times.nextSunrise.serverTime) { $('#nextSunrise').html(`${times.nextSunrise.serverTime} (Local time: ${nextSunrise})`); } else { $('#nextSunrise').html(times.nextSunrise.serverTime); } if (nextSunset !== times.nextSunset.serverTime) { $('#nextSunset').html(`${times.nextSunset.serverTime} (Local time: ${nextSunset})`); } else { $('#nextSunset').html(times.nextSunset.serverTime); } }); } function pathJoin(parts, sep){ var separator = sep || '/'; var replace = new RegExp(separator + '{1,}', 'g'); return parts.join(separator).replace(replace, separator); } function save(callback) { var obj = {}; $('.value').each(function () { var $this = $(this); if ($this.attr('type') === 'checkbox') { obj[$this.attr('id')] = $this.prop('checked'); } else { obj[$this.attr('id')] = $this.val(); } }); obj.libraries = chips2list('.libraries'); obj.libraryTypings = chips2list('.libraryTypings'); if (obj.latitude.indexOf('°') !== -1) { if (!convertGrad(obj, 'latitude')) { return; } $('#latitude').val(obj.latitude); } if (obj.longitude.indexOf('°') !== -1) { if (!convertGrad(obj, 'longitude')) { return; } $('#longitude').val(obj.longitude); } if (obj.mirrorPath) { var ioBDataDir = ioBrokerDataDir + pathSep; for (var dir of forbiddenMirrorLocations) { dir = pathJoin([ioBDataDir, dir], pathSep) + pathSep; if (dir.includes(obj.mirrorPath) || obj.mirrorPath.startsWith(dir)) { showError('This path is not allowed for mirroring. Please change it.'); return; } } } callback(obj); } </script> <style> .top-padding { padding: 1rem; } #map { width: 100%; height: calc(100% - 60px); } </style> </head> <body> <!-- you have to put your config page in a div with id adapter-container --> <div class="m adapter-container"> <div class="row"> <div class="col s12"> <ul class="tabs"> <li class="tab col s5"><a href="#tab-main" class="translate active">Main settings</a></li> <li class="tab col s5 tab-astro"><a href="#tab-astro" class="translate">Astro settings</a></li> </ul> </div> <div id="tab-main" class="col s12 page"> <div class="row"> <div class="col s6 m4 l2"> <img src="javascript.png" class="logo" alt="logo"/> </div> </div> <div class="row"> <div class="col s12"> <label class="translate">Additional npm modules:</label> <div class="chips libraries"></div> </div> </div> <div class="row"> <div class="col s12"> <label class="translate">Activate syntax help for these npm modules:</label> <div class="chips libraryTypings"></div> </div> </div> <div class="row"> <div class="col s8"> <input id="mirrorPath" class="value" type="text"/> <label for="mirrorPath" class="translate">Mirror scripts to file path</label> </div> <div class="col s4"> <select id="mirrorInstance" class="value"></select> <label for="mirrorInstance" class="translate">Instance, that do mirroring</label> </div> </div> <div class="row"> <div class="input-field col s12 m6"> <input id="enableSetObject" class="value" type="checkbox"/> <span for="enableSetObject" class="translate">Enable command "setObject":</span> </div> <div class="input-field col s12 m6"> <input id="enableSendToHost" class="value" type="checkbox"/> <span for="enableSendToHost" class="translate">Enable command "sendToHost":</span> </div> <div class="input-field col s12 m6"> <input id="enableExec" class="value" type="checkbox"/> <span for="enableExec" class="translate">Enable command "exec":</span> </div> <div class="input-field col s12 m6"> <input type="checkbox" id="subscribe" class="value" /> <span for="subscribe" class="translate">Do not subscribe all states on start:</span> </div> </div> <div class="row"> <div class="input-field col s12"> <input class="value validate" placeholder="1000" id="maxSetStatePerMinute" type="number" min="20" max="100000"> <label for="maxSetStatePerMinute" class="translate">Maximum setState requests per Minute per Script</label> </div> </div> <div class="row"> <div class="input-field col s12"> <input class="value validate" placeholder="100" id="maxTriggersPerScript" type="number" min="20" max="100000"> <label for="maxTriggersPerScript" class="translate">Maximum triggers per Script (until warning)</label> </div> </div> <div class="row"> <div class="input-field col s12"> <input type="checkbox" id="allowSelfSignedCerts" class="value" /> <span for="allowSelfSignedCerts" class="translate">Allow self-signed certificates for URL requests</span> </div> </div> <div class="row"> <div class="input-field col s12"> <input class="value validate" id="gptKey" type="text"> <label for="gptKey" class="translate">ChatGPT API key</label> </div> </div> </div> <div id="tab-astro" class="col s12 page"> <div class="row top-padding"> <div class="col s12 m6"> <div class="row"> <div class="input-field col s12"> <input id="useSystemGPS" type="checkbox" class="value filled-in" /> <span for="useSystemGPS" class="translate">Use system settings:</span> </div> </div> <div class="row local-coords"> <div class="input-field col s12"> <input id="latitude" type="text" class="value astro-calc" /> <label for="latitude" class="translate">Latitude °:</label> </div> </div> <div class="row local-coords"> <div class="input-field col s12"> <input id="longitude" type="text" class="value astro-calc" /> <label for="longitude" class="translate">Longitude °:</label> </div> </div> <div class="row local-coords"> <div class="col s12"> <p class="translate">Help</p> </div> </div> <div class="row"> <h6 class="translate">Day time settings</h6> </div> <div class="row"> <div class="input-field col s12"> <div class="input-field col s4"> <select id="sunriseEvent" class="value astro-calc" > <option value="nightEnd" class="translate">sch_astro_nightEnd</option> <option value="nauticalDawn" class="translate">sch_astro_nauticalDawn</option> <option value="dawn" class="translate">sch_astro_dawn</option> <option value="sunrise" class="translate">sch_astro_sunrise</option> <option value="sunriseEnd" class="translate">sch_astro_sunriseEnd</option> <option value="goldenHourEnd" class="translate">sch_astro_goldenHourEnd</option> </select> <span for="sunriseEvent" class="translate">Used as start of the daytime</span> </div> <div class="input-field col s2"> <input id="sunriseOffset" type="number" class="value astro-calc" /> <label for="sunriseOffset" class="translate">Offset</label> <span>± </span><span class="translate">in minutes</span> </div> <div class="input-field col s3"> <input id="sunriseLimitStart" type="text" class="value astro-calc" /> <label for="sunriseLimitStart" class="translate">But not earlier</label> </div> <div class="input-field col s3"> <input id="sunriseLimitEnd" type="text" class="value astro-calc" /> <label for="sunriseLimitEnd" class="translate">And not later</label> </div> </div> <div><span class="translate">Next sunrise:</span> <span id="nextSunrise"></span></div> </div> <div class="row"> <div class="input-field col s12"> <div class="input-field col s4"> <select id="sunsetEvent" class="value astro-calc"> <option value="goldenHour" class="translate">sch_astro_goldenHour</option> <option value="sunsetStart" class="translate">sch_astro_sunsetStart</option> <option value="sunset" class="translate">sch_astro_sunset</option> <option value="dusk" class="translate">sch_astro_dusk</option> <option value="nauticalDusk" class="translate">sch_astro_nauticalDusk</option> <option value="night" class="translate">sch_astro_night</option> </select> <span for="sunsetEvent" class="translate">Used as end of the daytime</span> </div> <div class="input-field col s2"> <input id="sunsetOffset" type="number" class="value astro-calc" /> <label for="sunsetOffset" class="translate">Offset</label> <span>± </span><span class="translate">in minutes</span> </div> <div class="input-field col s3"> <input id="sunsetLimitStart" type="text" class="value astro-calc" /> <label for="sunsetLimitStart" class="translate">But not earlier</label> </div> <div class="input-field col s3"> <input id="sunsetLimitEnd" type="text" class="value astro-calc" /> <label for="sunsetLimitEnd" class="translate">And not later</label> </div> </div> </div> <div class="row"> <div><span class="translate">Next sunset:</span> <span id="nextSunset"></span></div> </div> <div class="row"> <div class="input-field col s12"> <input type="checkbox" id="createAstroStates" class="value" /> <span for="createAstroStates" class="translate">Create states for all astro times</span> </div> </div> </div> <div class="col s12 m6 map"> <div id="map"></div> </div> </div> </div> </div> </div> </body> </html>