Finished JSON API

Added RV http api call
Fixed CY,PA,PC,PX api calls
Fixed CORS
This commit is contained in:
cschwinne 2019-03-06 21:31:12 +01:00
parent 66c224c954
commit 709ff7a701
16 changed files with 5608 additions and 1933 deletions

View File

@ -30,7 +30,7 @@ lib_deps_external =
ESPAsyncTCP@1.2.0
AsyncTCP@1.0.3
EspAsyncWebServer@1.2.0
ArduinoJson@5.13.5
#ArduinoJson@5.13.5
IRremoteESP8266@2.5.5
#Time@1.5
#Timezone@1.2.1

View File

@ -347,6 +347,7 @@ uint32_t WS2812FX::getColor(void) {
uint32_t WS2812FX::getPixelColor(uint16_t i)
{
if (_reverseMode) i = _length- 1 -i;
if (IS_REVERSE) i = SEGMENT.stop -1 -i - SEGMENT.start; //reverse just individual segment
if (_skipFirstMode) i += LED_SKIP_AMOUNT;
if (_cronixieMode)
{

View File

@ -2,7 +2,7 @@
<html>
<head><meta charset="utf-8"><meta name="theme-color" content="#fff">
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico'/>
<title>WLED 0.8.3</title>
<title>WLED 0.8.4</title>
<script>
var d=document;
var w=window.getComputedStyle(d.querySelector("html"));
@ -43,8 +43,8 @@
CV(0);
setInterval(GIO, 5000);
GIO();
setTimeout(function(){fillfx(0);}, 500);
setTimeout(function(){fillfx(1);}, 750);
setTimeout(function(){fillfx(0);}, 200);
setTimeout(function(){fillfx(1);}, 500);
}
function GIO()
{
@ -369,7 +369,7 @@
{
e="<option>Error loading list!</option>";
el = fp?Cf.FP:Cf.TX;
fetch(fp?'/json/palettes':'/json/effects')
fetch(fp?'/json/pal':'/json/eff')
.then(res => {
if (!res.ok) {
el.innerHTML=e;
@ -378,13 +378,12 @@
})
.then(json => {
var x="";
var l=fp?json.palettes:json.effects;
for (i in l) {
x += "<option value=\""+i+"\">"+l[i]+" ("+i+")</option>";
for (i in json) {
x += "<option value=\""+i+"\">"+json[i]+" ("+i+")</option>";
}
el.innerHTML=x;
el.selectedIndex=fp?fpi:fxi;
if(!fp)fxn=l.length;
if(!fp)fxn=json.length;
UV();
})
.catch(function () {

File diff suppressed because one or more lines are too long

100
wled00/data/jsontest.htm Normal file
View File

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<title>JSON client</title>
<style>
:root {
--bCol:#333;--cCol:#222;--dCol:#666;--tCol:#fff;
}
body {
font-family: Verdana, sans-serif;
text-align: center;
background: var(--cCol);
color: var(--tCol);
margin: 20px;
background-attachment: fixed;
}
button {
background: var(--cCol);
color: var(--tCol);
border: 0.3ch solid var(--cCol);
display: inline-block;
font-size: 20px;
margin: 8px;
margin-top: 12px;
}
input {
background: var(--cCol);
color: var(--tCol);
border: 0.5ch solid var(--cCol);
width: 100%;
}
h1{
margin: 0px;
font-size: 20px;
}
h2{
font-size: 16px;
margin-top: 20px;
}
form{
background: var(--bCol);
width: 500px;
padding: 20px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
display: inline-block;
}
textarea{
background: var(--cCol);
color: var(--tCol);
padding-top: 10px;
width: 100%;
font-family: monaco,monospace;
font-size: 12px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
}
</style>
</head>
<body>
<form name="cf">
<h1>JSON API test tool</h1>
<h2>URL:</h2>
<input name="cu" type="text" size="60" value="http://192.168.4.1/json">
<div id="buttons">
<button type="button" onclick="rq('GET')">GET</button>
<button type="button" onclick="rq('POST')">POST</button>
</div>
<h2>Body:</h2>
<textarea name="bd" rows="8" cols="100"></textarea>
<h2>Response:</h2>
<textarea name="rsp" rows="25" cols="100"></textarea>
</form>
</body>
</html>
<script>
function rq(cm)
{
var h = new XMLHttpRequest();
h.open(cm, document.cf.cu.value, true);
h.onreadystatechange = function()
{
if(h.readyState == 4)
{
if(h.status==200)
{
document.cf.rsp.value="Bad JSON: "+h.responseText
document.cf.rsp.value=JSON.stringify(JSON.parse(h.responseText), null, '\t');
}
else
{
document.cf.rsp.value="Error "+h.status+"\r\n\n"+h.responseText;
}
}
}
h.send(document.cf.bd.value);
}
</script>

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -72,8 +72,8 @@ const char PAGE_settings_leds[] PROGMEM = R"=====(<!DOCTYPE html>
function B(){window.open("/settings","_self");}function S(){GetV();setTimeout(function(){fillfx(0);},200);setTimeout(function(){fillfx(1);},400);UI();}
function UI(){var myC=document.querySelectorAll('.wc'),l=myC.length;for(i=0;i<l;i++){myC[i].style.display=(document.getElementById('rgbw').checked)?'inline':'none';}
var val=Math.ceil((100+document.Sf.LC.value*55)/500)/2;val=(val>5)?Math.ceil(val):val;var s="";if(val<1.02){s="ESP 5V pin with 1A USB supply";}else{s="External 5V ";s+=val;s+="A supply connected to LEDs";}document.getElementById('psu').innerHTML=s;}
function fillfx(pl){e="<option>Error loading list!</option>";el=pl?Sf.FP:Sf.FX;fetch(pl?'/json/palettes':'/json/effects').then(res=>{if(!res.ok){el.innerHTML=e;}
return res.json();}).then(json=>{var x="";var l=pl?json.palettes:json.effects;for(i in l){x+="<option value=\""+i+"\">"+l[i]+" ("+i+")</option>";}
function fillfx(pl){e="<option>Error loading list!</option>";el=pl?Sf.FP:Sf.FX;fetch(pl?'/json/pal':'/json/eff').then(res=>{if(!res.ok){el.innerHTML=e;}
return res.json();}).then(json=>{var x="";for(i in json){x+="<option value=\""+i+"\">"+json[i]+" ("+i+")</option>";}
el.innerHTML=x;el.selectedIndex=pl?p:f;}).catch(function(){el.innerHTML=e;})}function GetV(){var d=document;%CSS%%SCSS%</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post">

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
// AsyncJson.h
/*
Original file at: https://github.com/me-no-dev/ESPAsyncWebServer/blob/master/src/AsyncJson.h
Only changes are ArduinoJson lib path and removed content-type check
Async Response to use with ArduinoJson and AsyncWebServer
Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
--------------------
Async Request to use with ArduinoJson and AsyncWebServer
Written by Arsène von Wyss (avonwyss)
*/
#ifndef ASYNC_JSON_H_
#define ASYNC_JSON_H_
#include "ArduinoJson-v5.h"
constexpr char* JSON_MIMETYPE = "application/json";
/*
* Json Response
* */
class ChunkPrint : public Print {
private:
uint8_t* _destination;
size_t _to_skip;
size_t _to_write;
size_t _pos;
public:
ChunkPrint(uint8_t* destination, size_t from, size_t len)
: _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
virtual ~ChunkPrint(){}
size_t write(uint8_t c){
if (_to_skip > 0) {
_to_skip--;
return 1;
} else if (_to_write > 0) {
_to_write--;
_destination[_pos++] = c;
return 1;
}
return 0;
}
};
class AsyncJsonResponse: public AsyncAbstractResponse {
private:
DynamicJsonBuffer _jsonBuffer;
JsonVariant _root;
bool _isValid;
public:
AsyncJsonResponse(bool isArray=false): _isValid{false} {
_code = 200;
_contentType = JSON_MIMETYPE;
if(isArray)
_root = _jsonBuffer.createArray();
else
_root = _jsonBuffer.createObject();
}
~AsyncJsonResponse() {}
JsonVariant & getRoot() { return _root; }
bool _sourceValid() const { return _isValid; }
size_t setLength() {
_contentLength = _root.measureLength();
if (_contentLength) { _isValid = true; }
return _contentLength;
}
size_t getSize() { return _jsonBuffer.size(); }
size_t _fillBuffer(uint8_t *data, size_t len){
ChunkPrint dest(data, _sentLength, len);
_root.printTo( dest ) ;
return len;
}
};
typedef std::function<void(AsyncWebServerRequest *request, JsonVariant &json)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
private:
protected:
const String _uri;
WebRequestMethodComposite _method;
ArJsonRequestHandlerFunction _onRequest;
int _contentLength;
int _maxContentLength;
public:
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
void setMethod(WebRequestMethodComposite method){ _method = method; }
void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
virtual bool canHandle(AsyncWebServerRequest *request) override final{
if(!_onRequest)
return false;
if(!(_method & request->method()))
return false;
if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
return false;
request->addInterestingHeader("ANY");
return true;
}
virtual void handleRequest(AsyncWebServerRequest *request) override final {
if(_onRequest) {
if (request->_tempObject != NULL) {
DynamicJsonBuffer jsonBuffer;
JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
if (json.success()) {
_onRequest(request, json);
return;
}
}
request->send(_contentLength > _maxContentLength ? 413 : 400, "{\"error\":\"Empty body\"}");
} else {
request->send(500);
}
}
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
}
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
if (_onRequest) {
_contentLength = total;
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
request->_tempObject = malloc(total);
}
if (request->_tempObject != NULL) {
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
}
}
}
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
};
#endif

View File

@ -40,8 +40,6 @@
#endif
#include <ESPAsyncWebServer.h>
#include <AsyncJson.h>
#include <ArduinoJson.h> //please use v5.13.x!
#include <EEPROM.h>
#include <WiFiUdp.h>
#include <DNSServer.h>
@ -62,6 +60,8 @@
#endif
#include "src/dependencies/e131/E131.h"
#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h"
#include "src/dependencies/json/AsyncJson.h"
#include "src/dependencies/json/ArduinoJson-v5.h"
#include "html_classic.h"
#include "html_mobile.h"
#include "html_settings.h"
@ -89,7 +89,7 @@
//version code in format yymmddb (b = daily build)
#define VERSION 1903055
#define VERSION 1903062
char versionString[] = "0.8.4-dev";

View File

@ -588,8 +588,8 @@ bool handleSet(AsyncWebServerRequest *request, String req)
}
//Segment reverse
//pos = req.indexOf("SR=");
//if (pos > 0) strip.getSegment().reverse = (req.charAt(pos+3) != '0');
pos = req.indexOf("RV=");
if (pos > 0) strip.getSegment(0).setOption(1, req.charAt(pos+3) != '0');
//deactivate nightlight if target brightness is reached
if (bri == nightlightTargetBri) nightlightActive = false;
@ -617,7 +617,7 @@ bool handleSet(AsyncWebServerRequest *request, String req)
pos = req.indexOf("CY=");
if (pos > 0)
{
presetCyclingEnabled = req.charAt(pos+3 != '0');
presetCyclingEnabled = (req.charAt(pos+3) != '0');
presetCycCurr = presetCycleMin;
}
@ -628,13 +628,13 @@ bool handleSet(AsyncWebServerRequest *request, String req)
}
pos = req.indexOf("PA="); //apply brightness from preset
if (pos > 0) presetApplyBri = req.charAt(pos+3 != '0');
if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0');
pos = req.indexOf("PC="); //apply color from preset
if (pos > 0) presetApplyCol = req.charAt(pos+3 != '0');
if (pos > 0) presetApplyCol = (req.charAt(pos+3) != '0');
pos = req.indexOf("PX="); //apply effects from preset
if (pos > 0) presetApplyFx = req.charAt(pos+3 != '0');
if (pos > 0) presetApplyFx = (req.charAt(pos+3) != '0');
pos = req.indexOf("PS="); //saves current in preset
if (pos > 0) savePreset(getNumVal(&req, pos));

View File

@ -6,6 +6,8 @@ void initServer()
{
//CORS compatiblity
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "*");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "*");
//settings page
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){

View File

@ -4,18 +4,23 @@
void deserializeState(JsonObject& root)
{
bri = root["bri"] | briLast;
bool on = root["on"] | (bri > 0);
if (!on != !bri) toggleOnOff();
briLast = root["bri"] | briLast;
if (bri > 0) bri = briLast;
if (root.containsKey("transition"))
{
transitionDelay = root["transition"];
transitionDelay *= 100;
}
int ps = root["ps"] | -1;
if (ps >= 0) applyPreset(ps);
int cy = root["pl"] | -1;
presetCyclingEnabled = (cy >= 0);
JsonObject& nl = root["nl"];
nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
@ -92,6 +97,9 @@ void serializeState(JsonObject& root)
root["bri"] = briLast;
root["transition"] = transitionDelay/100; //in 100ms
root["ps"] = -1; //
root["pl"] = (presetCyclingEnabled) ? 0: -1;
JsonObject& nl = root.createNestedObject("nl");
nl["on"] = nightlightActive;
nl["dur"] = nightlightDelayMins;
@ -111,14 +119,20 @@ void serializeSegment(JsonObject& root)
{
WS2812FX::Segment seg = strip.getSegment(0);
//root["i"] = i;
//root["id"] = i;
root["start"] = seg.start;
root["stop"] = seg.stop;
root["len"] = seg.stop - seg.start;
JsonArray& colarr = root.createNestedArray("col");
for (uint8_t i = 0; i < 3; i++)
//temporary
JsonArray& c0 = colarr.createNestedArray();
c0.add(col[0]); c0.add(col[1]); c0.add(col[2]); if (useRGBW) c0.add(col[3]);
JsonArray& c1 = colarr.createNestedArray();
c1.add(colSec[0]); c1.add(colSec[1]); c1.add(colSec[2]); if (useRGBW) c1.add(colSec[3]);
//set i back to 0 once temporary is removed!
for (uint8_t i = 2; i < 3; i++)
{
JsonArray& colX = colarr.createNestedArray();
colX.add((seg.colors[i] >> 16) & 0xFF);
@ -217,7 +231,7 @@ void serveJson(AsyncWebServerRequest* request)
return;
}
else if (url.length() > 6) { //not just /json
request->send( 500, "application/json", "{\"error\":\"Not implemented\"}");
request->send( 501, "application/json", "{\"error\":\"Not implemented\"}");
return;
}