283 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			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>
 |