esp32-timed-switch/html/index.html

283 lines
7.7 KiB
HTML

<html>
<head>
<title>ESP32 timed Switch</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.3/angular.js"></script>
</head>
<body>
<h1>Scheduler</h1>
<script>
class Schedule {
constructor(scheduleArray) {
if (scheduleArray && scheduleArray.length === 24) {
this.schedule = scheduleArray.slice(); // Copy the array to prevent direct mutation
} else {
// Initialize with all hours turned off if no valid array is provided
this.schedule = new Array(24).fill(0x000);
}
}
// Load a new schedule from an array of 24 integers
loadFromArrayBuffer = function(buffer) {
// Load from binary data
var view = new DataView(buffer);
for (var i = 0; i < 24; i++) {
this.schedule[i] = view.getUint32(i * 4);
}
};
// Update a single 5-minute segment for a single hour
updateSegment(hour, segment, state) {
if (hour < 0 || hour >= 24) {
throw new Error('Hour must be between 0 and 23.');
}
if (segment < 0 || segment >= 12) {
throw new Error('Segment must be between 0 and 11.');
}
// Calculate the bit position from the segment (0 for first segment, 11 for last)
const bitPosition = 11 - segment;
if (state) {
// Turn the segment on by setting the bit at the bit position
this.schedule[hour] |= (1 << bitPosition);
} else {
// Turn the segment off by clearing the bit at the bit position
this.schedule[hour] &= ~(1 << bitPosition);
}
}
serializeForDisplay() {
// Converts the schedule array into a display format with the appropriate class for coloring
return this.schedule.map(hour => {
if (hour === 0xFFF) { // All segments on
return { displayValue: ' ', class: 'on' };
} else if (hour === 0x000) { // All segments off
return { displayValue: ' ', class: 'off' };
} else {
return { displayValue: ' ', class: 'partial' };
}
});
}
prepareEditMatrix() {
// Creates a 24x12 matrix for editing the schedule
const editMatrix = [];
for (let hour = 0; hour < 24; hour++) {
const hourSegments = [];
for (let segment = 0; segment < 12; segment++) {
// Check if the segment is on or off
hourSegments.push({
value: (this.schedule[hour] & (1 << segment)) !== 0 ? 1 : 0
});
}
// Reverse the array to start with segment 0 on the left
editMatrix.push(hourSegments.reverse());
}
return editMatrix;
}
updateFromEditMatrix(editMatrix) {
// Update the schedule array based on the edit matrix
for (let hour = 0; hour < 24; hour++) {
let hourValue = 0;
for (let segment = 0; segment < 12; segment++) {
if (editMatrix[hour][11 - segment].value) {
hourValue |= (1 << segment);
}
}
this.schedule[hour] = hourValue;
}
}
serializeToArrayBuffer = function() {
// Convert to binary data
var buffer = new ArrayBuffer(24 * 4);
var view = new DataView(buffer);
for (var i = 0; i < this.schedule.length; i++) {
view.setUint32(i * 4, this.schedule[i]);
}
return buffer;
};
}
// End of Class
// Start of Angular
var app = angular.module('plugSchedulerApp', []);
app.controller('PlugScheduleController', ['$http', function($http) {
var ctrl = this;
ctrl.editMode = false;
ctrl.$onInit = function() {
ctrl.scheduleObj = new Schedule(); // Initialize with a default schedule
ctrl.getSchedule();
};
ctrl.getSchedule = function() {
if (ctrl.plugId === undefined) {
throw new Error('plugId is not defined');
}
// Fetch the schedule from the server for a given plug ID
var url = `/api/schedule/${ctrl.plugId}`
$http.get(url, { responseType: 'arraybuffer' }).then(function(response) {
ctrl.scheduleObj.loadFromArrayBuffer(response.data);
ctrl.displaySchedule = ctrl.scheduleObj.serializeForDisplay();
}).catch(function(error) {
console.error('Error fetching schedule:', error);
});
};
ctrl.modifySchedule = function() {
ctrl.editMode = true;
// Prepare a matrix for editing
ctrl.editMatrix = ctrl.scheduleObj.prepareEditMatrix();
};
ctrl.saveSchedule = function() {
ctrl.editMode = false;
// Convert the edit matrix back to schedule format
ctrl.scheduleObj.updateFromEditMatrix(ctrl.editMatrix);
ctrl.updateSchedule();
};
// Send the updated schedule to the server
ctrl.updateSchedule = function() {
var url = `/api/schedule/${ctrl.plugId}`
var serializedSchedule = ctrl.scheduleObj.serializeToArrayBuffer();
$http.post(url, serializedSchedule, {
headers: {
'Content-Type': 'application/octet-stream'
},
responseType: 'arraybuffer',
transformRequest: []
}).then(function(response) {
// ctrl.getSchedule();
console.log("successfully updated");
ctrl.displaySchedule = ctrl.scheduleObj.serializeForDisplay();
}, function(error) {
console.error('Error updating schedule:', error);
});
};
// check all segments for a specific hour
ctrl.checkAll = function(hourIndex) {
ctrl.editMatrix[hourIndex].forEach(segment => {
segment.value = 1;
});
};
// uncheck all segments for a specific hour
ctrl.uncheckAll = function(hourIndex) {
ctrl.editMatrix[hourIndex].forEach(segment => {
segment.value = 0;
});
};
}]);
app.component('plugScheduleWidget', {
bindings: {
plugId: '<'
},
template: `
<div class="plug-schedule-widget">
<h3>Schedule for Plug {{ $ctrl.plugId }}</h3>
<div ng-if="!$ctrl.editMode">
<div ng-repeat="hour in $ctrl.displaySchedule track by $index" class="hour-cell" ng-class="hour.class">
{{ hour.displayValue }}
</div>
<button ng-click="$ctrl.modifySchedule()">Modify Schedule</button>
</div>
<div ng-if="$ctrl.editMode">
<table>
<tr ng-repeat="hourSegments in $ctrl.editMatrix track by $index">
<td ng-repeat="segment in hourSegments track by $index">
<input type="checkbox" ng-model="segment.value" ng-true-value="1" ng-false-value="0">
</td>
<td>
<button ng-click="$ctrl.checkAll($index)">Check All</button>
<button ng-click="$ctrl.uncheckAll($index)">Uncheck All</button>
</td>
</tr>
</table>
<button ng-click="$ctrl.saveSchedule()">Save Schedule</button>
</div>
</div>
`,
controller: 'PlugScheduleController'
});
// End of Angular
</script>
<div ng-app="plugSchedulerApp">
<plug-schedule-widget plug-id="1"></plug-schedule-widget>
<plug-schedule-widget plug-id="2"></plug-schedule-widget>
<plug-schedule-widget plug-id="3"></plug-schedule-widget>
<plug-schedule-widget plug-id="4"></plug-schedule-widget>
<plug-schedule-widget plug-id="5"></plug-schedule-widget>
<plug-schedule-widget plug-id="6"></plug-schedule-widget>
<plug-schedule-widget plug-id="7"></plug-schedule-widget>
<plug-schedule-widget plug-id="8"></plug-schedule-widget>
</div>
<style>
* {
font-family: Calibri, Candara, Segoe, Segoe UI, Optima, Arial, sans-serif;
padding: 0px;
border-spacing: 0px;
border: transparent;
text-align: center;
color: #aaa;
font-weight: normal;
}
body {
width: 80%;
margin: auto;
background: #333340;
}
button {
padding: 4px 8px;
margin-left: 5px;
vertical-align: top;
border-radius: 12px;
background: #696983;
color: eee;
margin-top: 5px;
}
.hour-cell {
display: inline-block;
width: 10px;
height: 25px;
margin-top: 4px;
}
.off {
background-color: #eee;
}
.on {
background-color: #8e8;
}
.partial {
background-color: #bdc;
}
table {
margin: auto;
}
</style>
</body>
</html>