diff --git a/.clang-format b/.clang-format old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index facc712..6770efa --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /interface/build /interface/node_modules .vscode +*.json diff --git a/.travis.yml b/.travis.yml old mode 100644 new mode 100755 diff --git a/LICENSE.txt b/LICENSE.txt old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/factory_settings.ini b/factory_settings.ini old mode 100644 new mode 100755 diff --git a/features.ini b/features.ini old mode 100644 new mode 100755 index 6b6e231..24f69e6 --- a/features.ini +++ b/features.ini @@ -4,5 +4,5 @@ build_flags = -D FT_SECURITY=1 -D FT_MQTT=0 -D FT_NTP=1 - -D FT_OTA=1 - -D FT_UPLOAD_FIRMWARE=1 + -D FT_OTA=0 + -D FT_UPLOAD_FIRMWARE=0 diff --git a/interface/.env b/interface/.env old mode 100644 new mode 100755 diff --git a/interface/.env.development b/interface/.env.development old mode 100644 new mode 100755 diff --git a/interface/.env.production b/interface/.env.production old mode 100644 new mode 100755 diff --git a/interface/config-overrides.js b/interface/config-overrides.js old mode 100644 new mode 100755 diff --git a/interface/package-lock.json b/interface/package-lock.json old mode 100644 new mode 100755 index 014040e..9174735 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -6521,6 +6521,16 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "highcharts": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-8.2.2.tgz", + "integrity": "sha512-F63TXO7RxsvTcpO/KOubQZWualYpCMyCTuKtoWbt7KCsfQ3Kl7Fr6HEyyJdjkYl+XlnmnKlSRi9d3HjLK9Q0wg==" + }, + "highcharts-react-official": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/highcharts-react-official/-/highcharts-react-official-3.0.0.tgz", + "integrity": "sha512-VefJgDY2hkT9gfppsQGrRF2g5u8d9dtfHGcx2/xqiP+PkZXCqalw9xOeKVCRvJKTOh0coiDFwvVjOvB7KaGl4A==" + }, "history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", diff --git a/interface/package.json b/interface/package.json old mode 100644 new mode 100755 index b4080e1..51a0575 --- a/interface/package.json +++ b/interface/package.json @@ -14,6 +14,8 @@ "@types/react-router": "^5.1.8", "@types/react-router-dom": "^5.1.6", "compression-webpack-plugin": "^4.0.0", + "highcharts": "^8.2.2", + "highcharts-react-official": "^3.0.0", "jwt-decode": "^3.1.1", "lodash": "^4.17.20", "mime-types": "^2.1.27", diff --git a/interface/progmem-generator.js b/interface/progmem-generator.js old mode 100644 new mode 100755 diff --git a/interface/public/app/icon.png b/interface/public/app/icon.png old mode 100644 new mode 100755 diff --git a/interface/public/app/manifest.json b/interface/public/app/manifest.json old mode 100644 new mode 100755 diff --git a/interface/public/css/roboto.css b/interface/public/css/roboto.css old mode 100644 new mode 100755 diff --git a/interface/public/favicon.ico b/interface/public/favicon.ico old mode 100644 new mode 100755 diff --git a/interface/public/fonts/li.woff2 b/interface/public/fonts/li.woff2 old mode 100644 new mode 100755 diff --git a/interface/public/fonts/me.woff2 b/interface/public/fonts/me.woff2 old mode 100644 new mode 100755 diff --git a/interface/public/fonts/re.woff2 b/interface/public/fonts/re.woff2 old mode 100644 new mode 100755 diff --git a/interface/public/index.html b/interface/public/index.html old mode 100644 new mode 100755 diff --git a/interface/src/App.tsx b/interface/src/App.tsx old mode 100644 new mode 100755 diff --git a/interface/src/AppRouting.tsx b/interface/src/AppRouting.tsx old mode 100644 new mode 100755 diff --git a/interface/src/CustomMuiTheme.tsx b/interface/src/CustomMuiTheme.tsx old mode 100644 new mode 100755 diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx old mode 100644 new mode 100755 diff --git a/interface/src/ap/APModes.ts b/interface/src/ap/APModes.ts old mode 100644 new mode 100755 diff --git a/interface/src/ap/APSettingsController.tsx b/interface/src/ap/APSettingsController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/ap/APSettingsForm.tsx b/interface/src/ap/APSettingsForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/ap/APStatus.ts b/interface/src/ap/APStatus.ts old mode 100644 new mode 100755 diff --git a/interface/src/ap/APStatusController.tsx b/interface/src/ap/APStatusController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/ap/APStatusForm.tsx b/interface/src/ap/APStatusForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/ap/AccessPoint.tsx b/interface/src/ap/AccessPoint.tsx old mode 100644 new mode 100755 diff --git a/interface/src/ap/types.ts b/interface/src/ap/types.ts old mode 100644 new mode 100755 diff --git a/interface/src/api/Endpoints.ts b/interface/src/api/Endpoints.ts old mode 100644 new mode 100755 diff --git a/interface/src/api/Env.ts b/interface/src/api/Env.ts old mode 100644 new mode 100755 diff --git a/interface/src/api/index.ts b/interface/src/api/index.ts old mode 100644 new mode 100755 diff --git a/interface/src/authentication/AuthenticatedRoute.tsx b/interface/src/authentication/AuthenticatedRoute.tsx old mode 100644 new mode 100755 diff --git a/interface/src/authentication/Authentication.ts b/interface/src/authentication/Authentication.ts old mode 100644 new mode 100755 diff --git a/interface/src/authentication/AuthenticationContext.tsx b/interface/src/authentication/AuthenticationContext.tsx old mode 100644 new mode 100755 diff --git a/interface/src/authentication/AuthenticationWrapper.tsx b/interface/src/authentication/AuthenticationWrapper.tsx old mode 100644 new mode 100755 diff --git a/interface/src/authentication/UnauthenticatedRoute.tsx b/interface/src/authentication/UnauthenticatedRoute.tsx old mode 100644 new mode 100755 diff --git a/interface/src/authentication/index.ts b/interface/src/authentication/index.ts old mode 100644 new mode 100755 diff --git a/interface/src/components/ApplicationError.tsx b/interface/src/components/ApplicationError.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/BlockFormControlLabel.tsx b/interface/src/components/BlockFormControlLabel.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/ErrorButton.tsx b/interface/src/components/ErrorButton.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/FormActions.tsx b/interface/src/components/FormActions.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/FormButton.tsx b/interface/src/components/FormButton.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/FullScreenLoading.tsx b/interface/src/components/FullScreenLoading.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/HighlightAvatar.tsx b/interface/src/components/HighlightAvatar.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/MenuAppBar.tsx b/interface/src/components/MenuAppBar.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/PasswordValidator.tsx b/interface/src/components/PasswordValidator.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/RestController.tsx b/interface/src/components/RestController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/RestFormLoader.tsx b/interface/src/components/RestFormLoader.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/SectionContent.tsx b/interface/src/components/SectionContent.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/SingleUpload.tsx b/interface/src/components/SingleUpload.tsx old mode 100644 new mode 100755 diff --git a/interface/src/components/WebSocketController.tsx b/interface/src/components/WebSocketController.tsx old mode 100644 new mode 100755 index 5fe9fa3..e8436ec --- a/interface/src/components/WebSocketController.tsx +++ b/interface/src/components/WebSocketController.tsx @@ -62,6 +62,7 @@ export function webSocketController>(ws } onMessage = (event: MessageEvent) => { + console.log("incoming web socket message : ", event.data); const rawData = event.data; if (typeof rawData === 'string' || rawData instanceof String) { this.handleMessage(JSON.parse(rawData as string) as WebSocketMessage); diff --git a/interface/src/components/WebSocketFormLoader.tsx b/interface/src/components/WebSocketFormLoader.tsx old mode 100644 new mode 100755 index ee5f335..83a7d1f --- a/interface/src/components/WebSocketFormLoader.tsx +++ b/interface/src/components/WebSocketFormLoader.tsx @@ -37,4 +37,4 @@ export default function WebSocketFormLoader(props: WebSocketFormLoaderProps { + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + return ( + + + + + + + + + + + + + + + ) + } +} + +export default AquariumController; \ No newline at end of file diff --git a/interface/src/project/DemoInformation.tsx b/interface/src/project/DemoInformation.tsx deleted file mode 100644 index 2b9967b..0000000 --- a/interface/src/project/DemoInformation.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { Component } from 'react'; -import { Typography, Box, List, ListItem, ListItemText } from '@material-ui/core'; -import { SectionContent } from '../components'; - -class DemoInformation extends Component { - - render() { - return ( - - - This simple demo project allows you to control the built-in LED. - It demonstrates how the esp8266-react framework may be extended for your own IoT project. - - - It is recommended that you keep your project interface code under the project directory. - This serves to isolate your project code from the from the rest of the user interface which should - simplify merges should you wish to update your project with future framework changes. - - - The demo project interface code is stored in the 'interface/src/project' directory: - - - - - - - - - - - - - - - - - - - - - - - - - - - See the project README for a full description of the demo project. - - - - ) - } - -} - -export default DemoInformation; diff --git a/interface/src/project/DemoProject.tsx b/interface/src/project/DemoProject.tsx deleted file mode 100644 index 74f25e5..0000000 --- a/interface/src/project/DemoProject.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' - -import { Tabs, Tab } from '@material-ui/core'; - -import { PROJECT_PATH } from '../api'; -import { MenuAppBar } from '../components'; -import { AuthenticatedRoute } from '../authentication'; - -import DemoInformation from './DemoInformation'; -import LightStateRestController from './LightStateRestController'; -import LightStateWebSocketController from './LightStateWebSocketController'; -import LightMqttSettingsController from './LightMqttSettingsController'; - -class DemoProject extends Component { - - handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { - this.props.history.push(path); - }; - - render() { - return ( - - - - - - - - - - - - - - - - ) - } - -} - -export default DemoProject; diff --git a/interface/src/project/Information.tsx b/interface/src/project/Information.tsx new file mode 100755 index 0000000..83271dc --- /dev/null +++ b/interface/src/project/Information.tsx @@ -0,0 +1,42 @@ +import React, { Component } from 'react'; +import { Typography, Box, List, ListItem, ListItemText } from '@material-ui/core'; +import { SectionContent } from '../components'; + +class DemoInformation extends Component { + + render() { + return ( + + + This interface allows you to control the aquarium lights. + + + It is still in development process and new features are being added on regular basis. + + + + + + + + + + + + See my projects at github . + + + + ) + } + +} + +export default DemoInformation; diff --git a/interface/src/project/LightMqttSettingsController.tsx b/interface/src/project/LightMqttSettingsController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/project/LightStateRestController.tsx b/interface/src/project/LightStateRestController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/project/LightStateWebSocketController.tsx b/interface/src/project/LightStateWebSocketController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/project/ProjectMenu.tsx b/interface/src/project/ProjectMenu.tsx old mode 100644 new mode 100755 index b7d2739..ba89168 --- a/interface/src/project/ProjectMenu.tsx +++ b/interface/src/project/ProjectMenu.tsx @@ -12,11 +12,11 @@ class ProjectMenu extends Component { const path = this.props.match.url; return ( - + - + ) diff --git a/interface/src/project/ProjectRouting.tsx b/interface/src/project/ProjectRouting.tsx old mode 100644 new mode 100755 index fc378e6..598a3bc --- a/interface/src/project/ProjectRouting.tsx +++ b/interface/src/project/ProjectRouting.tsx @@ -4,7 +4,7 @@ import { Redirect, Switch } from 'react-router'; import { PROJECT_PATH } from '../api'; import { AuthenticatedRoute } from '../authentication'; -import DemoProject from './DemoProject'; +import AquariumController from './AquariumController'; class ProjectRouting extends Component { @@ -16,14 +16,14 @@ class ProjectRouting extends Component { * Add your project page routing below. */ } - + { /* * The redirect below caters for the default project route and redirecting invalid paths. * The "to" property must match one of the routes above for this to work correctly. */ } - + ) } diff --git a/interface/src/project/RgbCycleController.js b/interface/src/project/RgbCycleController.js new file mode 100755 index 0000000..3466920 --- /dev/null +++ b/interface/src/project/RgbCycleController.js @@ -0,0 +1,40 @@ +import React, { Component, useEffect } from 'react'; +import { Switch } from '@material-ui/core'; +import { WEB_SOCKET_ROOT } from '../api'; +import { WebSocketFormLoader, webSocketController } from '../components'; +import { SectionContent } from '../components'; +import RgbSettings from './components/RgbSettings'; + +export const RGB_SETTINGS_WEBSOCKET_URL = WEB_SOCKET_ROOT + "rgbCycle"; +export const GREEN_COLOR_WEBSOCKET_URL = WEB_SOCKET_ROOT + "greenLight"; +// const test = webSocketController(GREEN_COLOR_WEBSOCKET_URL, 100, RgbCycleController); + +const RgbCycleController = (props) => { + const Test = webSocketController(GREEN_COLOR_WEBSOCKET_URL, 100, RgbCycleController); + + return ( + + ( + + )} + /> + + ) +} + +export default webSocketController(RGB_SETTINGS_WEBSOCKET_URL, 100, RgbCycleController); + +const RgbCycleControllerForm = (props) => { + + const { data, saveData, setData } = props; + + return ( + + ); +} \ No newline at end of file diff --git a/interface/src/project/RgbStateWebSocketController.tsx b/interface/src/project/RgbStateWebSocketController.tsx new file mode 100755 index 0000000..846899e --- /dev/null +++ b/interface/src/project/RgbStateWebSocketController.tsx @@ -0,0 +1,75 @@ +import React, { Component } from 'react'; +import { ValidatorForm } from 'react-material-ui-form-validator'; + +import { Typography, Box, Switch } from '@material-ui/core'; +import { WEB_SOCKET_ROOT } from '../api'; +import { WebSocketControllerProps, WebSocketFormLoader, WebSocketFormProps, webSocketController } from '../components'; +import { SectionContent, BlockFormControlLabel } from '../components'; +import RgbSlider from './components/RgbSlider'; + +import { RgbState } from './types'; + +export const RGB_SETTINGS_WEBSOCKET_URL = WEB_SOCKET_ROOT + "rgbState"; + +type RgbStateWebSocketControllerProps = WebSocketControllerProps; + +class RgbStateWebSocketController extends Component { + + render() { + return ( + + ( + + )} + /> + + ) + } +} + +export default webSocketController(RGB_SETTINGS_WEBSOCKET_URL, 100, RgbStateWebSocketController); + +type RgbStateWebSocketControllerFormProps = WebSocketFormProps; + +function RgbStateWebSocketControllerForm(props: RgbStateWebSocketControllerFormProps) { + + const { data, saveData, setData } = props; + + const changeLedOn = (event: React.ChangeEvent) => { + setData( + { + led_on: event.target.checked, + red_value: data.red_value, + green_value: data.green_value, + blue_value: data.blue_value + }, + saveData); + } + + return ( + + + + Toggle RGB state. + + + + } + label="LED State?" + /> + + + ); +} \ No newline at end of file diff --git a/interface/src/project/components/RgbSettings.js b/interface/src/project/components/RgbSettings.js new file mode 100755 index 0000000..8e12e3a --- /dev/null +++ b/interface/src/project/components/RgbSettings.js @@ -0,0 +1,252 @@ +import React, { useState, useEffect } from "react"; +import Highcharts from "highcharts/highstock"; +import HighchartsReact from "highcharts-react-official"; +import HC_more from "highcharts/highcharts-more"; +import { FormActions, FormButton } from '../../components'; +import { Radio, RadioGroup, FormControl, FormLabel, FormControlLabel, } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; + +HC_more(Highcharts); +require("highcharts/modules/draggable-points")(Highcharts); + +export default ({ setDataHandler, saveDataHandler, data: colorData }) => { + + const [selectedColor, setSelectedColor] = useState('red'); + + const [options, setOptions] = useState({ + + chart: { + type: 'spline', + zoomType: 'x', + // panning: true, + events: { + load: function () { + } + } + }, + navigator: { + series: { + color: '#FF00FF', + lineWidth: 1 + } + }, + credits: { + text: '© Janis Davidsons', + href: '' + }, + yAxis: { + dragMaxY: 255, + + min: 0, + max: 255 + }, + + xAxis: { + min: Date.UTC(2020, 0, 1, 0, 0, 0), + max: Date.UTC(2020, 0, 2, 0, 0, 0), + type: 'datetime', + title: { + text: 'Hours' + }, + + + + dateTimeLabelFormats: { hour: '%H:%M' }, + lineWidth: 1, + dateTimeLabelFormats: { + day: '%H:%M' + }, + title: { + enabled: false + } + }, + + time: { + useUTC: false + }, + + plotOptions: { + series: { + showInNavigator: true + } + }, + + rangeSelector: { + buttons: [{ + count: 1, + type: 'hour', + text: '1H' + }, { + count: 5, + type: 'hour', + text: '5H' + }, { + type: 'all', + text: 'All' + }], + inputEnabled: false, + selected: 2 + }, + + title: { + text: "RGB day/night cycle" + }, + + exporting: { + enabled: false + }, + + tooltip: { + split: true, + xDateFormat: '%H:%M', + valueDecimals: 0, + followTouchMove: false, + pointFormat: '{series.name}: {point.y}
', + }, + + series: [ + { + name: "Red", + color: "red", + type: 'spline', + dragDrop: { + draggableY: true, + dragMaxY: 255, + dragMinY: 0, + }, + visible: true, + + data: (function () { + let data = [] + let index = 0; + for (let i = Date.UTC(2020, 0, 1, -2, 0, 0); i <= Date.UTC(2020, 0, 2, -2, 0, 0); i += 3600 * 500) { + data.push({ x: i, y: colorData.graph.red[index] }); + index++; + } + + return data; + }()) + }, + { + name: "Green", + color: "green", + type: 'spline', + dragDrop: { + draggableY: true, + dragMaxY: 255, + dragMinY: 0, + }, + + data: (function () { + var data = [] + + for (let i = Date.UTC(2020, 0, 1, -2, 0, 0); i <= Date.UTC(2020, 0, 2, -2, 0, 0); i += 3600 * 500) { + data.push({ x: i, y: 150 }); + } + + return data; + }()) + }, + { + name: "Blue", + color: "blue", + type: 'spline', + dragDrop: { + draggableY: true, + dragMaxY: 255, + dragMinY: 0, + }, + + data: (function () { + var data = [] + for (let i = Date.UTC(2020, 0, 1, -2, 0, 0); i <= Date.UTC(2020, 0, 2, -2, 0, 0); i += 3600 * 500) { + data.push({ x: i, y: 100 }); + // Math.round(Math.random() * 255) + } + + return data; + }()) + } + ] + }) + + useEffect(() => { + const redData = colorData.graph.red; + for (let index = 0; index < redData.length; index++) { + options.series[0].data[index][1] = redData[index] + } + }, []) + + const handleSubmit = () => { + let result = { red: [], green: [], blue: [] }; + + let conv = arr => arr.map(v => Array.isArray(v) ? conv(v) : String(v) || 0); + + options.series.map(color => { + color.data.map(value => { + result[color.color].push(Math.round(value.y).toString()) + }) + }); + + setDataHandler({ + graph: { red: result.red } + }, saveDataHandler); + } + + const handleRadioChange = (event) => { + let newValue = event.target.value; + setSelectedColor(newValue); + setOptions({ ...options }, options.series[0].dragDrop.draggableY = false); + setOptions({ ...options }, options.series[1].dragDrop.draggableY = false); + setOptions({ ...options }, options.series[2].dragDrop.draggableY = false); + + switch (newValue) { + case "red": + const redDraggable = options.series[0].dragDrop.draggableY; + setOptions({ ...options }, options.series[0].dragDrop.draggableY = redDraggable ? false : true) + console.log('red: ', { ...options.series }) + + break; + case "green": + const greenDraggable = options.series[1].dragDrop.draggableY; + setOptions({ ...options }, options.series[1].dragDrop.draggableY = greenDraggable ? false : true) + console.log('green: ', { ...options.series }) + + break; + case "blue": + const blueDraggable = options.series[2].dragDrop.draggableY; + setOptions({ ...options }, options.series[2].dragDrop.draggableY = blueDraggable ? false : true) + console.log('blue: ', { ...options.series }) + + break; + default: + break; + } + } + + return ( + <> + + + + Select to enagble draggable line chart + + } label="Red" /> + } label="Green" /> + } label="Blue" /> + + + + + } variant="contained" color="primary" type="submit" onClick={handleSubmit}> + Save + + + + ); +} \ No newline at end of file diff --git a/interface/src/project/components/RgbSlider.js b/interface/src/project/components/RgbSlider.js new file mode 100755 index 0000000..c39ff47 --- /dev/null +++ b/interface/src/project/components/RgbSlider.js @@ -0,0 +1,215 @@ +import React, { useState } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import Slider from '@material-ui/core/Slider'; +import Input from '@material-ui/core/Input'; +import { Typography } from '@material-ui/core'; + +const useStyles = makeStyles({ + root: { + + width: "100%", + }, + input: { + width: 52, + }, + thumb: { + height: 24, + width: 24, + backgroundColor: "#fff", + border: "2px solid currentColor", + marginTop: -8, + marginLeft: -12, + "&:focus, &:hover, &$active": { + boxShadow: "inherit" + } + }, + active: {}, + valueLabel: { + left: "calc(-50% + 4px)" + }, + track: { + height: 8, + borderRadius: 4 + }, + rail: { + height: 8, + borderRadius: 4 + } +}); + +export default ({ setDataHandler, saveDataHandler, data }) => { + + const classes = useStyles(); + const [red, setRed] = useState(data.red_value); + const [green, setGreen] = useState(data.green_value); + const [blue, setBlue] = useState(data.blue_value); + + const handleRedSlider = (event, newValue) => { + console.log("red: ",newValue) + setRed(newValue) + sendToApi(); + }; + + const handleGreenSlider = (event, newValue) => { + console.log("green: ",newValue) + setGreen(newValue) + sendToApi(); + }; + + const handleBlueSlider = (event, newValue) => { + console.log("blue: ", newValue) + setBlue(newValue) + sendToApi(); + }; + + const handleRedInputChange = event => { + setRed(event.target.value === '' ? '' : Number(event.target.value)); + sendToApi(); + } + + const handleGreenInputChange = event => { + setGreen(event.target.value === '' ? '' : Number(event.target.value)); + sendToApi(); + } + + const handleBlueInputChange = event => { + setBlue(event.target.value === '' ? '' : Number(event.target.value)); + sendToApi(); + } + + const sendToApi = () => { + setDataHandler( + { led_on: true, red_value: red, green_value: green, blue_value: blue }, + saveDataHandler); + } + + const handleBlur = (color) => { + switch (color) { + case "red": + if (red < 0) { + setRed(0); + } else if (red > 255) setRed(255); + break; + case "green": + if (green < 0) { + setGreen(0); + } else if (green > 255) setGreen(255); + break; + case "blue": + if (blue < 0) { + setBlue(0); + } else if (blue > 255) setBlue(255); + break; + default: + break; + } + }; + + return ( +
+ + {/* Red slider */} + + Red value + + + + + + + + + + + {/* Green slider */} + + Green value + + + + + + + + + + + {/* blue sider */} + + Blue value + + + + + + + + + +
+ ) +} \ No newline at end of file diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts old mode 100644 new mode 100755 index 3221255..a47a94e --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -2,8 +2,28 @@ export interface LightState { led_on: boolean; } -export interface LightMqttSettings { - unique_id : string; +export interface LightMqttSettings { + unique_id: string; name: string; - mqtt_path : string; + mqtt_path: string; } + +export interface RgbState { + led_on: boolean; + red_value: number; + green_value: number; + blue_value: number; +} + +interface Series { + name:string, + color:string, + type:string, + dragDrop: object, + visible:boolean, + data: [][] +} + +export interface RgbOptions { + series: Series[] +} \ No newline at end of file diff --git a/interface/src/react-app-env.d.ts b/interface/src/react-app-env.d.ts old mode 100644 new mode 100755 diff --git a/interface/src/security/ManageUsersController.tsx b/interface/src/security/ManageUsersController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/security/ManageUsersForm.tsx b/interface/src/security/ManageUsersForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/security/Security.tsx b/interface/src/security/Security.tsx old mode 100644 new mode 100755 diff --git a/interface/src/security/SecuritySettingsController.tsx b/interface/src/security/SecuritySettingsController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/security/SecuritySettingsForm.tsx b/interface/src/security/SecuritySettingsForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/security/UserForm.tsx b/interface/src/security/UserForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/security/types.ts b/interface/src/security/types.ts old mode 100644 new mode 100755 diff --git a/interface/src/serviceWorker.ts b/interface/src/serviceWorker.ts old mode 100644 new mode 100755 diff --git a/interface/src/system/OTASettingsController.tsx b/interface/src/system/OTASettingsController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/system/OTASettingsForm.tsx b/interface/src/system/OTASettingsForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/system/System.tsx b/interface/src/system/System.tsx old mode 100644 new mode 100755 diff --git a/interface/src/system/SystemStatusController.tsx b/interface/src/system/SystemStatusController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/system/SystemStatusForm.tsx b/interface/src/system/SystemStatusForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/system/UploadFirmwareController.tsx b/interface/src/system/UploadFirmwareController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/system/UploadFirmwareForm.tsx b/interface/src/system/UploadFirmwareForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/system/types.ts b/interface/src/system/types.ts old mode 100644 new mode 100755 diff --git a/interface/src/validators/index.ts b/interface/src/validators/index.ts old mode 100644 new mode 100755 diff --git a/interface/src/validators/isHostname.ts b/interface/src/validators/isHostname.ts old mode 100644 new mode 100755 diff --git a/interface/src/validators/isIP.ts b/interface/src/validators/isIP.ts old mode 100644 new mode 100755 diff --git a/interface/src/validators/optional.ts b/interface/src/validators/optional.ts old mode 100644 new mode 100755 diff --git a/interface/src/validators/or.ts b/interface/src/validators/or.ts old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiConnection.tsx b/interface/src/wifi/WiFiConnection.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiConnectionContext.tsx b/interface/src/wifi/WiFiConnectionContext.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiNetworkScanner.tsx b/interface/src/wifi/WiFiNetworkScanner.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiNetworkSelector.tsx b/interface/src/wifi/WiFiNetworkSelector.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiSecurityModes.ts b/interface/src/wifi/WiFiSecurityModes.ts old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiSettingsController.tsx b/interface/src/wifi/WiFiSettingsController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiSettingsForm.tsx b/interface/src/wifi/WiFiSettingsForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiStatus.ts b/interface/src/wifi/WiFiStatus.ts old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiStatusController.tsx b/interface/src/wifi/WiFiStatusController.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/WiFiStatusForm.tsx b/interface/src/wifi/WiFiStatusForm.tsx old mode 100644 new mode 100755 diff --git a/interface/src/wifi/types.ts b/interface/src/wifi/types.ts old mode 100644 new mode 100755 diff --git a/interface/tsconfig.json b/interface/tsconfig.json old mode 100644 new mode 100755 diff --git a/lib/framework/APSettingsService.cpp b/lib/framework/APSettingsService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/APSettingsService.h b/lib/framework/APSettingsService.h old mode 100644 new mode 100755 diff --git a/lib/framework/APStatus.cpp b/lib/framework/APStatus.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/APStatus.h b/lib/framework/APStatus.h old mode 100644 new mode 100755 diff --git a/lib/framework/ArduinoJsonJWT.cpp b/lib/framework/ArduinoJsonJWT.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/ArduinoJsonJWT.h b/lib/framework/ArduinoJsonJWT.h old mode 100644 new mode 100755 diff --git a/lib/framework/AuthenticationService.cpp b/lib/framework/AuthenticationService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/AuthenticationService.h b/lib/framework/AuthenticationService.h old mode 100644 new mode 100755 diff --git a/lib/framework/ESP8266React.cpp b/lib/framework/ESP8266React.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/ESP8266React.h b/lib/framework/ESP8266React.h old mode 100644 new mode 100755 diff --git a/lib/framework/ESPFS.h b/lib/framework/ESPFS.h old mode 100644 new mode 100755 diff --git a/lib/framework/ESPUtils.h b/lib/framework/ESPUtils.h old mode 100644 new mode 100755 diff --git a/lib/framework/FSPersistence.h b/lib/framework/FSPersistence.h old mode 100644 new mode 100755 index 9fc547b..7b5a615 --- a/lib/framework/FSPersistence.h +++ b/lib/framework/FSPersistence.h @@ -25,12 +25,16 @@ class FSPersistence { void readFromFS() { File settingsFile = _fs->open(_filePath, "r"); - + Serial.println(_filePath); if (settingsFile) { DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); DeserializationError error = deserializeJson(jsonDocument, settingsFile); if (error == DeserializationError::Ok && jsonDocument.is()) { JsonObject jsonObject = jsonDocument.as(); + + Serial.println("FS read called"); + // serializeJsonPretty(jsonDocument, Serial); + _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); settingsFile.close(); return; @@ -57,7 +61,11 @@ class FSPersistence { return false; } + Serial.println("FS write called"); + // serializeJsonPretty(jsonDocument, Serial); + // serialize the data to the file + serializeJson(jsonDocument, settingsFile); settingsFile.close(); return true; diff --git a/lib/framework/FactoryResetService.cpp b/lib/framework/FactoryResetService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/FactoryResetService.h b/lib/framework/FactoryResetService.h old mode 100644 new mode 100755 diff --git a/lib/framework/Features.h b/lib/framework/Features.h old mode 100644 new mode 100755 diff --git a/lib/framework/FeaturesService.cpp b/lib/framework/FeaturesService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/FeaturesService.h b/lib/framework/FeaturesService.h old mode 100644 new mode 100755 diff --git a/lib/framework/HttpEndpoint.h b/lib/framework/HttpEndpoint.h old mode 100644 new mode 100755 diff --git a/lib/framework/JsonUtils.h b/lib/framework/JsonUtils.h old mode 100644 new mode 100755 diff --git a/lib/framework/MqttPubSub.h b/lib/framework/MqttPubSub.h old mode 100644 new mode 100755 diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h old mode 100644 new mode 100755 diff --git a/lib/framework/MqttStatus.cpp b/lib/framework/MqttStatus.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/MqttStatus.h b/lib/framework/MqttStatus.h old mode 100644 new mode 100755 diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/NTPSettingsService.h b/lib/framework/NTPSettingsService.h old mode 100644 new mode 100755 diff --git a/lib/framework/NTPStatus.cpp b/lib/framework/NTPStatus.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/NTPStatus.h b/lib/framework/NTPStatus.h old mode 100644 new mode 100755 diff --git a/lib/framework/OTASettingsService.cpp b/lib/framework/OTASettingsService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/OTASettingsService.h b/lib/framework/OTASettingsService.h old mode 100644 new mode 100755 diff --git a/lib/framework/RestartService.cpp b/lib/framework/RestartService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/RestartService.h b/lib/framework/RestartService.h old mode 100644 new mode 100755 diff --git a/lib/framework/SecurityManager.h b/lib/framework/SecurityManager.h old mode 100644 new mode 100755 diff --git a/lib/framework/SecuritySettingsService.cpp b/lib/framework/SecuritySettingsService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/SecuritySettingsService.h b/lib/framework/SecuritySettingsService.h old mode 100644 new mode 100755 diff --git a/lib/framework/StatefulService.cpp b/lib/framework/StatefulService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/StatefulService.h b/lib/framework/StatefulService.h old mode 100644 new mode 100755 diff --git a/lib/framework/SystemStatus.cpp b/lib/framework/SystemStatus.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/SystemStatus.h b/lib/framework/SystemStatus.h old mode 100644 new mode 100755 diff --git a/lib/framework/UploadFirmwareService.cpp b/lib/framework/UploadFirmwareService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/UploadFirmwareService.h b/lib/framework/UploadFirmwareService.h old mode 100644 new mode 100755 diff --git a/lib/framework/WebSocketTxRx.h b/lib/framework/WebSocketTxRx.h old mode 100644 new mode 100755 diff --git a/lib/framework/WiFiScanner.cpp b/lib/framework/WiFiScanner.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/WiFiScanner.h b/lib/framework/WiFiScanner.h old mode 100644 new mode 100755 diff --git a/lib/framework/WiFiSettingsService.cpp b/lib/framework/WiFiSettingsService.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/WiFiSettingsService.h b/lib/framework/WiFiSettingsService.h old mode 100644 new mode 100755 diff --git a/lib/framework/WiFiStatus.cpp b/lib/framework/WiFiStatus.cpp old mode 100644 new mode 100755 diff --git a/lib/framework/WiFiStatus.h b/lib/framework/WiFiStatus.h old mode 100644 new mode 100755 diff --git a/lib/readme.txt b/lib/readme.txt old mode 100644 new mode 100755 diff --git a/media/build.png b/media/build.png old mode 100644 new mode 100755 diff --git a/media/dark.png b/media/dark.png old mode 100644 new mode 100755 diff --git a/media/devserver.png b/media/devserver.png old mode 100644 new mode 100755 diff --git a/media/esp12e.jpg b/media/esp12e.jpg old mode 100644 new mode 100755 diff --git a/media/esp32.jpg b/media/esp32.jpg old mode 100644 new mode 100755 diff --git a/media/framework.png b/media/framework.png old mode 100644 new mode 100755 diff --git a/media/screenshots.png b/media/screenshots.png old mode 100644 new mode 100755 diff --git a/media/uploadfs.png b/media/uploadfs.png old mode 100644 new mode 100755 diff --git a/media/uploadfw.png b/media/uploadfw.png old mode 100644 new mode 100755 diff --git a/platformio.ini b/platformio.ini old mode 100644 new mode 100755 index d11e760..aaabc34 --- a/platformio.ini +++ b/platformio.ini @@ -1,49 +1,98 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + [platformio] extra_configs = - factory_settings.ini - features.ini + factory_settings.ini + features.ini default_envs = esp12e -;default_envs = node32s [env] -build_flags= - ${factory_settings.build_flags} - ${features.build_flags} - -D NO_GLOBAL_ARDUINOOTA - ; Uncomment ENABLE_CORS to enable Cross-Origin Resource Sharing (required for local React development) - -D ENABLE_CORS - -D CORS_ORIGIN=\"http://localhost:3000\" - ; Uncomment PROGMEM_WWW to enable the storage of the WWW data in PROGMEM - -D PROGMEM_WWW - -; ensure transitive dependencies are included for correct platforms only +build_flags = + ${factory_settings.build_flags} + ${features.build_flags} + -D NO_GLOBAL_ARDUINOOTA + -D ENABLE_CORS + -D CORS_ORIGIN=\"http://localhost:3000\" + -D PROGMEM_WWW lib_compat_mode = strict - -; Uncomment & modify the lines below in order to configure OTA updates -;upload_flags = -; --port=8266 -; --auth=esp-react -;upload_port = 192.168.0.11 - framework = arduino monitor_speed = 115200 - extra_scripts = - pre:scripts/build_interface.py + pre:scripts/build_interface.py +lib_deps = + ArduinoJson@>=6.0.0,<7.0.0 + ESP Async WebServer@>=1.2.0,<2.0.0 + AsyncMqttClient@>=0.8.2,<1.0.0 + fastled/FastLED @ ^3.3.3 + paulstoffregen/Time @ ^1.6 + paulstoffregen/TimeAlarms @ 0.0.0-alpha+sha.c291c1ddad -lib_deps = - ArduinoJson@>=6.0.0,<7.0.0 - ESP Async WebServer@>=1.2.0,<2.0.0 - AsyncMqttClient@>=0.8.2,<1.0.0 - [env:esp12e] platform = espressif8266 board = esp12e board_build.f_cpu = 160000000L board_build.filesystem = littlefs +lib_deps = + paulstoffregen/TimeAlarms@0.0.0-alpha+sha.c291c1ddad + paulstoffregen/Time@^1.6.0 [env:node32s] -; Comment out min_spiffs.csv setting if disabling PROGMEM_WWW with ESP32 board_build.partitions = min_spiffs.csv platform = espressif32 board = node32s +lib_deps = + paulstoffregen/TimeAlarms@0.0.0-alpha+sha.c291c1ddad + paulstoffregen/Time@^1.6.0 + +[factory_settings] +build_flags = + -D FACTORY_WIFI_SSID=\"Janis_network\" + -D FACTORY_WIFI_PASSWORD=\"8378969948\" + -D FACTORY_WIFI_HOSTNAME=\"esp-react\" + + -D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED + -D FACTORY_AP_SSID=\"ESP8266-React\" + -D FACTORY_AP_PASSWORD=\"esp-react\" + -D FACTORY_AP_LOCAL_IP=\"192.168.4.1\" + -D FACTORY_AP_GATEWAY_IP=\"192.168.4.1\" + -D FACTORY_AP_SUBNET_MASK=\"255.255.255.0\" + + -D FACTORY_ADMIN_USERNAME=\"admin\" + -D FACTORY_ADMIN_PASSWORD=\"admin\" + -D FACTORY_GUEST_USERNAME=\"guest\" + -D FACTORY_GUEST_PASSWORD=\"guest\" + + -D FACTORY_NTP_ENABLED=true + -D FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/Riga\" + -D FACTORY_NTP_TIME_ZONE_FORMAT=\"GMT0BST,M3.5.0/1,M10.5.0\" + -D FACTORY_NTP_SERVER=\"time.google.com\" + + -D FACTORY_OTA_PORT=8266 + -D FACTORY_OTA_PASSWORD=\"esp-react\" + -D FACTORY_OTA_ENABLED=true + + -D FACTORY_MQTT_ENABLED=false + -D FACTORY_MQTT_HOST=\"test.mosquitto.org\" + -D FACTORY_MQTT_PORT=1883 + -D FACTORY_MQTT_USERNAME=\"\" + -D FACTORY_MQTT_PASSWORD=\"\" + -D FACTORY_MQTT_KEEP_ALIVE=60 + -D FACTORY_MQTT_CLEAN_SESSION=true + -D FACTORY_MQTT_MAX_TOPIC_LENGTH=128 + +[features] +build_flags = + -D FT_PROJECT=1 + -D FT_SECURITY=1 + -D FT_MQTT=0 + -D FT_NTP=1 + -D FT_OTA=0 + -D FT_UPLOAD_FIRMWARE=0 diff --git a/scripts/build_interface.py b/scripts/build_interface.py old mode 100644 new mode 100755 diff --git a/src/LightMqttSettingsService.cpp b/src/LightMqttSettingsService.cpp deleted file mode 100644 index bddd909..0000000 --- a/src/LightMqttSettingsService.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -LightMqttSettingsService::LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : - _httpEndpoint(LightMqttSettings::read, - LightMqttSettings::update, - this, - server, - LIGHT_BROKER_SETTINGS_PATH, - securityManager, - AuthenticationPredicates::IS_AUTHENTICATED), - _fsPersistence(LightMqttSettings::read, LightMqttSettings::update, this, fs, LIGHT_BROKER_SETTINGS_FILE) { -} - -void LightMqttSettingsService::begin() { - _fsPersistence.readFromFS(); -} diff --git a/src/LightMqttSettingsService.h b/src/LightMqttSettingsService.h deleted file mode 100644 index 23a2218..0000000 --- a/src/LightMqttSettingsService.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef LightMqttSettingsService_h -#define LightMqttSettingsService_h - -#include -#include -#include - -#define LIGHT_BROKER_SETTINGS_FILE "/config/brokerSettings.json" -#define LIGHT_BROKER_SETTINGS_PATH "/rest/brokerSettings" - -class LightMqttSettings { - public: - String mqttPath; - String name; - String uniqueId; - - static void read(LightMqttSettings& settings, JsonObject& root) { - root["mqtt_path"] = settings.mqttPath; - root["name"] = settings.name; - root["unique_id"] = settings.uniqueId; - } - - static StateUpdateResult update(JsonObject& root, LightMqttSettings& settings) { - settings.mqttPath = root["mqtt_path"] | ESPUtils::defaultDeviceValue("homeassistant/light/"); - settings.name = root["name"] | ESPUtils::defaultDeviceValue("light-"); - settings.uniqueId = root["unique_id"] | ESPUtils::defaultDeviceValue("light-"); - return StateUpdateResult::CHANGED; - } -}; - -class LightMqttSettingsService : public StatefulService { - public: - LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); - void begin(); - - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; -}; - -#endif // end LightMqttSettingsService_h diff --git a/src/LightStateService.cpp b/src/LightStateService.cpp deleted file mode 100644 index 8169622..0000000 --- a/src/LightStateService.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include - -LightStateService::LightStateService(AsyncWebServer* server, - SecurityManager* securityManager, - AsyncMqttClient* mqttClient, - LightMqttSettingsService* lightMqttSettingsService) : - _httpEndpoint(LightState::read, - LightState::update, - this, - server, - LIGHT_SETTINGS_ENDPOINT_PATH, - securityManager, - AuthenticationPredicates::IS_AUTHENTICATED), - _mqttPubSub(LightState::haRead, LightState::haUpdate, this, mqttClient), - _webSocket(LightState::read, - LightState::update, - this, - server, - LIGHT_SETTINGS_SOCKET_PATH, - securityManager, - AuthenticationPredicates::IS_AUTHENTICATED), - _mqttClient(mqttClient), - _lightMqttSettingsService(lightMqttSettingsService) { - // configure led to be output - pinMode(LED_PIN, OUTPUT); - - // configure MQTT callback - _mqttClient->onConnect(std::bind(&LightStateService::registerConfig, this)); - - // configure update handler for when the light settings change - _lightMqttSettingsService->addUpdateHandler([&](const String& originId) { registerConfig(); }, false); - - // configure settings service update handler to update LED state - addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); -} - -void LightStateService::begin() { - _state.ledOn = DEFAULT_LED_STATE; - onConfigUpdated(); -} - -void LightStateService::onConfigUpdated() { - digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF); -} - -void LightStateService::registerConfig() { - if (!_mqttClient->connected()) { - return; - } - String configTopic; - String subTopic; - String pubTopic; - - DynamicJsonDocument doc(256); - _lightMqttSettingsService->read([&](LightMqttSettings& settings) { - configTopic = settings.mqttPath + "/config"; - subTopic = settings.mqttPath + "/set"; - pubTopic = settings.mqttPath + "/state"; - doc["~"] = settings.mqttPath; - doc["name"] = settings.name; - doc["unique_id"] = settings.uniqueId; - }); - doc["cmd_t"] = "~/set"; - doc["stat_t"] = "~/state"; - doc["schema"] = "json"; - doc["brightness"] = false; - - String payload; - serializeJson(doc, payload); - _mqttClient->publish(configTopic.c_str(), 0, false, payload.c_str()); - - _mqttPubSub.configureTopics(pubTopic, subTopic); -} diff --git a/src/LightStateService.h b/src/LightStateService.h deleted file mode 100644 index e9c9a9c..0000000 --- a/src/LightStateService.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef LightStateService_h -#define LightStateService_h - -#include - -#include -#include -#include - -#define LED_PIN 2 -#define PRINT_DELAY 5000 - -#define DEFAULT_LED_STATE false -#define OFF_STATE "OFF" -#define ON_STATE "ON" - -// Note that the built-in LED is on when the pin is low on most NodeMCU boards. -// This is because the anode is tied to VCC and the cathode to the GPIO 4 (Arduino pin 2). -#ifdef ESP32 -#define LED_ON 0x1 -#define LED_OFF 0x0 -#elif defined(ESP8266) -#define LED_ON 0x0 -#define LED_OFF 0x1 -#endif - -#define LIGHT_SETTINGS_ENDPOINT_PATH "/rest/lightState" -#define LIGHT_SETTINGS_SOCKET_PATH "/ws/lightState" - -class LightState { - public: - bool ledOn; - - static void read(LightState& settings, JsonObject& root) { - root["led_on"] = settings.ledOn; - } - - static StateUpdateResult update(JsonObject& root, LightState& lightState) { - boolean newState = root["led_on"] | DEFAULT_LED_STATE; - if (lightState.ledOn != newState) { - lightState.ledOn = newState; - return StateUpdateResult::CHANGED; - } - return StateUpdateResult::UNCHANGED; - } - - static void haRead(LightState& settings, JsonObject& root) { - root["state"] = settings.ledOn ? ON_STATE : OFF_STATE; - } - - static StateUpdateResult haUpdate(JsonObject& root, LightState& lightState) { - String state = root["state"]; - // parse new led state - boolean newState = false; - if (state.equals(ON_STATE)) { - newState = true; - } else if (!state.equals(OFF_STATE)) { - return StateUpdateResult::ERROR; - } - // change the new state, if required - if (lightState.ledOn != newState) { - lightState.ledOn = newState; - return StateUpdateResult::CHANGED; - } - return StateUpdateResult::UNCHANGED; - } -}; - -class LightStateService : public StatefulService { - public: - LightStateService(AsyncWebServer* server, - SecurityManager* securityManager, - AsyncMqttClient* mqttClient, - LightMqttSettingsService* lightMqttSettingsService); - void begin(); - - private: - HttpEndpoint _httpEndpoint; - MqttPubSub _mqttPubSub; - WebSocketTxRx _webSocket; - AsyncMqttClient* _mqttClient; - LightMqttSettingsService* _lightMqttSettingsService; - - void registerConfig(); - void onConfigUpdated(); -}; - -#endif diff --git a/src/RgbCycleService.cpp b/src/RgbCycleService.cpp new file mode 100755 index 0000000..facb435 --- /dev/null +++ b/src/RgbCycleService.cpp @@ -0,0 +1,26 @@ +#include + +RgbCycleService::RgbCycleService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs) : + _httpEndpoint(RgbCycleState::read, + RgbCycleState::update, + this, + server, + RGB_CYCLE_ENDPOINT_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _webSocket(RgbCycleState::read, + RgbCycleState::update, + this, + server, + RGB_CYCLE_SOCKET_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _fsPersistence(RgbCycleState::read, RgbCycleState::update, this, fs, "/config/RgbCycle.json", 4096) { + // add event listener for rgbCycle Object + // addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); +} + +void RgbCycleService::begin() { + Serial.println("Cycle begin ..."); + _fsPersistence.readFromFS(); +} \ No newline at end of file diff --git a/src/RgbCycleService.h b/src/RgbCycleService.h new file mode 100755 index 0000000..057bf0a --- /dev/null +++ b/src/RgbCycleService.h @@ -0,0 +1,120 @@ +#ifndef RgbCycleService_h +#define RgbCycleService_h + +#include +#include +#include +#include + + +#define RGB_CYCLE_ENDPOINT_PATH "/rest/rgbCycle" +#define RGB_CYCLE_SOCKET_PATH "/ws/rgbCycle" + +class RgbCycleState { + public: + RgbDriver* driver; + String time; + String color; + int red[49] = {0}; + int green[49] = {0}; + int blue[49] = {0}; + + // Class constructor + RgbCycleState() { + driver = driver->getInstance(); + } + + static void read(RgbCycleState& settings, JsonObject& root) { + root.createNestedObject("graph"); + root["graph"].createNestedArray("red"); + root["graph"].createNestedArray("green"); + root["graph"].createNestedArray("blue"); + + for (size_t i = 0; i < 49; i++) { + root["graph"]["red"].add(settings.red[i]); + + // root["graph"]["green"].add(settings.green[i]); + // root["graph"]["blue"].add(settings.blue[i]); + + // Serial.println(settings.red[i]); + } + + Serial.println("api read called..."); + + // for (const int& value : settings.red) { + // root["graph"]["red"].add(value); + // Serial.println(value); + // i++; + // } + } + + static StateUpdateResult update(JsonObject& root, RgbCycleState& rgbCycleState) { + JsonArray red = root["graph"]["red"].as(); + JsonArray green = root["graph"]["green"].as(); + JsonArray blue = root["graph"]["blue"].as(); + + if (red) { + // Serial.println("red"); + int i = 0; + for (JsonVariant value : red) { + rgbCycleState.red[i] = value.as(); + // Serial.println(value.as()); + // Serial.println(rgbCycleState.red[i]); + i++; + } + } + if (green) { + // Serial.println("green"); + int i = 0; + for (JsonVariant value : green) { + rgbCycleState.green[i] = value.as(); + i++; + } + } + if (blue) { + // Serial.println("blue"); + int i = 0; + for (JsonVariant value : blue) { + rgbCycleState.blue[i] = value.as(); + i++; + } + } + + Serial.println("Cycle update..."); + Serial.println("red"); + for (size_t i = 0; i < 49; i++) { + Serial.print(rgbCycleState.red[i]); + } + Serial.println(""); + Serial.println("green"); + for (size_t i = 0; i < 49; i++) { + Serial.print(rgbCycleState.green[i]); + } + Serial.println(""); + Serial.println("blue"); + for (size_t i = 0; i < 49; i++) { + Serial.print(rgbCycleState.blue[i]); + } + Serial.println(""); + + // serializeJsonPretty(root, Serial); + // Serial.println(time); + + return StateUpdateResult::CHANGED; + } +}; + +class RgbCycleService : public StatefulService { + public: + RgbCycleService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs); + void begin(); + + private: + WebSocketTxRx _webSocket; + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + + void registerConfig(); + void onConfigUpdated(); +}; +#endif \ No newline at end of file diff --git a/src/RgbDriver.cpp b/src/RgbDriver.cpp new file mode 100755 index 0000000..fda98f7 --- /dev/null +++ b/src/RgbDriver.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +RgbDriver* RgbDriver::pInstance = 0; + +RgbDriver* RgbDriver::getInstance() { + if (pInstance == 0) { + pInstance = new RgbDriver; + FastLED.addLeds(pInstance->leds, NUM_LEDS); + } + + return pInstance; +} + +void RgbDriver::updateRgb(int red, int green, int blue) { + pInstance->leds[0].setRGB(red, green, blue); + FastLED.show(); +} + +void RgbDriver::updateRgb() { + FastLED.show(); +} + +void RgbDriver::turnOff() { + pInstance->leds[0].setRGB(0, 0, 0); + FastLED.show(); +} \ No newline at end of file diff --git a/src/RgbDriver.h b/src/RgbDriver.h new file mode 100755 index 0000000..80e01b4 --- /dev/null +++ b/src/RgbDriver.h @@ -0,0 +1,23 @@ +#ifndef RgbDriver_h +#define RgbDriver_h + +#include + +#define NUM_LEDS 1 +#define CLOCK_PIN 14 // D5 +#define DATA_PIN 12 // D6 + +class RgbDriver { + public: + CRGB leds[NUM_LEDS]; + static void updateRgb(int red, int blue, int green); + static void updateRgb(); + static void turnOn(); + static void turnOff(); + static RgbDriver* getInstance(); + + private: + static RgbDriver* pInstance; +}; + +#endif \ No newline at end of file diff --git a/src/RgbStateService.cpp b/src/RgbStateService.cpp old mode 100644 new mode 100755 index 86c2247..bd79bf1 --- a/src/RgbStateService.cpp +++ b/src/RgbStateService.cpp @@ -1,6 +1,6 @@ #include -RgbStateService::RgbStateService(AsyncWebServer* server,SecurityManager* securityManager) : +RgbStateService::RgbStateService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs) : _httpEndpoint(RgbState::read, RgbState::update, this, @@ -14,25 +14,23 @@ RgbStateService::RgbStateService(AsyncWebServer* server,SecurityManager* securit server, RGB_SETTINGS_SOCKET_PATH, securityManager, - AuthenticationPredicates::IS_AUTHENTICATED) { - // configure led to be output - pinMode(LED_PIN, OUTPUT); + AuthenticationPredicates::IS_AUTHENTICATED), + _fsPersistence(RgbState::read, RgbState::update, this, fs, "/config/RgbState.json") { // configure settings service update handler to update LED state addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); } void RgbStateService::begin() { - _state.ledOn = DEFAULT_LED_STATE; - onConfigUpdated(); + // onConfigUpdated(); + _fsPersistence.readFromFS(); } void RgbStateService::onConfigUpdated() { - digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF); + Serial.println("onConfigUpdated called .."); } void RgbStateService::registerConfig() { - String configTopic; String subTopic; String pubTopic; diff --git a/src/RgbStateService.h b/src/RgbStateService.h old mode 100644 new mode 100755 index dfcaaab..892bc42 --- a/src/RgbStateService.h +++ b/src/RgbStateService.h @@ -3,11 +3,13 @@ #include #include - -#define LED_PIN 2 -#define PRINT_DELAY 5000 +#include +#include #define DEFAULT_LED_STATE false +#define DEFAULT_RED_VALUE 255 +#define DEFAULT_GREEN_VALUE 210 +#define DEFAULT_BLUE_VALUE 45 #define OFF_STATE "OFF" #define ON_STATE "ON" @@ -19,6 +21,7 @@ #elif defined(ESP8266) #define LED_ON 0x0 #define LED_OFF 0x1 + #endif #define RGB_SETTINGS_ENDPOINT_PATH "/rest/rgbState" @@ -27,53 +30,79 @@ class RgbState { public: bool ledOn; - uint8_t RedBrightness = 255; - uint8_t GreenBrightness = 255; - uint8_t BlueBrightness = 255; + uint8_t redValue; + uint8_t greenValue; + uint8_t blueValue; + RgbDriver* driver; + time_t time; + + // Class constructor + RgbState() { + driver = driver->getInstance(); + } static void read(RgbState& settings, JsonObject& root) { root["led_on"] = settings.ledOn; + root["red_value"] = settings.redValue; + root["green_value"] = settings.greenValue; + root["blue_value"] = settings.blueValue; } static StateUpdateResult update(JsonObject& root, RgbState& lightState) { boolean newState = root["led_on"] | DEFAULT_LED_STATE; - if (lightState.ledOn != newState) { - lightState.ledOn = newState; + int red = root["red_value"] | DEFAULT_RED_VALUE; + int green = root["green_value"] | DEFAULT_GREEN_VALUE; + int blue = root["blue_value"] | DEFAULT_BLUE_VALUE; + + bool onValueChanged = lightState.checkLightOnValue(newState); + bool rgbValueChanged = lightState.checkRgbValues(red, green, blue); + + if (onValueChanged || rgbValueChanged) { return StateUpdateResult::CHANGED; } return StateUpdateResult::UNCHANGED; } - static void haRead(RgbState& settings, JsonObject& root) { - root["state"] = settings.ledOn ? ON_STATE : OFF_STATE; - } + // static int getColorValues(){ + // Serial.println(redValue); + // } - static StateUpdateResult haUpdate(JsonObject& root, RgbState& lightState) { - String state = root["state"]; - // parse new led state - boolean newState = false; - if (state.equals(ON_STATE)) { - newState = true; - } else if (!state.equals(OFF_STATE)) { - return StateUpdateResult::ERROR; + bool checkRgbValues(int red, int green, int blue) { + if (red != redValue || green != greenValue || blue != blueValue) { + redValue = red; + greenValue = green; + blueValue = blue; + driver->updateRgb(red, green, blue); + return true; } - // change the new state, if required - if (lightState.ledOn != newState) { - lightState.ledOn = newState; - return StateUpdateResult::CHANGED; + return false; + } + + bool checkLightOnValue(bool onValue) { + if (ledOn != onValue) { + if (ledOn) { + ledOn = false; + driver->turnOff(); + return true; + } + ledOn = true; + driver->updateRgb(redValue, greenValue, blueValue); + return true; } - return StateUpdateResult::UNCHANGED; + return false; } }; class RgbStateService : public StatefulService { public: - RgbStateService(AsyncWebServer* server, SecurityManager* securityManager); + RgbStateService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs); + void begin(); private: HttpEndpoint _httpEndpoint; WebSocketTxRx _webSocket; + FSPersistence _fsPersistence; void registerConfig(); void onConfigUpdated(); diff --git a/src/colorService/BlueService.cpp b/src/colorService/BlueService.cpp new file mode 100644 index 0000000..a412b7f --- /dev/null +++ b/src/colorService/BlueService.cpp @@ -0,0 +1,19 @@ +#include "BlueService.h" + +BlueService::BlueService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs) : + _webSocket(BlueState::read, + BlueState::update, + this, + server, + BLUE_LIGHT_SOCKET_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _fsPersistence(BlueState::read, BlueState::update, this, fs, "/config/blueColor.json", 4096) { + // add event listener for rgbCycle Object + // addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); +} + +void BlueService::begin() { + Serial.println("blue begin ..."); + _fsPersistence.readFromFS(); +} \ No newline at end of file diff --git a/src/colorService/BlueService.h b/src/colorService/BlueService.h new file mode 100644 index 0000000..b22ef25 --- /dev/null +++ b/src/colorService/BlueService.h @@ -0,0 +1,38 @@ +#ifndef BlueService_h +#define BlueService_h + +#include +#include +#include + +#define BLUE_LIGHT_SOCKET_PATH "/ws/blueColor" + +class BlueState { + public: + RgbDriver* driver; + String time; + int blue[49] = {0}; + + // Class constructor + BlueState() { + driver = driver->getInstance(); + } + + static void read(BlueState& settings, JsonObject& root) {} + + static StateUpdateResult update(JsonObject& root, BlueState& redState) {} +}; + +class BlueService : public StatefulService { + public: + BlueService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs); + void begin(); + + private: + WebSocketTxRx _webSocket; + FSPersistence _fsPersistence; + + void registerConfig(); + void onConfigUpdated(); +}; +#endif \ No newline at end of file diff --git a/src/colorService/GreenService.cpp b/src/colorService/GreenService.cpp new file mode 100644 index 0000000..cdb5af7 --- /dev/null +++ b/src/colorService/GreenService.cpp @@ -0,0 +1,19 @@ +#include "GreenService.h" + +GreenService::GreenService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs) : + _webSocket(GreenState::read, + GreenState::update, + this, + server, + GREEN_LIGHT_SOCKET_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _fsPersistence(GreenState::read, GreenState::update, this, fs, "/config/greenColor.json", 4096) { + // add event listener for rgbCycle Object + // addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); +} + +void GreenService::begin() { + Serial.println("green begin ..."); + _fsPersistence.readFromFS(); +} \ No newline at end of file diff --git a/src/colorService/GreenService.h b/src/colorService/GreenService.h new file mode 100644 index 0000000..09a9890 --- /dev/null +++ b/src/colorService/GreenService.h @@ -0,0 +1,64 @@ +#ifndef GreenService_h +#define GreenService_h + +#include +#include +#include + +#define GREEN_LIGHT_SOCKET_PATH "/ws/greenLight" + +class GreenState { + public: + RgbDriver* driver; + String time; + int green[49] = {0}; + + // Class constructor + GreenState() { + driver = driver->getInstance(); + } + + static void read(GreenState& settings, JsonObject& root) { + Serial.println("green test read called"); + root.createNestedObject("graph"); + root["graph"].createNestedArray("green"); + + for (size_t i = 0; i < 49; i++) { + root["graph"]["green"].add(settings.green[i]); + } + } + + static StateUpdateResult update(JsonObject& root, GreenState& greenState) { + JsonArray green = root["graph"]["green"].as(); + + int i = 0; + for (JsonVariant value : green) { + greenState.green[i] = value.as(); + i++; + } + + Serial.println("Cycle update..."); + Serial.println(""); + Serial.println("green"); + for (size_t i = 0; i < 49; i++) { + Serial.print(greenState.green[i]); + } + Serial.println(""); + + return StateUpdateResult::CHANGED; + } +}; + +class GreenService : public StatefulService { + public: + GreenService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs); + void begin(); + + private: + WebSocketTxRx _webSocket; + FSPersistence _fsPersistence; + + void registerConfig(); + void onConfigUpdated(); +}; +#endif \ No newline at end of file diff --git a/src/colorService/RedService.cpp b/src/colorService/RedService.cpp new file mode 100644 index 0000000..c6553d2 --- /dev/null +++ b/src/colorService/RedService.cpp @@ -0,0 +1,19 @@ +#include "RedService.h" + +RedService::RedService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs) : + _webSocket(RedState::read, + RedState::update, + this, + server, + RED_LIGHT_SOCKET_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _fsPersistence(RedState::read, RedState::update, this, fs, "/config/redColor.json", 4096) { + // add event listener for rgbCycle Object + // addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); +} + +void RedService::begin() { + Serial.println("red begin ..."); + _fsPersistence.readFromFS(); +} \ No newline at end of file diff --git a/src/colorService/RedService.h b/src/colorService/RedService.h new file mode 100644 index 0000000..23d3393 --- /dev/null +++ b/src/colorService/RedService.h @@ -0,0 +1,38 @@ +#ifndef RedService_h +#define RedService_h + +#include +#include +#include + +#define RED_LIGHT_SOCKET_PATH "/ws/redColor" + +class RedState { + public: + RgbDriver* driver; + String time; + int red[49] = {0}; + + // Class constructor + RedState() { + driver = driver->getInstance(); + } + + static void read(RedState& settings, JsonObject& root) {} + + static StateUpdateResult update(JsonObject& root, RedState& redState) {} +}; + +class RedService : public StatefulService { + public: + RedService(AsyncWebServer* server, SecurityManager* securityManager, FS* fs); + void begin(); + + private: + WebSocketTxRx _webSocket; + FSPersistence _fsPersistence; + + void registerConfig(); + void onConfigUpdated(); +}; +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp old mode 100644 new mode 100755 index 6c72025..eb0b22f --- a/src/main.cpp +++ b/src/main.cpp @@ -1,53 +1,121 @@ #include #include +#include +#include "./colorService/RedService.h" +#include "./colorService/GreenService.h" +#include "./colorService/BlueService.h" +#include #define SERIAL_BAUD_RATE 115200 AsyncWebServer server(80); ESP8266React esp8266React(&server); -// LightMqttSettingsService lightMqttSettingsService = -// LightMqttSettingsService(&server, esp8266React.getFS(), esp8266React.getSecurityManager()); +RgbStateService rgbStateService = RgbStateService(&server, esp8266React.getSecurityManager(), esp8266React.getFS()); +RgbCycleService rgbCycleService = RgbCycleService(&server, esp8266React.getSecurityManager(), esp8266React.getFS()); +RedService redService = RedService(&server, esp8266React.getSecurityManager(), esp8266React.getFS()); +GreenService greenService = GreenService(&server, esp8266React.getSecurityManager(), esp8266React.getFS()); +BlueService blueService = BlueService(&server, esp8266React.getSecurityManager(), esp8266React.getFS()); -// LightStateService lightStateService = LightStateService(&server, -// esp8266React.getSecurityManager(), -// esp8266React.getMqttClient(), -// &lightMqttSettingsService); - -RgbStateService rgbStateService = RgbStateService(&server, esp8266React.getSecurityManager()); +// update_handler_id_t updateHandler = rgbCycleService.addUpdateHandler([&](const String& originId) { +// Serial.print("The RgbCycle's state has been updated by: "); +// Serial.println(originId); +int currentSecond = 0; +const int trigerTime = 15; +bool isTimeSet = false; +void turnLightsOn(); +void turnLightsOff(); +void onEverySecond(); void setup() { // start serial and filesystem Serial.begin(SERIAL_BAUD_RATE); - // start the framework and demo project + // start the framework and project esp8266React.begin(); - // load the initial light settings - // lightStateService.begin(); + // load the initial RGB object rgbStateService.begin(); - - // start the light service - // lightMqttSettingsService.begin(); + rgbCycleService.begin(); + redService.begin(); + greenService.begin(); + blueService.begin(); // start the server server.begin(); + + // Once a second call onEverySecond function + Alarm.timerRepeat(1, onEverySecond); } void loop() { // run the framework's loop function esp8266React.loop(); + Alarm.delay(1000); + + // rgbCycleService.update( + // [&](RgbCycleState& state) { + // Serial.println(state.red[22]); + // return StateUpdateResult::CHANGED; // notify StatefulService by returning CHANGED + // }, + // "timer"); + // } } +void turnLightsOff() { + rgbStateService.update( + [&](RgbState& state) { + if (state.ledOn) { + RgbDriver::updateRgb(0, 0, 0); + state.ledOn = false; + return StateUpdateResult::CHANGED; + } + return StateUpdateResult::UNCHANGED; + }, + "rgbState"); +} + +void turnLightsOn() { + rgbStateService.update( + [&](RgbState& state) { + if (!state.ledOn) { + RgbDriver::updateRgb(state.redValue, state.greenValue, state.blueValue); + state.ledOn = true; + return StateUpdateResult::CHANGED; + } + return StateUpdateResult::UNCHANGED; + }, + "rgbState"); +} + +void onEverySecond() { + if (!isTimeSet && currentSecond > trigerTime) { + Serial.println("Setting time..."); + time_t now = time(nullptr); + tm* timeinfo = localtime(&now); + int sec = timeinfo->tm_sec; + int min = timeinfo->tm_min; + int hour = timeinfo->tm_hour; + int day = timeinfo->tm_mday; + int month = timeinfo->tm_mon + 1; + int year = timeinfo->tm_year + 1900; + setTime(hour, min, sec, day, month, year); + Alarm.alarmRepeat(12, 00, 0, turnLightsOn); + Alarm.alarmRepeat(22, 00, 0, turnLightsOff); + isTimeSet = true; + } + + if (!isTimeSet) { + currentSecond++; + } -// class RgbState { -// public: -// bool on = false; -// uint8_t RedBrightness = 255; -// uint8_t GreenBrightness = 255; -// uint8_t BlueBrightness = 255; -// }; + // rgbCycleService.update( + // [&](RgbCycleState& state) { + // Serial.println(state.red[22]); + // return StateUpdateResult::CHANGED; // notify StatefulService by returning CHANGED + // }, + // "timer"); -// class RgbStateService : public StatefulService { -// }; + RgbDriver::updateRgb(); +} \ No newline at end of file