Alarm page demo using javascript and tag - fleeceable - 29.04.2024
Hello!
I haven't found any slim alarm notification example so I created one (using forum magic search button and all kind of code examples). I bet there is quite many good programmers who maybe want to add some additional features. Would be nice to see these in this thread...
About page. It gonna check alarms by tags and shows in two separate table. One is active alarm table and another is event table. It's possible to delete all past events as well.
Integration should take less than few minutes.
Alarm page date and time is actual alarm variable update time. Event date and time is time when system checked event variable (not actual alarm time).
Common functions:
Code: function eventlog(text)
local max = 30 -- MAX NUMBER OF ENTRIES
text = os.date('%d/%m/%Y %T ') .. text
storage.exec('lpush', 'eventlog', text)
storage.exec('ltrim', 'eventlog', 0, max - 1)
end
Resident script 3s:
1. Define group address base
2. Add tag-s what to check periodically
3. On first run change first three lines to true. Later change back to false
4. Activate script
Code: create_eventlog_file = false --Execute on first run to create lp file
create_all_log_file = false --Execute on first run to create lp file
create_group_addresses = false --Execute on first run to create group addresses
group_address_base = '51/1/' --System generates two group addresses. Make sure that addresses are free (x/x/1 and x/x/2).
alarm_tags = {'SV1','SV2','SV3','EMAIL','HEATING'} --These are alarm tags what is gonna checked constantly
-------------------------------------------------------
data1 = {
type = 'event',
name = 'ALARM PAGE - Update frontend',
active = 1,
params = group_address_base .. '1',
subparams = 0,
script = [[val = event.getvalue()
if val then
grp.checkupdate("]] .. group_address_base .. [[1", 0)
end]],
category = '',
description = 'Automatically generated',
}
data2 = {
type = 'event',
name = 'ALARM PAGE - Delete eventlog',
active = 1,
params = group_address_base .. '2',
subparams = 0,
script = [[val = event.getvalue()
if val then
storage.delete('eventlog')
grp.update(']]..group_address_base .. [[2',0)
grp.update(']]..group_address_base .. [[1',1)
end]],
category = '',
description = 'Automatically generated',
}
function create_scripts(data)
exists = script.get(data.name)
if not exists then
db:insert('scripting', data)
data.id = db:getlastautoid()
script.save(data, true)
script.reloadsingle(data)
end
end
function createga(groupaddress,datatype2,name2)
exist = grp.alias(groupaddress)
if exist == nil then
address = grp.create({
datatype = datatype2,
address = groupaddress,
name = name2,
comment = 'Automatically created object',
units = '',
tags = { },
})
end
end
if create_group_addresses then
createga(group_address_base .. 1,dt.bool,'ALARM PAGE - Update frontend')
createga(group_address_base .. 2,dt.bool,'ALARM PAGE - Delete eventlog')
os.sleep(0.5)
create_scripts(data1)
create_scripts(data2)
end
function file_exists(name)
local f = io.open(name, "r")
return f ~= nil and io.close(f)
end
if create_eventlog_file then
fil_name = '/www/user/eventlog.lp'
if file_exists(fil_name) == false then
dst = fil_name
io.writefile(dst, [[<?
require('apps')
items = storage.exec('lrange', 'eventlog', 0, 999)
text = table.concat(items, '\r\n')
print(text)
?>]])
end
end
if create_all_log_file then
fil_name = '/www/user/all_alarms.lp'
if file_exists(fil_name) == false then
dst = fil_name
io.writefile(dst, [[<?
require('json')
require('genohm-scada')
mydata =storage.get('ALL_ALARMS')
mydata = json.encode(mydata)
print(mydata)
?>]])
end
end
function check_events(tag, GA)
index = 1
all_alarms = {}
update_table = false
for it=1, #tag, 1 do
old = {}
old_val = storage.get(tag[it] .. '_sent')
update_storage = false
events = grp.tag(tag[it])
for i=1, #events, 1 do
if old_val == nil then --if no info - update it to latest info
update_storage = true
update_table = true
end
old[i] = {sent =events[i].value}
all_alarms[index] = {status =events[i].value, description =events[i].name, date =os.date('%d/%m/%Y', events[i].updatetime), time =os.date(' %T ', events[i].updatetime), tag =events[i].tagcache }
index = index + 1
if events[i].value == true then
if old_val ~= nil then
if old_val[i].sent == false then
eventlog('1 ' .. events[i].tagcache .. ' ' .. events[i].name) -- 1 means alarm
update_storage = true
update_table = true
end
end
elseif events[i].value == false then
if old_val ~= nil then
if old_val[i].sent == true then
eventlog('0 ' .. events[i].tagcache .. ' ' .. events[i].name ) -- 0 means ok
update_storage = true
update_table = true
end
end
end
end
if update_storage then
storage.set(tag[it] ..'_sent',old)
end
end
if update_table then
storage.set('ALL_ALARMS',all_alarms)
os.sleep(0.5)
grp.update(group_address_base .. '1', 1)
end
end
check_events(alarm_tags)
Custom Javascript:
1. Define your plan ID where alarm and event table gonna be placed
2. Edit updateTableGA address.
Do that on both scripts...
Code: //////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////ALARM PAGE - ALARM LOG/////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
$(function() {
var planID = 7 ////change this
var updateTableGA = '51/1/1' ////change this
$('body').on('showplan', function(event, id) {
var planid = window.currentPlanId;
grp.listen(updateTableGA, function(object) {
if (object.value == true || planid == planID ){
$(function() {
const tableArray = [];
var DataArray = []
$.get( "/user/eventlog.lp", function( data ) {
chartdata = data
});
setTimeout(() => { //little delay because reading chart data file
const lines = chartdata.split('\n');
lines.forEach(line => {
// Split the line by space
const parts = line.split(' ');
// Extract date, time, and description
const date = parts[0];
const time = parts[1];
const status = parts[2];
const tag = parts[3];
const description = parts.slice(4).join(' '); // Join the remaining parts as description
// Push the data to the table array
if (date != ''){
tableArray.push({ date, time, description,tag, status });
}
});
for (var i = 0; i <= tableArray.length-1; i++) {
DataArray[i] = { date: tableArray[i].date, time: tableArray[i].time, description: tableArray[i].description, status:tableArray[i].status, tag:tableArray[i].tag }
}
console.log(DataArray)
// Function to generate the HTML structure for the table
function generateTable(data, statusIconColor1, statusIconColor0) {
var tableHtml = '<table id="data-table" style="background-color: #212120;" >' +
'<thead >' +
'<tr>' +
'<th class="align-left text-col">DATE</th>' +
'<th class="align-left text-col">TIME</th>' +
'<th class="align-left text-col">DESCRIPTION</th>' +
'<th class="align-center text-col">SYSTEM</th>' +
'<th class="align-center text-col">STATUS</th>' +
'</tr>' +
'</thead>' +
'<tbody >';
data.forEach(function(item, index) {
var statusIconColor = (item.status === "1") ? statusIconColor1 : statusIconColor0;
var statusIconStyle = 'color:' + statusIconColor + '; text-align: center;';
console.log(item)
tableHtml += '<tr>' +
'<td class="align-left text-col">' + item.date + '</td>' +
'<td class="align-left text-col">' + item.time + '</td>' +
'<td class="align-left text-col">' + item.description + '</td>' +
'<td class="align-center text-col">' + item.tag + '</td>' +
'<td class="align-center"><span style="' + statusIconStyle + '">' + ((item.status === true) ? "⬤" : "⬤") + '</span></td>' +
'</tr>';
});
tableHtml += '</tbody></table>';
// Create a style element and append it to the document head
var styleElement = document.createElement('style');
styleElement.innerHTML = '#data-table td.align-center {text-align: center;} #data-table td.align-left {text-align: left;} #data-table th.align-center {text-align: center;}'+
'.text-col { color: #E5E5E5 }';
document.head.appendChild(styleElement);
return tableHtml;
}
$('#data-table').remove();
html_code = generateTable(DataArray, '#FF0000', "#00ff00")
var el = $('<div>' + html_code + '</div>').css({
position: 'absolute',
width: '750px',
//height: '300px',
left: '1050px',
bottom: '100px',
top: '250px',
zIndex: 999,
}).appendTo('#plan-'+planID);
}, 500);
})
}
})
})
})
//////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////ALARM PAGE - ACTIVE ALARMS/////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
$(function() {
var planID = 7 ///change this
var updateTableGA = '51/1/1' ///change this
$('body').on('showplan', function(event, id) {
var planid = window.currentPlanId;
grp.listen(updateTableGA, function(object) {
if (object.value == true || planid == planID ){
// Fetch alarm data
$.get( "/user/all_alarms.lp", function( data ) {
var chartdata = JSON.parse(data);
// Generate table with alarm data after a delay
setTimeout(function() {
generateTable(chartdata);
}, 500); // Adjust the delay time as needed
});
}
});
// Function to generate the HTML structure for the table
function generateTable(data) {
// Filter out data where value is false
var filteredData = data.filter(function(item) {
return item.status === true;
});
// Format date and time strings for proper parsing
filteredData.forEach(function(item) {
// Convert date from "DD/MM/YYYY" to "YYYY-MM-DD"
var dateParts = item.date.split('/');
item.date = dateParts[2] + '-' + dateParts[1] + '-' + dateParts[0];
// Trim and remove leading whitespace from time
item.time = item.time.trim();
});
// Sort filteredData by date and time in ascending order
filteredData.sort(function(a, b) {
var dateA = new Date(a.date + ' ' + a.time);
var dateB = new Date(b.date + ' ' + b.time);
return dateB - dateA; // Reverse order to sort newest first
});
var tableHtml = '<table id="data-table2" style="background-color: #212120;" >' +
'<thead >' +
'<tr>' +
'<th class="align-left text-col">DATE</th>' +
'<th class="align-left text-col">TIME</th>' +
'<th class="align-left text-col">DESCRIPTION</th>' +
'<th class="align-center text-col">SYSTEM</th>' +
'<th class="align-center text-col">STATUS</th>' +
'</tr>' +
'</thead>' +
'<tbody >';
filteredData.forEach(function(item) {
console.log(item)
var statusIconColor = (item.status === true) ? '#FF0000' : '#00ff00';
var statusIconStyle = 'color:' + statusIconColor + '; text-align: center;';
tableHtml += '<tr>' +
'<td class="align-left text-col">' + item.date + '</td>' +
'<td class="align-left text-col">' + item.time + '</td>' +
'<td class="align-left text-col">' + item.description + '</td>' +
'<td class="align-center text-col" >' + item.tag + '</td>' +
'<td class="align-center"><span style="' + statusIconStyle + '">' + ((item.status === "1") ? "⬤" : "⬤") + '</span></td>' +
'</tr>';
});
tableHtml += '</tbody></table>';
// Remove existing table if it exists
$('#data-table2').remove();
// Create the table and append it to the same location
var $table = $(tableHtml);
$table.css({
position: 'absolute',
width: '790px',
left: '105px',
bottom: '100px',
top: '640px',
zIndex: 1
});
$('#plan-' + planID).append($table);
// Create a style element and append it to the document head
var styleElement = document.createElement('style');
styleElement.innerHTML = '#data-table2 td.align-center {text-align: center;} #data-table2 td.align-left {text-align: left;} #data-table2 th.align-center {text-align: center;}'+
'.text-col { color: #E5E5E5; }'
document.head.appendChild(styleElement);
}
});
});
Email notification part is running on another script right now but maybe I'm gonna integrate into this script in future.
|