add demo project implementation
This commit is contained in:
		
							
								
								
									
										3
									
								
								data/config/demoSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								data/config/demoSettings.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | { | ||||||
|  |   "blink_speed": 100 | ||||||
|  | } | ||||||
| @@ -1 +1 @@ | |||||||
| REACT_APP_ENDPOINT_ROOT=http://192.168.0.19/rest/ | REACT_APP_ENDPOINT_ROOT=http://192.168.0.20/rest/ | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								interface/src/components/LoadingNotification.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								interface/src/components/LoadingNotification.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  |  | ||||||
|  | import { makeStyles } from '@material-ui/core/styles'; | ||||||
|  | import Button from '@material-ui/core/Button'; | ||||||
|  | import LinearProgress from '@material-ui/core/LinearProgress'; | ||||||
|  | import Typography from '@material-ui/core/Typography'; | ||||||
|  |  | ||||||
|  | const useStyles = makeStyles(theme => ({ | ||||||
|  |   loadingSettings: { | ||||||
|  |     margin: theme.spacing(0.5), | ||||||
|  |   }, | ||||||
|  |   loadingSettingsDetails: { | ||||||
|  |     margin: theme.spacing(4), | ||||||
|  |     textAlign: "center" | ||||||
|  |   }, | ||||||
|  |   button: { | ||||||
|  |     marginRight: theme.spacing(2), | ||||||
|  |     marginTop: theme.spacing(2), | ||||||
|  |   } | ||||||
|  | })); | ||||||
|  |  | ||||||
|  | export default function LoadingNotification(props) { | ||||||
|  |   const classes = useStyles(); | ||||||
|  |   const { fetched, errorMessage, onReset, children } = props; | ||||||
|  |   return ( | ||||||
|  |     <div> | ||||||
|  |       { | ||||||
|  |         fetched ? | ||||||
|  |           errorMessage ? | ||||||
|  |             <div className={classes.loadingSettings}> | ||||||
|  |               <Typography variant="h4" className={classes.loadingSettingsDetails}> | ||||||
|  |                 {errorMessage} | ||||||
|  |               </Typography> | ||||||
|  |               <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||||
|  |                 Reset | ||||||
|  |               </Button> | ||||||
|  |             </div> | ||||||
|  |             : | ||||||
|  |             children | ||||||
|  |           : | ||||||
|  |           <div className={classes.loadingSettings}> | ||||||
|  |             <LinearProgress className={classes.loadingSettingsDetails} /> | ||||||
|  |             <Typography variant="h4" className={classes.loadingSettingsDetails}> | ||||||
|  |               Loading... | ||||||
|  |             </Typography> | ||||||
|  |           </div> | ||||||
|  |       } | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | LoadingNotification.propTypes = { | ||||||
|  |   fetched: PropTypes.bool.isRequired, | ||||||
|  |   onReset: PropTypes.func.isRequired, | ||||||
|  |   errorMessage: PropTypes.string | ||||||
|  | }; | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { withSnackbar } from 'notistack'; | import { withSnackbar } from 'notistack'; | ||||||
| import { redirectingAuthorizedFetch } from '../authentication/Authentication'; | import { redirectingAuthorizedFetch } from '../authentication/Authentication'; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| * It is unlikely this application will grow complex enough to require redux. | * It is unlikely this application will grow complex enough to require redux. | ||||||
| * | * | ||||||
| @@ -86,9 +87,9 @@ export const restComponent = (endpointUrl, FormComponent) => { | |||||||
|           }); |           }); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       handleValueChange = name => event => { |       handleValueChange = name => (event, newValue) => { | ||||||
|         const { data } = this.state; |         const { data } = this.state; | ||||||
|         data[name] = event.target.value; |         data[name] = newValue; | ||||||
|         this.setState({ data }); |         this.setState({ data }); | ||||||
|       }; |       }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,16 +12,16 @@ class SecuritySettings extends Component { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   render() { |   render() { | ||||||
|     const { data, fetched, errorMessage } = this.props; |     const { data, fetched, errorMessage, saveData, loadData, handleValueChange } = this.props; | ||||||
|     return ( |     return ( | ||||||
|       <SectionContent title="Security Settings"> |       <SectionContent title="Security Settings"> | ||||||
|         <SecuritySettingsForm |         <SecuritySettingsForm | ||||||
|           securitySettings={data} |           securitySettings={data} | ||||||
|           securitySettingsFetched={fetched} |           securitySettingsFetched={fetched} | ||||||
|           errorMessage={errorMessage} |           errorMessage={errorMessage} | ||||||
|           onSubmit={this.props.saveData} |           onSubmit={saveData} | ||||||
|           onReset={this.props.loadData} |           onReset={loadData} | ||||||
|           handleValueChange={this.props.handleValueChange} |           handleValueChange={handleValueChange} | ||||||
|         /> |         /> | ||||||
|       </SectionContent> |       </SectionContent> | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -1,22 +1,78 @@ | |||||||
| import React, { Component } from 'react'; | import React, { Component } from 'react'; | ||||||
|  |  | ||||||
| import { withStyles } from '@material-ui/core/styles'; |  | ||||||
| import SectionContent from '../components/SectionContent'; | import SectionContent from '../components/SectionContent'; | ||||||
|  | import { restComponent } from '../components/RestComponent'; | ||||||
|  | import LoadingNotification from '../components/LoadingNotification'; | ||||||
|  |  | ||||||
| const styles = theme => ({ | 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 { ValidatorForm } from 'react-material-ui-form-validator'; | ||||||
|  |  | ||||||
| }); | export const DEMO_SETTINGS_ENDPOINT = process.env.REACT_APP_ENDPOINT_ROOT + "demoSettings"; | ||||||
|  |  | ||||||
|  | const valueToPercentage = (value) => `${Math.round(value / 255 * 100)}%`; | ||||||
|  |  | ||||||
| class DemoController extends Component { | class DemoController extends Component { | ||||||
|  |   componentDidMount() { | ||||||
|   render() {     |     this.props.loadData(); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   render() { | ||||||
|  |     const { data, fetched, errorMessage, saveData, loadData, handleValueChange } = this.props; | ||||||
|     return ( |     return ( | ||||||
|       <SectionContent title="Controller" titleGutter> |       <SectionContent title="Controller" titleGutter> | ||||||
|         TODO - This will contain a form which controls the speed of the built in LED. |         <LoadingNotification | ||||||
|  |           onReset={loadData} | ||||||
|  |           fetched={fetched} | ||||||
|  |           errorMessage={errorMessage}> | ||||||
|  |           <DemoControllerForm | ||||||
|  |             demoSettings={data} | ||||||
|  |             onReset={loadData} | ||||||
|  |             onSubmit={saveData} | ||||||
|  |             handleValueChange={handleValueChange} | ||||||
|  |           /> | ||||||
|  |         </LoadingNotification> | ||||||
|       </SectionContent> |       </SectionContent> | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export default withStyles(styles)(DemoController); | const useStyles = makeStyles(theme => ({ | ||||||
|  |   button: { | ||||||
|  |     marginRight: theme.spacing(2), | ||||||
|  |     marginTop: theme.spacing(2), | ||||||
|  |   }, | ||||||
|  |   blinkSpeedLabel:{ | ||||||
|  |     marginBottom: theme.spacing(5), | ||||||
|  |   } | ||||||
|  | })); | ||||||
|  |    | ||||||
|  | function DemoControllerForm(props) { | ||||||
|  |   const { demoSettings, onSubmit, onReset, handleValueChange } = props; | ||||||
|  |   const classes = useStyles(); | ||||||
|  |   return ( | ||||||
|  |     <ValidatorForm onSubmit={onSubmit}> | ||||||
|  |       <Typography id="blink-speed-slider" className={classes.blinkSpeedLabel}> | ||||||
|  |         Blink Speed | ||||||
|  |       </Typography> | ||||||
|  |       <Slider | ||||||
|  |         value={demoSettings.blink_speed} | ||||||
|  |         valueLabelFormat={valueToPercentage} | ||||||
|  |         aria-labelledby="blink-speed-slider" | ||||||
|  |         valueLabelDisplay="on" | ||||||
|  |         min={0} | ||||||
|  |         max={255} | ||||||
|  |         onChange={handleValueChange('blink_speed')} | ||||||
|  |       /> | ||||||
|  |       <Button variant="contained" color="primary" className={classes.button} type="submit"> | ||||||
|  |         Save | ||||||
|  |       </Button> | ||||||
|  |       <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||||
|  |         Reset | ||||||
|  |       </Button> | ||||||
|  |     </ValidatorForm> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default restComponent(DEMO_SETTINGS_ENDPOINT, DemoController); | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import TableCell from '@material-ui/core/TableCell'; | |||||||
| import TableBody from '@material-ui/core/TableBody'; | import TableBody from '@material-ui/core/TableBody'; | ||||||
| import TableRow from '@material-ui/core/TableRow'; | import TableRow from '@material-ui/core/TableRow'; | ||||||
| import Typography from '@material-ui/core/Typography'; | import Typography from '@material-ui/core/Typography'; | ||||||
|  |  | ||||||
| import SectionContent from '../components/SectionContent'; | import SectionContent from '../components/SectionContent'; | ||||||
|  |  | ||||||
| const styles = theme => ({ | const styles = theme => ({ | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import ListItem from '@material-ui/core/ListItem'; | |||||||
| import ListItemIcon from '@material-ui/core/ListItemIcon'; | import ListItemIcon from '@material-ui/core/ListItemIcon'; | ||||||
| import ListItemText from '@material-ui/core/ListItemText'; | import ListItemText from '@material-ui/core/ListItemText'; | ||||||
| import SettingsRemoteIcon from '@material-ui/icons/SettingsRemote'; | import SettingsRemoteIcon from '@material-ui/icons/SettingsRemote'; | ||||||
| import Divider from '@material-ui/core/Divider'; |  | ||||||
|  |  | ||||||
| class ProjectMenu extends Component { | class ProjectMenu extends Component { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,9 +7,10 @@ | |||||||
| ; | ; | ||||||
| ; Please visit documentation for the other options and examples | ; Please visit documentation for the other options and examples | ||||||
| ; http://docs.platformio.org/page/projectconf.html | ; http://docs.platformio.org/page/projectconf.html | ||||||
| [env:node32s] | [env:esp12e] | ||||||
| platform = espressif32 | platform = espressif8266 | ||||||
| board = node32s | board = esp12e | ||||||
|  | board_build.f_cpu = 160000000L | ||||||
|  |  | ||||||
| extra_scripts = pre:timelib_fix.py | extra_scripts = pre:timelib_fix.py | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,9 +1,25 @@ | |||||||
| #include <DemoProject.h> | #include <DemoProject.h> | ||||||
|  |  | ||||||
| void DemoProject::begin() { | void DemoProject::init(AsyncWebServer* server) { | ||||||
|  |   AdminSettingsService::init(server); | ||||||
|  |   pinMode(BLINK_LED, OUTPUT);   | ||||||
| } | } | ||||||
|  |  | ||||||
| void DemoProject::loop() { | void DemoProject::loop() { | ||||||
|  |   unsigned delay = MAX_DELAY / 255 * (255 - _blinkSpeed); | ||||||
|  |   unsigned long currentMillis = millis(); | ||||||
|  |   if (!_lastBlink || (unsigned long)(currentMillis - _lastBlink) >= delay) { | ||||||
|  |     _lastBlink = currentMillis; | ||||||
|  |     digitalWrite(BLINK_LED, !digitalRead(BLINK_LED)); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void DemoProject::readFromJsonObject(JsonObject& root) { | ||||||
|  |   _blinkSpeed = root["blink_speed"] | DEFAULT_BLINK_SPEED; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void DemoProject::writeToJsonObject(JsonObject& root) { | ||||||
|  |   // connection settings | ||||||
|  |   root["blink_speed"] = _blinkSpeed; | ||||||
|  | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,23 +1,35 @@ | |||||||
| #ifndef DemoProject_h | #ifndef DemoProject_h | ||||||
| #define DemoProject_h | #define DemoProject_h | ||||||
|  |  | ||||||
| #include <ESPAsyncWebServer.h> | #include <SettingsService.h> | ||||||
| #include <SecurityManager.h> |  | ||||||
|  |  | ||||||
| class DemoProject { | #define BLINK_LED 2 | ||||||
|  | #define MAX_DELAY 1000 | ||||||
|  |  | ||||||
|  | #define DEFAULT_BLINK_SPEED 100 | ||||||
|  | #define DEMO_SETTINGS_FILE "/config/demoSettings.json" | ||||||
|  | #define DEMO_SETTINGS_PATH "/rest/demoSettings" | ||||||
|  |  | ||||||
|  | class DemoProject : public AdminSettingsService { | ||||||
|  |  | ||||||
|   public: |   public: | ||||||
|  |  | ||||||
|     DemoProject(AsyncWebServer *server, SecurityManager* securityManager) : _server(server), _securityManager(securityManager) {} |     DemoProject(FS* fs, SecurityManager* securityManager) : AdminSettingsService(fs, securityManager, DEMO_SETTINGS_PATH, DEMO_SETTINGS_FILE) {} | ||||||
|  |     ~DemoProject() {} | ||||||
|      |      | ||||||
|     void begin(); |     void init(AsyncWebServer* server); | ||||||
|     void loop(); |     void loop(); | ||||||
|  |  | ||||||
|   private: |   private: | ||||||
|  |  | ||||||
|     AsyncWebServer* _server; |     unsigned long _lastBlink = 0; | ||||||
|     SecurityManager* _securityManager; |     uint8_t _blinkSpeed = 255; | ||||||
|  |  | ||||||
|  |   protected: | ||||||
|  |  | ||||||
|  |     void readFromJsonObject(JsonObject& root); | ||||||
|  |     void writeToJsonObject(JsonObject& root); | ||||||
|  |    | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/main.cpp
									
									
									
									
									
								
							| @@ -7,9 +7,10 @@ | |||||||
| AsyncWebServer server(80); | AsyncWebServer server(80); | ||||||
| ESP8266React espServer(&SPIFFS); | ESP8266React espServer(&SPIFFS); | ||||||
|  |  | ||||||
| DemoProject demoProject = DemoProject(&server, espServer.getSecurityManager()); | DemoProject demoProject = DemoProject(&SPIFFS, espServer.getSecurityManager()); | ||||||
|  |  | ||||||
| void setup() { | void setup() { | ||||||
|  |   // start serial and filesystem | ||||||
|   Serial.begin(SERIAL_BAUD_RATE); |   Serial.begin(SERIAL_BAUD_RATE); | ||||||
|   SPIFFS.begin(); |   SPIFFS.begin(); | ||||||
|  |  | ||||||
| @@ -17,15 +18,16 @@ void setup() { | |||||||
|   espServer.init(&server); |   espServer.init(&server); | ||||||
|  |  | ||||||
|   // begin the demo project |   // begin the demo project | ||||||
|   demoProject.begin(); |   demoProject.init(&server); | ||||||
|    |    | ||||||
|  |   // start the server | ||||||
|   server.begin(); |   server.begin(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void loop() { | void loop() { | ||||||
|   // run the framework loop |   // run the framework's loop function | ||||||
|   espServer.loop(); |   espServer.loop(); | ||||||
|  |  | ||||||
|   // run the demo project loop |   // run the demo project's loop function | ||||||
|   demoProject.loop(); |   demoProject.loop(); | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user