Browse Source

Merge branch 'master' into ft_remove_arduinojson6_workaround

master
Rick Watson 5 years ago
parent
commit
6179435880
  1. 188
      interface/package-lock.json
  2. 6
      interface/package.json
  3. 37
      interface/src/App.js
  4. 1
      interface/src/constants/Endpoints.js
  5. 3
      interface/src/containers/APStatus.js
  6. 3
      interface/src/containers/NTPStatus.js
  7. 77
      interface/src/containers/SystemStatus.js
  8. 3
      interface/src/containers/WiFiStatus.js
  9. 3
      interface/src/forms/APSettingsForm.js
  10. 6
      interface/src/forms/ManageUsersForm.js
  11. 3
      interface/src/forms/NTPSettingsForm.js
  12. 3
      interface/src/forms/OTASettingsForm.js
  13. 3
      interface/src/forms/SecuritySettingsForm.js
  14. 3
      interface/src/forms/WiFiNetworkSelector.js
  15. 3
      interface/src/forms/WiFiSettingsForm.js
  16. 3
      interface/src/project/DemoController.js
  17. 1
      lib/framework/ESP8266React.cpp
  18. 3
      lib/framework/ESP8266React.h
  19. 18
      lib/framework/ResetService.cpp
  20. 29
      lib/framework/ResetService.h

188
interface/package-lock.json

@ -1184,21 +1184,19 @@
}
},
"@material-ui/core": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.4.3.tgz",
"integrity": "sha512-Lz8sMFeCrtq5/pbhqClWFHpveL0huixjca0tw7uvh9xKKB7VyyYOyTu7RamSZLxb34UCSMPlobR+KK25Nqzkqw==",
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.7.0.tgz",
"integrity": "sha512-mwLehUo0Q9ZxjuWo7J1uy1/Grh3nRxlOAaWJ3EtKeJP2HwqlSy8bWrcvRQYlapaYIPXa5jN8zWbTwi8Pk30VQg==",
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/styles": "^4.4.3",
"@material-ui/system": "^4.4.3",
"@material-ui/styles": "^4.6.0",
"@material-ui/system": "^4.5.2",
"@material-ui/types": "^4.1.1",
"@material-ui/utils": "^4.4.0",
"@material-ui/utils": "^4.5.2",
"@types/react-transition-group": "^4.2.0",
"clsx": "^1.0.2",
"convert-css-length": "^2.0.1",
"deepmerge": "^4.0.0",
"hoist-non-react-statics": "^3.2.1",
"is-plain-object": "^3.0.0",
"normalize-scroll-left": "^0.2.0",
"popper.js": "^1.14.1",
"prop-types": "^15.7.2",
@ -1206,44 +1204,43 @@
}
},
"@material-ui/icons": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.4.3.tgz",
"integrity": "sha512-HVVvUyc/78kmaBd93LkfWyGkXMM+zOMKzUfulWXxaV/fFAZ3N0pD0oHjWUd94zrOoF3tZP9JC7EPlIpIcZSNow==",
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.5.1.tgz",
"integrity": "sha512-YZ/BgJbXX4a0gOuKWb30mBaHaoXRqPanlePam83JQPZ/y4kl+3aW0Wv9tlR70hB5EGAkEJGW5m4ktJwMgxQAeA==",
"requires": {
"@babel/runtime": "^7.4.4"
}
},
"@material-ui/styles": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.4.3.tgz",
"integrity": "sha512-kNUdHFWsrvWKIEPx8Xy2/qayqsGMrYmCMq+FIiJiYczVZl5hiS8j5+KayonnpVta/O+Dktk+cxWkVcgwtxMrHg==",
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.6.0.tgz",
"integrity": "sha512-lqqh4UEMdIYcU1Yth4pQyMTah02uAkg3NOT3MirN9FUexdL8pNA6zCHigEgDSfwmvnXyxHhxTkphfy0DRfnt9w==",
"requires": {
"@babel/runtime": "^7.4.4",
"@emotion/hash": "^0.7.1",
"@material-ui/types": "^4.1.1",
"@material-ui/utils": "^4.1.0",
"@material-ui/utils": "^4.5.2",
"clsx": "^1.0.2",
"csstype": "^2.5.2",
"deepmerge": "^4.0.0",
"hoist-non-react-statics": "^3.2.1",
"jss": "10.0.0-alpha.25",
"jss-plugin-camel-case": "10.0.0-alpha.25",
"jss-plugin-default-unit": "10.0.0-alpha.25",
"jss-plugin-global": "10.0.0-alpha.25",
"jss-plugin-nested": "10.0.0-alpha.25",
"jss-plugin-props-sort": "10.0.0-alpha.25",
"jss-plugin-rule-value-function": "10.0.0-alpha.25",
"jss-plugin-vendor-prefixer": "10.0.0-alpha.25",
"jss": "^10.0.0",
"jss-plugin-camel-case": "^10.0.0",
"jss-plugin-default-unit": "^10.0.0",
"jss-plugin-global": "^10.0.0",
"jss-plugin-nested": "^10.0.0",
"jss-plugin-props-sort": "^10.0.0",
"jss-plugin-rule-value-function": "^10.0.0",
"jss-plugin-vendor-prefixer": "^10.0.0",
"prop-types": "^15.7.2"
}
},
"@material-ui/system": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.4.3.tgz",
"integrity": "sha512-Cb05vLXsaCzssXD/iZKa0/qC6YOwbFWnYdnOEdkXZ3Fn2Ytz7rsnMgFejUSQV1luVhUBlEIm8DVz40N25WwW7w==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.5.2.tgz",
"integrity": "sha512-h9RWvdM9XKlHHqwiuhyvWdobptQkHli+m2jJFs7i1AI/hmGsIc4reDmS7fInhETgt/Txx7uiAIznfRNIIVHmQw==",
"requires": {
"@babel/runtime": "^7.4.4",
"deepmerge": "^4.0.0",
"@material-ui/utils": "^4.5.2",
"prop-types": "^15.7.2"
}
},
@ -1256,9 +1253,9 @@
}
},
"@material-ui/utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.4.0.tgz",
"integrity": "sha512-UXoQVwArQEQWXxf2FPs0iJGT+MePQpKr0Qh0CPoLc1OdF0GSMTmQczcqCzwZkeHxHAOq/NkIKM1Pb/ih1Avicg==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.5.2.tgz",
"integrity": "sha512-zhbNfHd1gLa8At6RPDG7uMZubHxbY+LtM6IkSfeWi6Lo4Ax80l62YaN1QmUpO1IvGCkn/j62tQX3yObiQZrJsQ==",
"requires": {
"@babel/runtime": "^7.4.4",
"prop-types": "^15.7.2",
@ -1486,18 +1483,18 @@
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
},
"@types/react": {
"version": "16.9.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.4.tgz",
"integrity": "sha512-ItGNmJvQ0IvWt8rbk5PLdpdQhvBVxAaXI9hDlx7UMd8Ie1iMIuwMNiKeTfmVN517CdplpyXvA22X4zm4jGGZnw==",
"version": "16.9.13",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.13.tgz",
"integrity": "sha512-LikzRslbiufJYHyzbHSW0GrAiff8QYLMBFeZmSxzCYGXKxi8m/1PHX+rsVOwhr7mJNq+VIu2Dhf7U6mjFERK6w==",
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
}
},
"@types/react-transition-group": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.2.tgz",
"integrity": "sha512-YfoaTNqBwbIqpiJ5NNfxfgg5kyFP1Hqf/jqBtSWNv0E+EkkxmN+3VD6U2fu86tlQvdAc1o0SdWhnWFwcRMTn9A==",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.3.tgz",
"integrity": "sha512-Hk8jiuT7iLOHrcjKP/ZVSyCNXK73wJAUz60xm0mVhiRujrdiI++j4duLiL282VGxwAgxetHQFfqA29LgEeSkFA==",
"requires": {
"@types/react": "*"
}
@ -4210,11 +4207,6 @@
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
},
"deepmerge": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.0.0.tgz",
"integrity": "sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww=="
},
"default-gateway": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
@ -4439,12 +4431,27 @@
}
},
"dom-helpers": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.0.tgz",
"integrity": "sha512-zRRYDhpiKuAJHasOqCm7lBnsd22nrM4+OYI4ASWCxen+ocTMl7BIAKgGag97TlLiTl6rrau5aPe1VGUm9jQBng==",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
"integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
"requires": {
"@babel/runtime": "^7.5.5",
"csstype": "^2.6.6"
"@babel/runtime": "^7.6.3",
"csstype": "^2.6.7"
},
"dependencies": {
"@babel/runtime": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
"integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"csstype": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
"integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ=="
}
}
},
"dom-serializer": {
@ -6548,14 +6555,6 @@
"path-is-inside": "^1.0.1"
}
},
"is-plain-object": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz",
"integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==",
"requires": {
"isobject": "^4.0.0"
}
},
"is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
@ -6630,11 +6629,6 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"isobject": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
"integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA=="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@ -7889,9 +7883,9 @@
}
},
"jss": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.25.tgz",
"integrity": "sha512-zqKnXv181B9vue2yYhmVhc+6ggbbxHF/33rjXfXEjaa22nOvknTI21QDfq3oZ8uCC50kcFp3Z8KU1ghUXdFvIA==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"csstype": "^2.6.5",
@ -7900,13 +7894,13 @@
}
},
"jss-plugin-camel-case": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.25.tgz",
"integrity": "sha512-J5ZEGDTy9ddqdTUPAF4SJQ25u5kiG1ORP8F+ZPEZAkkiMQJp+/Aol4I7xhTS2aW1Lhg8xNxdhdRfBi5yU7wOvg==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0.tgz",
"integrity": "sha512-yALDL00+pPR4FJh+k07A8FeDvfoPPuXU48HLy63enAubcVd3DnS+2rgqPXglHDGixIDVkCSXecl/l5GAMjzIbA==",
"requires": {
"@babel/runtime": "^7.3.1",
"hyphenate-style-name": "^1.0.3",
"jss": "10.0.0-alpha.25"
"jss": "10.0.0"
}
},
"jss-plugin-compose": {
@ -7933,12 +7927,12 @@
}
},
"jss-plugin-default-unit": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.25.tgz",
"integrity": "sha512-auOG459B+yEqkojgaXH02SYO9+xjmAxlmP+WbzhVpXqOFJ2CN/kaxd8P4NJZLdj3BQxHiM7WIyMVh786StE+EA==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0.tgz",
"integrity": "sha512-sURozIOdCtGg9ap18erQ+ijndAfEGtTaetxfU3H4qwC18Bi+fdvjlY/ahKbuu0ASs7R/+WKCP7UaRZOjUDMcdQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.25"
"jss": "10.0.0"
}
},
"jss-plugin-expand": {
@ -7987,40 +7981,40 @@
}
},
"jss-plugin-global": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.25.tgz",
"integrity": "sha512-cS98Q8X8jwltuaBZd9eYuxMXxkUL+mJGl2Ok3/nmJzH9nLzj6i7kLxSoDtuJNqsRmbP7ogIXVozJUq9lUu2hlQ==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0.tgz",
"integrity": "sha512-80ofWKSQUo62bxLtRoTNe0kFPtHgUbAJeOeR36WEGgWIBEsXLyXOnD5KNnjPqG4heuEkz9eSLccjYST50JnI7Q==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.25"
"jss": "10.0.0"
}
},
"jss-plugin-nested": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.25.tgz",
"integrity": "sha512-7sk7/6mX1YTgXe+AyeD1zEyKTgIGbbhYtg+wWQcHJlE1flW2JHfcQ5mw84FgHcHQRQ8Dq3l9I3aEY51ev0J1Wg==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0.tgz",
"integrity": "sha512-waxxwl/po1hN3azTyixKnr8ReEqUv5WK7WsO+5AWB0bFndML5Yqnt8ARZ90HEg8/P6WlqE/AB2413TkCRZE8bA==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.25",
"jss": "10.0.0",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-props-sort": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.25.tgz",
"integrity": "sha512-8B/6QLQuUX8cIlZbXdjEm5l0jCX4EgacYMcFJhdKwDKEZYeAghpgQQrCKl0/CYHW7iFge5wim67P+uL6QxMzyw==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0.tgz",
"integrity": "sha512-41mf22CImjwNdtOG3r+cdC8+RhwNm616sjHx5YlqTwtSJLyLFinbQC/a4PIFk8xqf1qpFH1kEAIw+yx9HaqZ3g==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.25"
"jss": "10.0.0"
}
},
"jss-plugin-rule-value-function": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.25.tgz",
"integrity": "sha512-CQQtWO+/OZRGaFRBSGQUgAci9YlVtdoXcWQKBNo70tmpp+kaXKlFNCYaL3jmHbJHMiwKQYG2RYFQNIrwJ9SGmA==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0.tgz",
"integrity": "sha512-Jw+BZ8JIw1f12V0SERqGlBT1JEPWax3vuZpMym54NAXpPb7R1LYHiCTIlaJUyqvIfEy3kiHMtgI+r2whGgRIxQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.25"
"jss": "10.0.0"
}
},
"jss-plugin-rule-value-observable": {
@ -8070,13 +8064,13 @@
}
},
"jss-plugin-vendor-prefixer": {
"version": "10.0.0-alpha.25",
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.25.tgz",
"integrity": "sha512-5FXpB/TiwckbrkoDCmd27YsWCESl1K4hAX/oro2/geEXgnVQvDgQOf2eWCsjYO2K1lYPPXtskMfws/Q3eKmbYg==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0.tgz",
"integrity": "sha512-qslqvL0MUbWuzXJWdUxpj6mdNUX8jr4FFTo3aZnAT65nmzWL7g8oTr9ZxmTXXgdp7ANhS1QWE7036/Q2isFBpw==",
"requires": {
"@babel/runtime": "^7.3.1",
"css-vendor": "^2.0.6",
"jss": "10.0.0-alpha.25"
"jss": "10.0.0"
}
},
"jss-preset-default": {
@ -8940,9 +8934,9 @@
"integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg=="
},
"notistack": {
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/notistack/-/notistack-0.8.9.tgz",
"integrity": "sha512-nRHQVWUfgHnvnKrjRbRX9f+YAnbyh96yRyO5bEP/FCLVLuTZcJOwUr0GZ7Xr/8wK3+hXa9JYpXUkUhSxj1K8NQ==",
"version": "0.9.6",
"resolved": "https://registry.npmjs.org/notistack/-/notistack-0.9.6.tgz",
"integrity": "sha512-vo1zOwhQBxwWiMxwVjeSDXNzJuaM/nfkayv4uRo+9ON9CAtaPSNt15QHeELKkbOSLH29fb7zmoZl4AlkCqhGsA==",
"requires": {
"classnames": "^2.2.6",
"hoist-non-react-statics": "^3.3.0",
@ -9526,9 +9520,9 @@
}
},
"popper.js": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz",
"integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA=="
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz",
"integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw=="
},
"portfinder": {
"version": "1.0.24",

6
interface/package.json

@ -3,12 +3,12 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.4.3",
"@material-ui/icons": "^4.4.3",
"@material-ui/core": "^4.7.0",
"@material-ui/icons": "^4.5.1",
"compression-webpack-plugin": "^2.0.0",
"jwt-decode": "^2.2.0",
"moment": "^2.24.0",
"notistack": "^0.8.9",
"notistack": "^0.9.6",
"prop-types": "^15.7.2",
"react": "^16.10.1",
"react-dom": "^16.10.1",

37
interface/src/App.js

@ -2,22 +2,16 @@ import React, { Component } from 'react';
import { Redirect, Route, Switch } from 'react-router';
import AppRouting from './AppRouting';
import { SnackbarProvider } from 'notistack';
import CssBaseline from '@material-ui/core/CssBaseline';
import blueGrey from '@material-ui/core/colors/blueGrey';
import indigo from '@material-ui/core/colors/indigo';
import orange from '@material-ui/core/colors/orange';
import red from '@material-ui/core/colors/red';
import green from '@material-ui/core/colors/green';
import { PROJECT_NAME } from './constants/Env';
import { SnackbarProvider } from 'notistack';
import { create } from 'jss';
import { CssBaseline, IconButton, MuiThemeProvider, createMuiTheme } from '@material-ui/core';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { blueGrey, indigo, orange, red, green } from '@material-ui/core/colors';
import CloseIcon from '@material-ui/icons/Close';
import {
MuiThemeProvider,
createMuiTheme
} from '@material-ui/core/styles';
// Our theme
const theme = createMuiTheme({
@ -38,11 +32,28 @@ const jss = create(jssPreset());
const unauthorizedRedirect = () => <Redirect to="/" />;
class App extends Component {
notistackRef = React.createRef();
componentDidMount() {
document.title = PROJECT_NAME;
}
onClickDismiss = (key) => () => {
this.notistackRef.current.closeSnackbar(key);
}
render() {
return (
<StylesProvider jss={jss}>
<MuiThemeProvider theme={theme}>
<SnackbarProvider maxSnack={3}>
<SnackbarProvider maxSnack={3} anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
ref={this.notistackRef}
action={(key) => (
<IconButton onClick={this.onClickDismiss(key)} size="small">
<CloseIcon />
</IconButton>
)}>
<CssBaseline />
<Switch>
<Route exact path="/unauthorized" component={unauthorizedRedirect} />

1
interface/src/constants/Endpoints.js

@ -13,3 +13,4 @@ export const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + "systemStatus";
export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + "signIn";
export const VERIFY_AUTHORIZATION_ENDPOINT = ENDPOINT_ROOT + "verifyAuthorization";
export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "securitySettings";
export const RESET_ENDPOINT = ENDPOINT_ROOT + "reset";

3
interface/src/containers/APStatus.js

@ -11,6 +11,7 @@ import Divider from '@material-ui/core/Divider';
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
import ComputerIcon from '@material-ui/icons/Computer';
import RefreshIcon from '@material-ui/icons/Refresh';
import { restComponent } from '../components/RestComponent';
import LoadingNotification from '../components/LoadingNotification';
@ -93,7 +94,7 @@ class APStatus extends Component {
<List>
{this.createListItems(data, classes)}
</List>
<Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
<Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
Refresh
</Button>
</div>

3
interface/src/containers/NTPStatus.js

@ -14,6 +14,7 @@ import DNSIcon from '@material-ui/icons/Dns';
import TimerIcon from '@material-ui/icons/Timer';
import UpdateIcon from '@material-ui/icons/Update';
import AvTimerIcon from '@material-ui/icons/AvTimer';
import RefreshIcon from '@material-ui/icons/Refresh';
import { isSynchronized, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus';
import * as Highlight from '../constants/Highlight';
@ -118,7 +119,7 @@ class NTPStatus extends Component {
<List>
{this.createListItems(data, classes)}
</List>
<Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
<Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
Refresh
</Button>
</div>

77
interface/src/containers/SystemStatus.js

@ -1,4 +1,5 @@
import React, { Component, Fragment } from 'react';
import { withSnackbar } from 'notistack';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
@ -8,16 +9,24 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import ListItemText from '@material-ui/core/ListItemText';
import Avatar from '@material-ui/core/Avatar';
import Divider from '@material-ui/core/Divider';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import DevicesIcon from '@material-ui/icons/Devices';
import MemoryIcon from '@material-ui/icons/Memory';
import ShowChartIcon from '@material-ui/icons/ShowChart';
import SdStorageIcon from '@material-ui/icons/SdStorage';
import DataUsageIcon from '@material-ui/icons/DataUsage';
import AutorenewIcon from '@material-ui/icons/Autorenew';
import RefreshIcon from '@material-ui/icons/Refresh';
import { SYSTEM_STATUS_ENDPOINT } from '../constants/Endpoints';
import { SYSTEM_STATUS_ENDPOINT, RESET_ENDPOINT } from '../constants/Endpoints';
import { restComponent } from '../components/RestComponent';
import LoadingNotification from '../components/LoadingNotification';
import SectionContent from '../components/SectionContent';
import { redirectingAuthorizedFetch } from '../authentication/Authentication';
const styles = theme => ({
button: {
@ -28,6 +37,16 @@ const styles = theme => ({
class SystemStatus extends Component {
constructor(props) {
super(props);
this.state = {
confirmReset: false,
processing: false
}
}
componentDidMount() {
this.props.loadData();
}
@ -90,13 +109,63 @@ class SystemStatus extends Component {
<List>
{this.createListItems(data, classes)}
</List>
<Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
<Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
Refresh
</Button>
<Button startIcon={<AutorenewIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.onReset}>
Reset
</Button>
</div>
);
}
onReset = () => {
this.setState({ confirmReset: true });
}
onResetRejected = () => {
this.setState({ confirmReset: false });
}
onResetConfirmed = () => {
this.setState({ processing: true });
redirectingAuthorizedFetch(RESET_ENDPOINT, { method: 'POST' })
.then(response => {
if (response.status === 200) {
this.props.enqueueSnackbar("Device is reseting", { variant: 'info' });
this.setState({ processing: false, confirmReset: false });
} else {
throw Error("Invalid status code: " + response.status);
}
})
.catch(error => {
this.props.enqueueSnackbar(error.message || "Problem resetting device", { variant: 'error' });
this.setState({ processing: false, confirmReset: false });
});
}
renderResetDialog() {
return (
<Dialog
open={this.state.confirmReset}
onClose={this.onResetRejected}
>
<DialogTitle>Confirm Reset</DialogTitle>
<DialogContent dividers={true}>
Are you sure you want to reset the device?
</DialogContent>
<DialogActions>
<Button startIcon={<AutorenewIcon />} variant="contained" onClick={this.onResetConfirmed} disabled={this.state.processing} color="primary" autoFocus>
Reset
</Button>
<Button variant="contained" onClick={this.onResetRejected} color="secondary">
Cancel
</Button>
</DialogActions>
</Dialog>
)
}
render() {
const { data, fetched, errorMessage, loadData, classes } = this.props;
return (
@ -109,9 +178,11 @@ class SystemStatus extends Component {
() => this.renderSystemStatus(data, classes)
}
/>
{this.renderResetDialog()}
</SectionContent>
)
}
}
export default restComponent(SYSTEM_STATUS_ENDPOINT, withStyles(styles)(SystemStatus));
export default withSnackbar(restComponent(SYSTEM_STATUS_ENDPOINT, withStyles(styles)(SystemStatus)));

3
interface/src/containers/WiFiStatus.js

@ -13,6 +13,7 @@ import DNSIcon from '@material-ui/icons/Dns';
import SettingsInputComponentIcon from '@material-ui/icons/SettingsInputComponent';
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
import RefreshIcon from '@material-ui/icons/Refresh';
import SectionContent from '../components/SectionContent';
import { WIFI_STATUS_ENDPOINT } from '../constants/Endpoints';
@ -130,7 +131,7 @@ class WiFiStatus extends Component {
<List>
{this.createListItems(data, classes)}
</List>
<Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
<Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
Refresh
</Button>
</div>

3
interface/src/forms/APSettingsForm.js

@ -8,6 +8,7 @@ import PasswordValidator from '../components/PasswordValidator';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import MenuItem from '@material-ui/core/MenuItem';
import SaveIcon from '@material-ui/icons/Save';
const styles = theme => ({
textField: {
@ -61,7 +62,7 @@ class APSettingsForm extends React.Component {
/>
</Fragment>
}
<Button variant="contained" color="primary" className={classes.button} type="submit">
<Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>

6
interface/src/forms/ManageUsersForm.js

@ -18,6 +18,8 @@ import DeleteIcon from '@material-ui/icons/Delete';
import CloseIcon from '@material-ui/icons/Close';
import CheckIcon from '@material-ui/icons/Check';
import IconButton from '@material-ui/core/IconButton';
import SaveIcon from '@material-ui/icons/Save';
import PersonAddIcon from '@material-ui/icons/PersonAdd';
import UserForm from './UserForm';
import { withAuthenticationContext } from '../authentication/Context';
@ -161,7 +163,7 @@ class ManageUsersForm extends React.Component {
<TableRow>
<TableCell colSpan={2} />
<TableCell align="center">
<Button variant="contained" color="secondary" onClick={this.createUser}>
<Button startIcon={<PersonAddIcon />} variant="contained" color="secondary" onClick={this.createUser}>
Add User
</Button>
</TableCell>
@ -176,7 +178,7 @@ class ManageUsersForm extends React.Component {
</Box>
</Typography>
}
<Button variant="contained" color="primary" className={classes.button} type="submit" disabled={this.noAdminConfigured()}>
<Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit" disabled={this.noAdminConfigured()}>
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>

3
interface/src/forms/NTPSettingsForm.js

@ -4,6 +4,7 @@ import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import SaveIcon from '@material-ui/icons/Save';
import isIP from '../validators/isIP';
import isHostname from '../validators/isHostname';
@ -50,7 +51,7 @@ class NTPSettingsForm extends React.Component {
onChange={handleValueChange('interval')}
margin="normal"
/>
<Button variant="contained" color="primary" className={classes.button} type="submit">
<Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>

3
interface/src/forms/OTASettingsForm.js

@ -6,6 +6,7 @@ import Button from '@material-ui/core/Button';
import Switch from '@material-ui/core/Switch';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import SaveIcon from '@material-ui/icons/Save';
import isIP from '../validators/isIP';
import isHostname from '../validators/isHostname';
@ -69,7 +70,7 @@ class OTASettingsForm extends React.Component {
onChange={handleValueChange('password')}
margin="normal"
/>
<Button variant="contained" color="primary" className={classes.button} type="submit">
<Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>

3
interface/src/forms/SecuritySettingsForm.js

@ -6,6 +6,7 @@ import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import SaveIcon from '@material-ui/icons/Save';
import PasswordValidator from '../components/PasswordValidator';
import { withAuthenticationContext } from '../authentication/Context';
@ -46,7 +47,7 @@ class SecuritySettingsForm extends React.Component {
If you modify the JWT Secret, all users will be logged out.
</Box>
</Typography>
<Button variant="contained" color="primary" className={classes.button} type="submit">
<Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>

3
interface/src/forms/WiFiNetworkSelector.js

@ -18,6 +18,7 @@ import Badge from '@material-ui/core/Badge';
import WifiIcon from '@material-ui/icons/Wifi';
import LockIcon from '@material-ui/icons/Lock';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import PermScanWifiIcon from '@material-ui/icons/PermScanWifi';
import { isNetworkOpen, networkSecurityMode } from '../constants/WiFiSecurityModes';
@ -86,7 +87,7 @@ class WiFiNetworkSelector extends Component {
</div>
}
<Button variant="contained" color="secondary" className={classes.button} onClick={requestNetworkScan} disabled={scanningForNetworks}>
<Button startIcon={<PermScanWifiIcon />} variant="contained" color="secondary" className={classes.button} onClick={requestNetworkScan} disabled={scanningForNetworks}>
Scan again...
</Button>
</div>

3
interface/src/forms/WiFiSettingsForm.js

@ -16,6 +16,7 @@ import IconButton from '@material-ui/core/IconButton';
import LockIcon from '@material-ui/icons/Lock';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import DeleteIcon from '@material-ui/icons/Delete';
import SaveIcon from '@material-ui/icons/Save';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import { isNetworkOpen, networkSecurityMode } from '../constants/WiFiSecurityModes';
@ -175,7 +176,7 @@ class WiFiSettingsForm extends React.Component {
/>
</Fragment>
}
<Button variant="contained" color="primary" className={classes.button} type="submit">
<Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>

3
interface/src/project/DemoController.js

@ -10,6 +10,7 @@ import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import Slider from '@material-ui/core/Slider';
import { makeStyles } from '@material-ui/core/styles';
import SaveIcon from '@material-ui/icons/Save';
export const DEMO_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "demoSettings";
@ -69,7 +70,7 @@ function DemoControllerForm(props) {
max={255}
onChange={handleSliderChange('blink_speed')}
/>
<Button variant="contained" color="primary" className={classes.button} type="submit">
<Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>

1
lib/framework/ESP8266React.cpp

@ -6,6 +6,7 @@ ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs):
_apSettingsService(server, fs, &_securitySettingsService),
_ntpSettingsService(server, fs, &_securitySettingsService),
_otaSettingsService(server, fs, &_securitySettingsService),
_ResetService(server, &_securitySettingsService),
_authenticationService(server, &_securitySettingsService),
_wifiScanner(server, &_securitySettingsService),
_wifiStatus(server, &_securitySettingsService),

3
lib/framework/ESP8266React.h

@ -24,6 +24,7 @@
#include <NTPStatus.h>
#include <APStatus.h>
#include <SystemStatus.h>
#include <ResetService.h>
class ESP8266React {
@ -46,8 +47,10 @@ class ESP8266React {
APSettingsService _apSettingsService;
NTPSettingsService _ntpSettingsService;
OTASettingsService _otaSettingsService;
ResetService _ResetService;
AuthenticationService _authenticationService;
WiFiScanner _wifiScanner;
WiFiStatus _wifiStatus;
NTPStatus _ntpStatus;

18
lib/framework/ResetService.cpp

@ -0,0 +1,18 @@
#include <ResetService.h>
ResetService::ResetService(AsyncWebServer* server, SecurityManager* securityManager) {
server->on(RESET_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest(
std::bind(&ResetService::reset, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN
));
}
void ResetService::reset(AsyncWebServerRequest* request) {
request->onDisconnect([]() {
#if defined(ESP8266)
ESP.reset();
#elif defined(ESP_PLATFORM)
ESP.restart();
#endif
});
request->send(200);
}

29
lib/framework/ResetService.h

@ -0,0 +1,29 @@
#ifndef ResetService_h
#define ResetService_h
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM)
#include <WiFi.h>
#include <AsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#define RESET_SERVICE_PATH "/rest/reset"
class ResetService {
public:
ResetService(AsyncWebServer* server, SecurityManager* securityManager);
private:
void reset(AsyncWebServerRequest *request);
};
#endif // end ResetService_h
Loading…
Cancel
Save