www/js/fermenter.js

changeset 678
cc49115e769e
child 680
8b3c86124a08
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/www/js/fermenter.js	Mon Apr 15 17:04:57 2024 +0200
@@ -0,0 +1,494 @@
+/*****************************************************************************
+ * Copyright (C) 2024
+ *
+ * Michiel Broek <mbroek at mbse dot eu>
+ *
+ * This file is part of mbsePi-apps thermferm
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Brewery Management System istributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ThermFerm; see the file COPYING.  If not, write to the Free
+ * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *****************************************************************************/
+
+function createAbortElements() {
+ $('#eventWindow').jqxWindow({
+  theme: theme,
+  position: { x: 440, y: 210 },
+  width: 400,
+  height: 200,
+  resizable: false,
+  isModal: true,
+  modalOpacity: 0.4,
+  okButton: $('#delOk'),
+  cancelButton: $('#delCancel'),
+  initContent: function() {
+   $('#delOk').jqxButton({ template: 'danger', width: '65px', theme: theme });
+   $('#delCancel').jqxButton({ template: 'success', width: '65px', theme: theme });
+   $('#delCancel').focus();
+  }
+ });
+ $('#eventWindow').jqxWindow('hide');
+}
+
+
+$(document).ready(function() {
+
+ var record = {},
+ global = {},
+ blank = {},
+ ppayload = '',
+ yl = 12, // Normal yeast temp range
+ yh = 24,
+
+ gaugeoptions = {
+  min: 0, max: 45, width: 375, height: 375,
+  ranges: [{ startValue: 0, endValue: yl, style: { fill: '#3399FF', stroke: '#3399FF' }, endWidth: 10, startWidth: 10 },
+           { startValue: yl, endValue: yh, style: { fill: '#00CC33', stroke: '#00CC33' }, endWidth: 10, startWidth: 10 },
+           { startValue: yh, endValue: 45, style: { fill: '#FC6A6A', stroke: '#FC6A6A' }, endWidth: 10, startWidth: 10 }],
+  ticksMinor: { interval: 1, size: '5%' },
+  ticksMajor: { interval: 5, size: '9%' },
+  labels: { interval: 5 },
+  style: { fill: '#eeeeee', stroke: '#666666' },
+  value: 0,
+  colorScheme: 'scheme05'
+ },
+ gaugeSmalloptions = {
+  min: -15, max: 25, width: 190, height: 190,
+  ranges: [{ startValue: -15, endValue: 0, startWidth: 5, endWidth: 5, style: { fill: '#3399FF', stroke: '#3399FF' }},
+           { startValue: 0, endValue: 10, startWidth: 5, endWidth: 5, style: { fill: '#00CC33', stroke: '#00CC33' }},
+           { startValue: 10, endValue: 25, startWidth: 5, endWidth: 5, style: { fill: '#FC6A6A', stroke: '#FC6A6A' }}],
+  ticksMinor: { interval: 1, size: '5%' },
+  ticksMajor: { interval: 5, size: '9%' },
+  labels: { interval: 5 },
+  style: { fill: '#eeeeee', stroke: '#666666' },
+  value: 0,
+  colorScheme: 'scheme05',
+  caption: { value: 'Chiller', position: 'bottom', offset: [0, 10] }
+ },
+ switchoptions = {
+  height: 68,
+  width: 35,
+  onLabel: 'ON',
+  offLabel: 'OFF',
+  theme: theme,
+  thumbSize: '50%',
+  orientation: 'vertical'
+ },
+ targetoptions = { inputMode: 'simple', theme: theme, width: 70, min: 0, max: 45, decimalDigits: 1, spinButtons: true },
+
+ globalSource = {
+  datatype: 'json',
+  cache: false,
+  datafields: [
+   { name: 'type' },
+   { name: 'name' },
+   { name: 'node' },
+   { name: 'os' },
+   { name: 'os_version' },
+   { name: 'FW' },
+   { name: 'room_temp', map: 'THB>temperature', type: 'float' },
+   { name: 'room_hum', map: 'THB>humidity', type: 'float' }
+  ],
+  id: 'name',
+  url: 'getglobal.php'
+ },
+ globalData = new $.jqx.dataAdapter(globalSource, {
+  loadComplete: function(records) {
+   global = globalData.records[0];
+   updateScreen();
+  }
+ }),
+ url = 'getfermenter.php?uuid=' + my_uuid,
+ source = {
+  datatype: 'json',
+  datafields: [
+   { name: 'type' },
+   { name: 'unit' },
+   { name: 'beercode', map: 'metric>product>code' },
+   { name: 'beername', map: 'metric>product>name' },
+   { name: 'yeast_lo', map: 'metric>product>yeast_lo' },
+   { name: 'yeast_hi', map: 'metric>product>yeast_hi' },
+   { name: 'air_state', map: 'metric>air>state' },
+   { name: 'air_temperature', map: 'metric>air>temperature' },
+   { name: 'beer_state', map: 'metric>beer>state' },
+   { name: 'beer_temperature', map: 'metric>beer>temperature' },
+   { name: 'chiller_state', map: 'metric>chiller>state' },
+   { name: 'chiller_temperature', map: 'metric>chiller>temperature' },
+   { name: 'heater_state', map: 'metric>heater>state' },
+   { name: 'heater_usage', map: 'metric>heater>usage' },
+   { name: 'cooler_state', map: 'metric>cooler>state' },
+   { name: 'cooler_usage', map: 'metric>cooler>usage' },
+   { name: 'fan_state', map: 'metric>fan>state' },
+   { name: 'fan_usage', map: 'metric>fan>usage' },
+   { name: 'light_address', map: 'metric>light>address' },
+   { name: 'light_state', map: 'metric>light>state' },
+   { name: 'light_usage', map: 'metric>light>usage' },
+   { name: 'door_address', map: 'metric>door>address' },
+   { name: 'door_state', map: 'metric>door>state' },
+   { name: 'psu_address', map: 'metric>psu>address' },
+   { name: 'psu_state', map: 'metric>psu>state' },
+   { name: 'mode', map: 'metric>mode' },
+   { name: 'alarm', map: 'metric>alarm', type: 'int' },
+   { name: 'setpoint_high', map: 'metric>setpoint>high' },
+   { name: 'setpoint_low', map: 'metric>setpoint>low' },
+   { name: 'profile_uuid', type: 'string' },
+   { name: 'profile_name', type: 'string' },
+   { name: 'profile_state', type: 'string' },
+   { name: 'profile_percent', type: 'int' },
+   { name: 'profile_inittemp_high', type: 'float' },
+   { name: 'profile_inittemp_low', type: 'float' },
+   { name: 'profile_steps', type: 'string' },
+   { name: 'stage', map: 'metric>stage', type: 'string' },
+   { name: 'beeruuid', map: 'metric>product>uuid' }
+  ],
+  id: 'alias',
+  url: url
+ },
+ dataAdapter = new $.jqx.dataAdapter(source, {
+  loadComplete: function(records) {
+   record = dataAdapter.records[0];
+   updateScreen();
+  }
+ });
+
+ function updateScreen() {
+   $('#room_thb').html(global.room_temp + '&deg;C&nbsp;&nbsp;' + global.room_hum + '% humidity');
+   $('#info_system').html(record.unit);
+   $('#info_beer').html(record.beercode + ' - ' + record.beername);
+   $('#info_mode').jqxDropDownList('selectItem', record.mode);
+   $('#info_stage').jqxDropDownList('selectItem', record.stage);
+   if (record.door_address) {
+     if (record.door_state != '0') {
+      $('#fermenter_doorled').html('<div class="LEDyellow_on"></div>Door');
+     } else {
+      $('#fermenter_doorled').html('<div class="LEDyellow_off"></div>Door');
+     }
+   }
+   if (record.light_address) {
+     if (record.light_state != '0') {
+      $('#fermenter_lightled').html('<div class="LEDyellow_on"></div>Light');
+     } else {
+      $('#fermenter_lightled').html('<div class="LEDyellow_off"></div>Light');
+     }
+   }
+   if (record.mode != 'OFF') {
+    $('#fermenter_powerled').html('<div class="LEDblue_on"></div>Power');
+   } else {
+    $('#fermenter_powerled').html('<div class="LEDblue_off"></div>Power');
+   }
+    if (record.alarm != '0') {
+     $('#fermenter_alarmled').html('<div class="LEDred_on"></div>Alarm');
+    } else {
+     $('#fermenter_alarmled').html('<div class="LEDred_off"></div>Alarm');
+    }
+
+    $('#target_lo').val(record.setpoint_low);
+    $('#target_hi').val(record.setpoint_high);
+    if ((record.mode == 'FRIDGE') || (record.mode == 'BEER')) {
+     $('#target_lo').jqxNumberInput({ readOnly: false, Width: 70, spinButtons: true });
+     $('#target_hi').jqxNumberInput({ readOnly: false, Width: 70, spinButtons: true });
+    } else {
+     $('#target_lo').jqxNumberInput({ readOnly: true, Width: 50, spinButtons: false });
+     $('#target_hi').jqxNumberInput({ readOnly: true, Width: 50, spinButtons: false });
+    }
+
+    $('.f_control_leds').show();
+    if (record.heater_state != '0') {
+     $('#fermenter_led1').html('<div class="LEDgreen_on"></div>Heat');
+    } else {
+     $('#fermenter_led1').html('<div class="LEDgreen_off"></div>Heat');
+    }
+    if (record.cooler_state != '0') {
+     $('#fermenter_led2').html('<div class="LEDgreen_on"></div>Cool');
+    } else {
+     $('#fermenter_led2').html('<div class="LEDgreen_off"></div>Cool');
+    }
+    if (record.fan_state != '0') {
+     $('#fermenter_led3').html('<div class="LEDgreen_on"></div>Fan');
+    } else {
+     $('#fermenter_led3').html('<div class="LEDgreen_off"></div>Fan');
+    }
+
+    if (record.mode == 'NONE') {
+     $('.f_control_switches').show();
+    } else {
+     $('.f_control_switches').hide();
+    }
+    if ((record.heater_state != '0') != $('#fermenter_toggle1').jqxSwitchButton('val'))
+     $('#fermenter_toggle1').val(record.heater_state != '0');
+    if ((record.cooler_state != '0') != $('#fermenter_toggle2').jqxSwitchButton('val'))
+     $('#fermenter_toggle2').val(record.cooler_state != '0');
+    if ((record.fan_state != '0') != $('#fermenter_toggle3').jqxSwitchButton('val'))
+     $('#fermenter_toggle3').val(record.fan_state != '0');
+
+    $('#info_profile').html(record.profile_name);
+    if (record.profile_name == '')
+     $('#info_mode').jqxDropDownList('disableItem', 'PROFILE');
+    else
+     $('#info_mode').jqxDropDownList('enableItem', 'PROFILE');
+
+    if (record.mode == 'PROFILE') {
+     if (record.profile_state == 'OFF') {
+      $('#info_mode').jqxDropDownList({ disabled: false });
+      $('#Profile1').jqxButton({ template: 'success', value: 'Starten' });
+      $('#Profile1').show();
+      $('#Profile2').hide();
+      $('#status_profile').html('');
+     } else if (record.profile_state == 'RUN') {
+      $('#info_mode').jqxDropDownList({ disabled: true });
+      $('#Profile1').jqxButton({ template: 'danger', value: 'Afbreken' });
+      $('#Profile2').jqxButton({ template: 'primary', value: 'Pauze' });
+      $('#Profile1').show();
+      $('#Profile2').show();
+      $('#status_profile').html('Profiel actief, ' + record.profile_percent + '% gereed');
+     } else if (record.profile_state == 'PAUSE') {
+      $('#info_mode').jqxDropDownList({ disabled: true });
+      $('#Profile1').jqxButton({ template: 'danger', value: 'Afbreken' });
+      $('#Profile2').jqxButton({ template: 'success', value: 'Doorgaan' });
+      $('#Profile1').show();
+      $('#Profile2').show();
+      $('#status_profile').html('Profiel pauze, ' + record.profile_percent + '% gereed');
+     } else if (record.profile_state == 'DONE') {
+      $('#info_mode').jqxDropDownList({ disabled: true });
+      $('#Profile1').jqxButton({ template: 'primary', value: 'Profiel Ok' });
+      $('#Profile1').show();
+      $('#Profile2').hide();
+      $('#status_profile').html('Profiel is gereed');
+     }
+    } else {
+     $('#info_mode').jqxDropDownList({ disabled: false });
+     $('#Profile1').hide();
+     $('#Profile2').hide();
+     $('#status_profile').html('');
+    }
+
+    var yl = record.yeast_lo;
+    var yh = record.yeast_hi;
+    var range = { ranges: [{ startValue: 0, endValue: yl, style: { fill: '#3399FF', stroke: '#3399FF' }, endWidth: 10, startWidth: 10 },
+                      { startValue: yl, endValue: yh, style: { fill: '#00CC33', stroke: '#00CC33' }, endWidth: 10, startWidth: 10 },
+                      { startValue: yh, endValue: 45, style: { fill: '#FC6A6A', stroke: '#FC6A6A' }, endWidth: 10, startWidth: 10 }]};
+    $('#gaugeContainer_air').jqxGauge(range);
+    $('#gaugeContainer_beer').jqxGauge(range);
+
+    if (record.air_temperature !== undefined) {
+     $('#gaugeContainer_air').jqxGauge({ caption: { value: 'Air: ' + record.air_temperature.toFixed(3) }});
+     $('#gaugeContainer_air').jqxGauge({ value: record.air_temperature });
+    }
+    if (record.air_state == 'OK') {
+     $('#gaugeContainer_air').jqxGauge({ disabled: false });
+    } else {
+     $('#gaugeContainer_air').jqxGauge({ disabled: true });
+    }
+    if (record.beer_temperature !== undefined) {
+     $('#gaugeContainer_beer').jqxGauge({ caption: { value: 'Beer: ' + record.beer_temperature.toFixed(3) }});
+     $('#gaugeContainer_beer').jqxGauge({ value: record.beer_temperature });
+    }
+    if (record.beer_state == 'OK') {
+     $('#gaugeContainer_beer').jqxGauge({ disabled: false });
+    } else {
+     $('#gaugeContainer_beer').jqxGauge({ disabled: true });
+    }
+    if (record.chiller_temperature !== undefined) {
+     $('#gaugeContainer_chiller').jqxGauge({ value: record.chiller_temperature });
+    }
+    if (record.chiller_state == 'OK') {
+     $('#gaugeContainer_chiller').jqxGauge({ disabled: false });
+    } else {
+     $('#gaugeContainer_chiller').jqxGauge({ disabled: true });
+    }
+ }
+
+ $('#gaugeContainer_air').jqxGauge(gaugeoptions);
+ $('#gaugeContainer_air').jqxGauge({ caption: { value: 'Air: 00.000' }});
+ $('#gaugeContainer_beer').jqxGauge(gaugeoptions);
+ $('#gaugeContainer_beer').jqxGauge({ caption: { value: 'Beer: 00.000' }});
+ $('#gaugeContainer_chiller').jqxGauge(gaugeSmalloptions);
+
+ $('#fermenter_toggle1').jqxSwitchButton(switchoptions);
+ $('#fermenter_toggle2').jqxSwitchButton(switchoptions);
+ $('#fermenter_toggle3').jqxSwitchButton(switchoptions);
+
+ srcMode = ['OFF', 'NONE', 'FRIDGE', 'BEER', 'PROFILE'];
+ srcStage = ['PRIMARY', 'SECONDARY', 'TERTIARY', 'CARBONATION'];
+ $('#info_mode').jqxDropDownList({ theme: theme, source: srcMode, width: 100, height: 24, dropDownHeight: 156 });
+ $('#info_stage').jqxDropDownList({ theme: theme, source: srcStage, width: 150, height: 24, dropDownHeight: 125 });
+
+ $('#target_lo').jqxNumberInput(targetoptions);
+ $('#target_hi').jqxNumberInput(targetoptions);
+
+ $('#Profile1').jqxButton({ template: 'info', width: '150px', height: 24, theme: theme });
+ $('#Profile2').jqxButton({ template: 'info', width: '150px', height: 24, theme: theme });
+ $('#Profile1').hide(); // Hide these until they are needed.
+ $('#Profile2').hide();
+
+ // Get the data immediatly and then at regular intervals to refresh.
+ dataAdapter.dataBind();
+ globalData.dataBind();
+
+ $('#info_mode').on('select', function(event) {
+  if (event.args && event.args.item.value != record.mode) {
+   record.mode = event.args.item.value;
+   console.log('set mode ' + record.mode);
+   var msg = '{"type":"fermenter","unit":"' + record.unit + '","mode":"' + record.mode + '"}';
+   websocket.send(msg);
+  }
+ });
+ $('#info_stage').on('select', function(event) {
+  if (event.args && event.args.item.value != record.stage) {
+   record.stage = event.args.item.value;
+   console.log('set stage ' + record.stage);
+   var msg = '{"type":"fermenter","unit":"' + record.unit + '","stage":"' + record.stage + '"}';
+   websocket.send(msg);
+  }
+ });
+
+ $('#target_lo').on('change', function(event) {
+  record.setpoint_low = parseFloat(event.args.value);
+  // Keep the high target above the low.
+  if (record.setpoint_low > record.setpoint_high) {
+   record.setpoint_high = record.setpoint_low;
+   $('#target_hi').val(record.setpoint_high);
+  }
+  console.log('set setpoints ' + record.setpoint_low + ' ' + record.setpoint_high);
+  websocket.send('{"type":"fermenter","unit":"' + record.unit +
+            '","setpoint_low":' + record.setpoint_low + ',"setpoint_high":' + record.setpoint_high + '}');
+ });
+ $('#target_hi').on('change', function(event) {
+  record.setpoint_high = parseFloat(event.args.value);
+  // Keep the low target below the high.
+  if (record.setpoint_high < record.setpoint_low) {
+   record.setpoint_low = record.setpoint_high;
+   $('#target_lo').val(record.setpoint_low);
+  }
+  console.log('set setpoints ' + record.setpoint_low + ' ' + record.setpoint_high);
+  websocket.send('{"type":"fermenter","unit":"' + record.unit +
+            '","setpoint_low":' + record.setpoint_low + ',"setpoint_high":' + record.setpoint_high + '}');
+ });
+
+ $('#fermenter_toggle1').on('checked', function(event) {
+  if (record.mode == 'NONE' && record.heater_state != 0) {
+   console.log('set heater ' + $("#fermenter_toggle1").jqxSwitchButton('val'));
+   websocket.send('{"type":"fermenter","unit":"' + record.unit + '","heater_state":0}');
+  }
+ });
+ $('#fermenter_toggle1').on('unchecked', function(event) {
+  if (record.mode == 'NONE' && record.heater_state == 0) {
+   console.log('set heater ' + $("#fermenter_toggle1").jqxSwitchButton('val'));
+   websocket.send('{"type":"fermenter","unit":"' + record.unit + '","heater_state":100,"cooler_state":0}');
+  }
+ });
+ $('#fermenter_toggle2').on('checked', function(event) {
+  if (record.mode == 'NONE' && record.cooler_state != 0) {
+   console.log('set cooler ' + $("#fermenter_toggle2").jqxSwitchButton('val'));
+   websocket.send('{"type":"fermenter","unit":"' + record.unit + '","cooler_state":0}');
+  }
+ });
+ $('#fermenter_toggle2').on('unchecked', function(event) {
+  if (record.mode == 'NONE' & record.cooler_state == 0) {
+   console.log('set cooler ' + $("#fermenter_toggle2").jqxSwitchButton('val'));
+   websocket.send('{"type":"fermenter","unit":"' + record.unit + '","cooler_state":100,"heater_state":0}');
+  }
+ });
+ $('#fermenter_toggle3').on('checked', function(event) {
+  if (record.mode == 'NONE' && record.fan_state != 0) {
+   websocket.send('{"type":"fermenter","unit":"' + record.unit + '","fan_state":0}');
+  }
+ });
+ $('#fermenter_toggle3').on('unchecked', function(event) {
+  if (record.mode == 'NONE' && record.fan_state == 0) {
+   websocket.send('{"type":"fermenter","unit":"' + record.unit + '","fan_state":100}');
+  }
+ });
+ $('#Profile1').click(function() {
+  if (record.mode == 'PROFILE') {
+   if (record.profile_state == 'OFF') {
+    websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"start"}}');
+   } else if ((record.profile_state == 'RUN') || (record.profile_state == 'PAUSE')) {
+    // Open a popup to confirm this action.
+    $('#eventWindow').jqxWindow('open');
+    $('#delOk').click(function() {
+     websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"abort"}}');
+    });
+   } else if (record.profile_state == 'DONE') {
+    websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"done"}}');
+   }
+  }
+ });
+ $('#Profile2').click(function() {
+  if (record.mode == 'PROFILE') {
+   if ((record.profile_state == 'RUN') || (record.profile_state == 'PAUSE')) {
+    websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"pause"}}');
+   }
+  }
+ });
+
+ createAbortElements();
+
+ websocket.onmessage = function(evt) {
+  var msg = evt.data;
+  var obj = JSON.parse(msg);
+
+  console.log('ws got ' + msg);
+
+  if (obj.ping == 1) {
+   console.log('ws got ping');
+   websocket.send('{"pong":1}');
+  }
+
+  if (obj.type == 'fermenter' && obj.unit == record.unit) {
+   console.log('ws got this device ' + msg);
+   record.beeruuid = obj.metric.product.uuid;
+   record.beercode = obj.metric.product.code;
+   record.beername = obj.metric.product.name;
+   record.yeast_lo = obj.metric.product.yeast_lo;
+   record.yeast_hi = obj.metric.product.yeast_hi;
+   record.air_state = obj.metric.air.state;
+   record.air_temperature = obj.metric.air.temperature;
+   record.beer_state = obj.metric.beer.state;
+   record.beer_temperature = obj.metric.beer.temperature;
+   record.chiller_state = obj.metric.chiller.state;
+   record.chiller_temperature = obj.metric.chiller.temperature;
+   if (obj.metric.heater.state !== undefined)
+     record.heater_state = obj.metric.heater.state;
+   if (obj.metric.cooler.state !== undefined)
+     record.cooler_state = obj.metric.cooler.state;
+   if (obj.metric.fan.state !== undefined)
+    record.fan_state = obj.metric.fan.state;
+   if (obj.metric.door)
+     record.door_state = obj.metric.door.state;
+   if (obj.metric.light)
+     record.light_state = obj.metric.light.state;
+   if (obj.metric.psu)
+     record.psu_state = obj.metric.psu.state;
+   record.mode = obj.metric.mode;
+   record.stage = obj.metric.stage;
+   record.alarm = obj.metric.alarm;
+   record.setpoint_low = obj.metric.setpoint.low;
+   record.setpoint_high = obj.metric.setpoint.high;
+   if (obj.profile) {
+     record.profile_uuid = obj.profile_uuid;
+     record.profile_name = obj.profile_name;
+     record.profile_state = obj.profile_state;
+     record.profile_percent = obj.profile_percent;
+     record.profile_inittemp_high = obj.profile_inittemp_high;
+     record.profile_inittemp_low = obj.profile_inittemp_low;
+   } else {
+     record.profile_uuid = '';
+     record.profile_name = '';
+     record.profile_state = '';
+     record.profile_percent = 0;
+   }
+   updateScreen();
+  }
+ }
+});

mercurial