diff --git a/CMakeLists.txt b/CMakeLists.txt index 825935a43..5721903da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,7 @@ src/addons/display.cpp src/addons/neopicoleds.cpp src/addons/playernum.cpp src/addons/playerleds.cpp +src/addons/reactiveleds.cpp src/addons/rotaryencoder.cpp src/addons/reverse.cpp src/addons/drv8833_rumble.cpp diff --git a/headers/addons/reactiveleds.h b/headers/addons/reactiveleds.h new file mode 100644 index 000000000..ac59a1a98 --- /dev/null +++ b/headers/addons/reactiveleds.h @@ -0,0 +1,56 @@ +#ifndef REACTIVELEDS_H_ +#define REACTIVELEDS_H_ + +#include "gpaddon.h" + +#ifndef REACTIVE_LED_ENABLED +#define REACTIVE_LED_ENABLED 0 +#endif + +#ifndef REACTIVE_LED_COUNT +#define REACTIVE_LED_COUNT 8 +#endif + +#ifndef REACTIVE_LED_DELAY +#define REACTIVE_LED_DELAY 1 +#endif + +#ifndef REACTIVE_LED_MAX_BRIGHTNESS +#define REACTIVE_LED_MAX_BRIGHTNESS 255 +#endif + +#ifndef REACTIVE_LED_FADE_INC +#define REACTIVE_LED_FADE_INC 1 +#endif + +// Reactive LED Module +#define ReactiveLEDName "ReactiveLED" + +// Reactive LED +class ReactiveLEDAddon : public GPAddon +{ + public: + virtual bool available(); + virtual void setup(); + virtual void preprocess() {} + virtual void process(); + virtual std::string name() { return ReactiveLEDName; } + private: + struct ReactiveLEDPinState { + uint16_t pinNumber = -1; + ReactiveLEDMode modeDown = ReactiveLEDMode::REACTIVE_LED_STATIC_ON; + ReactiveLEDMode modeUp = ReactiveLEDMode::REACTIVE_LED_STATIC_OFF; + GpioAction action = GpioAction::NONE; + uint8_t value = 0; + bool currState = false; + bool prevState = false; + uint32_t lastUpdate; + uint32_t currUpdate; + }; + + ReactiveLEDPinState ledPins[REACTIVE_LED_COUNT]; + + void setLEDByMode(ReactiveLEDPinState &ledState, bool pressed); +}; + +#endif \ No newline at end of file diff --git a/proto/config.proto b/proto/config.proto index 1bc4114d0..132c88b5d 100644 --- a/proto/config.proto +++ b/proto/config.proto @@ -750,6 +750,20 @@ message DRV8833RumbleOptions optional float dutyMax = 7; } +message ReactiveLEDInfo +{ + optional int32 pin = 1; + optional GpioAction action = 2; + optional ReactiveLEDMode modeDown = 3; + optional ReactiveLEDMode modeUp = 4; +} + +message ReactiveLEDOptions +{ + optional bool enabled = 1; + repeated ReactiveLEDInfo leds = 2 [(nanopb).max_count = 8]; +} + message AddonOptions { optional BootselButtonOptions bootselButtonOptions = 1; @@ -778,6 +792,7 @@ message AddonOptions optional RotaryOptions rotaryOptions = 24; optional PCF8575Options pcf8575Options = 25; optional DRV8833RumbleOptions drv8833RumbleOptions = 26; + optional ReactiveLEDOptions reactiveLEDOptions = 27; } message MigrationHistory diff --git a/proto/enums.proto b/proto/enums.proto index d2beeac98..d1eeb06bb 100644 --- a/proto/enums.proto +++ b/proto/enums.proto @@ -404,3 +404,13 @@ enum RotaryEncoderPinMode ENCODER_MODE_DPAD_X = 7; ENCODER_MODE_DPAD_Y = 8; }; + +enum ReactiveLEDMode +{ + option (nanopb_enumopt).long_names = false; + + REACTIVE_LED_STATIC_OFF = 0; + REACTIVE_LED_STATIC_ON = 1; + REACTIVE_LED_FADE_IN = 2; + REACTIVE_LED_FADE_OUT = 3; +}; diff --git a/src/addons/reactiveleds.cpp b/src/addons/reactiveleds.cpp new file mode 100644 index 000000000..f5d35def0 --- /dev/null +++ b/src/addons/reactiveleds.cpp @@ -0,0 +1,114 @@ +#include "hardware/pwm.h" +#include "addons/reactiveleds.h" +#include "storagemanager.h" +#include "usbdriver.h" +#include "helper.h" +#include "config.pb.h" + +bool ReactiveLEDAddon::available() { + bool pinsEnabled = false; + const ReactiveLEDOptions& options = Storage::getInstance().getAddonOptions().reactiveLEDOptions; + for (uint8_t led = 0; led < sizeof(REACTIVE_LED_COUNT); led++) { + if (isValidPin(options.leds[led].pin)) { + pinsEnabled = true; + break; + } + } + return options.enabled && pinsEnabled; +} + +void ReactiveLEDAddon::setup() { + const ReactiveLEDOptions& options = Storage::getInstance().getAddonOptions().reactiveLEDOptions; + + for (uint8_t led = 0; led < sizeof(REACTIVE_LED_COUNT); led++) { + ReactiveLEDInfo ledInfo = options.leds[led]; + + ledPins[led].pinNumber = ledInfo.pin; + ledPins[led].action = ledInfo.action; + ledPins[led].modeDown = ledInfo.modeDown; + ledPins[led].modeUp = ledInfo.modeUp; + + if (isValidPin(ledPins[led].pinNumber)) { + gpio_init(ledPins[led].pinNumber); + gpio_set_dir(ledPins[led].pinNumber, GPIO_OUT); + gpio_set_function(ledPins[led].pinNumber, GPIO_FUNC_PWM); + + pwm_set_wrap(pwm_gpio_to_slice_num(ledPins[led].pinNumber), REACTIVE_LED_MAX_BRIGHTNESS); + pwm_set_enabled(pwm_gpio_to_slice_num(ledPins[led].pinNumber), true); + + ledPins[led].lastUpdate = to_ms_since_boot(get_absolute_time()); + + setLEDByMode(ledPins[led], false); + } + } +} + +void ReactiveLEDAddon::process() { + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + + uint32_t currUpdate = to_ms_since_boot(get_absolute_time()); + + for (uint8_t led = 0; led < sizeof(REACTIVE_LED_COUNT); led++) { + if (isValidPin(ledPins[led].pinNumber) && ledPins[led].action != GpioAction::NONE) { + ledPins[led].currUpdate = currUpdate; + switch (ledPins[led].action) { + case BUTTON_PRESS_UP: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_UP)); break; + case BUTTON_PRESS_DOWN: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_DOWN)); break; + case BUTTON_PRESS_LEFT: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_LEFT)); break; + case BUTTON_PRESS_RIGHT: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_RIGHT)); break; + case BUTTON_PRESS_B1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B1)); break; + case BUTTON_PRESS_B2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B2)); break; + case BUTTON_PRESS_B3: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B3)); break; + case BUTTON_PRESS_B4: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B4)); break; + case BUTTON_PRESS_L1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_L1)); break; + case BUTTON_PRESS_R1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_R1)); break; + case BUTTON_PRESS_L2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_L2)); break; + case BUTTON_PRESS_R2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_R2)); break; + case BUTTON_PRESS_S1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_S1)); break; + case BUTTON_PRESS_S2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_S2)); break; + case BUTTON_PRESS_A1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_A1)); break; + case BUTTON_PRESS_A2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_A2)); break; + case BUTTON_PRESS_L3: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_L3)); break; + case BUTTON_PRESS_R3: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_R3)); break; + default: break; + } + } + } +} + +void ReactiveLEDAddon::setLEDByMode(ReactiveLEDPinState &ledState, bool pressed) { + ledState.currState = pressed; + + switch (pressed ? ledState.modeDown : ledState.modeUp) { + case ReactiveLEDMode::REACTIVE_LED_STATIC_OFF: + ledState.value = 0; + break; + case ReactiveLEDMode::REACTIVE_LED_STATIC_ON: + ledState.value = 255; + break; + case ReactiveLEDMode::REACTIVE_LED_FADE_IN: + if (ledState.currUpdate - ledState.lastUpdate >= REACTIVE_LED_DELAY) { + if (ledState.prevState != pressed) ledState.value = 0; + if (ledState.value < REACTIVE_LED_MAX_BRIGHTNESS) { + ledState.value+=REACTIVE_LED_FADE_INC; + } + + ledState.lastUpdate = ledState.currUpdate; + } + break; + case ReactiveLEDMode::REACTIVE_LED_FADE_OUT: + if (ledState.currUpdate - ledState.lastUpdate >= REACTIVE_LED_DELAY) { + if (ledState.prevState != pressed) ledState.value = REACTIVE_LED_MAX_BRIGHTNESS; + if (ledState.value > 0) { + ledState.value-=REACTIVE_LED_FADE_INC; + } + + ledState.lastUpdate = ledState.currUpdate; + } + break; + } + + pwm_set_gpio_level(ledState.pinNumber, ledState.value); + + ledState.prevState = pressed; +} diff --git a/src/config_utils.cpp b/src/config_utils.cpp index e553687b8..21844b3ed 100644 --- a/src/config_utils.cpp +++ b/src/config_utils.cpp @@ -24,6 +24,7 @@ #include "addons/neopicoleds.h" #include "addons/playernum.h" #include "addons/pleds.h" +#include "addons/reactiveleds.h" #include "addons/reverse.h" #include "addons/slider_socd.h" #include "addons/spi_analog_ads1256.h" @@ -724,6 +725,17 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config) INIT_UNSET_PROPERTY(config.addonOptions.rotaryOptions.encoderTwo, allowWrapAround, ENCODER_TWO_WRAP); INIT_UNSET_PROPERTY(config.addonOptions.rotaryOptions.encoderTwo, multiplier, ENCODER_TWO_MULTIPLIER); + // addonOptions.reactiveLEDOptions + INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions, enabled, !!REACTIVE_LED_ENABLED); + for (uint16_t led = 0; led < REACTIVE_LED_COUNT; led++) { + INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], pin, -1); + INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], action, GpioAction::NONE); + INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], modeDown, REACTIVE_LED_STATIC_ON); + INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], modeUp, REACTIVE_LED_STATIC_OFF); + } + // reminder that this must be set or else nanopb won't retain anything + config.addonOptions.reactiveLEDOptions.leds_count = REACTIVE_LED_COUNT; + // keyboardMapping INIT_UNSET_PROPERTY(config.addonOptions.keyboardHostOptions, enabled, KEYBOARD_HOST_ENABLED); INIT_UNSET_PROPERTY(config.addonOptions.keyboardHostOptions, deprecatedPinDplus, KEYBOARD_HOST_PIN_DPLUS); diff --git a/src/configs/webconfig.cpp b/src/configs/webconfig.cpp index 431656cbd..93660c7e3 100644 --- a/src/configs/webconfig.cpp +++ b/src/configs/webconfig.cpp @@ -1343,6 +1343,40 @@ std::string setExpansionPins() return serialize_json(doc); } +std::string getReactiveLEDs() +{ + DynamicJsonDocument doc(LWIP_HTTPD_POST_MAX_PAYLOAD_LEN); + ReactiveLEDInfo* ledInfo = Storage::getInstance().getAddonOptions().reactiveLEDOptions.leds; + + for (uint16_t led = 0; led < 8; led++) { + writeDoc(doc, "leds", led, "pin", ledInfo[led].pin); + writeDoc(doc, "leds", led, "action", ledInfo[led].action); + writeDoc(doc, "leds", led, "modeDown", ledInfo[led].modeDown); + writeDoc(doc, "leds", led, "modeUp", ledInfo[led].modeUp); + } + + return serialize_json(doc); +} + +std::string setReactiveLEDs() +{ + DynamicJsonDocument doc = get_post_data(); + + ReactiveLEDInfo* ledInfo = Storage::getInstance().getAddonOptions().reactiveLEDOptions.leds; + + for (uint16_t led = 0; led < 8; led++) { + ledInfo[led].pin = doc["leds"][led]["pin"]; + ledInfo[led].action = doc["leds"][led]["action"]; + ledInfo[led].modeDown = doc["leds"][led]["modeDown"]; + ledInfo[led].modeUp = doc["leds"][led]["modeUp"]; + } + Storage::getInstance().getAddonOptions().reactiveLEDOptions.leds_count = 8; + + Storage::getInstance().save(); + + return serialize_json(doc); +} + std::string setAddonOptions() { DynamicJsonDocument doc = get_post_data(); @@ -1515,6 +1549,9 @@ std::string setAddonOptions() PCF8575Options& pcf8575Options = Storage::getInstance().getAddonOptions().pcf8575Options; docToValue(pcf8575Options.enabled, doc, "PCF8575AddonEnabled"); + ReactiveLEDOptions& reactiveLEDOptions = Storage::getInstance().getAddonOptions().reactiveLEDOptions; + docToValue(reactiveLEDOptions.enabled, doc, "ReactiveLEDAddonEnabled"); + DRV8833RumbleOptions& drv8833RumbleOptions = Storage::getInstance().getAddonOptions().drv8833RumbleOptions; docToValue(drv8833RumbleOptions.enabled, doc, "DRV8833RumbleAddonEnabled"); docToPin(drv8833RumbleOptions.leftMotorPin, doc, "drv8833RumbleLeftMotorPin"); @@ -1944,6 +1981,9 @@ std::string getAddonOptions() PCF8575Options& pcf8575Options = Storage::getInstance().getAddonOptions().pcf8575Options; writeDoc(doc, "PCF8575AddonEnabled", pcf8575Options.enabled); + ReactiveLEDOptions& reactiveLEDOptions = Storage::getInstance().getAddonOptions().reactiveLEDOptions; + writeDoc(doc, "ReactiveLEDAddonEnabled", reactiveLEDOptions.enabled); + const DRV8833RumbleOptions& drv8833RumbleOptions = Storage::getInstance().getAddonOptions().drv8833RumbleOptions; writeDoc(doc, "DRV8833RumbleAddonEnabled", drv8833RumbleOptions.enabled); writeDoc(doc, "drv8833RumbleLeftMotorPin", cleanPin(drv8833RumbleOptions.leftMotorPin)); @@ -2213,6 +2253,8 @@ static const std::pair handlerFuncs[] = { "/api/getI2CPeripheralMap", getI2CPeripheralMap }, { "/api/setExpansionPins", setExpansionPins }, { "/api/getExpansionPins", getExpansionPins }, + { "/api/setReactiveLEDs", setReactiveLEDs }, + { "/api/getReactiveLEDs", getReactiveLEDs }, { "/api/setKeyMappings", setKeyMappings }, { "/api/setAddonsOptions", setAddonOptions }, { "/api/setMacroAddonOptions", setMacroAddonOptions }, diff --git a/src/gp2040aux.cpp b/src/gp2040aux.cpp index 531c31604..bd669989b 100644 --- a/src/gp2040aux.cpp +++ b/src/gp2040aux.cpp @@ -11,6 +11,7 @@ #include "addons/display.h" #include "addons/pleds.h" #include "addons/neopicoleds.h" +#include "addons/reactiveleds.h" #include "addons/drv8833_rumble.h" #include @@ -35,6 +36,7 @@ void GP2040Aux::setup() { addons.LoadAddon(new BoardLedAddon(), CORE1_LOOP); addons.LoadAddon(new BuzzerSpeakerAddon(), CORE1_LOOP); addons.LoadAddon(new DRV8833RumbleAddon(), CORE1_LOOP); + addons.LoadAddon(new ReactiveLEDAddon(), CORE1_LOOP); // Initialize our input driver's auxilliary functions inputDriver = DriverManager::getInstance().getDriver(); diff --git a/www/server/app.js b/www/server/app.js index 89cd33a11..040c1739a 100644 --- a/www/server/app.js +++ b/www/server/app.js @@ -509,6 +509,7 @@ app.get('/api/getAddonsOptions', (req, res) => { RotaryAddonEnabled: 1, PCF8575AddonEnabled: 1, DRV8833RumbleAddonEnabled: 1, + ReactiveLEDAddonEnabled: 1, usedPins: Object.values(picoController), }); }); @@ -731,6 +732,21 @@ app.get('/api/getButtonLayoutDefs', (req, res) => { }); }); +app.get('/api/getReactiveLEDs', (req, res) => { + return res.send({ + leds: [ + { pin: -1, action: -10, modeDown: 0, modeUp: 1 }, + { pin: -1, action: -10, modeDown: 1, modeUp: 0 }, + { pin: -1, action: -10, modeDown: 1, modeUp: 0 }, + { pin: -1, action: -10, modeDown: 1, modeUp: 0 }, + { pin: -1, action: -10, modeDown: 1, modeUp: 0 }, + { pin: -1, action: -10, modeDown: 1, modeUp: 0 }, + { pin: -1, action: -10, modeDown: 1, modeUp: 0 }, + { pin: -1, action: -10, modeDown: 1, modeUp: 0 }, + ], + }); +}); + app.get('/api/reboot', (req, res) => { return res.send({}); }); diff --git a/www/src/Addons/ReactiveLED.tsx b/www/src/Addons/ReactiveLED.tsx new file mode 100644 index 000000000..7f2225f22 --- /dev/null +++ b/www/src/Addons/ReactiveLED.tsx @@ -0,0 +1,176 @@ +import { AppContext } from '../Contexts/AppContext'; +import React, { useContext, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import * as yup from 'yup'; + +import invert from 'lodash/invert'; +import omit from 'lodash/omit'; +import zip from 'lodash/zip'; + +import Section from '../Components/Section'; +import FormSelect from '../Components/FormSelect'; +import FormControl from '../Components/FormControl'; +import { Button, FormCheck, Row, Col } from 'react-bootstrap'; + +import WebApi, { } from '../Services/WebApi'; + +import { + BUTTON_ACTIONS, + PinActionValues, +} from '../Data/Pins'; +import { getButtonLabels } from '../Data/Buttons'; + +const NON_SELECTABLE_BUTTON_ACTIONS = [ + -5, 0, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40 +]; + +const REACTIVE_LED_MODES = [ + { label: 'static-off', value: 0 }, + { label: 'static-on', value: 1 }, + { label: 'fade-in', value: 2 }, + { label: 'fade-out', value: 3 }, +]; + +export const reactiveLEDScheme = { + ReactiveLEDAddonEnabled: yup + .number() + .required() + .label('Reactive LED Add-On Enabled'), +}; + +export const reactiveLEDState = { + ReactiveLEDAddonEnabled: 0, +}; + +const isNonSelectable = (value) => + NON_SELECTABLE_BUTTON_ACTIONS.includes(value); + +const getOption = (o, actionId) => { + return { + label: invert(BUTTON_ACTIONS)[actionId], + value: actionId, + }; +}; + +const ReactiveLED = ({ values, errors, handleChange, handleCheckbox }) => { + const { t } = useTranslation(); + const [reactiveLEDs, setSelectedLEDs] = useState([]); + const [selectedLEDs] = useState([]); + const { setLoading, buttonLabels } = useContext(AppContext); + + const { buttonLabelType, swapTpShareLabels } = buttonLabels; + const CURRENT_BUTTONS = getButtonLabels(buttonLabelType, swapTpShareLabels); + const buttonNames = omit(CURRENT_BUTTONS, ['label', 'value']); + + useEffect(() => { + async function fetchData() { + const ledSettings = await WebApi.getReactiveLEDs(setLoading); + setSelectedLEDs(ledSettings.leds); + } + + fetchData(); + }, [setSelectedLEDs, selectedLEDs]); + + const handleLEDChange = (c : any, idx : Number, field : String, value : Number) => { + c[idx][field] = value; + console.dir(c); + return [...c]; + }; + + const saveLEDSettings = async () => { + const ledSettings = await WebApi.setReactiveLEDs({leds: reactiveLEDs}); + }; + + return ( +
+ + { + handleCheckbox('ReactiveLEDAddonEnabled', values); + handleChange(e); + }} + /> +
+ ); +}; + +export default ReactiveLED; diff --git a/www/src/Locales/en/Addons/ReactiveLED.jsx b/www/src/Locales/en/Addons/ReactiveLED.jsx new file mode 100644 index 000000000..d49d7f8b3 --- /dev/null +++ b/www/src/Locales/en/Addons/ReactiveLED.jsx @@ -0,0 +1,15 @@ +export default { + 'header-text': 'Reactive LEDs', + 'button-save': 'Save LEDs', + 'led-panel-label': 'LED #{{index}}', + 'led-pin-label': 'LED Pin', + 'led-action-label': 'Action', + 'led-mode-up-label': 'Default Mode', + 'led-mode-down-label': 'Active Mode', + 'led-mode': { + 'static-on': 'Static On', + 'static-off': 'Static Off', + 'fade-in': 'Fade In', + 'fade-out': 'Fade Out', + }, +}; diff --git a/www/src/Locales/en/Index.jsx b/www/src/Locales/en/Index.jsx index cc394cfb5..743d868a7 100644 --- a/www/src/Locales/en/Index.jsx +++ b/www/src/Locales/en/Index.jsx @@ -15,6 +15,7 @@ import CaptureButton from './CaptureButton'; import WiiAddon from './Addons/WiiAddon'; import Rotary from './Addons/Rotary'; import PCF8575 from './Addons/PCF8575'; +import ReactiveLED from './Addons/ReactiveLED'; import InputMacroAddon from './InputMacroAddon'; import LayoutConfig from './LayoutConfig'; @@ -38,4 +39,5 @@ export default { InputMacroAddon, LayoutConfig, PCF8575, + ReactiveLED, }; diff --git a/www/src/Pages/AddonsConfigPage.jsx b/www/src/Pages/AddonsConfigPage.jsx index 376ab012d..382e4f048 100644 --- a/www/src/Pages/AddonsConfigPage.jsx +++ b/www/src/Pages/AddonsConfigPage.jsx @@ -55,6 +55,7 @@ import DRV8833Rumble, { drv8833RumbleScheme, drv8833RumbleState, } from '../Addons/DRV8833'; +import ReactiveLED, { reactiveLEDScheme, reactiveLEDState } from '../Addons/ReactiveLED'; const schema = yup.object().shape({ ...analogScheme, @@ -77,6 +78,7 @@ const schema = yup.object().shape({ ...rotaryScheme, ...pcf8575Scheme, ...drv8833RumbleScheme, + ...reactiveLEDScheme, }); const defaultValues = { @@ -101,6 +103,7 @@ const defaultValues = { ...rotaryState, ...pcf8575State, ...drv8833RumbleState, + ...reactiveLEDState, }; const ADDONS = [ @@ -125,6 +128,7 @@ const ADDONS = [ Rotary, PCF8575, DRV8833Rumble, + ReactiveLED, ]; const FormContext = ({ setStoredData }) => { diff --git a/www/src/Services/WebApi.js b/www/src/Services/WebApi.js index 57d052d7c..92416d180 100644 --- a/www/src/Services/WebApi.js +++ b/www/src/Services/WebApi.js @@ -565,6 +565,22 @@ async function setWiiControls(mappings) { }); } +async function getReactiveLEDs(setLoading) { + setLoading(true); + try { + const response = await Http.get(`${baseUrl}/api/getReactiveLEDs`); + return response.data; + } catch (error) { + console.error(error); + } +} + +async function setReactiveLEDs(leds) { + console.dir(leds); + + return Http.post(`${baseUrl}/api/setReactiveLEDs`, leds); +} + async function getPeripheralOptions(setLoading) { setLoading(true); try { @@ -710,6 +726,8 @@ export default { setPeripheralOptions, getExpansionPins, setExpansionPins, + getReactiveLEDs, + setReactiveLEDs, getButtonLayouts, getButtonLayoutDefs, getSplashImage,