From cbfa9ddfec3e5fc5d6d3c3997749e4d2034ffdce Mon Sep 17 00:00:00 2001 From: Jeremy Poulter Date: Mon, 11 Sep 2017 20:11:12 +0100 Subject: [PATCH] WIP: re-org of WiFi code --- .eslintrc.json | 4 +- platformio.ini | 2 +- src/html/config.js | 1197 +---------------- src/html/manifest.json | 9 +- src/html/view_models/BaseViewModel.js | 25 + src/html/view_models/ConfigViewModel.js | 44 + src/html/view_models/OpenEvseViewModel.js | 451 +++++++ src/html/view_models/OpenEvseWiFiViewModel.js | 436 ++++++ src/html/view_models/RapiViewModel.js | 23 + src/html/view_models/StatusViewModel.js | 103 ++ src/html/view_models/TimeViewModel.js | 92 ++ src/html/view_models/WiFiConfigViewModel.js | 124 ++ src/html/view_models/WiFiPortalViewModel.js | 55 + src/html/view_models/WiFiScanViewModel.js | 44 + src/html/wifi_portal.js | 272 +--- src/web_server.cpp | 14 +- src/web_server_static.cpp | 4 +- src/wifi.cpp | 169 +-- src/wifi.h | 19 +- 19 files changed, 1477 insertions(+), 1610 deletions(-) create mode 100644 src/html/view_models/BaseViewModel.js create mode 100644 src/html/view_models/ConfigViewModel.js create mode 100644 src/html/view_models/OpenEvseViewModel.js create mode 100644 src/html/view_models/OpenEvseWiFiViewModel.js create mode 100644 src/html/view_models/RapiViewModel.js create mode 100644 src/html/view_models/StatusViewModel.js create mode 100644 src/html/view_models/TimeViewModel.js create mode 100644 src/html/view_models/WiFiConfigViewModel.js create mode 100644 src/html/view_models/WiFiPortalViewModel.js create mode 100644 src/html/view_models/WiFiScanViewModel.js diff --git a/.eslintrc.json b/.eslintrc.json index 10eb91c2..5e2606d7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,7 @@ { "env": { "browser": true, - "commonjs": true, "es6": true, - "node": true, "jquery": true }, "globals": { @@ -14,7 +12,7 @@ "ecmaFeatures": { "jsx": true }, - "sourceType": "module" + "sourceType": "script" }, "rules": { "no-const-assign": "warn", diff --git a/platformio.ini b/platformio.ini index 1bd70a5d..842e0a0c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -83,7 +83,7 @@ board = esp12e framework = arduino lib_deps = PubSubClient@2.6, https://github.com/me-no-dev/ESPAsyncWebServer.git src_build_flags = ${common.version} ${common.ota_flags} ${common.debug_flags} -DENABLE_ASYNC_WIFI_SCAN -upload_port = 172.16.0.70 +upload_port = openevse.local monitor_baud = 115200 extra_scripts = ${common.extra_scripts} diff --git a/src/html/config.js b/src/html/config.js index b6a8a7ea..0d4e1acf 100644 --- a/src/html/config.js +++ b/src/html/config.js @@ -1,4 +1,4 @@ -/* global $, ko, OpenEVSE */ +/* global $, ko, OpenEvseWiFiViewModel */ (function() { "use strict"; @@ -11,1202 +11,9 @@ var baseHost = window.location.hostname; //var baseHost = "192.168.4.1"; //var baseHost = "172.16.0.70"; -function BaseViewModel(defaults, remoteUrl, mappings = {}) { - var self = this; - self.remoteUrl = remoteUrl; - - // Observable properties - ko.mapping.fromJS(defaults, mappings, self); - self.fetching = ko.observable(false); -} - -BaseViewModel.prototype.update = function (after = function () { }) { - var self = this; - self.fetching(true); - $.get(self.remoteUrl(), function (data) { - ko.mapping.fromJS(data, self); - }, "json").always(function () { - self.fetching(false); - after(); - }); -}; - -function StatusViewModel(baseEndpoint) { - var self = this; - var endpoint = ko.pureComputed(function () { return baseEndpoint() + "/status"; }); - - BaseViewModel.call(self, { - "mode": "ERR", - "srssi": "", - "ipaddress": "", - "packets_sent": 0, - "packets_success": 0, - "emoncms_connected": 0, - "mqtt_connected": 0, - "ohm_hour": "", - "free_heap": 0, - "comm_sent": 0, - "comm_success": 0, - "amp": 0, - "pilot": 0, - "temp1": 0, - "temp2": 0, - "temp3": 0, - "state": 0, - "elapsed": 0, - "wattsec": 0, - "watthour": 0, - "divertmode": 1 - }, endpoint); - - // Some devired values - self.isWifiClient = ko.pureComputed(function () { - return ("STA" === self.mode()) || ("STA+AP" === self.mode()); - }); - self.isWifiAccessPoint = ko.pureComputed(function () { - return ("AP" === self.mode()) || ("STA+AP" === self.mode()); - }); - self.fullMode = ko.pureComputed(function () { - switch (self.mode()) { - case "AP": - return "Access Point (AP)"; - case "STA": - return "Client (STA)"; - case "STA+AP": - return "Client + Access Point (STA+AP)"; - } - - return "Unknown (" + self.mode() + ")"; - }); - - - this.estate = ko.pureComputed(function () { - var estate; - switch (self.state()) { - case 0: - estate = "Starting"; - break; - case 1: - estate = "Not Connected"; - break; - case 2: - estate = "EV Connected"; - break; - case 3: - estate = "Charging"; - break; - case 4: - estate = "Vent Required"; - break; - case 5: - estate = "Diode Check Failed"; - break; - case 6: - estate = "GFCI Fault"; - break; - case 7: - estate = "No Earth Ground"; - break; - case 8: - estate = "Stuck Relay"; - break; - case 9: - estate = "GFCI Self Test Failed"; - break; - case 10: - estate = "Over Temperature"; - break; - case 254: - estate = "Waiting"; - break; - case 255: - estate = "Disabled"; - break; - default: - estate = "Invalid"; - break; - } - return estate; - }); -} -StatusViewModel.prototype = Object.create(BaseViewModel.prototype); -StatusViewModel.prototype.constructor = StatusViewModel; - - -function ConfigViewModel(baseEndpoint) { - var endpoint = ko.pureComputed(function () { return baseEndpoint() + "/config"; }); - BaseViewModel.call(this, { - "ssid": "", - "pass": "", - "emoncms_server": "data.openevse.com/emoncms", - "emoncms_apikey": "", - "emoncms_node": "", - "emoncms_fingerprint": "", - "emoncms_enabled": 0, - "mqtt_server": "", - "mqtt_topic": "", - "mqtt_user": "", - "mqtt_pass": "", - "mqtt_solar": "", - "mqtt_grid_ie": "", - "mqtt_enabled": 0, - "ohm_enabled": 0, - "ohmkey": "", - "www_username": "", - "www_password": "", - "firmware": "-", - "protocol": "-", - "espflash": 0, - "diodet": 0, - "gfcit": 0, - "groundt": 0, - "relayt": 0, - "ventt": 0, - "tempt": 0, - "scale": 1, - "offset": 0, - "gfcicount": 0, - "nogndcount": 0, - "stuckcount": 0, - "version": "0.0.0" - }, endpoint); -} -ConfigViewModel.prototype = Object.create(BaseViewModel.prototype); -ConfigViewModel.prototype.constructor = ConfigViewModel; - -function WiFiScanResultViewModel(data) -{ - var self = this; - ko.mapping.fromJS(data, {}, self); -} - -function WiFiScanViewModel(baseEndpoint) -{ - var self = this; - var endpoint = ko.pureComputed(function () { return baseEndpoint() + "/scan"; }); - - self.results = ko.mapping.fromJS([], { - key: function(data) { - return ko.utils.unwrapObservable(data.bssid); - }, - create: function (options) { - return new WiFiScanResultViewModel(options.data); - } - }); - - // Observable properties - self.fetching = ko.observable(false); - - self.update = function (after = function () { }) { - self.fetching(true); - $.get(endpoint(), function (data) { - ko.mapping.fromJS(data, self.results); - self.results.sort(function (left, right) { - if(left.ssid() === right.ssid()) { - return left.rssi() < right.rssi() ? 1 : -1; - } - return left.ssid() < right.ssid() ? -1 : 1; - }); - }, "json").always(function () { - self.fetching(false); - after(); - }); - }; -} - -function RapiViewModel(baseEndpoint) { - var self = this; - - self.baseEndpoint = baseEndpoint; - - self.rapiSend = ko.observable(false); - self.cmd = ko.observable(""); - self.ret = ko.observable(""); - - self.send = function() { - self.rapiSend(true); - $.get(self.baseEndpoint() + "/r?json=1&rapi="+encodeURI(self.cmd()), function (data) { - self.ret(">"+data.ret); - self.cmd(data.cmd); - }, "json").always(function () { - self.rapiSend(false); - }); - }; -} - -function TimeViewModel(openevse) -{ - var self = this; - - function addZero(val) { - return (val < 10 ? "0" : "") + val; - } - function startTimeUpdate() { - timeUpdateTimeout = setInterval(function () { - if(self.automaticTime()) { - self.nowTimedate(new Date(self.evseTimedate().getTime() + ((new Date()) - self.localTimedate()))); - } - if(openevse.isCharging()) { - self.elapsedNow(new Date((openevse.status.elapsed() * 1000) + ((new Date()) - self.elapsedLocal()))); - } - }, 1000); - } - function stopTimeUpdate() { - if(null !== timeUpdateTimeout) { - clearInterval(timeUpdateTimeout); - timeUpdateTimeout = null; - } - } - - self.evseTimedate = ko.observable(new Date()); - self.localTimedate = ko.observable(new Date()); - self.nowTimedate = ko.observable(null); - - self.elapsedNow = ko.observable(new Date(0)); - self.elapsedLocal = ko.observable(new Date()); - - self.date = ko.pureComputed({ - read: function () { - if(null === self.nowTimedate()) { - return ""; - } - - return self.nowTimedate().toISOString().split("T")[0]; - }, - write: function (val) { - self.evseTimedate(new Date(val)); - self.localTimedate(new Date()); - }}); - self.time = ko.pureComputed({ - read: function () { - if(null === self.nowTimedate()) { - return "--:--:--"; - } - var dt = self.nowTimedate(); - return addZero(dt.getHours())+":"+addZero(dt.getMinutes())+":"+addZero(dt.getSeconds()); - }, - write: function (val) { - var parts = val.split(":"); - var date = self.evseTimedate(); - date.setHours(parseInt(parts[0])); - date.setMinutes(parseInt(parts[1])); - self.evseTimedate(date); - self.localTimedate(new Date()); - }}); - self.elapsed = ko.pureComputed(function () { - if(null === self.nowTimedate()) { - return "0:00:00"; - } - var dt = self.elapsedNow(); - return addZero(dt.getHours())+":"+addZero(dt.getMinutes())+":"+addZero(dt.getSeconds()); - }); - - openevse.status.elapsed.subscribe(function (val) { - self.elapsedNow(new Date(val * 1000)); - self.elapsedLocal(new Date()); - }); - - var timeUpdateTimeout = null; - self.automaticTime = ko.observable(true); - self.setTime = function () { - var newTime = self.automaticTime() ? new Date() : self.evseTimedate(); - // IMPROVE: set a few times and work out an average transmission delay, PID loop? - openevse.openevse.time(self.timeUpdate, newTime); - }; - - self.timeUpdate = function (date) { - stopTimeUpdate(); - self.evseTimedate(date); - self.nowTimedate(date); - self.localTimedate(new Date()); - startTimeUpdate(); - }; -} - -function OpenEvseViewModel(baseEndpoint, statusViewModel) { - var self = this; - var endpoint = ko.pureComputed(function () { return baseEndpoint() + "/r"; }); - self.openevse = new OpenEVSE(endpoint()); - endpoint.subscribe(function (end) { - self.openevse.setEndpoint(end); - }); - self.status = statusViewModel; - self.time = new TimeViewModel(self); - - // Option lists - self.serviceLevels = [ - { name: "Auto", value: 0 }, - { name: "1", value: 1 }, - { name: "2", value: 2 }]; - self.currentLevels = ko.observableArray([]); - self.timeLimits = [ - { name: "none", value: 0 }, - { name: "15 min", value: 15 }, - { name: "30 min", value: 30 }, - { name: "45 min", value: 45 }, - { name: "1 hour", value: 60 }, - { name: "1.5 hours", value: 1.5 * 60 }, - { name: "2 hours", value: 2 * 60 }, - { name: "2.5 hours", value: 2.5 * 60 }, - { name: "3 hours", value: 3 * 60 }, - { name: "4 hours", value: 4 * 60 }, - { name: "5 hours", value: 5 * 60 }, - { name: "6 hours", value: 6 * 60 }, - { name: "7 hours", value: 7 * 60 }, - { name: "8 hours", value: 8 * 60 }]; - - self.chargeLimits = [ - { name: "none", value: 0 }, - { name: "1 kWh", value: 1 }, - { name: "2 kWh", value: 2 }, - { name: "3 kWh", value: 3 }, - { name: "4 kWh", value: 4 }, - { name: "5 kWh", value: 5 }, - { name: "6 kWh", value: 6 }, - { name: "7 kWh", value: 7 }, - { name: "8 kWh", value: 8 }, - { name: "9 kWh", value: 9 }, - { name: "10 kWh", value: 10 }, - { name: "15 kWh", value: 11 }, - { name: "20 kWh", value: 12 }, - { name: "25 kWh", value: 25 }, - { name: "30 kWh", value: 30 }, - { name: "35 kWh", value: 35 }, - { name: "40 kWh", value: 40 }, - { name: "45 kWh", value: 45 }, - { name: "50 kWh", value: 50 }, - { name: "55 kWh", value: 55 }, - { name: "60 kWh", value: 60 }, - { name: "70 kWh", value: 70 }, - { name: "80 kWh", value: 80 }, - { name: "90 kWh", value: 90 }]; - - self.serviceLevel = ko.observable(-1); - self.actualServiceLevel = ko.observable(-1); - self.minCurrentLevel = ko.observable(-1); - self.maxCurrentLevel = ko.observable(-1); - self.currentCapacity = ko.observable(-1); - self.timeLimit = ko.observable(-1); - self.chargeLimit = ko.observable(-1); - self.delayTimerEnabled = ko.observable(false); - self.delayTimerStart = ko.observable("--:--"); - self.delayTimerStop = ko.observable("--:--"); - - // Saftey tests - self.gfiSelfTestEnabled = ko.observable(false); - self.groundCheckEnabled = ko.observable(false); - self.stuckRelayEnabled = ko.observable(false); - self.tempCheckEnabled = ko.observable(false); - self.diodeCheckEnabled = ko.observable(false); - self.ventRequiredEnabled = ko.observable(false); - self.allTestsEnabled = ko.pureComputed(function () { - return self.gfiSelfTestEnabled() && - self.groundCheckEnabled() && - self.stuckRelayEnabled() && - self.tempCheckEnabled() && - self.diodeCheckEnabled() && - self.ventRequiredEnabled(); - }); - - self.tempCheckSupported = ko.observable(false); - - // Derived states - self.isConnected = ko.pureComputed(function () { - return [2, 3].indexOf(self.status.state()) !== -1; - }); - - self.isReady = ko.pureComputed(function () { - return [0, 1].indexOf(self.status.state()) !== -1; - }); - - self.isCharging = ko.pureComputed(function () { - return 3 === self.status.state(); - }); - - self.isError = ko.pureComputed(function () { - return [4, 5, 6, 7, 8, 9, 10].indexOf(self.status.state()) !== -1; - }); - - self.isEnabled = ko.pureComputed(function () { - return [0, 1, 2, 3].indexOf(self.status.state()) !== -1; - }); - - self.isSleeping = ko.pureComputed(function () { - return 254 === self.status.state(); - }); - - self.isDisabled = ko.pureComputed(function () { - return 255 === self.status.state(); - }); - - // helper to select an appropriate value for time limit - self.selectTimeLimit = function(limit) - { - if(self.timeLimit() === limit) { - return; - } - - for(var i = 0; i < self.timeLimits.length; i++) { - var time = self.timeLimits[i]; - if(time.value >= limit) { - self.timeLimit(time.value); - break; - } - } - }; - - // helper to select an appropriate value for charge limit - self.selectChargeLimit = function(limit) - { - if(self.chargeLimit() === limit) { - return; - } - - for(var i = 0; i < self.chargeLimits.length; i++) { - var charge = self.chargeLimits[i]; - if(charge.value >= limit) { - self.chargeLimit(charge.value); - break; - } - } - }; - - // List of items to update on calling update(). The list will be processed one item at - // a time. - var updateList = [ - function () { return self.openevse.time(self.time.timeUpdate); }, - function () { return self.openevse.service_level(function (level, actual) { - self.serviceLevel(level); - self.actualServiceLevel(actual); - }); }, - function () { return self.updateCurrentCapacity(); }, - function () { return self.openevse.current_capacity(function (capacity) { - self.currentCapacity(capacity); - }); }, - function () { return self.openevse.time_limit(function (limit) { - self.selectTimeLimit(limit); - }); }, - function () { return self.openevse.charge_limit(function (limit) { - self.selectChargeLimit(limit); - }); }, - function () { return self.openevse.gfi_self_test(function (enabled) { - self.gfiSelfTestEnabled(enabled); - }); }, - function () { return self.openevse.ground_check(function (enabled) { - self.groundCheckEnabled(enabled); - }); }, - function () { return self.openevse.stuck_relay_check(function (enabled) { - self.stuckRelayEnabled(enabled); - }); }, - function () { return self.openevse.temp_check(function (enabled) { - self.tempCheckEnabled(enabled); - }); }, - function () { return self.openevse.diode_check(function (enabled) { - self.diodeCheckEnabled(enabled); - }); }, - function () { return self.openevse.vent_required(function (enabled) { - self.ventRequiredEnabled(enabled); - }); }, - function () { return self.openevse.over_temperature_thresholds(function () { - self.tempCheckSupported(true); - }).error(function () { - self.tempCheckSupported(false); - }); }, - function () { return self.openevse.timer(function (enabled, start, stop) { - self.delayTimerEnabled(enabled); - self.delayTimerStart(start); - self.delayTimerStop(stop); - }); }, - ]; - var updateCount = -1; - - self.updateCurrentCapacity = function () { - return self.openevse.current_capacity_range(function (min, max) { - self.minCurrentLevel(min); - self.maxCurrentLevel(max); - var capacity = self.currentCapacity(); - self.currentLevels.removeAll(); - for(var i = self.minCurrentLevel(); i <= self.maxCurrentLevel(); i++) { - self.currentLevels.push({name: i+" A", value: i}); - } - self.currentCapacity(capacity); - }); - }; - - self.updatingServiceLevel = ko.observable(false); - self.savedServiceLevel = ko.observable(false); - self.updatingCurrentCapacity = ko.observable(false); - self.savedCurrentCapacity = ko.observable(false); - self.updatingTimeLimit = ko.observable(false); - self.savedTimeLimit = ko.observable(false); - self.updatingChargeLimit = ko.observable(false); - self.savedChargeLimit = ko.observable(false); - self.updatingDelayTimer = ko.observable(false); - self.savedDelayTimer = ko.observable(false); - self.updatingStatus = ko.observable(false); - self.savedStatus = ko.observable(false); - self.updatingGfiSelfTestEnabled = ko.observable(false); - self.savedGfiSelfTestEnabled = ko.observable(false); - self.updatingGroundCheckEnabled = ko.observable(false); - self.savedGroundCheckEnabled = ko.observable(false); - self.updatingStuckRelayEnabled = ko.observable(false); - self.savedStuckRelayEnabled = ko.observable(false); - self.updatingTempCheckEnabled = ko.observable(false); - self.savedTempCheckEnabled = ko.observable(false); - self.updatingDiodeCheckEnabled = ko.observable(false); - self.savedDiodeCheckEnabled = ko.observable(false); - self.updatingVentRequiredEnabled = ko.observable(false); - self.savedVentRequiredEnabled = ko.observable(false); - - self.setForTime = function (flag, time) { - flag(true); - setTimeout(function () { flag(false); }, time); - }; - - var subscribed = false; - self.subscribe = function () - { - if(subscribed) { - return; - } - - // Updates to the service level - self.serviceLevel.subscribe(function (val) { - self.updatingServiceLevel(true); - self.openevse.service_level(function (level, actual) { - self.setForTime(self.savedServiceLevel, 2000); - self.actualServiceLevel(actual); - self.updateCurrentCapacity().always(function () { - }); - }, val).always(function() { - self.updatingServiceLevel(false); - }); - }); - - // Updates to the current capacity - self.currentCapacity.subscribe(function (val) { - if(true === self.updatingServiceLevel()) { - return; - } - self.updatingCurrentCapacity(true); - self.openevse.current_capacity(function (capacity) { - self.setForTime(self.savedCurrentCapacity, 2000); - if(val !== capacity) { - self.currentCapacity(capacity); - } - }, val).always(function() { - self.updatingCurrentCapacity(false); - }); - }); - - // Updates to the time limit - self.timeLimit.subscribe(function (val) { - self.updatingTimeLimit(true); - self.openevse.time_limit(function (limit) { - self.setForTime(self.savedTimeLimit, 2000); - if(val !== limit) { - self.selectTimeLimit(limit); - } - }, val).always(function() { - self.updatingTimeLimit(false); - }); - }); - - // Updates to the charge limit - self.chargeLimit.subscribe(function (val) { - self.updatingChargeLimit(true); - self.openevse.charge_limit(function (limit) { - self.setForTime(self.savedChargeLimit, 2000); - if(val !== limit) { - self.selectChargeLimit(limit); - } - }, val).always(function() { - self.updatingChargeLimit(false); - }); - }); - - // Updates to the GFI self test - self.gfiSelfTestEnabled.subscribe(function (val) { - self.updatingGfiSelfTestEnabled(true); - self.openevse.gfi_self_test(function (enabled) { - self.setForTime(self.savedGfiSelfTestEnabled, 2000); - if(val !== enabled) { - self.gfiSelfTestEnabled(enabled); - } - }, val).always(function() { - self.updatingGfiSelfTestEnabled(false); - }); - }); - - // Updates to the ground check - self.groundCheckEnabled.subscribe(function (val) { - self.updatingGroundCheckEnabled(true); - self.openevse.ground_check(function (enabled) { - self.setForTime(self.savedGroundCheckEnabled, 2000); - if(val !== enabled) { - self.groundCheckEnabled(enabled); - } - }, val).always(function() { - self.updatingGroundCheckEnabled(false); - }); - }); - - // Updates to the stuck relay check - self.stuckRelayEnabled.subscribe(function (val) { - self.updatingStuckRelayEnabled(true); - self.savedStuckRelayEnabled(false); - self.openevse.stuck_relay_check(function (enabled) { - self.savedStuckRelayEnabled(true); - setTimeout(function () { self.savedStuckRelayEnabled(false); }, 2000); - if(val !== enabled) { - self.stuckRelayEnabled(enabled); - } - }, val).always(function() { - self.updatingStuckRelayEnabled(false); - }); - }); - - // Updates to the temp check - self.tempCheckEnabled.subscribe(function (val) { - self.updatingTempCheckEnabled(true); - self.openevse.temp_check(function (enabled) { - self.setForTime(self.savedTempCheckEnabled, 2000); - if(val !== enabled) { - self.tempCheckEnabled(enabled); - } - }, val).always(function() { - self.updatingTempCheckEnabled(false); - }); - }); - - // Updates to the diode check - self.diodeCheckEnabled.subscribe(function (val) { - self.updatingDiodeCheckEnabled(true); - self.openevse.diode_check(function (enabled) { - self.setForTime(self.savedDiodeCheckEnabled, 2000); - if(val !== enabled) { - self.diodeCheckEnabled(enabled); - } - }, val).always(function() { - self.updatingDiodeCheckEnabled(false); - }); - }); - - // Updates to the vent required - self.ventRequiredEnabled.subscribe(function (val) { - self.updatingVentRequiredEnabled(true); - self.openevse.vent_required(function (enabled) { - self.setForTime(self.savedVentRequiredEnabled, 2000); - if(val !== enabled) { - self.ventRequiredEnabled(enabled); - } - }, val).always(function() { - self.updatingVentRequiredEnabled(false); - }); - }); - - subscribed = true; - }; - - self.update = function (after = function () { }) { - updateCount = 0; - self.nextUpdate(after); - }; - self.nextUpdate = function (after) { - var updateFn = updateList[updateCount]; - updateFn().always(function () { - if(++updateCount < updateList.length) { - self.nextUpdate(after); - } else { - self.subscribe(); - after(); - } - }); - }; - - // delay timer logic - function isTime(val) { - var timeRegex = /([01]\d|2[0-3]):([0-5]\d)/; - return timeRegex.test(val); - } - self.delayTimerValid = ko.pureComputed(function () { - return isTime(self.delayTimerStart()) && isTime(self.delayTimerStop()); - }); - self.startDelayTimer = function () { - self.updatingDelayTimer(true); - self.openevse.timer(function () { - self.delayTimerEnabled(true); - }, self.delayTimerStart(), self.delayTimerStop()).always(function() { - self.updatingDelayTimer(false); - }); - }; - self.stopDelayTimer = function () { - self.updatingDelayTimer(true); - self.openevse.cancelTimer(function () { - self.delayTimerEnabled(false); - }).always(function() { - self.updatingDelayTimer(false); - }); - }; - - // support for changing status - self.setStatus = function (action) { - self.updatingStatus(true); - self.openevse.status(function (state) { - self.status.state(state); - }, action).always(function() { - self.updatingStatus(false); - }); - }; - - // Support for restarting the OpenEVSE - self.restartFetching = ko.observable(false); - self.restart = function() { - if (confirm("Restart OpenEVSE? Current config will be saved, takes approximately 10s.")) { - self.restartFetching(true); - self.openevse.reset().always(function () { - self.restartFetching(false); - }); - } - }; - -} - -function OpenEvseWiFiViewModel() { - var self = this; - - self.baseHost = ko.observable("" !== baseHost ? baseHost : "openevse.local"); - self.baseEndpoint = ko.pureComputed(function () { return "http://" + self.baseHost(); }); - - self.config = new ConfigViewModel(self.baseEndpoint); - self.status = new StatusViewModel(self.baseEndpoint); - self.rapi = new RapiViewModel(self.baseEndpoint); - self.scan = new WiFiScanViewModel(self.baseEndpoint); - self.openevse = new OpenEvseViewModel(self.baseEndpoint, self.status); - - self.initialised = ko.observable(false); - self.updating = ko.observable(false); - self.scanUpdating = ko.observable(false); - - self.bssid = ko.observable(""); - self.bssid.subscribe(function (bssid) { - for(var i = 0; i < self.scan.results().length; i++) { - var net = self.scan.results()[i]; - if(bssid === net.bssid()) { - self.config.ssid(net.ssid()); - return; - } - } - }); - - // Info text display state - self.showMqttInfo = ko.observable(false); - self.showSolarDivert = ko.observable(false); - - self.toggle = function (flag) { - flag(!flag()); - }; - - // Advanced mode - self.advancedMode = ko.observable(false); - - // Developer mode - self.developerMode = ko.observable(false); - self.developerMode.subscribe(function (val) { if(val) { - self.advancedMode(true); // Enabling dev mode implicitly enables advanced mode - }}); - - var updateTimer = null; - var updateTime = 5 * 1000; - - var scanTimer = null; - var scanTime = 3 * 1000; - - // Tabs - var tab = "status"; - if("" !== window.location.hash) { - tab = window.location.hash.substr(1); - } - self.tab = ko.observable(tab); - self.tab.subscribe(function (val) { - window.location.hash = "#" + val; - }); - self.isSystem = ko.pureComputed(function() { return "system" === self.tab(); }); - self.isServices = ko.pureComputed(function() { return "services" === self.tab(); }); - self.isStatus = ko.pureComputed(function() { return "status" === self.tab(); }); - self.isRapi = ko.pureComputed(function() { return "rapi" === self.tab(); }); - - // Upgrade URL - self.upgradeUrl = ko.observable("about:blank"); - - // ----------------------------------------------------------------------- - // Initialise the app - // ----------------------------------------------------------------------- - self.start = function () { - self.updating(true); - self.status.update(function () { - self.config.update(function () { - // If we are accessing on a .local domain try and redirect - if(self.baseHost().endsWith(".local") && "" !== self.status.ipaddress()) { - if("" === self.config.www_username()) - { - // Redirect to the IP internally - self.baseHost(self.status.ipaddress()); - } else { - window.location.replace("http://" + self.status.ipaddress()); - } - } - self.openevse.update(function () { - self.initialised(true); - updateTimer = setTimeout(self.update, updateTime); - - // Load the upgrade frame - self.upgradeUrl(self.baseEndpoint() + "/update"); - - // Load the images - var imgDefer = document.getElementsByTagName("img"); - for (var i=0; i= limit) { + self.timeLimit(time.value); + break; + } + } + }; + + // helper to select an appropriate value for charge limit + self.selectChargeLimit = function(limit) + { + if(self.chargeLimit() === limit) { + return; + } + + for(var i = 0; i < self.chargeLimits.length; i++) { + var charge = self.chargeLimits[i]; + if(charge.value >= limit) { + self.chargeLimit(charge.value); + break; + } + } + }; + + // List of items to update on calling update(). The list will be processed one item at + // a time. + var updateList = [ + function () { return self.openevse.time(self.time.timeUpdate); }, + function () { return self.openevse.service_level(function (level, actual) { + self.serviceLevel(level); + self.actualServiceLevel(actual); + }); }, + function () { return self.updateCurrentCapacity(); }, + function () { return self.openevse.current_capacity(function (capacity) { + self.currentCapacity(capacity); + }); }, + function () { return self.openevse.time_limit(function (limit) { + self.selectTimeLimit(limit); + }); }, + function () { return self.openevse.charge_limit(function (limit) { + self.selectChargeLimit(limit); + }); }, + function () { return self.openevse.gfi_self_test(function (enabled) { + self.gfiSelfTestEnabled(enabled); + }); }, + function () { return self.openevse.ground_check(function (enabled) { + self.groundCheckEnabled(enabled); + }); }, + function () { return self.openevse.stuck_relay_check(function (enabled) { + self.stuckRelayEnabled(enabled); + }); }, + function () { return self.openevse.temp_check(function (enabled) { + self.tempCheckEnabled(enabled); + }); }, + function () { return self.openevse.diode_check(function (enabled) { + self.diodeCheckEnabled(enabled); + }); }, + function () { return self.openevse.vent_required(function (enabled) { + self.ventRequiredEnabled(enabled); + }); }, + function () { return self.openevse.over_temperature_thresholds(function () { + self.tempCheckSupported(true); + }).error(function () { + self.tempCheckSupported(false); + }); }, + function () { return self.openevse.timer(function (enabled, start, stop) { + self.delayTimerEnabled(enabled); + self.delayTimerStart(start); + self.delayTimerStop(stop); + }); }, + ]; + var updateCount = -1; + + self.updateCurrentCapacity = function () { + return self.openevse.current_capacity_range(function (min, max) { + self.minCurrentLevel(min); + self.maxCurrentLevel(max); + var capacity = self.currentCapacity(); + self.currentLevels.removeAll(); + for(var i = self.minCurrentLevel(); i <= self.maxCurrentLevel(); i++) { + self.currentLevels.push({name: i+" A", value: i}); + } + self.currentCapacity(capacity); + }); + }; + + self.updatingServiceLevel = ko.observable(false); + self.savedServiceLevel = ko.observable(false); + self.updatingCurrentCapacity = ko.observable(false); + self.savedCurrentCapacity = ko.observable(false); + self.updatingTimeLimit = ko.observable(false); + self.savedTimeLimit = ko.observable(false); + self.updatingChargeLimit = ko.observable(false); + self.savedChargeLimit = ko.observable(false); + self.updatingDelayTimer = ko.observable(false); + self.savedDelayTimer = ko.observable(false); + self.updatingStatus = ko.observable(false); + self.savedStatus = ko.observable(false); + self.updatingGfiSelfTestEnabled = ko.observable(false); + self.savedGfiSelfTestEnabled = ko.observable(false); + self.updatingGroundCheckEnabled = ko.observable(false); + self.savedGroundCheckEnabled = ko.observable(false); + self.updatingStuckRelayEnabled = ko.observable(false); + self.savedStuckRelayEnabled = ko.observable(false); + self.updatingTempCheckEnabled = ko.observable(false); + self.savedTempCheckEnabled = ko.observable(false); + self.updatingDiodeCheckEnabled = ko.observable(false); + self.savedDiodeCheckEnabled = ko.observable(false); + self.updatingVentRequiredEnabled = ko.observable(false); + self.savedVentRequiredEnabled = ko.observable(false); + + self.setForTime = function (flag, time) { + flag(true); + setTimeout(function () { flag(false); }, time); + }; + + var subscribed = false; + self.subscribe = function () + { + if(subscribed) { + return; + } + + // Updates to the service level + self.serviceLevel.subscribe(function (val) { + self.updatingServiceLevel(true); + self.openevse.service_level(function (level, actual) { + self.setForTime(self.savedServiceLevel, 2000); + self.actualServiceLevel(actual); + self.updateCurrentCapacity().always(function () { + }); + }, val).always(function() { + self.updatingServiceLevel(false); + }); + }); + + // Updates to the current capacity + self.currentCapacity.subscribe(function (val) { + if(true === self.updatingServiceLevel()) { + return; + } + self.updatingCurrentCapacity(true); + self.openevse.current_capacity(function (capacity) { + self.setForTime(self.savedCurrentCapacity, 2000); + if(val !== capacity) { + self.currentCapacity(capacity); + } + }, val).always(function() { + self.updatingCurrentCapacity(false); + }); + }); + + // Updates to the time limit + self.timeLimit.subscribe(function (val) { + self.updatingTimeLimit(true); + self.openevse.time_limit(function (limit) { + self.setForTime(self.savedTimeLimit, 2000); + if(val !== limit) { + self.selectTimeLimit(limit); + } + }, val).always(function() { + self.updatingTimeLimit(false); + }); + }); + + // Updates to the charge limit + self.chargeLimit.subscribe(function (val) { + self.updatingChargeLimit(true); + self.openevse.charge_limit(function (limit) { + self.setForTime(self.savedChargeLimit, 2000); + if(val !== limit) { + self.selectChargeLimit(limit); + } + }, val).always(function() { + self.updatingChargeLimit(false); + }); + }); + + // Updates to the GFI self test + self.gfiSelfTestEnabled.subscribe(function (val) { + self.updatingGfiSelfTestEnabled(true); + self.openevse.gfi_self_test(function (enabled) { + self.setForTime(self.savedGfiSelfTestEnabled, 2000); + if(val !== enabled) { + self.gfiSelfTestEnabled(enabled); + } + }, val).always(function() { + self.updatingGfiSelfTestEnabled(false); + }); + }); + + // Updates to the ground check + self.groundCheckEnabled.subscribe(function (val) { + self.updatingGroundCheckEnabled(true); + self.openevse.ground_check(function (enabled) { + self.setForTime(self.savedGroundCheckEnabled, 2000); + if(val !== enabled) { + self.groundCheckEnabled(enabled); + } + }, val).always(function() { + self.updatingGroundCheckEnabled(false); + }); + }); + + // Updates to the stuck relay check + self.stuckRelayEnabled.subscribe(function (val) { + self.updatingStuckRelayEnabled(true); + self.savedStuckRelayEnabled(false); + self.openevse.stuck_relay_check(function (enabled) { + self.savedStuckRelayEnabled(true); + setTimeout(function () { self.savedStuckRelayEnabled(false); }, 2000); + if(val !== enabled) { + self.stuckRelayEnabled(enabled); + } + }, val).always(function() { + self.updatingStuckRelayEnabled(false); + }); + }); + + // Updates to the temp check + self.tempCheckEnabled.subscribe(function (val) { + self.updatingTempCheckEnabled(true); + self.openevse.temp_check(function (enabled) { + self.setForTime(self.savedTempCheckEnabled, 2000); + if(val !== enabled) { + self.tempCheckEnabled(enabled); + } + }, val).always(function() { + self.updatingTempCheckEnabled(false); + }); + }); + + // Updates to the diode check + self.diodeCheckEnabled.subscribe(function (val) { + self.updatingDiodeCheckEnabled(true); + self.openevse.diode_check(function (enabled) { + self.setForTime(self.savedDiodeCheckEnabled, 2000); + if(val !== enabled) { + self.diodeCheckEnabled(enabled); + } + }, val).always(function() { + self.updatingDiodeCheckEnabled(false); + }); + }); + + // Updates to the vent required + self.ventRequiredEnabled.subscribe(function (val) { + self.updatingVentRequiredEnabled(true); + self.openevse.vent_required(function (enabled) { + self.setForTime(self.savedVentRequiredEnabled, 2000); + if(val !== enabled) { + self.ventRequiredEnabled(enabled); + } + }, val).always(function() { + self.updatingVentRequiredEnabled(false); + }); + }); + + subscribed = true; + }; + + self.update = function (after = function () { }) { + updateCount = 0; + self.nextUpdate(after); + }; + self.nextUpdate = function (after) { + var updateFn = updateList[updateCount]; + updateFn().always(function () { + if(++updateCount < updateList.length) { + self.nextUpdate(after); + } else { + self.subscribe(); + after(); + } + }); + }; + + // delay timer logic + function isTime(val) { + var timeRegex = /([01]\d|2[0-3]):([0-5]\d)/; + return timeRegex.test(val); + } + self.delayTimerValid = ko.pureComputed(function () { + return isTime(self.delayTimerStart()) && isTime(self.delayTimerStop()); + }); + self.startDelayTimer = function () { + self.updatingDelayTimer(true); + self.openevse.timer(function () { + self.delayTimerEnabled(true); + }, self.delayTimerStart(), self.delayTimerStop()).always(function() { + self.updatingDelayTimer(false); + }); + }; + self.stopDelayTimer = function () { + self.updatingDelayTimer(true); + self.openevse.cancelTimer(function () { + self.delayTimerEnabled(false); + }).always(function() { + self.updatingDelayTimer(false); + }); + }; + + // support for changing status + self.setStatus = function (action) { + self.updatingStatus(true); + self.openevse.status(function (state) { + self.status.state(state); + }, action).always(function() { + self.updatingStatus(false); + }); + }; + + // Support for restarting the OpenEVSE + self.restartFetching = ko.observable(false); + self.restart = function() { + if (confirm("Restart OpenEVSE? Current config will be saved, takes approximately 10s.")) { + self.restartFetching(true); + self.openevse.reset().always(function () { + self.restartFetching(false); + }); + } + }; +} diff --git a/src/html/view_models/OpenEvseWiFiViewModel.js b/src/html/view_models/OpenEvseWiFiViewModel.js new file mode 100644 index 00000000..b2329ee4 --- /dev/null +++ b/src/html/view_models/OpenEvseWiFiViewModel.js @@ -0,0 +1,436 @@ +/* global $, ko, ConfigViewModel, StatusViewModel, RapiViewModel, WiFiScanViewModel, OpenEvseViewModel */ +/* exported OpenEvseWiFiViewModel */ + +function OpenEvseWiFiViewModel(baseHost) +{ + "use strict"; + var self = this; + + self.baseHost = ko.observable("" !== baseHost ? baseHost : "openevse.local"); + self.baseEndpoint = ko.pureComputed(function () { return "http://" + self.baseHost(); }); + + self.config = new ConfigViewModel(self.baseEndpoint); + self.status = new StatusViewModel(self.baseEndpoint); + self.rapi = new RapiViewModel(self.baseEndpoint); + self.scan = new WiFiScanViewModel(self.baseEndpoint); + self.openevse = new OpenEvseViewModel(self.baseEndpoint, self.status); + + self.initialised = ko.observable(false); + self.updating = ko.observable(false); + self.scanUpdating = ko.observable(false); + + self.bssid = ko.observable(""); + self.bssid.subscribe(function (bssid) { + for(var i = 0; i < self.scan.results().length; i++) { + var net = self.scan.results()[i]; + if(bssid === net.bssid()) { + self.config.ssid(net.ssid()); + return; + } + } + }); + + // Info text display state + self.showMqttInfo = ko.observable(false); + self.showSolarDivert = ko.observable(false); + + self.toggle = function (flag) { + flag(!flag()); + }; + + // Advanced mode + self.advancedMode = ko.observable(false); + + // Developer mode + self.developerMode = ko.observable(false); + self.developerMode.subscribe(function (val) { if(val) { + self.advancedMode(true); // Enabling dev mode implicitly enables advanced mode + }}); + + var updateTimer = null; + var updateTime = 5 * 1000; + + var scanTimer = null; + var scanTime = 3 * 1000; + + // Tabs + var tab = "status"; + if("" !== window.location.hash) { + tab = window.location.hash.substr(1); + } + self.tab = ko.observable(tab); + self.tab.subscribe(function (val) { + window.location.hash = "#" + val; + }); + self.isSystem = ko.pureComputed(function() { return "system" === self.tab(); }); + self.isServices = ko.pureComputed(function() { return "services" === self.tab(); }); + self.isStatus = ko.pureComputed(function() { return "status" === self.tab(); }); + self.isRapi = ko.pureComputed(function() { return "rapi" === self.tab(); }); + + // Upgrade URL + self.upgradeUrl = ko.observable("about:blank"); + + // ----------------------------------------------------------------------- + // Initialise the app + // ----------------------------------------------------------------------- + self.start = function () { + self.updating(true); + self.status.update(function () { + self.config.update(function () { + // If we are accessing on a .local domain try and redirect + if(self.baseHost().endsWith(".local") && "" !== self.status.ipaddress()) { + if("" === self.config.www_username()) + { + // Redirect to the IP internally + self.baseHost(self.status.ipaddress()); + } else { + window.location.replace("http://" + self.status.ipaddress()); + } + } + self.openevse.update(function () { + self.initialised(true); + updateTimer = setTimeout(self.update, updateTime); + + // Load the upgrade frame + self.upgradeUrl(self.baseEndpoint() + "/update"); + + // Load the images + var imgDefer = document.getElementsByTagName("img"); + for (var i=0; i"+data.ret); + self.cmd(data.cmd); + }, "json").always(function () { + self.rapiSend(false); + }); + }; +} diff --git a/src/html/view_models/StatusViewModel.js b/src/html/view_models/StatusViewModel.js new file mode 100644 index 00000000..4396d6ed --- /dev/null +++ b/src/html/view_models/StatusViewModel.js @@ -0,0 +1,103 @@ +/* global ko, BaseViewModel */ + +function StatusViewModel(baseEndpoint) { + "use strict"; + var self = this; + var endpoint = ko.pureComputed(function () { return baseEndpoint() + "/status"; }); + + BaseViewModel.call(self, { + "mode": "ERR", + "srssi": "", + "ipaddress": "", + "packets_sent": 0, + "packets_success": 0, + "emoncms_connected": 0, + "mqtt_connected": 0, + "ohm_hour": "", + "free_heap": 0, + "comm_sent": 0, + "comm_success": 0, + "amp": 0, + "pilot": 0, + "temp1": 0, + "temp2": 0, + "temp3": 0, + "state": 0, + "elapsed": 0, + "wattsec": 0, + "watthour": 0, + "divertmode": 1 + }, endpoint); + + // Some devired values + self.isWifiClient = ko.pureComputed(function () { + return ("STA" === self.mode()) || ("STA+AP" === self.mode()); + }); + self.isWifiAccessPoint = ko.pureComputed(function () { + return ("AP" === self.mode()) || ("STA+AP" === self.mode()); + }); + self.fullMode = ko.pureComputed(function () { + switch (self.mode()) { + case "AP": + return "Access Point (AP)"; + case "STA": + return "Client (STA)"; + case "STA+AP": + return "Client + Access Point (STA+AP)"; + } + + return "Unknown (" + self.mode() + ")"; + }); + + + this.estate = ko.pureComputed(function () { + var estate; + switch (self.state()) { + case 0: + estate = "Starting"; + break; + case 1: + estate = "Not Connected"; + break; + case 2: + estate = "EV Connected"; + break; + case 3: + estate = "Charging"; + break; + case 4: + estate = "Vent Required"; + break; + case 5: + estate = "Diode Check Failed"; + break; + case 6: + estate = "GFCI Fault"; + break; + case 7: + estate = "No Earth Ground"; + break; + case 8: + estate = "Stuck Relay"; + break; + case 9: + estate = "GFCI Self Test Failed"; + break; + case 10: + estate = "Over Temperature"; + break; + case 254: + estate = "Waiting"; + break; + case 255: + estate = "Disabled"; + break; + default: + estate = "Invalid"; + break; + } + return estate; + }); +} +StatusViewModel.prototype = Object.create(BaseViewModel.prototype); +StatusViewModel.prototype.constructor = StatusViewModel; diff --git a/src/html/view_models/TimeViewModel.js b/src/html/view_models/TimeViewModel.js new file mode 100644 index 00000000..a40bdfe1 --- /dev/null +++ b/src/html/view_models/TimeViewModel.js @@ -0,0 +1,92 @@ +/* global ko */ +/* exported TimeViewModel */ + +function TimeViewModel(openevse) +{ + "use strict"; + var self = this; + + function addZero(val) { + return (val < 10 ? "0" : "") + val; + } + function startTimeUpdate() { + timeUpdateTimeout = setInterval(function () { + if(self.automaticTime()) { + self.nowTimedate(new Date(self.evseTimedate().getTime() + ((new Date()) - self.localTimedate()))); + } + if(openevse.isCharging()) { + self.elapsedNow(new Date((openevse.status.elapsed() * 1000) + ((new Date()) - self.elapsedLocal()))); + } + }, 1000); + } + function stopTimeUpdate() { + if(null !== timeUpdateTimeout) { + clearInterval(timeUpdateTimeout); + timeUpdateTimeout = null; + } + } + + self.evseTimedate = ko.observable(new Date()); + self.localTimedate = ko.observable(new Date()); + self.nowTimedate = ko.observable(null); + + self.elapsedNow = ko.observable(new Date(0)); + self.elapsedLocal = ko.observable(new Date()); + + self.date = ko.pureComputed({ + read: function () { + if(null === self.nowTimedate()) { + return ""; + } + + return self.nowTimedate().toISOString().split("T")[0]; + }, + write: function (val) { + self.evseTimedate(new Date(val)); + self.localTimedate(new Date()); + }}); + self.time = ko.pureComputed({ + read: function () { + if(null === self.nowTimedate()) { + return "--:--:--"; + } + var dt = self.nowTimedate(); + return addZero(dt.getHours())+":"+addZero(dt.getMinutes())+":"+addZero(dt.getSeconds()); + }, + write: function (val) { + var parts = val.split(":"); + var date = self.evseTimedate(); + date.setHours(parseInt(parts[0])); + date.setMinutes(parseInt(parts[1])); + self.evseTimedate(date); + self.localTimedate(new Date()); + }}); + self.elapsed = ko.pureComputed(function () { + if(null === self.nowTimedate()) { + return "0:00:00"; + } + var dt = self.elapsedNow(); + return addZero(dt.getHours())+":"+addZero(dt.getMinutes())+":"+addZero(dt.getSeconds()); + }); + + openevse.status.elapsed.subscribe(function (val) { + self.elapsedNow(new Date(val * 1000)); + self.elapsedLocal(new Date()); + }); + + var timeUpdateTimeout = null; + self.automaticTime = ko.observable(true); + self.setTime = function () { + var newTime = self.automaticTime() ? new Date() : self.evseTimedate(); + // IMPROVE: set a few times and work out an average transmission delay, PID loop? + openevse.openevse.time(self.timeUpdate, newTime); + }; + + self.timeUpdate = function (date) { + stopTimeUpdate(); + self.evseTimedate(date); + self.nowTimedate(date); + self.localTimedate(new Date()); + startTimeUpdate(); + }; +} diff --git a/src/html/view_models/WiFiConfigViewModel.js b/src/html/view_models/WiFiConfigViewModel.js new file mode 100644 index 00000000..dd4e5695 --- /dev/null +++ b/src/html/view_models/WiFiConfigViewModel.js @@ -0,0 +1,124 @@ +/* global $, ko */ +/* exported WiFiScanViewModel */ + +function WiFiScanViewModel(baseEndpoint, config, status, scan) { + "use strict"; + var self = this; + + self.baseEndpoint = baseEndpoint; + self.config = config; + self.status = status; + self.scan = scan; + + self.scanUpdating = ko.observable(false); + + self.bssid = ko.observable(""); + self.bssid.subscribe(function (bssid) { + for(var i = 0; i < self.scan.results().length; i++) { + var net = self.scan.results()[i]; + if(bssid === net.bssid()) { + self.config.ssid(net.ssid()); + return; + } + } + }); + + var scanTimer = null; + var scanTime = 3 * 1000; + + // ----------------------------------------------------------------------- + // WiFi scan update + // ----------------------------------------------------------------------- + var scanEnabled = false; + self.startScan = function () { + if (self.scanUpdating()) { + return; + } + scanEnabled = true; + self.scanUpdating(true); + if (null !== scanTimer) { + clearTimeout(scanTimer); + scanTimer = null; + } + self.scan.update(function () { + if(scanEnabled) { + scanTimer = setTimeout(self.startScan, scanTime); + } + self.scanUpdating(false); + }); + }; + + self.stopScan = function() { + scanEnabled = false; + if (self.scanUpdating()) { + return; + } + + if (null !== scanTimer) { + clearTimeout(scanTimer); + scanTimer = null; + } + }; + + self.wifiConnecting = ko.observable(false); + self.status.mode.subscribe(function (newValue) { + if(newValue === "STA+AP" || newValue === "AP") { + self.startScan(); + } else { + self.stopScan(); + } + }); + self.status.wifi_client_connected.subscribe(function (newValue) { + if(newValue) { + self.wifiConnecting(false); + } + }); + + // ----------------------------------------------------------------------- + // Event: WiFi Connect + // ----------------------------------------------------------------------- + self.saveNetworkFetching = ko.observable(false); + self.saveNetworkSuccess = ko.observable(false); + self.saveNetwork = function () { + if (self.config.ssid() === "") { + alert("Please select network"); + } else { + self.saveNetworkFetching(true); + self.saveNetworkSuccess(false); + $.post(self.baseEndpoint() + "/savenetwork", { ssid: self.config.ssid(), pass: self.config.pass() }, function () { + self.saveNetworkSuccess(true); + self.wifiConnecting(true); + }).fail(function () { + alert("Failed to save WiFi config"); + }).always(function () { + self.saveNetworkFetching(false); + }); + } + }; + + // ----------------------------------------------------------------------- + // Event: Turn off Access Point + // ----------------------------------------------------------------------- + self.turnOffAccessPointFetching = ko.observable(false); + self.turnOffAccessPointSuccess = ko.observable(false); + self.turnOffAccessPoint = function () { + self.turnOffAccessPointFetching(true); + self.turnOffAccessPointSuccess(false); + $.post(self.baseEndpoint() + "/apoff", { + }, function (data) { + console.log(data); + if (self.status.ipaddress() !== "") { + setTimeout(function () { + window.location = "http://" + self.status.ipaddress(); + self.turnOffAccessPointSuccess(true); + }, 3000); + } else { + self.turnOffAccessPointSuccess(true); + } + }).fail(function () { + alert("Failed to turn off Access Point"); + }).always(function () { + self.turnOffAccessPointFetching(false); + }); + }; +} diff --git a/src/html/view_models/WiFiPortalViewModel.js b/src/html/view_models/WiFiPortalViewModel.js new file mode 100644 index 00000000..b1249e46 --- /dev/null +++ b/src/html/view_models/WiFiPortalViewModel.js @@ -0,0 +1,55 @@ +/* global $, ko, ConfigViewModel, StatusViewModel, WiFiScanViewModel */ +/* exported WiFiPortalViewModel */ + +function WiFiPortalViewModel(baseHost) +{ + "use strict"; + var self = this; + + self.baseHost = ko.observable("" !== baseHost ? baseHost : "openevse.local"); + self.baseEndpoint = ko.pureComputed(function () { return "http://" + self.baseHost(); }); + + self.config = new ConfigViewModel(self.baseEndpoint); + self.status = new StatusViewModel(self.baseEndpoint); + self.scan = new WiFiScanViewModel(self.baseEndpoint); + self.wifi = new WiFiConfigViewModel(self.baseEndpoint); + + self.initialised = ko.observable(false); + self.updating = ko.observable(false); + + var updateTimer = null; + var updateTime = 5 * 1000; + + // ----------------------------------------------------------------------- + // Initialise the app + // ----------------------------------------------------------------------- + self.start = function () { + self.updating(true); + self.config.update(function () { + self.status.update(function () { + self.initialised(true); + updateTimer = setTimeout(self.update, updateTime); + self.updating(false); + }); + }); + }; + + // ----------------------------------------------------------------------- + // Get the updated state from the ESP + // ----------------------------------------------------------------------- + self.update = function () { + if (self.updating()) { + return; + } + self.updating(true); + if (null !== updateTimer) { + clearTimeout(updateTimer); + updateTimer = null; + } + self.status.update(function () { + updateTimer = setTimeout(self.update, updateTime); + self.updating(false); + }); + }; +} + diff --git a/src/html/view_models/WiFiScanViewModel.js b/src/html/view_models/WiFiScanViewModel.js new file mode 100644 index 00000000..0a029000 --- /dev/null +++ b/src/html/view_models/WiFiScanViewModel.js @@ -0,0 +1,44 @@ +/* global $, ko */ +/* exported WiFiScanViewModel */ + +function WiFiScanResultViewModel(data) +{ + "use strict"; + var self = this; + ko.mapping.fromJS(data, {}, self); +} + +function WiFiScanViewModel(baseEndpoint) +{ + "use strict"; + var self = this; + var endpoint = ko.pureComputed(function () { return baseEndpoint() + "/scan"; }); + + self.results = ko.mapping.fromJS([], { + key: function(data) { + return ko.utils.unwrapObservable(data.bssid); + }, + create: function (options) { + return new WiFiScanResultViewModel(options.data); + } + }); + + // Observable properties + self.fetching = ko.observable(false); + + self.update = function (after = function () { }) { + self.fetching(true); + $.get(endpoint(), function (data) { + ko.mapping.fromJS(data, self.results); + self.results.sort(function (left, right) { + if(left.ssid() === right.ssid()) { + return left.rssi() < right.rssi() ? 1 : -1; + } + return left.ssid() < right.ssid() ? -1 : 1; + }); + }, "json").always(function () { + self.fetching(false); + after(); + }); + }; +} diff --git a/src/html/wifi_portal.js b/src/html/wifi_portal.js index 33bda685..9fc582b6 100644 --- a/src/html/wifi_portal.js +++ b/src/html/wifi_portal.js @@ -1,4 +1,4 @@ -/* global $, ko */ +/* global $, ko, WiFiPortalViewModel */ (function() { "use strict"; @@ -9,278 +9,10 @@ var baseHost = window.location.hostname; //var baseHost = "openevse.local"; //var baseHost = "192.168.4.1"; -var baseEndpoint = "http://" + baseHost; - -function BaseViewModel(defaults, remoteUrl, mappings = {}) { - var self = this; - self.remoteUrl = remoteUrl; - - // Observable properties - ko.mapping.fromJS(defaults, mappings, self); - self.fetching = ko.observable(false); -} - -BaseViewModel.prototype.update = function (after = function () { }) { - var self = this; - self.fetching(true); - $.get(self.remoteUrl, function (data) { - ko.mapping.fromJS(data, self); - }, "json").always(function () { - self.fetching(false); - after(); - }); -}; - -function StatusViewModel() { - var self = this; - - BaseViewModel.call(self, { - "mode": "ERR", - "srssi": "", - "ipaddress": "", - "packets_sent": "", - "packets_success": "", - "emoncms_connected": "", - "mqtt_connected": "", - "ohm_hour": "", - "free_heap": "" - }, baseEndpoint + "/status"); - - // Some devired values - self.isWifiClient = ko.pureComputed(function () { - return ("STA" === self.mode()) || ("STA+AP" === self.mode()); - }); - self.isWifiAccessPoint = ko.pureComputed(function () { - return ("AP" === self.mode()) || ("STA+AP" === self.mode()); - }); - self.fullMode = ko.pureComputed(function () { - switch (self.mode()) { - case "AP": - return "Access Point (AP)"; - case "STA": - return "Client (STA)"; - case "STA+AP": - return "Client + Access Point (STA+AP)"; - } - - return "Unknown (" + self.mode() + ")"; - }); -} -StatusViewModel.prototype = Object.create(BaseViewModel.prototype); -StatusViewModel.prototype.constructor = StatusViewModel; - -function WiFiScanResultViewModel(data) -{ - var self = this; - ko.mapping.fromJS(data, {}, self); -} - -function WiFiScanViewModel() -{ - var self = this; - - self.results = ko.mapping.fromJS([], { - key: function(data) { - return ko.utils.unwrapObservable(data.bssid); - }, - create: function (options) { - return new WiFiScanResultViewModel(options.data); - } - }); - - self.remoteUrl = baseEndpoint + "/scan"; - - // Observable properties - self.fetching = ko.observable(false); - - self.update = function (after = function () { }) { - self.fetching(true); - $.get(self.remoteUrl, function (data) { - ko.mapping.fromJS(data, self.results); - self.results.sort(function (left, right) { - if(left.ssid() === right.ssid()) { - return left.rssi() < right.rssi() ? 1 : -1; - } - return left.ssid() < right.ssid() ? -1 : 1; - }); - }, "json").always(function () { - self.fetching(false); - after(); - }); - }; -} - -function ConfigViewModel() { - BaseViewModel.call(this, { - "ssid": "", - "pass": "", - "firmware": "-", - "protocol": "-", - "espflash": "", - "version": "0.0.0" - }, baseEndpoint + "/config"); -} -ConfigViewModel.prototype = Object.create(BaseViewModel.prototype); -ConfigViewModel.prototype.constructor = ConfigViewModel; - -function WiFiPortal() { - var self = this; - - self.config = new ConfigViewModel(); - self.status = new StatusViewModel(); - self.scan = new WiFiScanViewModel(); - - self.initialised = ko.observable(false); - self.updating = ko.observable(false); - self.scanUpdating = ko.observable(false); - - self.bssid = ko.observable(""); - self.bssid.subscribe(function (bssid) { - for(var i = 0; i < self.scan.results().length; i++) { - var net = self.scan.results()[i]; - if(bssid === net.bssid()) { - self.config.ssid(net.ssid()); - return; - } - } - }); - - var updateTimer = null; - var updateTime = 5 * 1000; - - var scanTimer = null; - var scanTime = 3 * 1000; - - // ----------------------------------------------------------------------- - // Initialise the app - // ----------------------------------------------------------------------- - self.start = function () { - self.updating(true); - self.config.update(function () { - self.status.update(function () { - self.initialised(true); - updateTimer = setTimeout(self.update, updateTime); - self.updating(false); - }); - }); - }; - - // ----------------------------------------------------------------------- - // Get the updated state from the ESP - // ----------------------------------------------------------------------- - self.update = function () { - if (self.updating()) { - return; - } - self.updating(true); - if (null !== updateTimer) { - clearTimeout(updateTimer); - updateTimer = null; - } - self.status.update(function () { - updateTimer = setTimeout(self.update, updateTime); - self.updating(false); - }); - }; - - // ----------------------------------------------------------------------- - // WiFi scan update - // ----------------------------------------------------------------------- - var scanEnabled = false; - self.startScan = function () { - if (self.scanUpdating()) { - return; - } - scanEnabled = true; - self.scanUpdating(true); - if (null !== scanTimer) { - clearTimeout(scanTimer); - scanTimer = null; - } - self.scan.update(function () { - if(scanEnabled) { - scanTimer = setTimeout(self.startScan, scanTime); - } - self.scanUpdating(false); - }); - }; - - self.stopScan = function() { - scanEnabled = false; - if (self.scanUpdating()) { - return; - } - - if (null !== scanTimer) { - clearTimeout(scanTimer); - scanTimer = null; - } - }; - - self.wifiConnecting = ko.observable(false); - self.status.mode.subscribe(function (newValue) { - if(newValue === "STA+AP" || newValue === "STA") { - self.wifiConnecting(false); - } - if(newValue === "STA+AP" || newValue === "AP") { - self.startScan(); - } else { - self.stopScan(); - } - }); - - // ----------------------------------------------------------------------- - // Event: WiFi Connect - // ----------------------------------------------------------------------- - self.saveNetworkFetching = ko.observable(false); - self.saveNetworkSuccess = ko.observable(false); - self.saveNetwork = function () { - if (self.config.ssid() === "") { - alert("Please select network"); - } else { - self.saveNetworkFetching(true); - self.saveNetworkSuccess(false); - $.post(baseEndpoint + "/savenetwork", { ssid: self.config.ssid(), pass: self.config.pass() }, function () { - self.saveNetworkSuccess(true); - self.wifiConnecting(true); - }).fail(function () { - alert("Failed to save WiFi config"); - }).always(function () { - self.saveNetworkFetching(false); - }); - } - }; - - // ----------------------------------------------------------------------- - // Event: Turn off Access Point - // ----------------------------------------------------------------------- - self.turnOffAccessPointFetching = ko.observable(false); - self.turnOffAccessPointSuccess = ko.observable(false); - self.turnOffAccessPoint = function () { - self.turnOffAccessPointFetching(true); - self.turnOffAccessPointSuccess(false); - $.post(baseEndpoint + "/apoff", { - }, function (data) { - console.log(data); - if (self.status.ipaddress() !== "") { - setTimeout(function () { - window.location = "http://" + self.status.ipaddress(); - self.turnOffAccessPointSuccess(true); - }, 3000); - } else { - self.turnOffAccessPointSuccess(true); - } - }).fail(function () { - alert("Failed to turn off Access Point"); - }).always(function () { - self.turnOffAccessPointFetching(false); - }); - }; -} $(function () { // Activates knockout.js - var openevse = new WiFiPortal(); + var openevse = new WiFiPortalViewModel(baseHost); ko.applyBindings(openevse); openevse.start(); }); diff --git a/src/web_server.cpp b/src/web_server.cpp index 938cbc5a..4a2d6e5c 100644 --- a/src/web_server.cpp +++ b/src/web_server.cpp @@ -99,7 +99,7 @@ bool requestPreProcess(AsyncWebServerRequest *request, AsyncResponseStream *&res { dumpRequest(request); - if(wifi_mode == WIFI_MODE_STA && www_username!="" && + if(wifi_mode_is_sta() && www_username!="" && false == request->authenticate(www_username.c_str(), www_password.c_str())) { request->requestAuthentication(esp_hostname); return false; @@ -419,17 +419,19 @@ handleStatus(AsyncWebServerRequest *request) { } String s = "{"; - if (wifi_mode == WIFI_MODE_STA) { + WiFiMode_t wifi_mode = WiFi.getMode(); + if (wifi_mode == WIFI_STA) { s += "\"mode\":\"STA\","; - } else if (wifi_mode == WIFI_MODE_AP_STA_RETRY - || wifi_mode == WIFI_MODE_AP_ONLY) { + } else if (wifi_mode == WIFI_AP) { s += "\"mode\":\"AP\","; - } else if (wifi_mode == WIFI_MODE_AP_AND_STA) { + } else if (wifi_mode == WIFI_AP_STA) { s += "\"mode\":\"STA+AP\","; } + s += "\"wifi_client_connected\":" + String(wifi_client_connected()) + ","; s += "\"srssi\":" + String(WiFi.RSSI()) + ","; s += "\"ipaddress\":\"" + ipaddress + "\","; + s += "\"emoncms_connected\":" + String(emoncms_connected) + ","; s += "\"packets_sent\":" + String(packets_sent) + ","; s += "\"packets_success\":" + String(packets_success) + ","; @@ -811,7 +813,7 @@ void handleNotFound(AsyncWebServerRequest *request) DBUG("NOT_FOUND: "); dumpRequest(request); - if(wifi_mode == WIFI_MODE_AP_ONLY) { + if(wifi_mode_is_ap_only()) { // Redirect to the home page in AP mode (for the captive portal) AsyncResponseStream *response = request->beginResponseStream(String(CONTENT_TYPE_HTML)); diff --git a/src/web_server_static.cpp b/src/web_server_static.cpp index 446be0ab..3287a553 100644 --- a/src/web_server_static.cpp +++ b/src/web_server_static.cpp @@ -33,7 +33,7 @@ bool StaticFileWebHandler::_getFile(AsyncWebServerRequest *request, StaticFile * // Remove the found uri String path = request->url(); if(path == "/") { - path = String((wifi_mode == WIFI_MODE_AP_ONLY) ? WIFI_PAGE : HOME_PAGE); + path = String(wifi_mode_is_ap_only() ? WIFI_PAGE : HOME_PAGE); } DBUGF("Looking for %s", path.c_str()); @@ -72,7 +72,7 @@ void StaticFileWebHandler::handleRequest(AsyncWebServerRequest *request) dumpRequest(request); // Are we authenticated - if(wifi_mode == WIFI_MODE_STA && www_username!="" && + if(wifi_mode_is_sta() && www_username!="" && false == request->authenticate(www_username.c_str(), www_password.c_str())) { request->requestAuthentication(esp_hostname); return; diff --git a/src/wifi.cpp b/src/wifi.cpp index f5205aad..c552ac03 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -22,11 +22,11 @@ const char *esp_hostname = "openevse"; // Wifi Network Strings String connected_network = ""; -String status_string = ""; String ipaddress = ""; -unsigned long Timer; -String st, rssi; +int client_disconnects = 0; +bool client_retry = false; +unsigned long client_retry_time = 0; #ifdef WIFI_LED #ifndef WIFI_LED_ON_STATE @@ -57,41 +57,13 @@ unsigned long wifiLedTimeOut = millis(); #define WIFI_BUTTON_TIMEOUT 5 * 1000 #endif +#ifndef WIFI_CLIENT_RETRY_TIMEOUT +#define WIFI_CLIENT_RETRY_TIMEOUT (5 * 60 * 1000) +#endif + int wifiButtonState = HIGH; unsigned long wifiButtonTimeOut = millis(); -// ------------------------------------------------------------------- -int wifi_mode = WIFI_MODE_STA; - -// Client connection state -enum { - Client_Disconnected, - Client_Connecting, - Client_Retry, - Client_Connected -} clientState = Client_Disconnected; - -void sync_mode(bool retry = false) -{ - DBUGVAR(WiFi.getMode()); - switch(WiFi.getMode()) - { - case WIFI_OFF: - break; - case WIFI_STA: - wifi_mode = WIFI_MODE_STA; - break; - case WIFI_AP: - wifi_mode = WIFI_MODE_AP_ONLY; - break; - case WIFI_AP_STA: - wifi_mode = retry ? WIFI_MODE_AP_STA_RETRY : WIFI_MODE_AP_AND_STA; - break; - } - - DBUGVAR(wifi_mode); -} - // ------------------------------------------------------------------- // Start Access Point // Access point is used for wifi network selection @@ -102,15 +74,16 @@ startAP() { if((WiFi.getMode() & WIFI_STA) && WiFi.isConnected()) { WiFi.disconnect(true); - clientState = Client_Disconnected; } WiFi.enableAP(true); WiFi.softAPConfig(apIP, apIP, netMsk); + // Create Unique SSID e.g "emonESP_XXXXXX" String softAP_ssid_ID = String(softAP_ssid) + "_" + String(ESP.getChipId()); + // Pick a random channel out of 1, 6 or 11 int channel = (random(3) * 5) + 1; WiFi.softAP(softAP_ssid_ID.c_str(), softAP_password, channel); @@ -129,8 +102,6 @@ startAP() { lcd_display(F("SSID: openevse"), 0, 1, 15 * 1000, LCD_CLEAR_LINE); apClients = 0; - - sync_mode(); } // ------------------------------------------------------------------- @@ -144,13 +115,9 @@ startClient() // DEBUG.print(" epass:"); // DEBUG.println(epass.c_str()); - WiFi.hostname("openevse"); + WiFi.hostname(esp_hostname); WiFi.begin(esid.c_str(), epass.c_str()); WiFi.enableSTA(true); - - clientState = Client_Connecting; - - sync_mode(); } void wifi_onStationModeGotIP(const WiFiEventStationModeGotIP &event) @@ -168,10 +135,13 @@ void wifi_onStationModeGotIP(const WiFiEventStationModeGotIP &event) DEBUG.println(tmpStr); lcd_display(F("IP Address"), 0, 0, 0, LCD_CLEAR_LINE); lcd_display(tmpStr, 0, 1, 5000, LCD_CLEAR_LINE); + // Copy the connected network and ipaddress to global strings for use in status request connected_network = esid; - clientState = Client_Connected; + // Clear any error state + client_disconnects = 0; + client_retry = false; } void wifi_onStationModeDisconnected(const WiFiEventStationModeDisconnected &event) @@ -207,7 +177,7 @@ void wifi_onStationModeDisconnected(const WiFiEventStationModeDisconnected &even WIFI_DISCONNECT_REASON_HANDSHAKE_TIMEOUT == event.reason ? "WIFI_DISCONNECT_REASON_HANDSHAKE_TIMEOUT" : "UNKNOWN"); - clientState = WiFi.getMode() & WIFI_STA ? Client_Connecting : Client_Disconnected; + client_disconnects++; } void @@ -252,96 +222,58 @@ void wifi_loop() { Profile_Start(wifi_loop); + bool isClient = wifi_mode_is_sta(); + bool isClientOnly = wifi_mode_is_sta_only(); + bool isAp = wifi_mode_is_ap(); + bool isApOnly = wifi_mode_is_ap_only(); + #ifdef WIFI_LED - if ((WIFI_MODE_AP_ONLY == wifi_mode || - Client_Connecting == clientState) && + if ((isApOnly || !WiFi.isConnected()) && millis() > wifiLedTimeOut) { wifiLedState = !wifiLedState; digitalWrite(WIFI_LED, wifiLedState); - int ledTime = Client_Connecting == clientState ? WIFI_LED_STA_CONNECTING_TIME : - 0 == apClients ? WIFI_LED_AP_TIME : WIFI_LED_AP_CONNECTED_TIME; + int ledTime = isApOnly ? (0 == apClients ? WIFI_LED_AP_TIME : WIFI_LED_AP_CONNECTED_TIME) : WIFI_LED_STA_CONNECTING_TIME; wifiLedTimeOut = millis() + ledTime; } #endif - // Manage the client connecting state - switch(clientState) + // Manage state while connecting + if(isClient && !WiFi.isConnected()) { - case Client_Disconnected: - break; - - case Client_Connecting: - { - // Pressing the boot button for 5 seconds while connecting will turn on AP mode - #if !defined(WIFI_LED) || WIFI_BUTTON != WIFI_LED - int button = digitalRead(WIFI_BUTTON); - if(wifiButtonState != button) { - wifiButtonState = button; - if(LOW == button) { - wifiButtonTimeOut = millis() + WIFI_BUTTON_TIMEOUT; - } - } - - if(LOW == wifiButtonState && millis() > wifiButtonTimeOut) { - startAP(); - } - #endif +#if !defined(WIFI_LED) || WIFI_BUTTON != WIFI_LED + // Pressing the boot button for 5 seconds while connecting will turn on AP mode + int button = digitalRead(WIFI_BUTTON); + if(wifiButtonState != button) { + wifiButtonState = button; + if(LOW == button) { + wifiButtonTimeOut = millis() + WIFI_BUTTON_TIMEOUT; } - break; - - case Client_Connected: - break; + } - case Client_Retry: - break; - } -/* -int t = 0; -int attempt = 0; -while (WiFi.status() != WL_CONNECTED) { - delay(500); - t++; - // push and hold boot button after power on to skip stright to AP mode - if (t >= 20 -#if !defined(WIFI_LED) || 0 != WIFI_LED - || digitalRead(0) == LOW + if(LOW == wifiButtonState && millis() > wifiButtonTimeOut) { + startAP(); + } #endif - ) - { - DEBUG.println(" "); - DEBUG.println("Try Again..."); - delay(2000); - WiFi.disconnect(); - WiFi.begin(esid.c_str(), epass.c_str()); - t = 0; - attempt++; - if (attempt >= 5 || digitalRead(0) == LOW) { + + // If we have failed to connect turn on the AP + if(client_disconnects > 2) { startAP(); - // AP mode with SSID in EEPROM, connection will retry in 5 minutes - wifi_mode = WIFI_MODE_AP_STA_RETRY; - break; + client_retry = true; + client_retry_time = millis() + WIFI_CLIENT_RETRY_TIMEOUT; } } -} - -if (wifi_mode == WIFI_MODE_STA || wifi_mode == WIFI_MODE_AP_AND_STA) { -} - -*/ - - dnsServer.processNextRequest(); // Captive portal DNS re-dierct // Remain in AP mode for 5 Minutes before resetting - if (wifi_mode == WIFI_MODE_AP_STA_RETRY) { - if ((millis() - Timer) >= 300000) { - DEBUG.println("WIFI Mode = 1, resetting"); - delay(50); - ESP.reset(); - } + if(isApOnly && client_retry && millis() > client_retry_time) { + DEBUG.println("client re-try, resetting"); + delay(50); + ESP.reset(); } + dnsServer.processNextRequest(); // Captive portal DNS re-dierct + Profile_End(wifi_loop, 5); } @@ -357,23 +289,22 @@ wifi_disconnect() { void wifi_turn_off_ap() { - if(WIFI_MODE_AP_AND_STA == wifi_mode) + if(wifi_mode_is_ap()) { WiFi.softAPdisconnect(true); dnsServer.stop(); - sync_mode(); } } void wifi_turn_on_ap() { - DBUGF("wifi_turn_on_ap %d", wifi_mode); - if(WIFI_MODE_STA == wifi_mode) { + DBUGF("wifi_turn_on_ap %d", WiFi.getMode()); + if(!wifi_mode_is_ap()) { startAP(); } } bool wifi_client_connected() { - return WiFi.isConnected() && (wifi_mode == WIFI_MODE_STA || wifi_mode == WIFI_MODE_AP_AND_STA); + return WiFi.isConnected() && (WIFI_STA == (WiFi.getMode() & WIFI_STA)); } diff --git a/src/wifi.h b/src/wifi.h index e1778561..5e1ada82 100644 --- a/src/wifi.h +++ b/src/wifi.h @@ -3,19 +3,6 @@ #include -// Wifi mode -// 0 - STA (Client) -// 1 - AP with STA retry -// 2 - AP only -// 3 - AP + STA - -#define WIFI_MODE_STA 0 -#define WIFI_MODE_AP_STA_RETRY 1 -#define WIFI_MODE_AP_ONLY 2 -#define WIFI_MODE_AP_AND_STA 3 - -// The current WiFi mode -extern int wifi_mode; // Last discovered WiFi access points extern String st; @@ -36,4 +23,10 @@ extern void wifi_turn_off_ap(); extern void wifi_turn_on_ap(); extern bool wifi_client_connected(); +// Wifi mode +#define wifi_mode_is_sta() (WIFI_STA == (WiFi.getMode() & WIFI_STA)) +#define wifi_mode_is_sta_only() (WIFI_STA == WiFi.getMode()) +#define wifi_mode_is_ap() (WIFI_AP == (WiFi.getMode() & WIFI_AP)) +#define wifi_mode_is_ap_only() (WIFI_AP == WiFi.getMode()) + #endif // _EMONESP_WIFI_H