#include "wled.h" /* * Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS. * The structure of the JSON is not to be considered an official API and may change without notice. */ //simple macro for ArduinoJSON's or syntax #define CJSON(a,b) a = b | a void getStringFromJson(char* dest, const char* src, size_t len) { if (src != nullptr) strlcpy(dest, src, len); } bool deserializeConfig(JsonObject doc, bool fromFS) { //int rev_major = doc["rev"][0]; // 1 //int rev_minor = doc["rev"][1]; // 0 //long vid = doc[F("vid")]; // 2010020 JsonObject id = doc["id"]; getStringFromJson(cmDNS, id[F("mdns")], 33); getStringFromJson(serverDescription, id[F("name")], 33); getStringFromJson(alexaInvocationName, id[F("inv")], 33); JsonObject nw_ins_0 = doc["nw"][F("ins")][0]; getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33); //int nw_ins_0_pskl = nw_ins_0[F("pskl")]; //The WiFi PSK is normally not contained in the regular file for security reasons. //If it is present however, we will use it getStringFromJson(clientPass, nw_ins_0["psk"], 65); JsonArray nw_ins_0_ip = nw_ins_0["ip"]; JsonArray nw_ins_0_gw = nw_ins_0["gw"]; JsonArray nw_ins_0_sn = nw_ins_0["sn"]; for (byte i = 0; i < 4; i++) { CJSON(staticIP[i], nw_ins_0_ip[i]); CJSON(staticGateway[i], nw_ins_0_gw[i]); CJSON(staticSubnet[i], nw_ins_0_sn[i]); } JsonObject ap = doc["ap"]; getStringFromJson(apSSID, ap[F("ssid")], 33); getStringFromJson(apPass, ap["psk"] , 65); //normally not present due to security //int ap_pskl = ap[F("pskl")]; CJSON(apChannel, ap[F("chan")]); if (apChannel > 13 || apChannel < 1) apChannel = 1; CJSON(apHide, ap[F("hide")]); if (apHide > 1) apHide = 1; CJSON(apBehavior, ap[F("behav")]); #ifdef WLED_USE_ETHERNET JsonObject ethernet = doc[F("eth")]; CJSON(ethernetType, ethernet["type"]); #endif /* JsonArray ap_ip = ap["ip"]; for (byte i = 0; i < 4; i++) { apIP[i] = ap_ip; }*/ noWifiSleep = doc[F("wifi")][F("sleep")] | !noWifiSleep; // inverted noWifiSleep = !noWifiSleep; //int wifi_phy = doc[F("wifi")][F("phy")]; //force phy mode n? JsonObject hw = doc[F("hw")]; // initialize LED pins and lengths prior to other HW JsonObject hw_led = hw[F("led")]; CJSON(ledCount, hw_led[F("total")]); if (ledCount > MAX_LEDS) ledCount = MAX_LEDS; CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); CJSON(strip.rgbwMode, hw_led[F("rgbwm")]); JsonArray ins = hw_led["ins"]; if (fromFS || !ins.isNull()) { uint8_t s = 0; //bus iterator strip.isRgbw = false; busses.removeAll(); uint32_t mem = 0; for (JsonObject elm : ins) { if (s >= WLED_MAX_BUSSES) break; uint8_t pins[5] = {255, 255, 255, 255, 255}; JsonArray pinArr = elm[F("pin")]; if (pinArr.size() == 0) continue; pins[0] = pinArr[0]; uint8_t i = 0; for (int p : pinArr) { pins[i] = p; i++; if (i>4) break; } uint16_t length = elm[F("len")]; if (length==0) continue; uint8_t colorOrder = (int)elm[F("order")]; //only use skip from the first strip (this shouldn't have been in ins obj. but remains here for compatibility) if (s==0) skipFirstLed = elm[F("skip")]; uint16_t start = elm[F("start")] | 0; if (start >= ledCount) continue; //limit length of strip if it would exceed total configured LEDs if (start + length > ledCount) length = ledCount - start; uint8_t ledType = elm["type"] | TYPE_WS2812_RGB; bool reversed = elm["rev"]; //RGBW mode is enabled if at least one of the strips is RGBW strip.isRgbw = (strip.isRgbw || BusManager::isRgbw(ledType)); s++; BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed); mem += busses.memUsage(bc); if (mem <= MAX_LED_MEMORY) busses.add(bc); } strip.finalizeInit(ledCount, skipFirstLed); } if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0]; CJSON(buttonType, hw_btn_ins_0["type"]); int hw_btn_pin = hw_btn_ins_0[F("pin")][0] | -2; //-2 = not present in doc, keep current. -1 = disable if (hw_btn_pin > -2) { if (pinManager.allocatePin(hw_btn_pin,false)) { btnPin = hw_btn_pin; pinMode(btnPin, INPUT_PULLUP); } else { btnPin = -1; } } JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")]; CJSON(macroButton, hw_btn_ins_0_macros[0]); CJSON(macroLongPress,hw_btn_ins_0_macros[1]); CJSON(macroDoublePress, hw_btn_ins_0_macros[2]); #ifndef WLED_DISABLE_INFRARED int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 if (hw_ir_pin > -2) { if (pinManager.allocatePin(hw_ir_pin,false)) { irPin = hw_ir_pin; } else { irPin = -1; } } #endif CJSON(irEnabled, hw["ir"]["type"]); JsonObject relay = hw[F("relay")]; int hw_relay_pin = relay["pin"] | -2; if (hw_relay_pin > -2) { if (pinManager.allocatePin(hw_relay_pin,true)) { rlyPin = hw_relay_pin; pinMode(rlyPin, OUTPUT); } else { rlyPin = -1; } } if (relay.containsKey("rev")) { rlyMde = !relay["rev"]; } //int hw_status_pin = hw[F("status")][F("pin")]; // -1 JsonObject light = doc[F("light")]; CJSON(briMultiplier, light[F("scale-bri")]); CJSON(strip.paletteBlend, light[F("pal-mode")]); float light_gc_bri = light[F("gc")]["bri"]; float light_gc_col = light[F("gc")]["col"]; // 2.8 if (light_gc_bri > 1.5) strip.gammaCorrectBri = true; else if (light_gc_bri > 0.5) strip.gammaCorrectBri = false; if (light_gc_col > 1.5) strip.gammaCorrectCol = true; else if (light_gc_col > 0.5) strip.gammaCorrectCol = false; JsonObject light_tr = light[F("tr")]; CJSON(fadeTransition, light_tr[F("mode")]); int tdd = light_tr[F("dur")] | -1; if (tdd >= 0) transitionDelayDefault = tdd * 100; CJSON(strip.paletteFade, light_tr["pal"]); JsonObject light_nl = light["nl"]; CJSON(nightlightMode, light_nl[F("mode")]); byte prev = nightlightDelayMinsDefault; CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]); if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault; CJSON(nightlightTargetBri, light_nl[F("tbri")]); CJSON(macroNl, light_nl[F("macro")]); JsonObject def = doc[F("def")]; CJSON(bootPreset, def[F("ps")]); CJSON(turnOnAtBoot, def["on"]); // true CJSON(briS, def["bri"]); // 128 JsonObject def_cy = def[F("cy")]; CJSON(presetCyclingEnabled, def_cy["on"]); CJSON(presetCycleMin, def_cy[F("range")][0]); CJSON(presetCycleMax, def_cy[F("range")][1]); tdd = def_cy[F("dur")] | -1; if (tdd > 0) presetCycleTime = tdd; JsonObject interfaces = doc["if"]; JsonObject if_sync = interfaces[F("sync")]; CJSON(udpPort, if_sync[F("port0")]); // 21324 CJSON(udpPort2, if_sync[F("port1")]); // 65506 JsonObject if_sync_recv = if_sync["recv"]; CJSON(receiveNotificationBrightness, if_sync_recv["bri"]); CJSON(receiveNotificationColor, if_sync_recv["col"]); CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]); //! following line might be a problem if called after boot receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); JsonObject if_sync_send = if_sync["send"]; prev = notifyDirectDefault; CJSON(notifyDirectDefault, if_sync_send[F("dir")]); if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault; CJSON(notifyButton, if_sync_send[F("btn")]); CJSON(notifyAlexa, if_sync_send[F("va")]); CJSON(notifyHue, if_sync_send[F("hue")]); CJSON(notifyMacro, if_sync_send[F("macro")]); CJSON(notifyTwice, if_sync_send[F("twice")]); JsonObject if_nodes = interfaces["nodes"]; CJSON(nodeListEnabled, if_nodes[F("list")]); CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]); JsonObject if_live = interfaces["live"]; CJSON(receiveDirect, if_live["en"]); CJSON(e131Port, if_live["port"]); // 5568 CJSON(e131Multicast, if_live[F("mc")]); JsonObject if_live_dmx = if_live[F("dmx")]; CJSON(e131Universe, if_live_dmx[F("uni")]); CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]); CJSON(DMXAddress, if_live_dmx[F("addr")]); CJSON(DMXMode, if_live_dmx[F("mode")]); tdd = if_live[F("timeout")] | -1; if (tdd >= 0) realtimeTimeoutMs = tdd * 100; CJSON(arlsForceMaxBri, if_live[F("maxbri")]); CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false CJSON(arlsOffset, if_live[F("offset")]); // 0 CJSON(alexaEnabled, interfaces[F("va")][F("alexa")]); // false CJSON(macroAlexaOn, interfaces[F("va")][F("macros")][0]); CJSON(macroAlexaOff, interfaces[F("va")][F("macros")][1]); const char* apikey = interfaces["blynk"][F("token")] | "Hidden"; tdd = strnlen(apikey, 36); if (tdd > 20 || tdd == 0) getStringFromJson(blynkApiKey, apikey, 36); //normally not present due to security JsonObject if_blynk = interfaces["blynk"]; getStringFromJson(blynkHost, if_blynk[F("host")], 33); CJSON(blynkPort, if_blynk["port"]); JsonObject if_mqtt = interfaces["mqtt"]; CJSON(mqttEnabled, if_mqtt["en"]); getStringFromJson(mqttServer, if_mqtt[F("broker")], 33); CJSON(mqttPort, if_mqtt["port"]); // 1883 getStringFromJson(mqttUser, if_mqtt[F("user")], 41); getStringFromJson(mqttPass, if_mqtt["psk"], 41); //normally not present due to security getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41); getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test" getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // "" JsonObject if_hue = interfaces[F("hue")]; CJSON(huePollingEnabled, if_hue["en"]); CJSON(huePollLightId, if_hue["id"]); tdd = if_hue[F("iv")] | -1; if (tdd >= 2) huePollIntervalMs = tdd * 100; JsonObject if_hue_recv = if_hue["recv"]; CJSON(hueApplyOnOff, if_hue_recv["on"]); CJSON(hueApplyBri, if_hue_recv["bri"]); CJSON(hueApplyColor, if_hue_recv["col"]); JsonArray if_hue_ip = if_hue["ip"]; for (byte i = 0; i < 4; i++) CJSON(hueIP[i], if_hue_ip[i]); JsonObject if_ntp = interfaces[F("ntp")]; CJSON(ntpEnabled, if_ntp["en"]); getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org" CJSON(currentTimezone, if_ntp[F("tz")]); CJSON(utcOffsetSecs, if_ntp[F("offset")]); CJSON(useAMPM, if_ntp[F("ampm")]); CJSON(longitude, if_ntp[F("ln")]); CJSON(latitude, if_ntp[F("lt")]); JsonObject ol = doc[F("ol")]; prev = overlayDefault; CJSON(overlayDefault ,ol[F("clock")]); // 0 CJSON(countdownMode, ol[F("cntdwn")]); if (prev != overlayDefault) overlayCurrent = overlayDefault; CJSON(overlayMin, ol[F("min")]); CJSON(overlayMax, ol[F("max")]); CJSON(analogClock12pixel, ol[F("o12pix")]); CJSON(analogClock5MinuteMarks, ol[F("o5m")]); CJSON(analogClockSecondsTrail, ol[F("osec")]); //timed macro rules JsonObject tm = doc[F("timers")]; JsonObject cntdwn = tm[F("cntdwn")]; JsonArray cntdwn_goal = cntdwn[F("goal")]; CJSON(countdownYear, cntdwn_goal[0]); CJSON(countdownMonth, cntdwn_goal[1]); CJSON(countdownDay, cntdwn_goal[2]); CJSON(countdownHour, cntdwn_goal[3]); CJSON(countdownMin, cntdwn_goal[4]); CJSON(countdownSec, cntdwn_goal[5]); CJSON(macroCountdown, cntdwn[F("macro")]); setCountdown(); JsonArray timers = tm[F("ins")]; uint8_t it = 0; for (JsonObject timer : timers) { if (it > 9) break; if (it<8 && timer[F("hour")]==255) it=8; // hour==255 -> sunrise/sunset CJSON(timerHours[it], timer[F("hour")]); CJSON(timerMinutes[it], timer[F("min")]); CJSON(timerMacro[it], timer[F("macro")]); byte dowPrev = timerWeekday[it]; //note: act is currently only 0 or 1. //the reason we are not using bool is that the on-disk type in 0.11.0 was already int int actPrev = timerWeekday[it] & 0x01; CJSON(timerWeekday[it], timer[F("dow")]); if (timerWeekday[it] != dowPrev) { //present in JSON timerWeekday[it] <<= 1; //add active bit int act = timer["en"] | actPrev; if (act) timerWeekday[it]++; } it++; } JsonObject ota = doc["ota"]; const char* pwd = ota["psk"]; //normally not present due to security bool pwdCorrect = !otaLock; //always allow access if ota not locked if (pwd != nullptr && strncmp(otaPass, pwd, 33) == 0) pwdCorrect = true; if (pwdCorrect) { //only accept these values from cfg.json if ota is unlocked (else from wsec.json) CJSON(otaLock, ota[F("lock")]); CJSON(wifiLock, ota[F("lock-wifi")]); CJSON(aOtaEnabled, ota[F("aota")]); getStringFromJson(otaPass, pwd, 33); //normally not present due to security } #ifdef WLED_ENABLE_DMX JsonObject dmx = doc["dmx"]; CJSON(DMXChannels, dmx[F("chan")]); CJSON(DMXGap,dmx[F("gap")]); CJSON(DMXStart, dmx[F("start")]); CJSON(DMXStartLED,dmx[F("start-led")]); JsonArray dmx_fixmap = dmx[F("fixmap")]; it = 0; for (int i : dmx_fixmap) { if (it > 14) break; CJSON(DMXFixtureMap[i],dmx_fixmap[i]); it++; } #endif JsonObject usermods_settings = doc["um"]; if (!usermods_settings.isNull()) usermods.readFromConfig(usermods_settings); if (fromFS) return false; doReboot = doc[F("rb")] | doReboot; return (doc["sv"] | true); } void deserializeConfigFromFS() { bool fromeep = false; bool success = deserializeConfigSec(); if (!success) { //if file does not exist, try reading from EEPROM deEEPSettings(); fromeep = true; } DynamicJsonDocument doc(JSON_BUFFER_SIZE); DEBUG_PRINTLN(F("Reading settings from /cfg.json...")); success = readObjectFromFile("/cfg.json", nullptr, &doc); if (!success) { //if file does not exist, try reading from EEPROM if (!fromeep) deEEPSettings(); return; } deserializeConfig(doc.as(), true); } void serializeConfig() { serializeConfigSec(); DEBUG_PRINTLN(F("Writing settings to /cfg.json...")); DynamicJsonDocument doc(JSON_BUFFER_SIZE); JsonArray rev = doc.createNestedArray("rev"); rev.add(1); //major settings revision rev.add(0); //minor settings revision doc[F("vid")] = VERSION; JsonObject id = doc.createNestedObject("id"); id[F("mdns")] = cmDNS; id[F("name")] = serverDescription; id[F("inv")] = alexaInvocationName; JsonObject nw = doc.createNestedObject("nw"); JsonArray nw_ins = nw.createNestedArray("ins"); JsonObject nw_ins_0 = nw_ins.createNestedObject(); nw_ins_0[F("ssid")] = clientSSID; nw_ins_0[F("pskl")] = strlen(clientPass); JsonArray nw_ins_0_ip = nw_ins_0.createNestedArray("ip"); JsonArray nw_ins_0_gw = nw_ins_0.createNestedArray("gw"); JsonArray nw_ins_0_sn = nw_ins_0.createNestedArray("sn"); for (byte i = 0; i < 4; i++) { nw_ins_0_ip.add(staticIP[i]); nw_ins_0_gw.add(staticGateway[i]); nw_ins_0_sn.add(staticSubnet[i]); } JsonObject ap = doc.createNestedObject("ap"); ap[F("ssid")] = apSSID; ap[F("pskl")] = strlen(apPass); ap[F("chan")] = apChannel; ap[F("hide")] = apHide; ap[F("behav")] = apBehavior; JsonArray ap_ip = ap.createNestedArray("ip"); ap_ip.add(4); ap_ip.add(3); ap_ip.add(2); ap_ip.add(1); JsonObject wifi = doc.createNestedObject("wifi"); wifi[F("sleep")] = !noWifiSleep; wifi[F("phy")] = 1; #ifdef WLED_USE_ETHERNET JsonObject ethernet = doc.createNestedObject("eth"); ethernet["type"] = ethernetType; #endif JsonObject hw = doc.createNestedObject("hw"); JsonObject hw_led = hw.createNestedObject("led"); hw_led[F("total")] = ledCount; hw_led[F("maxpwr")] = strip.ablMilliampsMax; hw_led[F("ledma")] = strip.milliampsPerLed; hw_led[F("rgbwm")] = strip.rgbwMode; JsonArray hw_led_ins = hw_led.createNestedArray("ins"); for (uint8_t s = 0; s < busses.getNumBusses(); s++) { Bus *bus = busses.getBus(s); if (!bus || bus->getLength()==0) break; JsonObject ins = hw_led_ins.createNestedObject(); ins[F("start")] = bus->getStart(); ins[F("len")] = bus->getLength(); JsonArray ins_pin = ins.createNestedArray("pin"); uint8_t pins[5]; uint8_t nPins = bus->getPins(pins); for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]); ins[F("order")] = bus->getColorOrder(); ins["rev"] = bus->reversed; ins[F("skip")] = (skipFirstLed && s == 0) ? 1 : 0; ins["type"] = bus->getType(); } JsonObject hw_btn = hw.createNestedObject("btn"); JsonArray hw_btn_ins = hw_btn.createNestedArray("ins"); // button BTNPIN JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject(); hw_btn_ins_0["type"] = buttonType; JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin"); hw_btn_ins_0_pin.add(btnPin); JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros"); hw_btn_ins_0_macros.add(macroButton); hw_btn_ins_0_macros.add(macroLongPress); hw_btn_ins_0_macros.add(macroDoublePress); #ifndef WLED_DISABLE_INFRARED JsonObject hw_ir = hw.createNestedObject("ir"); hw_ir["pin"] = irPin; hw_ir[F("type")] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled ) #endif JsonObject hw_relay = hw.createNestedObject(F("relay")); hw_relay["pin"] = rlyPin; hw_relay["rev"] = !rlyMde; //JsonObject hw_status = hw.createNestedObject("status"); //hw_status["pin"] = -1; JsonObject light = doc.createNestedObject(F("light")); light[F("scale-bri")] = briMultiplier; light[F("pal-mode")] = strip.paletteBlend; JsonObject light_gc = light.createNestedObject("gc"); light_gc["bri"] = (strip.gammaCorrectBri) ? 2.8 : 1.0; light_gc["col"] = (strip.gammaCorrectCol) ? 2.8 : 1.0; JsonObject light_tr = light.createNestedObject("tr"); light_tr[F("mode")] = fadeTransition; light_tr[F("dur")] = transitionDelayDefault / 100; light_tr["pal"] = strip.paletteFade; JsonObject light_nl = light.createNestedObject("nl"); light_nl[F("mode")] = nightlightMode; light_nl[F("dur")] = nightlightDelayMinsDefault; light_nl[F("tbri")] = nightlightTargetBri; light_nl[F("macro")] = macroNl; JsonObject def = doc.createNestedObject("def"); def[F("ps")] = bootPreset; def["on"] = turnOnAtBoot; def["bri"] = briS; //to be removed once preset cycles are presets if (saveCurrPresetCycConf) { JsonObject def_cy = def.createNestedObject("cy"); def_cy["on"] = presetCyclingEnabled; JsonArray def_cy_range = def_cy.createNestedArray(F("range")); def_cy_range.add(presetCycleMin); def_cy_range.add(presetCycleMax); def_cy[F("dur")] = presetCycleTime; } JsonObject interfaces = doc.createNestedObject("if"); JsonObject if_sync = interfaces.createNestedObject("sync"); if_sync[F("port0")] = udpPort; if_sync[F("port1")] = udpPort2; JsonObject if_sync_recv = if_sync.createNestedObject("recv"); if_sync_recv["bri"] = receiveNotificationBrightness; if_sync_recv["col"] = receiveNotificationColor; if_sync_recv[F("fx")] = receiveNotificationEffects; JsonObject if_sync_send = if_sync.createNestedObject("send"); if_sync_send[F("dir")] = notifyDirect; if_sync_send[F("btn")] = notifyButton; if_sync_send[F("va")] = notifyAlexa; if_sync_send[F("hue")] = notifyHue; if_sync_send[F("macro")] = notifyMacro; if_sync_send[F("twice")] = notifyTwice; JsonObject if_nodes = interfaces.createNestedObject("nodes"); if_nodes[F("list")] = nodeListEnabled; if_nodes[F("bcast")] = nodeBroadcastEnabled; JsonObject if_live = interfaces.createNestedObject("live"); if_live["en"] = receiveDirect; if_live["port"] = e131Port; if_live[F("mc")] = e131Multicast; JsonObject if_live_dmx = if_live.createNestedObject("dmx"); if_live_dmx[F("uni")] = e131Universe; if_live_dmx[F("seqskip")] = e131SkipOutOfSequence; if_live_dmx[F("addr")] = DMXAddress; if_live_dmx[F("mode")] = DMXMode; if_live[F("timeout")] = realtimeTimeoutMs / 100; if_live[F("maxbri")] = arlsForceMaxBri; if_live[F("no-gc")] = arlsDisableGammaCorrection; if_live[F("offset")] = arlsOffset; JsonObject if_va = interfaces.createNestedObject("va"); if_va[F("alexa")] = alexaEnabled; JsonArray if_va_macros = if_va.createNestedArray("macros"); if_va_macros.add(macroAlexaOn); if_va_macros.add(macroAlexaOff); JsonObject if_blynk = interfaces.createNestedObject("blynk"); if_blynk[F("token")] = strlen(blynkApiKey) ? "Hidden":""; if_blynk[F("host")] = blynkHost; if_blynk["port"] = blynkPort; JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); if_mqtt["en"] = mqttEnabled; if_mqtt[F("broker")] = mqttServer; if_mqtt["port"] = mqttPort; if_mqtt[F("user")] = mqttUser; if_mqtt[F("pskl")] = strlen(mqttPass); if_mqtt[F("cid")] = mqttClientID; JsonObject if_mqtt_topics = if_mqtt.createNestedObject(F("topics")); if_mqtt_topics[F("device")] = mqttDeviceTopic; if_mqtt_topics[F("group")] = mqttGroupTopic; JsonObject if_hue = interfaces.createNestedObject("hue"); if_hue["en"] = huePollingEnabled; if_hue["id"] = huePollLightId; if_hue[F("iv")] = huePollIntervalMs / 100; JsonObject if_hue_recv = if_hue.createNestedObject("recv"); if_hue_recv["on"] = hueApplyOnOff; if_hue_recv["bri"] = hueApplyBri; if_hue_recv["col"] = hueApplyColor; JsonArray if_hue_ip = if_hue.createNestedArray("ip"); for (byte i = 0; i < 4; i++) { if_hue_ip.add(hueIP[i]); } JsonObject if_ntp = interfaces.createNestedObject("ntp"); if_ntp["en"] = ntpEnabled; if_ntp[F("host")] = ntpServerName; if_ntp[F("tz")] = currentTimezone; if_ntp[F("offset")] = utcOffsetSecs; if_ntp[F("ampm")] = useAMPM; if_ntp[F("ln")] = longitude; if_ntp[F("lt")] = latitude; JsonObject ol = doc.createNestedObject("ol"); ol[F("clock")] = overlayDefault; ol[F("cntdwn")] = countdownMode; ol[F("min")] = overlayMin; ol[F("max")] = overlayMax; ol[F("o12pix")] = analogClock12pixel; ol[F("o5m")] = analogClock5MinuteMarks; ol[F("osec")] = analogClockSecondsTrail; JsonObject timers = doc.createNestedObject(F("timers")); JsonObject cntdwn = timers.createNestedObject(F("cntdwn")); JsonArray goal = cntdwn.createNestedArray(F("goal")); goal.add(countdownYear); goal.add(countdownMonth); goal.add(countdownDay); goal.add(countdownHour); goal.add(countdownMin); goal.add(countdownSec); cntdwn[F("macro")] = macroCountdown; JsonArray timers_ins = timers.createNestedArray("ins"); for (byte i = 0; i < 10; i++) { if (timerMacro[i] == 0 && timerHours[i] == 0 && timerMinutes[i] == 0) continue; // sunrise/sunset get saved always (timerHours=255) JsonObject timers_ins0 = timers_ins.createNestedObject(); timers_ins0["en"] = (timerWeekday[i] & 0x01); timers_ins0[F("hour")] = timerHours[i]; timers_ins0[F("min")] = timerMinutes[i]; timers_ins0[F("macro")] = timerMacro[i]; timers_ins0[F("dow")] = timerWeekday[i] >> 1; } JsonObject ota = doc.createNestedObject("ota"); ota[F("lock")] = otaLock; ota[F("lock-wifi")] = wifiLock; ota[F("pskl")] = strlen(otaPass); ota[F("aota")] = aOtaEnabled; #ifdef WLED_ENABLE_DMX JsonObject dmx = doc.createNestedObject("dmx"); dmx[F("chan")] = DMXChannels; dmx[F("gap")] = DMXGap; dmx[F("start")] = DMXStart; dmx[F("start-led")] = DMXStartLED; JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap")); for (byte i = 0; i < 15; i++) dmx_fixmap.add(DMXFixtureMap[i]); #endif //} JsonObject usermods_settings = doc.createNestedObject("um"); usermods.addToConfig(usermods_settings); File f = WLED_FS.open("/cfg.json", "w"); if (f) serializeJson(doc, f); f.close(); } //settings in /wsec.json, not accessible via webserver, for passwords and tokens bool deserializeConfigSec() { DEBUG_PRINTLN(F("Reading settings from /wsec.json...")); DynamicJsonDocument doc(JSON_BUFFER_SIZE); bool success = readObjectFromFile("/wsec.json", nullptr, &doc); if (!success) return false; JsonObject nw_ins_0 = doc["nw"][F("ins")][0]; getStringFromJson(clientPass, nw_ins_0["psk"], 65); JsonObject ap = doc["ap"]; getStringFromJson(apPass, ap["psk"] , 65); JsonObject interfaces = doc["if"]; const char* apikey = interfaces["blynk"][F("token")] | "Hidden"; int tdd = strnlen(apikey, 36); if (tdd > 20 || tdd == 0) getStringFromJson(blynkApiKey, apikey, 36); JsonObject if_mqtt = interfaces["mqtt"]; getStringFromJson(mqttPass, if_mqtt["psk"], 41); getStringFromJson(hueApiKey, interfaces[F("hue")][F("key")], 47); JsonObject ota = doc["ota"]; getStringFromJson(otaPass, ota[F("pwd")], 33); CJSON(otaLock, ota[F("lock")]); CJSON(wifiLock, ota[F("lock-wifi")]); CJSON(aOtaEnabled, ota[F("aota")]); return true; } void serializeConfigSec() { DEBUG_PRINTLN(F("Writing settings to /wsec.json...")); DynamicJsonDocument doc(JSON_BUFFER_SIZE); JsonObject nw = doc.createNestedObject("nw"); JsonArray nw_ins = nw.createNestedArray("ins"); JsonObject nw_ins_0 = nw_ins.createNestedObject(); nw_ins_0["psk"] = clientPass; JsonObject ap = doc.createNestedObject("ap"); ap["psk"] = apPass; JsonObject interfaces = doc.createNestedObject("if"); JsonObject if_blynk = interfaces.createNestedObject("blynk"); if_blynk[F("token")] = blynkApiKey; JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); if_mqtt["psk"] = mqttPass; JsonObject if_hue = interfaces.createNestedObject("hue"); if_hue[F("key")] = hueApiKey; JsonObject ota = doc.createNestedObject("ota"); ota[F("pwd")] = otaPass; ota[F("lock")] = otaLock; ota[F("lock-wifi")] = wifiLock; ota[F("aota")] = aOtaEnabled; File f = WLED_FS.open("/wsec.json", "w"); if (f) serializeJson(doc, f); f.close(); }