diff --git a/www/css/analog.css b/www/css/analog.css
index 4b2688349..2f4b90608 100644
--- a/www/css/analog.css
+++ b/www/css/analog.css
@@ -1,17 +1,39 @@
-@media screen and (max-width: 800px) and (min-width: 300px) {
- .hidecol{ display:none; }
- .hidecol2{ display:none; }
+.editAndDeleteBtn {
+ margin: 5px auto !important;
+ padding: 4px 0px !important;
+ font-size: 12px !important;
}
-@media screen and (max-width: 1200px) and (min-width: 800px) {
- .hidecol{ display:none; }
+
+@media screen and (max-width: 799px) and (min-width: 300px) {
+ .hidecol{display:none; }
+ .hidecol2{display:none; }
+ .editAndDeleteBtn {
+ margin: 4px auto !important;
+ padding: 4px 0px !important;
+ font-size: 11px !important;
+ }
+}
+
+@media screen and (max-width: 1199px) and (min-width: 800px) {
+ .hidecol{display:none; }
+ .editAndDeleteBtn {
+ margin: 4px auto !important;
+ padding: 4px 0px !important;
+ font-size: 11px !important;
+ }
}
#analogsensorlist table {
border: 1px solid lightgray;
border-collapse: collapse;
- width: 100%;
}
#analogsensorlist table tr {
border: 1px solid lightgray;
border-collapse: collapse;
}
+
+#analogsensorlist table th {
+ border: 1px solid lightgray;
+ border-collapse: collapse;
+ background: lightskyblue;
+}
diff --git a/www/img/check-black.png b/www/img/check-black.png
deleted file mode 100755
index 2cf2fc6ab..000000000
Binary files a/www/img/check-black.png and /dev/null differ
diff --git a/www/img/check-blue.png b/www/img/check-blue.png
new file mode 100644
index 000000000..89b7e2896
Binary files /dev/null and b/www/img/check-blue.png differ
diff --git a/www/index.html b/www/index.html
index bc93455de..23eedfb17 100644
--- a/www/index.html
+++ b/www/index.html
@@ -53,6 +53,7 @@
+
diff --git a/www/js/modules/analog.js b/www/js/modules/analog.js
index 71be23ce6..be5ba2abb 100644
--- a/www/js/modules/analog.js
+++ b/www/js/modules/analog.js
@@ -11,17 +11,216 @@
var OSApp = OSApp || {};
OSApp.Analog = {
- analogSensors: {},
- progAdjusts: {},
+ analogSensors: [],
+ progAdjusts: [],
+ monitors : [],
+ monitorAlerts : [],
+ expandItem : new Set(["sensors"]),
+ timer : null,
+
+ lastSensorHtml : "",
+
Constants: {
- CHARTS: 11,
- USERDEF_SENSOR: 49,
- USERDEF_UNIT: 99
+ CHARTS: 14,
+ USERDEF_UNIT: 99,
+
+ // Firmware version(s) required for analog sensor support
+ MIN_REQ_FW_VERSION : "2.3.3(172)", // Suggested upgrade for users with insufficient fw (see checkFirmwareUpdate)
+ MIN_REQ_FW_ID : 231,
+ MIN_REQ_FW_MIN : 150,
+
+ COLORS : ["#F3B415", "#F27036", "#663F59", "#6A6E94", "#4E88B4", "#00A7C6", "#18D8D8", '#A9D794', '#46AF78', '#A93F55', '#8C5E58', '#2176FF', '#33A1FD', '#7A918D', '#BAFF29'],
+ COLCOUNT : 15,
+
+ // channel ids for cordova notifications
+ CHANNELS: {
+ LOW: 'os_low',
+ MEDIUM: 'os_med',
+ HIGH: 'os_high'
+ },
+
+ // units used for sensor config, chart titles, etc
+ UNITS: {
+ DEFAULT: {ID: 0, NAME: 'Default', UNIT: '', GRAPH_TITLE: 'Default'},
+ SOIL_MOISTURE_PCT: {ID: 1, NAME: 'Soil Moisture %', UNIT: '%', GRAPH_TITLE: 'Soil Moisture'},
+ DEGREE_CELCIUS: {ID: 2, NAME: `Degree Celcius ${String.fromCharCode(176)}C`, UNIT: String.fromCharCode(176) + 'C', GRAPH_TITLE: 'Temperature'},
+ DEGREE_FARENHEIT: {ID: 3, NAME: `Degree Fahrenheit ${String.fromCharCode(176)}F`, UNIT: String.fromCharCode(176) + 'F', GRAPH_TITLE: 'Temperature'},
+ VOLT_V: {ID: 4, NAME: 'Volt V', UNIT: 'V', GRAPH_TITLE: 'Voltage'},
+ AIR_HUMIDITY_PCT: {ID: 5, NAME: 'Air Humidity %', UNIT: '%', GRAPH_TITLE: 'Air Humidity'},
+ INCH_IN: {ID: 6, NAME: 'Inch in', UNIT: 'in', GRAPH_TITLE: 'Rainfall'},
+ MILLIMETER_MM: {ID: 7, NAME: 'Millimeter mm', UNIT: 'mm', GRAPH_TITLE: 'Rainfall'},
+ MPH: {ID: 8, NAME: 'MPH', UNIT: 'mph', GRAPH_TITLE: 'Wind'},
+ KMH: {ID: 9, NAME: 'KM/H', UNIT: 'kmh', GRAPH_TITLE: 'Wind'},
+ LEVEL_PCT: {ID: 10, NAME: 'Level %', UNIT: '%', GRAPH_TITLE: 'Level'},
+ DK: {ID: 11, NAME: 'DK', UNIT: 'DK', GRAPH_TITLE: 'DK'},
+ LUMEN_LM: {ID: 12, NAME: 'Lumen (lm)', UNIT: 'lm', GRAPH_TITLE: 'LUmen'},
+ LUX_LX: {ID: 13, NAME: 'Lux (lx)', UNIT: 'lx', GRAPH_TITLE: 'Lux'},
+ OWN_UNIT: {ID: 99, NAME: 'Own Unit', UNIT: '', GRAPH_TITLE: ''},
+ },
+
+ // backup related items for import/export functionality
+ BACKUPS: {
+ SENSORS: {ID: 1, STORAGE_NAME: 'backupSensors', FILENAME: 'BackupSensorConfig'},
+ ADJUSTMENTS: {ID: 2, STORAGE_NAME: 'backupAdjustments', FILENAME: 'BackupSensorAdjustments'},
+ MONITOR: {ID: 4, STORAGE_NAME: 'backupMonitor', FILENAME: 'BackupMonitorConfig'},
+ ALL: {ID: 7, STORAGE_NAME: 'backupAll', FILENAME: 'BackupAllConfig'},
+ },
+
+ //detected Analog Sensor Boards:
+ ASB_BOARD1 : 0x01,
+ ASB_BOARD2 : 0x02,
+ OSPI_PCF8591 : 0x04,
+ OSPI_ADS1115 : 0x08,
+ UART_SC16IS752 : 0x10,
+ RS485_TRUEBNER1 : 0x20,
+ RS485_TRUEBNER2 : 0x40,
+ RS485_TRUEBNER3 : 0x80,
+ RS485_TRUEBNER4 : 0x100,
+ OSPI_USB_RS485 : 0x200,
+
+ NOTIFICATION_COLORS : ["#baffc9", "#faf0be", "#ffb3ba"],
+
+ MONITOR_DELETE : 0,
+ MONITOR_MIN : 1,
+ MONITOR_MAX : 2,
+ MONITOR_SENSOR12 : 3, //Digital OS Sensors
+ MONITOR_SET_SENSOR12 : 4, //Set Digital OS Sensors
+ MONITOR_AND : 10,
+ MONITOR_OR : 11,
+ MONITOR_XOR : 12,
+ MONITOR_NOT : 13,
+ MONITOR_TIME : 14,
+ MONITOR_REMOTE : 100,
+
+ //SENSOR TYPES:
+ SENSOR_NONE : 0, // None or deleted sensor
+ SENSOR_SMT100_MOIS : 1, // Truebner SMT100 RS485, moisture mode
+ SENSOR_SMT100_TEMP : 2, // Truebner SMT100 RS485, temperature mode
+ SENSOR_SMT100_PMTY : 3, // Truebner SMT100 RS485, permittivity mode
+ SENSOR_TH100_MOIS : 4, // Truebner TH100 RS485, humidity mode
+ SENSOR_TH100_TEMP : 5, // Truebner TH100 RS485, temperature mode
+ SENSOR_ANALOG_EXTENSION_BOARD : 10, // New OpenSprinkler analog extension board x8 - voltage mode 0..4V
+ SENSOR_ANALOG_EXTENSION_BOARD_P : 11, // New OpenSprinkler analog extension board x8 - percent 0..3.3V to 0..100%
+ SENSOR_SMT50_MOIS : 15, // New OpenSprinkler analog extension board x8 - SMT50 VWC [%] = (U * 50) : 3
+ SENSOR_SMT50_TEMP : 16, // New OpenSprinkler analog extension board x8 - SMT50 T [°C] = (U – 0,5) * 100
+ SENSOR_SMT100_ANALOG_MOIS : 17, // New OpenSprinkler analog extension board x8 - SMT100 VWC [%] = (U * 100) : 3
+ SENSOR_SMT100_ANALOG_TEMP : 18, // New OpenSprinkler analog extension board x8 - SMT50 T [°C] = (U * 100) : 3 - 40
+ SENSOR_VH400 : 30, // New OpenSprinkler analog extension board x8 - Vegetronix VH400
+ SENSOR_THERM200 : 31, // New OpenSprinkler analog extension board x8 - Vegetronix THERM200
+ SENSOR_AQUAPLUMB : 32, // New OpenSprinkler analog extension board x8 - Vegetronix Aquaplumb
+ SENSOR_USERDEF : 49, // New OpenSprinkler analog extension board x8 - User defined sensor
+ SENSOR_OSPI_ANALOG : 50, // Old OSPi analog input - voltage mode 0..3.3V
+ SENSOR_OSPI_ANALOG_P : 51, // Old OSPi analog input - percent 0..3.3V to 0...100%
+ SENSOR_OSPI_ANALOG_SMT50_MOIS : 52, // Old OSPi analog input - SMT50 VWC [%] = (U * 50) : 3
+ SENSOR_OSPI_ANALOG_SMT50_TEMP : 53, // Old OSPi analog input - SMT50 T [°C] = (U – 0,5) * 100
+ SENSOR_OSPI_INTERNAL_TEMP : 54, // Internal OSPI Temperature
+
+ SENSOR_MQTT : 90, // subscribe to a MQTT server and query a value
+
+ SENSOR_REMOTE : 100, // Remote sensor of an remote opensprinkler
+ SENSOR_WEATHER_TEMP_F : 101, // Weather service - temperature (Fahrenheit)
+ SENSOR_WEATHER_TEMP_C : 102, // Weather service - temperature (Celcius)
+ SENSOR_WEATHER_HUM : 103, // Weather service - humidity (%)
+ SENSOR_WEATHER_PRECIP_IN : 105, // Weather service - precip (inch)
+ SENSOR_WEATHER_PRECIP_MM : 106, // Weather service - precip (mm)
+ SENSOR_WEATHER_WIND_MPH : 107, // Weather service - wind (mph)
+ SENSOR_WEATHER_WIND_KMH : 108, // Weather service - wind (kmh)
+
+ SENSOR_GROUP_MIN : 1000, // Sensor group with min value
+ SENSOR_GROUP_MAX : 1001, // Sensor group with max value
+ SENSOR_GROUP_AVG : 1002, // Sensor group with avg value
+ SENSOR_GROUP_SUM : 1003, // Sensor group with sum value
+
+ PROG_LINEAR : 1, //formula see above
+ PROG_DIGITAL_MIN : 2, //under or equal min : factor1 else factor2
+ PROG_DIGITAL_MAX : 3, //over or equal max : factor2 else factor1
+ PROG_DIGITAL_MINMAX : 4, //under min or over max : factor1 else factor2
+ }
+};
+
+OSApp.Analog.success_callback = function() {
+};
+
+
+OSApp.Analog.asb_init = function() {
+ if (!OSApp.currentDevice.isAndroid && !OSApp.currentDevice.isiOS) return;
+
+ if (OSApp.currentDevice.isAndroid) {
+ window.cordova.plugins.notification.local.createChannel({
+ channelId: OSApp.Analog.Constants.CHANNELS.LOW,
+ channel: OSApp.Analog.Constants.CHANNELS.LOW,
+ channelName:'OpenSprinklerLowNotifications',
+ vibrate: false, // bool (optional), default is false
+ importance: 2, // int (optional) 0 to 4, default is IMPORTANCE_DEFAULT (3)
+ soundUsage: 5, // int (optional), default is USAGE_NOTIFICATION
+ }, OSApp.Analog.success_callback, this);
+ window.cordova.plugins.notification.local.createChannel({
+ channelId: OSApp.Analog.Constants.CHANNELS.MEDIUM,
+ channel: OSApp.Analog.Constants.CHANNELS.MEDIUM,
+ channelName:'OpenSprinklerMedNotifications',
+ vibrate: false, // bool (optional), default is false
+ importance: 3, // int (optional) 0 to 4, default is IMPORTANCE_DEFAULT (3)
+ soundUsage: 5, // int (optional), default is USAGE_NOTIFICATION
+ }, OSApp.Analog.success_callback, this);
+ window.cordova.plugins.notification.local.createChannel({
+ channelId: OSApp.Analog.Constants.CHANNELS.HIGH,
+ channel: OSApp.Analog.Constants.CHANNELS.HIGH,
+ channelName:'OpenSprinklerHighNotifications',
+ vibrate: true, // bool (optional), default is false
+ importance: 4, // int (optional) 0 to 4, default is IMPORTANCE_DEFAULT (3)
+ soundUsage: 5, // int (optional), default is USAGE_NOTIFICATION
+ }, OSApp.Analog.success_callback, this);
+ }
+ if (window.cordova && window.cordova.plugins) {
+
+ OSApp.Analog.timer = new window.nativeTimer();
+ OSApp.Analog.timer.onTick = function() {
+ OSApp.Analog.updateAnalogSensor( function() {
+ OSApp.Analog.updateMonitors();
+ });
+ };
+
+ window.cordova.plugins.backgroundMode.on('activate', function() {
+ OSApp.Analog.timer.start(1, 30*1000);
+ });
+ window.cordova.plugins.backgroundMode.on('deactivate', function() {
+ OSApp.Analog.timer.stop();
+ });
+
+ window.cordova.plugins.backgroundMode.setDefaults({
+ title: "OpenSprinklerASB",
+ text: OSApp.Language._("OpenSprinkler is running in background mode"),
+ subText: OSApp.Language._("active monitor and controlling notifications"),
+ channelName: "BackgroundChannel",
+ allowClose: false,
+ visibility: "public",
+ });
+ }
+ if (window.cordova && window.BackgroundFetch) {
+ var BackgroundFetch = window.BackgroundFetch;
+ var fetchCallback = function(taskId) {
+ console.log('[js] BackgroundFetch event received: ', taskId);
+ OSApp.Analog.updateAnalogSensor( function() {
+ OSApp.Analog.updateMonitors( function() {
+ BackgroundFetch.finish(taskId);
+ });
+ });
+ };
+
+ var failureCallback = function(taskId) {
+ console.log('- BackgroundFetch failed');
+ BackgroundFetch.finish(taskId);
+ };
+
+ BackgroundFetch.configure({
+ minimumFetchInterval: 15,
+ requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY
+ }, fetchCallback, failureCallback);
}
};
OSApp.Analog.checkAnalogSensorAvail = function() {
- return OSApp.currentSession.controller.options && OSApp.currentSession.controller.options.feature === "ASB";
+ return OSApp.currentSession.controller.options && OSApp.currentSession.controller.options.feature?.includes("ASB");
};
OSApp.Analog.refresh = function() {
@@ -30,6 +229,14 @@ OSApp.Analog.refresh = function() {
}, 100 );
};
+OSApp.Analog.enc = function(s) {
+ //encodeURIComponent does not encode a single "%" !
+ if (s) {
+ return encodeURIComponent(s);
+ }
+ return s;
+};
+
OSApp.Analog.updateProgramAdjustments = function( callback ) {
callback = callback || function() { };
return OSApp.Firmware.sendToOS( "/se?pw=", "json" ).then( function( data ) {
@@ -38,52 +245,275 @@ OSApp.Analog.updateProgramAdjustments = function( callback ) {
} );
};
+OSApp.Analog.checkBackgroundMode = function() {
+ if (!OSApp.currentDevice.isAndroid && !OSApp.currentDevice.isiOS) return;
+ if (!window.cordova) return;
+ //Enable background mode only if we have a monitor configured:
+ if (OSApp.Analog.monitors && OSApp.Analog.monitors.length > 0) {
+ if (!window.cordova.plugins.backgroundMode.isActive() && !window.cordova.plugins.backgroundMode.isEnabled())
+ window.cordova.plugins.backgroundMode.setEnabled(true);
+ } else if (window.cordova.plugins.backgroundMode.isEnabled()) {
+ window.cordova.plugins.backgroundMode.setEnabled(false);
+ }
+};
+
+OSApp.Analog.updateMonitors = function(callback) {
+ callback = callback || function () { };
+
+ OSApp.Analog.checkBackgroundMode();
+
+ if (OSApp.Firmware.checkOSVersion(233)) {
+ return OSApp.Firmware.sendToOS("/ml?pw=", "json").then(function (data) {
+
+ OSApp.Analog.monitors = data.monitors;
+ OSApp.Analog.checkMonitorAlerts();
+ callback();
+ });
+ } else callback();
+};
+
OSApp.Analog.updateAnalogSensor = function( callback ) {
callback = callback || function() { };
return OSApp.Firmware.sendToOS( "/sl?pw=", "json" ).then( function( data ) {
OSApp.Analog.analogSensors = data.sensors;
+ if (Object.prototype.hasOwnProperty.call(data, "detected"))
+ OSApp.Analog.analogSensors.detected = data.detected;
callback();
} );
};
-OSApp.Analog.updateSensorShowArea = function( page ) {
- var showArea = page.find( "#os-sensor-show" ),
- html = "";
-
- if ( OSApp.Analog.checkAnalogSensorAvail() ) {
- var i, j;
- for ( i = 0; i < OSApp.Analog.progAdjusts.length; i++ ) {
- var progAdjust = OSApp.Analog.progAdjusts[ i ];
- var sensorName = "";
- for ( j = 0; j < OSApp.Analog.analogSensors.length; j++ ) {
- if ( OSApp.Analog.analogSensors[ j ].nr === progAdjust.sensor ) {
- sensorName = OSApp.Analog.analogSensors[ j ].name;
+OSApp.Analog.notification_action_callback = function() {
+ // monitorAlerts[monitor.nr] = false;
+};
+
+OSApp.Analog.checkMonitorAlerts = function() {
+ if (!window.cordova || !window.cordova.plugins || !OSApp.Analog.monitors || (!OSApp.currentDevice.isAndroid && !OSApp.currentDevice.isiOS))
+ return;
+
+ for (let i = 0; i < OSApp.Analog.monitors.length; i++) {
+ var monitor = OSApp.Analog.monitors[i];
+ if (monitor.active) {
+
+ if (!OSApp.Analog.monitorAlerts[monitor.nr]) {
+ OSApp.Analog.monitorAlerts[monitor.nr] = true;
+ var dname, chan;
+ if ( typeof OSApp.currentSession.controller.settings.dname !== "undefined" )
+ dname = OSApp.currentSession.controller.settings.dname;
+ else
+ dname = "OpenSprinkler";
+ let prio = Object.prototype.hasOwnProperty.call(monitor, "prio")?monitor.prio:0;
+
+ switch (prio) {
+ case 0:
+ chan = OSApp.Analog.Constants.CHANNELS.LOW;
+ break;
+ case 1:
+ chan = OSApp.Analog.Constants.CHANNELS.MEDIUM;
+ break;
+ default:
+ chan = OSApp.Analog.Constants.CHANNELS.HIGH;
}
+
+ window.cordova.plugins.notification.local.schedule({
+ id: monitor.nr,
+ channelId: chan,
+ channel: chan,
+ title: dname,
+ text: monitor.name,
+ priority: prio,
+ beep: prio>=2,
+ lockscreen: true,
+ color: OSApp.Analog.Constants.NOTIFICATION_COLORS[prio],
+ }, OSApp.Analog.notification_action_callback, monitor);
}
- var progName = "?";
- if ( progAdjust.prog >= 1 && progAdjust.prog <= OSApp.currentSession.controller.programs.pd.length ) {
- progName = OSApp.Programs.readProgram( OSApp.currentSession.controller.programs.pd[ progAdjust.prog - 1 ] ).name;
- }
+ }
+ else if (OSApp.Analog.monitorAlerts[monitor.nr]) {
+ OSApp.Analog.monitorAlerts[monitor.nr] = false;
+ }
+ }
+};
- html += "
";
- html += "";
- html += "
";
+OSApp.Analog.updateSensorShowArea = function( page ) {
+ if (OSApp.Analog.checkAnalogSensorAvail()) {
+ var showArea = page.find("#os-sensor-show");
+ var html = "", i, j;
+ html += "
";
+ var cols = Math.round(window.innerWidth / 300);
+
+ for (i = 0; i < OSApp.Analog.progAdjusts.length; i++) {
+ if (i % cols == 0) {
+ if (i > 0)
+ html += "";
+ html += "
";
+ }
+ html += "
";
+ }
+ if (i > 0)
+ html += "
";
+ html += "
";
+
+ if (OSApp.Firmware.checkOSVersion(233) && OSApp.Analog.monitors) {
+ for (i = 0; i < OSApp.Analog.monitors.length; i++) {
+ var monitor = OSApp.Analog.monitors[i];
+ if (monitor.active) {
+ let prio = Object.prototype.hasOwnProperty.call(monitor, "prio")?monitor.prio:0;
+ let pcolor = OSApp.Analog.Constants.NOTIFICATION_COLORS[prio];
+ html += "
";
+ html += "";
+ html += "
";
+ }
+ }
}
- for ( i = 0; i < OSApp.Analog.analogSensors.length; i++ ) {
- var sensor = OSApp.Analog.analogSensors[ i ];
- if ( sensor.show ) {
+ for (i = 0; i < OSApp.Analog.analogSensors.length; i++) {
+ var sensor = OSApp.Analog.analogSensors[i];
+ if (sensor.show) {
html += "
" + (progAdjust.nr > 0 ? OSApp.Language._("Edit Program Adjustment") : OSApp.Language._("New Program Adjustment")) + "
" +
"
" +
"
" +
"
" +
- OSApp.Language._( "Notice: If you want to combine multiple sensors, then build a sensor group. " ) +
- OSApp.Language._( "See help documentation for details." ) +
+ OSApp.Language._("Notice: If you want to combine multiple sensors, then build a sensor group. ") +
+ OSApp.Language._("See help documentation for details.") +
"
" +
+ OSApp.Language._("Notice: If you want to combine multiple sensors, then build a sensor group. ") +
+ OSApp.Language._("See help documentation for details.") +
+ "