`;
liveTable.append(dataStr);
}
// Table is finished
// Finally, activate the active checkbox
// Background: If active=true, you always can disable the fetching
// But if active=false, we need to wait for the Loxone Outputs, otherwise we get empty outputs on save
if( statmatch?.active != "true" && statmatch?.active != true ) {
$("#LoxoneDetails_s4lstatactive")
.prop('disabled', false)
.checkboxradio('refresh');
}
// Done
}
else {
console.log("LiveView done with error", data);
liveTable.html( popupLoxoneDetails_LiveViewError(data) );
}
$("#valuesLoxoneDetails").html(dataStr);
})
.fail(function(data){
console.log("LiveView fail", data);
liveTable.html( popupLoxoneDetails_LiveViewError(data) );
});
}
// This function returns an error html if Detail Live data have errors
function popupLoxoneDetails_LiveViewError( data ) {
$("#valuesLoxoneDetailsLive_title").html(`Error getting Live data`);
dataStr = "";
dataStr = `
Information
Could not query Live data. Possibly S4L has no permissions to this block, or the block isself has no data to return.
`;
if( data.code ) {
dataStr += `
Error
${data.code}
`;
}
if( data.response ) {
dataStr += `
Original response
${data.response}
`;
}
console.log("popupLoxoneDetails_LiveViewError", data);
return dataStr;
}
// Saves all filter properties
function saveFilters() {
localStorage.setItem("s4l_loxone_filters", JSON.stringify(filters));
// console.log("saveFilters", filters, localStorage.getItem("s4l_loxone_filters"));
}
function restoreFilters() {
// console.log("restoreFilters", localStorage.getItem("s4l_loxone_filters"));
try {
filters = JSON.parse( localStorage.getItem("s4l_loxone_filters") );
for( const [key, value] of Object.entries(filters)) {
checkboxes = $(`input[type="radio"][id="${key}_${value}"]`);
selects = $(`select[name="${key}"]`);
// console.log("restore", key, value, checkboxes, selects);
// console.log(key, value);
if( checkboxes.length > 0 ) {
// console.log("INPUT", checkboxes);
$(checkboxes).attr("checked", "checked");
$(`input[type="radio"][name="${key}"]`).checkboxradio("refresh");
}
else if( selects.length > 0 ) {
// console.log("SELECT", $(selects), value);
$(selects).val(value).selectmenu("refresh");
if(value != "all")
$(selects).closest('.ui-btn').addClass('filter-highlight');
}
else if( key == "filter_search" ) {
$(`#${key}`).val( value );
filterSearchString = value;
if (filterSearchString != "") {
// $('#filter_search').css({'backgroundColor':'#FFFF99'});
$('#filter_search').addClass('filter-highlight');
$('#filter_search').data("clear-btn", true);
} else {
// $('#filter_search').css({'backgroundColor':'white'});
$('#filter_search').removeClass('filter-highlight');
$('#filter_search').data("clear-btn", false);
}
}
}
} catch(e) {
console.log("restoreFilters Exception catched (filters possibly empty)");
filters = { };
}
}
function scheduleImport( msno, uid ) {
var control = statsconfigLoxone.find(obj => {
return obj.uuid === uid && obj.msno == msno })
console.log("scheduleImport", msno, uid, control );
if( control ) {
$("#LoxoneDetails_s4lstatimportbutton")
.addClass("ui-disabled");
// Element found in internal data
$.post( "ajax.cgi", {
action : "scheduleimport",
importtype : "full",
uuid : uid,
msno : msno,
category : control.category,
description: control.description,
name : control.name,
room: control.room,
type : control.type,
})
.done(function(data){
console.log(data);
})
.always(function(data){
getImportSchedulerReport();
});
}
else {
throw `scheduleImport: ${msno} and ${uid} not found in internal list`;
}
}
function getImportSchedulerReport() {
if( !timer ) {
return;
}
if( getImportSchedulerReport_running == true ) {
return;
}
getImportSchedulerReport_running = true;
$.post( "ajax.cgi", {
action : "import_scheduler_report",
})
.done(function(data){
// console.log("import_scheduler_report done", data);
imports = Object.keys(data.filelist)
.map(key => ({file: key, data: data.filelist[key]}));
updateReportTables(data);
})
.fail(function(data){
console.log("import_scheduler_report fail", data);
})
.always(function(data){
getImportSchedulerReport_running = false;
});
}
function updateReportTables(data) {
// Get IDs for Detail View Import status
var detail_msno = $(".LoxoneDetails_table").data("msno");
var detail_uuid = $(".LoxoneDetails_table").data("uid");
$("#LoxoneDetails_importstatus").empty();
// List view generation
for( imp of imports ) {
var data = imp.data;
var status = imp.data?.status;
var state = data.status?.status;
var msno = data.msno;
var uuid = data.uuid;
// console.log("updateReportTables", msno, uuid, state, status);
// Find
var target_tr = $(`tr[data-uid=${uuid}][data-msno=${msno}]`);
var target = target_tr.find('.importInfo');
// console.log("target", target);
// $(target).html("Found!");
var endtime_dt = new Date(Math.round(status?.endtime*1000));
var endtime = endtime_dt.toLocaleString();
var finished_percent;
var estimatedTimeLeft_min;
var progress_html;
var html = "";
switch(state) {
case "running":
finished_percent = Math.round (status?.stats?.record_count_finished / (status?.stats?.record_count_finished + status?.stats?.estimate_records_left) * 100 );
finished_percent = !isNaN(finished_percent) ? finished_percent : 0;
estimatedTimeLeft_min = status?.stats?.estimate_time_left_secs ? "("+Math.ceil((status?.stats?.estimate_time_left_secs/60)).toString()+" min. left)" : "Calculating...";
progress_html = `
${finished_percent}%
${estimatedTimeLeft_min}`;
html+= progress_html;
break;
case "finished":
html+= ` Finished ${endtime}`;
break;
case "error":
case "dead":
html+= `
Finished with error ${endtime}`;
break;
case "scheduled":
html+= ` Queued`;
break;
}
// Update Detail View Import status
if( detail_msno == msno && detail_uuid == uuid ) {
$("#LoxoneDetails_importstatus").html(html);
}
$(target).html(html);
}
}
// Validates a measurementname and returns a suggestion if not valid
// measurementname is the string to verify
// selfIndex is the index key in statsconfigLoxone of itself (otherwise it may find itself)
function validateMeasurementname( measurementname, msno, uid ) {
// Find own array index in statsconfigLoxone
var selfIndex = statsconfigLoxone.findIndex( obj => { return obj.uuid === uid && obj.msno == msno })
if( typeof measurementname === 'undefined' || measurementname == "" ) {
// Check if statsconfigLoxone already knows name or description
// console.log( "1st try: measurementname is undefined" );
measurementname = statsconfigLoxone[selfIndex]?.description ? statsconfigLoxone[selfIndex].description : statsconfigLoxone[selfIndex]?.name;
}
console.log("measurementname", measurementname);
var measurementnameDefault = measurementname;
if( typeof measurementname === 'undefined') {
console.log( "2st try: measurementname is undefined" );
throw "measurementname is empty. Must be defined for validation.";
}
// Retrying different things
var counter =-1;
while(1) {
counter++;
// console.log("----------------", counter, "---------------");
switch(counter) {
case 0:
break;
case 1:
if( statsconfigLoxone[selfIndex]?.name && measurementname == statsconfigLoxone[selfIndex].name)
measurementname = statsconfigLoxone[selfIndex].name;
else
continue;
break;
case 20:
throw `Could not find an alternative measurementname after ${counter} tries`;
default:
measurementname = measurementnameDefault+"_"+counter.toString();
}
// Check if measurementname is already defined
var foundIndex = statsconfigLoxone.findIndex( function(obj, index) {
// console.log("findIndex", measurementname, selfIndex, obj["measurementname"], index);
if(selfIndex == index) { return false };
if(obj["measurementname"] != measurementname) { return false; }
return true;
})
// console.log("Try", counter, measurementname, foundIndex);
if(foundIndex == -1) {
break;
}
}
// console.log("foundIndex", foundIndex);
return measurementname.trim();
}
function clearTimer() {
console.log("Timer cleared");
window.clearInterval(timer);
timer = false;
}
function setTimer() {
console.log("Timer set");
timer = window.setInterval(getImportSchedulerReport, timer_interval);
}
function restore_hints_hide() {
try {
hints_hide = JSON.parse( localStorage.getItem("s4l_loxone_hints_hide") );
if( hints_hide == null ) {
hints_hide = { };
}
}
catch(e) {
console.log("restore_hints_hide", e);
hints_hide = { };
}
}
function hint_hide(hintid) {
hints_hide[hintid] = true;
$("#"+hintid).fadeOut();
localStorage.setItem("s4l_loxone_hints_hide", JSON.stringify(hints_hide));
}
// Sort function for arrays of objects
// https://stackoverflow.com/a/4760279/3466839
// Usage: arrayOfObjects.sort(dynamicSortMultiple("Name", "-Surname"));
function dynamicSort(property) {
var sortOrder = 1;
if(property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a,b) {
/* next line works with strings and numbers,
* and you may want to customize it to your needs
*/
var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result * sortOrder;
}
}
function dynamicSortMultiple() {
/*
* save the arguments object as it will be overwritten
* note that arguments object is an array-like object
* consisting of the names of the properties to sort by
*/
var props = arguments;
return function (obj1, obj2) {
var i = 0, result = 0, numberOfProperties = props.length;
/* try getting a different result from 0 (equal)
* as long as we have extra properties to compare
*/
while(result === 0 && i < numberOfProperties) {
result = dynamicSort(props[i])(obj1, obj2);
i++;
}
return result;
}
}
// https://stackoverflow.com/a/22581382/3466839
function copyToClipboard(elem) {
// create hidden text element, if it doesn't already exist
var targetId = "_hiddenCopyText_";
var isInput = elem.tagName === "INPUT" || elem.tagName === "TEXTAREA";
var origSelectionStart, origSelectionEnd;
if (isInput) {
// can just use the original source element for the selection and copy
target = elem;
origSelectionStart = elem.selectionStart;
origSelectionEnd = elem.selectionEnd;
} else {
// must use a temporary form element for the selection and copy
target = document.getElementById(targetId);
if (!target) {
var target = document.createElement("textarea");
target.style.position = "absolute";
target.style.left = "-9999px";
target.style.top = "0";
target.id = targetId;
document.body.appendChild(target);
}
target.textContent = elem.textContent;
}
// select the content
var currentFocus = document.activeElement;
target.focus();
target.setSelectionRange(0, target.value.length);
// copy the selection
var succeed;
try {
succeed = document.execCommand("copy");
} catch(e) {
succeed = false;
}
// restore original focus
if (currentFocus && typeof currentFocus.focus === "function") {
currentFocus.focus();
}
if (isInput) {
// restore prior selection
elem.setSelectionRange(origSelectionStart, origSelectionEnd);
} else {
// clear temporary content
target.textContent = "";
}
return succeed;
}