diff --git a/data/main.jsx b/data/main.jsx
index 2f16181e8..404e57b9a 100644
--- a/data/main.jsx
+++ b/data/main.jsx
@@ -138,12 +138,15 @@ const mainAppStyle = theme => ({
"align-items": "center"
},
content: {
- flexGrow: 1,
padding: theme.spacing.unit * 10,
transition: theme.transitions.create('padding-left', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
- })
+ }),
+ display: "flex",
+ flexDirection: "row",
+ flexWrap: "wrap",
+ rowGap: "10px"
},
contentShrinked: {
"padding-left": drawerWidth + 10,
@@ -178,6 +181,45 @@ const mainAppStyle = theme => ({
hidden: {
display: "none"
}
+});const designStyle = theme => ({
+ root: {
+ "display": "flex",
+ "flex-direction": "column",
+ border: "green solid 2px",
+ borderRadius: "15px",
+ padding: "10px",
+ },
+ hidden: {
+ display: "none"
+ },
+ effectsHeader: {
+ display: "flex",
+ flexDirection: "row",
+ borderBottom: "green solid 1px",
+ columnGap: "5px",
+ },
+ effectsHeaderValue: {
+ display: "flex",
+ flexDirection: "row",
+ columnGap: "3px",
+ alignItems: "center",
+ },
+ effect: {
+ display: "flex",
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ timeremaining: {
+ width: "50px"
+ },
+ effectAttribute: {
+ display: "flex",
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ unselected: {
+ opacity: "30%"
+ }
});const statsStyle = theme => ({
root: {
"display": "flex",
@@ -458,8 +500,8 @@ const MainApp = withStyles(mainAppStyle)(props => {
const { classes } = props;
const [drawerOpened, setDrawerOpened] = useState(false);
const [mode, setMode] = useState('dark');
- const [stats, setStats] = useState(true);
- const [designer, setDesigner] = useState(false);
+ const [stats, setStats] = useState(false);
+ const [designer, setDesigner] = useState(true);
const [config, setConfig] = useState(false);
const [statsRefreshRate, setStatsRefreshRate ] = useState(3);
const [maxSamples, setMaxSamples ] = useState(50);
@@ -504,9 +546,9 @@ const MainApp = withStyles(mainAppStyle)(props => {
Night Driver Strip
-
- notifications
-
+
+ notifications
+
@@ -535,7 +577,8 @@ const MainApp = withStyles(mainAppStyle)(props => {
}
-
+
+
{setConfig(false)}} />
@@ -643,6 +686,135 @@ const ConfigDialog = withStyles(configStyle)(props => {
);
+});const DesignerPanel = withStyles(designStyle)(props => {
+ const { classes, siteConfig, open } = props;
+ const [ effects, setEffects ] = useState(undefined);
+ const [ abortControler, setAbortControler ] = useState(undefined);
+ const [ nextRefreshDate, setNextRefreshDate] = useState(undefined);
+ const [ editing, setEditing ] = useState(false);
+ const [ pendingInterval, setPendingInterval ] = useState(undefined);
+ const [ timeRemaining, setTimeRemaining ] = useState(undefined);
+
+ useEffect(() => {
+ if (abortControler) {
+ abortControler.abort();
+ }
+
+ if (open) {
+ const aborter = new AbortController();
+ setAbortControler(aborter);
+
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/getEffectList`,{signal:aborter.signal})
+ .then(resp => resp.json())
+ .then(setEffects)
+ .catch(console.error);
+
+ return () => {
+ abortControler && abortControler.abort();
+ }
+ }
+ },[open,nextRefreshDate]);
+
+ useEffect(() => {
+ if (effects) {
+ const timeReference = Date.now()+effects.millisecondsRemaining;
+ var requestSent = false;
+ const interval = setInterval(()=>{
+ const remaining = timeReference-Date.now();
+ if (remaining >= 0) {
+ setTimeRemaining(remaining);
+ }
+ if ((remaining <= 100) && !requestSent) {
+ setNextRefreshDate(Date.now());
+ requestSent=true;
+ }
+ },50);
+ return ()=>clearInterval(interval);
+ }
+ },[effects]);
+
+ const navigate = (up)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${up ? "nextEffect" : "previousEffect"}`,{method:"POST"})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ const navigateTo = (idx)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/setCurrentEffectIndex?`,{method:"POST", body: new URLSearchParams({currentEffectIndex:idx})})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ const updatePendingInterval = (interval)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/settings`,{method:"POST", body: new URLSearchParams({effectInterval:interval})})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ const effectEnable = (idx,enable)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${enable?"enable":"disable"}Effect`,{method:"POST", body:new URLSearchParams({effectIndex:idx})})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ if (!effects && open){
+ return Loading....;
+ }
+
+ return effects &&
+
+ {editing ?
+
+ setPendingInterval(event.target.value) } />
+
+ {
+ updatePendingInterval(pendingInterval);
+ setEditing(false);
+ }}>
+ save
+
+ {
+ setEditing(false);
+ }}>
+ cancel
+
+
+ :
+
+ Interval:
+ {effects.effectInterval}
+ setEditing(true)}>edit
+ }
+
+ Time Remaining:
+ {timeRemaining}
+
+ {(effects.Effects.length > 1) &&
+ navigate(false)}>skip_previous
+ navigate(true)}>skip_next
+ }
+
+ {effects.Effects.map((effect,idx) =>
+
+ {(idx === effects.currentEffect) ?
+ effects.Effects.length > 1 && arrow_right_alt:
+ navigateTo(idx)}>{effect.enabled ? "arrow_right_alt":""}}
+
+ effectEnable(idx,!effect.enabled)}>{effect.enabled ? "check" : "close"}
+ {effect.name}
+
+ )}
+
});const StaticStatsPanel = withStyles(staticStatStyle)(props => {
const { classes, stat, name, detail } = props;
@@ -867,28 +1039,32 @@ const ConfigDialog = withStyles(configStyle)(props => {
if (abortControler) {
abortControler.abort();
}
- const aborter = new AbortController();
- setAbortControler(aborter);
- getStats(aborter).then(setStatistics)
- .catch(console.error);
-
- if (timer) {
- clearTimeout(timer);
- setTimer(undefined);
- }
-
- if (statsRefreshRate.value && open) {
- setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000));
- }
-
- return () => {
- timer && clearTimeout(timer);
- abortControler && abortControler.abort();
+ if (open) {
+ const aborter = new AbortController();
+ setAbortControler(aborter);
+
+ getStats(aborter)
+ .then(setStatistics)
+ .catch(console.error);
+
+ if (timer) {
+ clearTimeout(timer);
+ setTimer(undefined);
+ }
+
+ if (statsRefreshRate.value && open) {
+ setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000));
+ }
+
+ return () => {
+ timer && clearTimeout(timer);
+ abortControler && abortControler.abort();
+ }
}
},[statsRefreshRate.value, lastRefreshDate, open]);
- if (!statistics) {
+ if (!statistics && open) {
return Loading...
}
diff --git a/platformio.ini b/platformio.ini
index 2f6160213..68b2fc16f 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -263,9 +263,17 @@ board = lolin_d32_pro
monitor_speed = 115200
upload_speed = 921600
build_flags = -DLEDSTRIP=1
+ -DMESMERIZER=1
+ -DUSEMATRIX=1
+ -DUSE_PSRAM=1
+ -DBOARD_HAS_PSRAM=1
+ -mfix-esp32-psram-cache-issue
+ -D_IR_ENABLE_DEFAULT_=false
-std=gnu++17
-Dregister= ; Sinister: redefine 'register' so FastLED can use that keyword under C++17
-Ofast
+lib_deps = ${common.lib_deps}
+ https://github.com/davepl/SmartMatrix.git
[env:generic]
board = esp32dev
diff --git a/site/src/components/home/designer/designer.jsx b/site/src/components/home/designer/designer.jsx
new file mode 100644
index 000000000..8e47d4efb
--- /dev/null
+++ b/site/src/components/home/designer/designer.jsx
@@ -0,0 +1,130 @@
+const DesignerPanel = withStyles(designStyle)(props => {
+ const { classes, siteConfig, open } = props;
+ const [ effects, setEffects ] = useState(undefined);
+ const [ abortControler, setAbortControler ] = useState(undefined);
+ const [ nextRefreshDate, setNextRefreshDate] = useState(undefined);
+ const [ editing, setEditing ] = useState(false);
+ const [ pendingInterval, setPendingInterval ] = useState(undefined);
+ const [ timeRemaining, setTimeRemaining ] = useState(undefined);
+
+ useEffect(() => {
+ if (abortControler) {
+ abortControler.abort();
+ }
+
+ if (open) {
+ const aborter = new AbortController();
+ setAbortControler(aborter);
+
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/getEffectList`,{signal:aborter.signal})
+ .then(resp => resp.json())
+ .then(setEffects)
+ .catch(console.error);
+
+ return () => {
+ abortControler && abortControler.abort();
+ }
+ }
+ },[open,nextRefreshDate]);
+
+ useEffect(() => {
+ if (effects) {
+ const timeReference = Date.now()+effects.millisecondsRemaining;
+ var requestSent = false;
+ const interval = setInterval(()=>{
+ const remaining = timeReference-Date.now();
+ if (remaining >= 0) {
+ setTimeRemaining(remaining);
+ }
+ if ((remaining <= 100) && !requestSent) {
+ setNextRefreshDate(Date.now());
+ requestSent=true;
+ }
+ },50);
+ return ()=>clearInterval(interval);
+ }
+ },[effects]);
+
+ const navigate = (up)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${up ? "nextEffect" : "previousEffect"}`,{method:"POST"})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ const navigateTo = (idx)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/setCurrentEffectIndex?`,{method:"POST", body: new URLSearchParams({currentEffectIndex:idx})})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ const updatePendingInterval = (interval)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/settings`,{method:"POST", body: new URLSearchParams({effectInterval:interval})})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ const effectEnable = (idx,enable)=>{
+ fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${enable?"enable":"disable"}Effect`,{method:"POST", body:new URLSearchParams({effectIndex:idx})})
+ .then(setNextRefreshDate(Date.now()))
+ .catch(console.error);
+ }
+
+ if (!effects && open){
+ return Loading....;
+ }
+
+ return effects &&
+
+ {editing ?
+
+ setPendingInterval(event.target.value) } />
+
+ {
+ updatePendingInterval(pendingInterval);
+ setEditing(false);
+ }}>
+ save
+
+ {
+ setEditing(false);
+ }}>
+ cancel
+
+
+ :
+
+ Interval:
+ {effects.effectInterval}
+ setEditing(true)}>edit
+ }
+
+ Time Remaining:
+ {timeRemaining}
+
+ {(effects.Effects.length > 1) &&
+ navigate(false)}>skip_previous
+ navigate(true)}>skip_next
+ }
+
+ {effects.Effects.map((effect,idx) =>
+
+ {(idx === effects.currentEffect) ?
+ effects.Effects.length > 1 && arrow_right_alt:
+ navigateTo(idx)}>{effect.enabled ? "arrow_right_alt":""}}
+
+ effectEnable(idx,!effect.enabled)}>{effect.enabled ? "check" : "close"}
+ {effect.name}
+
+ )}
+
+});
\ No newline at end of file
diff --git a/site/src/components/home/designer/style.jsx b/site/src/components/home/designer/style.jsx
new file mode 100644
index 000000000..d8ea3deba
--- /dev/null
+++ b/site/src/components/home/designer/style.jsx
@@ -0,0 +1,40 @@
+const designStyle = theme => ({
+ root: {
+ "display": "flex",
+ "flex-direction": "column",
+ border: "green solid 2px",
+ borderRadius: "15px",
+ padding: "10px",
+ },
+ hidden: {
+ display: "none"
+ },
+ effectsHeader: {
+ display: "flex",
+ flexDirection: "row",
+ borderBottom: "green solid 1px",
+ columnGap: "5px",
+ },
+ effectsHeaderValue: {
+ display: "flex",
+ flexDirection: "row",
+ columnGap: "3px",
+ alignItems: "center",
+ },
+ effect: {
+ display: "flex",
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ timeremaining: {
+ width: "50px"
+ },
+ effectAttribute: {
+ display: "flex",
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ unselected: {
+ opacity: "30%"
+ }
+});
\ No newline at end of file
diff --git a/site/src/components/home/home.jsx b/site/src/components/home/home.jsx
index 0243cc9ad..9af241ebc 100644
--- a/site/src/components/home/home.jsx
+++ b/site/src/components/home/home.jsx
@@ -2,8 +2,8 @@ const MainApp = withStyles(mainAppStyle)(props => {
const { classes } = props;
const [drawerOpened, setDrawerOpened] = useState(false);
const [mode, setMode] = useState('dark');
- const [stats, setStats] = useState(true);
- const [designer, setDesigner] = useState(false);
+ const [stats, setStats] = useState(false);
+ const [designer, setDesigner] = useState(true);
const [config, setConfig] = useState(false);
const [statsRefreshRate, setStatsRefreshRate ] = useState(3);
const [maxSamples, setMaxSamples ] = useState(50);
@@ -48,9 +48,9 @@ const MainApp = withStyles(mainAppStyle)(props => {
Night Driver Strip
-
- notifications
-
+
+ notifications
+
@@ -79,7 +79,8 @@ const MainApp = withStyles(mainAppStyle)(props => {
}
-
+
+
{setConfig(false)}} />
diff --git a/site/src/components/home/statistics/stats.jsx b/site/src/components/home/statistics/stats.jsx
index 095b76d8a..51efc03a4 100644
--- a/site/src/components/home/statistics/stats.jsx
+++ b/site/src/components/home/statistics/stats.jsx
@@ -101,28 +101,32 @@ const StatsPanel = withStyles(statsStyle)(props => {
if (abortControler) {
abortControler.abort();
}
- const aborter = new AbortController();
- setAbortControler(aborter);
- getStats(aborter).then(setStatistics)
- .catch(console.error);
-
- if (timer) {
- clearTimeout(timer);
- setTimer(undefined);
- }
-
- if (statsRefreshRate.value && open) {
- setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000));
- }
-
- return () => {
- timer && clearTimeout(timer);
- abortControler && abortControler.abort();
+ if (open) {
+ const aborter = new AbortController();
+ setAbortControler(aborter);
+
+ getStats(aborter)
+ .then(setStatistics)
+ .catch(console.error);
+
+ if (timer) {
+ clearTimeout(timer);
+ setTimer(undefined);
+ }
+
+ if (statsRefreshRate.value && open) {
+ setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000));
+ }
+
+ return () => {
+ timer && clearTimeout(timer);
+ abortControler && abortControler.abort();
+ }
}
},[statsRefreshRate.value, lastRefreshDate, open]);
- if (!statistics) {
+ if (!statistics && open) {
return Loading...
}
diff --git a/site/src/components/home/style.jsx b/site/src/components/home/style.jsx
index 056d799b9..9061751e2 100644
--- a/site/src/components/home/style.jsx
+++ b/site/src/components/home/style.jsx
@@ -65,12 +65,15 @@ const mainAppStyle = theme => ({
"align-items": "center"
},
content: {
- flexGrow: 1,
padding: theme.spacing.unit * 10,
transition: theme.transitions.create('padding-left', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
- })
+ }),
+ display: "flex",
+ flexDirection: "row",
+ flexWrap: "wrap",
+ rowGap: "10px"
},
contentShrinked: {
"padding-left": drawerWidth + 10,