node-red-contrib-majordomus
Version:
Smart Home nodes not only for Majordomus platform.
327 lines (283 loc) • 14.1 kB
HTML
<script type="text/javascript">
RED.nodes.registerType('scheduler', {
category: 'majordomus',
color: '#66CCFF',
defaults: {
name: { value: '' },
scheduleList: { value: [], required: false },
},
inputs: 0,
outputs: 1,
icon: "font-awesome/fa-calendar",
label: function () {
return this.name || "scheduler";
},
oneditprepare: function () {
const listContainer = $("#scheduler-list");
listContainer.editableList({
addButton: true, // Adds a "Add" button
removable: true, // Allows removal of items
sortable: true, // Allows reordering
height: '300px',
sortItems: function () {
updateScheduleTable();
},
removeItem: function () {
updateScheduleTable();
},
addItem: function (container, index, data) {
// Initialize data if not present
data.text = data.text || "Monday";
data.startTime = data.startTime || "00:00";
data.endTime = data.endTime || "23:59";
data.color = data.color || `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')}`;
data.type = data.type || "num";
data.value = data.value || "" ;
// Create the content of each list item
const row = $('<div style="display: flex; gap: 10px; align-items: center; flex-wrap: nowrap;">').appendTo(container);
// Text input
$('<select style="flex: 1;">')
.append('<option value="Monday">Monday</option>')
.append('<option value="Tuesday">Tuesday</option>')
.append('<option value="Wednesday">Wednesday</option>')
.append('<option value="Thursday">Thursday</option>')
.append('<option value="Friday">Friday</option>')
.append('<option value="Saturday">Saturday</option>')
.append('<option value="Sunday">Sunday</option>')
.append('<option value="Monday-Friday">Monday-Friday</option>')
.append('<option value="Saturday-Sunday">Saturday-Sunday</option>')
.append('<option value="Monday-Sunday">Monday-Sunday</option>')
.val(data.text)
.appendTo(row)
.change(function () {
data.text = $(this).val();
updateScheduleTable();
RED.nodes.dirty(true);
});
// Time selection inputs
$('<input type="time" style="width: auto;">')
.val(data.startTime)
.appendTo(row)
.change(function () {
data.startTime = $(this).val();
updateScheduleTable();
RED.nodes.dirty(true);
});
$('<span">-</span>').appendTo(row);
$('<input type="time" style="width: auto;">')
.val(data.endTime)
.appendTo(row)
.change(function () {
data.endTime = $(this).val();
updateScheduleTable();
RED.nodes.dirty(true);
});
// Color picker
$('<input type="color" style="position: relative; width: 50px;">')
.val(data.color)
.appendTo(row)
.change(function () {
data.color = $(this).val();
updateScheduleTable();
RED.nodes.dirty(true);
});
// Typed input for value and type
$('<input type="text" id="node-input-example1" style="width: auto;">')
.val(data.value)
.appendTo(row)
.typedInput({
type: data.type || 'num',
types: ['num', 'str', 'bool']
})
.typedInput('type', data.type) // Nastaví typ při načtení
.typedInput('value', data.value) // Nastaví hodnotu při načtení
.change(function () {
data.type = $(this).typedInput('type');
data.value = $(this).typedInput('value');
updateScheduleTable();
RED.nodes.dirty(true);
});
updateScheduleTable();
}
});
const currentList = this.scheduleList || []; // Ujistíme se, že pracujeme s celým seznamem
if (currentList.length > 0) {
currentList.forEach(item => {
listContainer.editableList('addItem', item); // Přidání každé položky do seznamu
});
} else {
console.warn("No items to load into scheduler list.");
}
},
oneditsave: function () {
// Inicializace pole pro uložení dat
const items = [];
$("#scheduler-list").editableList('items').each(function () {
const data = $(this).data('data'); // Získání dat pro každou položku
if (data) {
items.push(data); // Přidání do pole
} else {
console.warn("Item data is undefined or invalid", $(this));
}
});
this.scheduleList = items; // Uložení dat do konfigurace nodu
},
oneditcancel: function () {
RED.nodes.dirty(false); // Reset dirty state if canceled
},
});
function updateScheduleTable() {
const tableBody = document.getElementById('table-body');
// Reset all cells to default (white background)
const rows = tableBody.children;
for (let row of rows) {
const cells = row.children;
for (let i = 1; i < cells.length; i++) {
cells[i].style.background = 'none';
}
}
// Apply schedule rules
const scheduleItems = $('#scheduler-list').editableList('items');
scheduleItems.each(function () {
const data = $(this).data('data');
if (!data || !data.startTime || !data.endTime || !data.color) return;
const days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
const startMinutes = parseTime(data.startTime);
const endMinutes = parseTime(data.endTime);
days.forEach((day, dayIndex) => {
if (data.text === day || (data.text === "Monday-Friday" && dayIndex < 5) || (data.text === "Saturday-Sunday" && dayIndex >= 5) || data.text === "Monday-Sunday") {
const row = tableBody.children[dayIndex];
if (!row) {
return;
}
const cells = row.children;
for (let i = 1; i < cells.length; i++) {
const cellStartMinutes = (i - 1) * 60;
const cellEndMinutes = i * 60;
if (startMinutes < cellEndMinutes && endMinutes > cellStartMinutes) {
const gradientStart = Math.max(0, (startMinutes - cellStartMinutes) / (cellEndMinutes - cellStartMinutes) * 100);
const gradientEnd = Math.min(100, (endMinutes - cellStartMinutes) / (cellEndMinutes - cellStartMinutes) * 100);
const existingBackground = cells[i].style.background;
const newBackground = `linear-gradient(to right, transparent ${gradientStart}%, ${data.color} ${gradientStart}%, ${data.color} ${gradientEnd}%, transparent ${gradientEnd}%)`;
cells[i].style.background = existingBackground ? `${newBackground}, ${existingBackground}` : newBackground;
}
}
}
});
});
}
function parseTime(time) {
if (!time || typeof time !== 'string') {
console.error("Invalid time format:", time);
return 0; // Vrátíme výchozí hodnotu 0 minut
}
const [hours, minutes] = time.split(":").map(Number);
return (hours || 0) * 60 + (minutes || 0);
}
</script>
<script type="text/html" data-template-name="scheduler">
<div class="form-row">
<label for="node-input-name" style="width: 100%;">
Name
</label>
<input type="text" id="node-input-name" style="width: 100%;" placeholder="Enter node name">
</div>
<div class="form-row">
<label for="scheduler-list" style="width: 100%;">
Rules list
</label>
<ol id="scheduler-list" style="min-height: 150px; height: 300px; overflow-y: auto; border: 1px solid #ccc; padding: 10px;"></ol>
</div>
<div class="form-row">
<label for="schedule-preview" style="width: 100%;">
Week Schedule Preview
</label>
<table id="schedule-table" style="width: 100%; border-collapse: collapse; border: 1px solid lightgray; table-layout: fixed;">
<thead>
<tr id="table-header">
<th style="border: 1px solid lightgray; width: 150px;">Day / Hour</th>
</tr>
</thead>
<tbody id="table-body">
</tbody>
</table>
<script>
// Generate column headers
headerRow = document.getElementById('table-header');
for (let hour = 0; hour < 24; hour += 1) {
const th = document.createElement('th');
th.style.border = '1px solid lightgray';
th.textContent = `${hour}`;
headerRow.appendChild(th);
}
// Generate rows for days
tableBody = document.getElementById('table-body');
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
days.forEach(day => {
const row = document.createElement('tr');
// Add day name
const dayCell = document.createElement('td');
dayCell.style.border = '1px solid lightgray';
dayCell.textContent = day;
row.appendChild(dayCell);
// Add time cells
for (let hour = 0; hour < 24; hour += 1) {
const cell = document.createElement('td');
cell.style.border = '1px solid lightgray';
row.appendChild(cell);
}
tableBody.appendChild(row);
});
</script>
</div>
</script>
<script type="text/html" data-help-name="scheduler">
<div class="form-row">
<label>Scheduler Node Help</label>
<p>The Scheduler node allows you to configure time-based rules to control your application.</p>
<h3>Features:</h3>
<ul>
<li>Define rules for specific days or day ranges.</li>
<li>Set start and end times for each rule.</li>
<li>Assign a value to output during active periods.</li>
</ul>
<h3>Configuration Options:</h3>
<dl>
<dt><b>Name</b></dt>
<dd>Specify a unique name for the node.</dd>
<dt><b>Rules List</b></dt>
<dd>Manage a list of time-based rules:</dd>
<ul>
<li><b>Day Range:</b> Select specific days or day ranges (e.g., Monday-Sunday).</li>
<li><b>Start Time:</b> Define when the rule becomes active.</li>
<li><b>End Time:</b> Define when the rule becomes inactive.</li>
<li><b>Value:</b> Set the value to output during the active period.</li>
<li><b>Color:</b> Assign a color to visualize the rule in the preview table.</li>
</ul>
</dl>
<h3>Outputs:</h3>
<ul>
<li>The node outputs a value defined in the rules when the current time falls within a rule's active period.</li>
</ul>
<h3>Examples:</h3>
<p>Example 1: Workday Schedule</p>
<ul>
<li>Day Range: Monday-Friday</li>
<li>Time: 09:00 - 17:00</li>
<li>Output Value: "Working Hours"</li>
</ul>
<p>Example 2: Weekend Relaxation</p>
<ul>
<li>Day Range: Saturday-Sunday</li>
<li>Time: 10:00 - 22:00</li>
<li>Output Value: "Relaxing"</li>
</ul>
<h3>Status Indicators:</h3>
<ul>
<li><b>Green dot:</b> Indicates an active rule is being executed.</li>
<li><b>Gray ring:</b> No active rules match the current time.</li>
</ul>
<h3>More Information:</h3>
<p>Visit the <a href="https://github.com/jirihusak/majordomus" target="_blank">project's GitHub repository</a> for more details and updates.</p>
</div>
</script>