diff --git a/README.md b/README.md index 2dae1fa..9912435 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ To install the library, simply clone this repository in the /libraries folder of ## API documentation -The API currently supports five type of commands: digital, analog, and mode, variables, and user-defined functions. +The API currently supports five type of commands: digital, analog, and mode, variables, and user-defined functions and api-extensions. ### Digital @@ -189,6 +189,24 @@ You can also define your own functions in your sketch that can be called using t * `rest.function("led",ledControl);` declares the function in the Arduino sketch * `/led?params=0` executes the function +### API-Extensions + +With api-extensions you have the possibility to extend the api by your own subcommands and customized responses. + +You define your api extensions in your sketch that can be called using the REST API. To access an user-defined api-extension defined in your sketch, you have to declare it first, and then call it from with a REST call. Note that all api-extension functions need to have the following signature `void api_extension(aREST *arest, const String& name, const String& request_url)`. For example, if your aREST instance is called "rest" and the function "aquariumController": + * `rest.api_extension("aquarium",aquariumController);` declares the api extension in the Arduino sketch + * `/aquarium/water_limit/lower/set/65` executes the api-extension function and passes the value `"/aquarium/water_limit/lower/set/65"` as the third parameter (`request_url`) into the api-extension function + * You can then customize your JSON result and extend it to something like this: + ``` + { + "sensor-ids": ["100", "101", "102", "103", "104"], + "id": "008", + "name": "dapper_drake", + "hardware": "arduino", + "connected": true + } + ``` + ### Log data to the cloud You can also directly tell your board to log data on our cloud server, to be stored there & retrieved later or displayed on the [aREST cloud dashboard](https://dashboard.arest.io/). This is useful when you want for example to record the data coming from a sensor at regular intervals. The data is then stored along with the current date, the ID of the device sending the data, and also an event name that is used to identifiy the data. This can be done via the following commands: diff --git a/aREST.h b/aREST.h index 904349b..c4a9056 100644 --- a/aREST.h +++ b/aREST.h @@ -135,6 +135,8 @@ #define LIGHTWEIGHT 0 #endif + +// -------- for backwards compatibility -------- #ifdef AREST_NUMBER_VARIABLES #define NUMBER_VARIABLES AREST_NUMBER_VARIABLES #endif @@ -143,21 +145,36 @@ #define NUMBER_FUNCTIONS AREST_NUMBER_FUNCTIONS #endif -// Default number of max. exposed variables -#ifndef NUMBER_VARIABLES +#if defined(NUMBER_VARIABLES) && defined(NUMBER_FUNCTIONS) + #define AREST_NUMBER_HANDLERS (NUMBER_VARIABLES + NUMBER_FUNCTIONS) +#elif defined(NUMBER_VARIABLES) + #if defined(__AVR_ATmega1280__) || defined(ESP32) || defined(__AVR_ATmega2560__) || defined(CORE_WILDFIRE) || defined(ESP8266) + #define NUMBER_FUNCTIONS 10 + #else + #define NUMBER_FUNCTIONS 5 + #endif + #define AREST_NUMBER_HANDLERS (NUMBER_VARIABLES + NUMBER_FUNCTIONS) +#elif defined(NUMBER_FUNCTIONS) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(CORE_WILDFIRE) || defined(ESP8266)|| defined(ESP32) || !defined(ADAFRUIT_CC3000_H) #define NUMBER_VARIABLES 10 #else #define NUMBER_VARIABLES 5 #endif + #define AREST_NUMBER_HANDLERS (NUMBER_VARIABLES + NUMBER_FUNCTIONS) #endif +// -------- end -------- -// Default number of max. exposed functions -#ifndef NUMBER_FUNCTIONS - #if defined(__AVR_ATmega1280__) || defined(ESP32) || defined(__AVR_ATmega2560__) || defined(CORE_WILDFIRE) || defined(ESP8266) - #define NUMBER_FUNCTIONS 10 + +#ifdef AREST_NUMBER_HANDLERS +#define NUMBER_HANDLERS AREST_NUMBER_HANDLERS +#endif + +// Default number of max. exposed handlers +#ifndef NUMBER_HANDLERS + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(CORE_WILDFIRE) || defined(ESP8266)|| defined(ESP32) || !defined(ADAFRUIT_CC3000_H) + #define NUMBER_HANDLERS 20 #else - #define NUMBER_FUNCTIONS 5 + #define NUMBER_HANDLERS 10 #endif #endif @@ -170,8 +187,30 @@ class aREST { private: -struct Variable { +struct Handler { + bool include_into_root_answer; + + Handler() : include_into_root_answer{false} { } + Handler(bool include) : include_into_root_answer{include} { } + + virtual void addToBuffer(aREST *arest, const String& name, const String& request_url) const = 0; +}; + + +struct Variable: Handler { + Variable() : Handler(true) { } + virtual void addToBuffer(aREST *arest) const = 0; + + void addToBuffer(aREST *arest, const String& name, const String& request_url) const override { + if (LIGHTWEIGHT) { + addToBuffer(arest); + } else { + arest->addStringToBuffer(name.c_str(), true); + arest->addToBufferF(F(": ")); + addToBuffer(arest); + } + } }; @@ -187,6 +226,56 @@ struct TypedVariable: Variable { } }; + +struct FunctionHandler: Handler { + int (*func)(String); + + FunctionHandler(int (*f)(String)) : func{f} { } + + void addToBuffer(aREST *arest, const String& name, const String& request_url) const override { + String arguments = extractParams(name, request_url); + int result = func(arguments); + + if (!LIGHTWEIGHT) { + arest->addToBufferF(F("\"return_value\": ")); + arest->addToBuffer(result, true); + // arest->addToBufferF(F(", \"message\": \"")); + // arest->addStringToBuffer(name.c_str()); + // arest->addToBufferF(F(" executed\", ")); + } + } + + String extractParams(const String& name, const String& request_url) const { + // We're expecting a string of the form ?xxxxx=, where xxxxx can be almost anything as long as it's followed by an '=' + // Get command -- Anything following the first '=' in answer will be put in the arguments string. + uint16_t header_length = name.length() + 1; // +1 for the '/' at the start + if (request_url.substring(header_length, header_length + 1) == "?") { + // Standard operation --> strip off anything preceeding the first "=", pass the rest to the handler + if(AREST_PARAMS_MODE == 0) { + uint16_t eq_position = request_url.indexOf('=', header_length); // Replacing 'magic number' 8 for fixed location of '=' + if (eq_position != -1) + return request_url.substring(eq_position + 1, request_url.length()); + } + // All params mode --> pass all parameters, if any, to the handler. Handler will be resonsible for parsing + else if(AREST_PARAMS_MODE == 1) { + return request_url.substring(header_length + 1, request_url.length()); + } + } + return String(""); + } +}; + + +struct ApiHandler: Handler { + void (*func)(aREST *, const String&, const String&); + + ApiHandler(void (*f)(aREST *, const String&, const String&)) : func{f} { } + + void addToBuffer(aREST *arest, const String& name, const String& request_url) const override { + func(arest, name, request_url); + } +}; + public: public: @@ -207,9 +296,9 @@ aREST(char* rest_remote_server, int rest_port) { template void variable(const char *name, T *var, bool quotable) { - variables[variables_index] = new TypedVariable(var, quotable); - variable_names[variables_index] = name; - variables_index++; + handlers[handlers_index] = new TypedVariable(var, quotable); + handler_names[handlers_index] = name; + handlers_index++; } template @@ -218,6 +307,20 @@ void variable(const char *name, T *var) { } +void function(const char *name, int (*f)(String)) { + handlers[handlers_index] = new FunctionHandler(f); + handler_names[handlers_index] = name; + handlers_index++; +} + + +void api_extension(const char *name, void (*f)(aREST *, const String&, const String&)) { + handlers[handlers_index] = new ApiHandler(f); + handler_names[handlers_index] = name; + handlers_index++; +} + + private: void initialize() { @@ -350,8 +453,6 @@ void setKey(char* api_key, PubSubClient& client) { // Build client ID client_id = id + String(api_key); - client_id = id + String(proKey); - } #endif @@ -422,7 +523,7 @@ void reset_status() { reset(); answer = ""; - arguments = ""; + request_url = ""; index = 0; //memset(&buffer[0], 0, sizeof(buffer)); @@ -1106,61 +1207,34 @@ void process(char c) { #endif } - // Variable or function request received ? + // Handler request received ? if (command == 'u') { - // Check if variable name is in int array - for (uint8_t i = 0; i < variables_index; i++) { - if (answer.startsWith(variable_names[i])) { + if (answer.endsWith(" HTTP/") || answer.endsWith(" /") || answer.endsWith("\r")) { + // Check if handler name is registered in array + for (uint8_t i = 0; i < handlers_index; i++) { + if (answer.startsWith(handler_names[i])) { - // End here - pin_selected = true; - state = 'x'; + // End here + pin_selected = true; + state = 'x'; - // Set state - command = 'v'; - value = i; + // Set state + command = 'h'; + value = i; - break; // We found what we're looking for - } - } + answer.trim(); - // Check if function name is in array - for (uint8_t i = 0; i < functions_index; i++) { - if (answer.startsWith(functions_names[i])) { - - // End here - pin_selected = true; - state = 'x'; - - // Set state - command = 'f'; - value = i; - - answer.trim(); - - // We're expecting a string of the form ?xxxxx=, where xxxxx can be almost anything as long as it's followed by an '=' - // Get command -- Anything following the first '=' in answer will be put in the arguments string. - arguments = ""; - uint16_t header_length = strlen(functions_names[i]); - if (answer.substring(header_length, header_length + 1) == "?") { - uint16_t footer_start = answer.length(); - if (answer.endsWith(" HTTP/")) - footer_start -= 6; // length of " HTTP/" - - // Standard operation --> strip off anything preceeding the first "=", pass the rest to the function - if(AREST_PARAMS_MODE == 0) { - uint16_t eq_position = answer.indexOf('=', header_length); // Replacing 'magic number' 8 for fixed location of '=' - if (eq_position != -1) - arguments = answer.substring(eq_position + 1, footer_start); - } - // All params mode --> pass all parameters, if any, to the function. Function will be resonsible for parsing - else if(AREST_PARAMS_MODE == 1) { - arguments = answer.substring(header_length + 1, footer_start); + if (answer.endsWith(" HTTP/")) { + request_url = "/" + answer.substring(0, answer.length() - 6); // length of " HTTP/" + } else if (answer.endsWith(" /")) { + request_url = "/" + answer.substring(0, answer.length() - 2); // length of " /" + } else { + request_url = "/" + answer; } - } - break; // We found what we're looking for + break; // We found what we're looking for + } } } @@ -1195,19 +1269,23 @@ void process(char c) { // Serial.print("Selected method: "); // Serial.println(method); // } + } else { + answer = ""; } - answer = ""; + if (c == '\r' || answer.startsWith("GET /") || answer.startsWith("/")) { + answer = ""; + } } -// Modifies arguments in place -void urldecode(String &arguments) { +// Modifies request_url in place +void urldecode(String &request_url) { char a, b; int j = 0; - for(int i = 0; i < arguments.length(); i++) { - // %20 ==> arguments[i] = '%', a = '2', b = '0' - if ((arguments[i] == '%') && ((a = arguments[i + 1]) && (b = arguments[i + 2])) && (isxdigit(a) && isxdigit(b))) { + for(int i = 0; i < request_url.length(); i++) { + // %20 ==> request_url[i] = '%', a = '2', b = '0' + if ((request_url[i] == '%') && ((a = request_url[i + 1]) && (b = request_url[i + 2])) && (isxdigit(a) && isxdigit(b))) { if (a >= 'a') a -= 'a'-'A'; if (a >= 'A') a -= ('A' - 10); else a -= '0'; @@ -1216,17 +1294,17 @@ void urldecode(String &arguments) { if (b >= 'A') b -= ('A' - 10); else b -= '0'; - arguments[j] = char(16 * a + b); + request_url[j] = char(16 * a + b); i += 2; // Skip ahead - } else if (arguments[i] == '+') { - arguments[j] = ' '; + } else if (request_url[i] == '+') { + request_url[j] = ' '; } else { - arguments[j] = arguments[i]; + request_url[j] = request_url[i]; } j++; } - arguments.remove(j); // Truncate string to new possibly reduced length + request_url.remove(j); // Truncate string to new possibly reduced length } @@ -1423,35 +1501,22 @@ bool send_command(bool headers, bool decodeArgs) { } } - // Variable selected - if (command == 'v') { + // Handler selected + if (command == 'h') { + if (decodeArgs) { + urldecode(request_url); // Modifies request_url + } // Send feedback to client if (LIGHTWEIGHT) { - variables[value]->addToBuffer(this); + addHandlerToBuffer(value, request_url); } else { addToBufferF(F("{")); - addVariableToBuffer(value); - addToBufferF(F(", ")); - } - } - - // Function selected - if (command == 'f') { - - // Execute function - if (decodeArgs) - urldecode(arguments); // Modifies arguments - - int result = functions[value](arguments); - - // Send feedback to client - if (!LIGHTWEIGHT) { - addToBufferF(F("{\"return_value\": ")); - addToBuffer(result, true); - addToBufferF(F(", ")); - // addToBufferF(F(", \"message\": \"")); - // addStringToBuffer(functions_names[value]); - // addToBufferF(F(" executed\", ")); + auto bufferPos = index; + addHandlerToBuffer(value, request_url); + if (bufferPos < index) { + // index has changed -> the handler added some stuff to the buffer + addToBufferF(F(", ")); + } } } @@ -1510,11 +1575,16 @@ virtual void root_answer() { else { addToBufferF(F("{\"variables\": {")); - for (uint8_t i = 0; i < variables_index; i++){ - addVariableToBuffer(i); - - if (i < variables_index - 1) { - addToBufferF(F(", ")); + bool isFirst = true; + for (uint8_t i = 0; i < handlers_index; i++){ + if (handlers[i]->include_into_root_answer) { + // variable should be included into root answer + if (isFirst) { + isFirst = false; + } else { + addToBufferF(F(", ")); + } + addHandlerToBuffer(i, String("")); } } @@ -1530,13 +1600,6 @@ virtual void root_answer() { } -void function(char * function_name, int (*f)(String)){ - - functions_names[functions_index] = function_name; - functions[functions_index] = f; - functions_index++; -} - // Set device ID void set_id(const String& device_id) { @@ -1871,10 +1934,8 @@ uint8_t esp_12_pin_map(uint8_t pin) { } -void addVariableToBuffer(uint8_t index) { - addStringToBuffer(variable_names[index], true); - addToBufferF(F(": ")); - variables[index]->addToBuffer(this); +void addHandlerToBuffer(uint8_t index, const String& request_url) { + handlers[index]->addToBuffer(this, String(handler_names[index]), request_url); } @@ -1927,7 +1988,7 @@ void setMQTTServer(char* new_mqtt_server){ char name[NAME_SIZE]; String id; String proKey; - String arguments; + String request_url; // Output uffer char buffer[OUTPUT_BUFFER_SIZE]; @@ -1936,10 +1997,10 @@ void setMQTTServer(char* new_mqtt_server){ // Status LED uint8_t status_led_pin; - // Int variables arrays - uint8_t variables_index; - Variable* variables[NUMBER_VARIABLES]; - const char * variable_names[NUMBER_VARIABLES]; + // Handlers arrays + uint8_t handlers_index; + Handler* handlers[NUMBER_HANDLERS]; + const char * handler_names[NUMBER_HANDLERS]; // MQTT client #if defined(PubSubClient_h) @@ -1961,11 +2022,6 @@ void setMQTTServer(char* new_mqtt_server){ #endif - // Functions array - uint8_t functions_index; - int (*functions[NUMBER_FUNCTIONS])(String); - char * functions_names[NUMBER_FUNCTIONS]; - // Memory debug #if defined(ESP8266) || defined(ESP32) int freeMemory; diff --git a/examples/BLE/BLE.ino b/examples/BLE/BLE.ino index 8a6eff0..c7a17e0 100644 --- a/examples/BLE/BLE.ino +++ b/examples/BLE/BLE.ino @@ -90,4 +90,4 @@ int ledControl(String command) { digitalWrite(7,state); return 1; -} \ No newline at end of file +} diff --git a/examples/ESP32/ESP32.ino b/examples/ESP32/ESP32.ino index 9a12956..4ba973c 100644 --- a/examples/ESP32/ESP32.ino +++ b/examples/ESP32/ESP32.ino @@ -85,4 +85,5 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } diff --git a/examples/ESP32_cloud/ESP32_cloud.ino b/examples/ESP32_cloud/ESP32_cloud.ino index fd0d6b3..edf4007 100644 --- a/examples/ESP32_cloud/ESP32_cloud.ino +++ b/examples/ESP32_cloud/ESP32_cloud.ino @@ -91,4 +91,5 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } diff --git a/examples/ESP8266/ESP8266.ino b/examples/ESP8266/ESP8266.ino index 955274e..ce628f1 100644 --- a/examples/ESP8266/ESP8266.ino +++ b/examples/ESP8266/ESP8266.ino @@ -86,4 +86,5 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } diff --git a/examples/ESP8266_softAP/ESP8266_softAP.ino b/examples/ESP8266_softAP/ESP8266_softAP.ino index 09a65c8..fa6649a 100644 --- a/examples/ESP8266_softAP/ESP8266_softAP.ino +++ b/examples/ESP8266_softAP/ESP8266_softAP.ino @@ -84,4 +84,5 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } diff --git a/examples/Ethernet/Ethernet.ino b/examples/Ethernet/Ethernet.ino index e26908a..1c6c281 100644 --- a/examples/Ethernet/Ethernet.ino +++ b/examples/Ethernet/Ethernet.ino @@ -83,4 +83,3 @@ int ledControl(String command) { return 1; } - diff --git a/examples/Ethernet_API_extension/Ethernet_API_extension.ino b/examples/Ethernet_API_extension/Ethernet_API_extension.ino new file mode 100644 index 0000000..22f9cd0 --- /dev/null +++ b/examples/Ethernet_API_extension/Ethernet_API_extension.ino @@ -0,0 +1,139 @@ +/* + This a simple example of the aREST Library for Arduino (Uno/Mega/Due/Teensy) + using the Ethernet library (for example to be used with the Ethernet shield). + See the README file for more details. + + Written in 2014 by Marco Schwartz under a GPL license. +*/ + +// Libraries +#include +#include +#include +#include + +// Enter a MAC address for your controller below. +byte mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0xFE, 0x40 }; + +// IP address in case DHCP fails +IPAddress ip(192,168,2,2); + +// Ethernet server +EthernetServer server(80); + +// Create aREST instance +aREST rest = aREST(); + +// Variables to be exposed to the API +int temperature; +int humidity; + +// Declare functions to be exposed to the API +int ledControl(String command); +void aquariumController(aREST *arest, const String& name, const String& request_url); + +void setup(void) +{ + // Start Serial + Serial.begin(115200); + + // Init variables and expose them to REST API + temperature = 24; + humidity = 40; + rest.variable("temperature",&temperature); + rest.variable("humidity",&humidity); + + // Function to be exposed + rest.function("led",ledControl); + + // API-Extension to be exposed + rest.api_extension("aquarium", aquariumController); + + // Give name & ID to the device (ID should be 6 characters long) + rest.set_id("008"); + rest.set_name("dapper_drake"); + + // Start the Ethernet connection and the server + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + // try to congifure using IP address instead of DHCP: + Ethernet.begin(mac, ip); + } + server.begin(); + Serial.print("server is at "); + Serial.println(Ethernet.localIP()); + + // Start watchdog + wdt_enable(WDTO_4S); +} + +void loop() { + + // listen for incoming clients + EthernetClient client = server.available(); + rest.handle(client); + wdt_reset(); + +} + +// Custom function accessible by the API +int ledControl(String command) { + + // Get state from command + int state = command.toInt(); + + digitalWrite(6,state); + return 1; + +} + +void aquariumController(aREST *arest, const String& name, const String& request_url) { + // check format of request_url + if (request_url == F("/aquarium") + || request_url == F("/aquarium/")) { + // Send feedback to client + if (LIGHTWEIGHT) { + bool isFirstSensor = true; + auto count = 5; + for (uint32_t i = 0; i < count; ++i) { + if (isFirstSensor) { + isFirstSensor = false; + } else { + arest->addToBufferF(F(",")); + } + auto id = i + 100; + arest->addToBuffer(id); + } + } else { + arest->addToBufferF(F("\"sensor-ids\": [")); + bool isFirstSensor = true; + auto count = 5; + for (uint32_t i = 0; i < count; ++i) { + if (isFirstSensor) { + isFirstSensor = false; + } else { + arest->addToBufferF(F(", ")); + } + arest->addToBufferF(F("\"")); + auto id = i + 100; + arest->addToBuffer(id); + arest->addToBufferF(F("\"")); + } + arest->addToBufferF(F("]")); + } + } else if (request_url.startsWith(F("/aquarium/water_limit/lower/set/"))) { + String args = request_url.substring(32); // 32 = length of "/aquarium/water_limit/lower/set/" + + // Send feedback to client + if (!LIGHTWEIGHT) { + arest->addToBufferF(F("\"message\": \"lower water limit set to ")); + arest->addToBuffer(args); + arest->addToBufferF(F("cm\"")); + } + } else { + arest->addToBufferF(F("\"message\": \"Unknown request_url '")); + arest->addToBuffer(request_url); + arest->addToBufferF(F("'.\"")); + } +} diff --git a/examples/MKR1000_cloud/MKR1000_cloud.ino b/examples/MKR1000_cloud/MKR1000_cloud.ino index 0ce3944..5493cc2 100644 --- a/examples/MKR1000_cloud/MKR1000_cloud.ino +++ b/examples/MKR1000_cloud/MKR1000_cloud.ino @@ -87,8 +87,8 @@ int ledControl(String command) { digitalWrite(6,state); return 1; -} +} // Handles message arrived on subscribed topic(s) void callback(char* topic, byte* payload, unsigned int length) { diff --git a/examples/MKR1000_cloud_and_local/MKR1000_cloud_and_local.ino b/examples/MKR1000_cloud_and_local/MKR1000_cloud_and_local.ino index 5511012..965efd8 100644 --- a/examples/MKR1000_cloud_and_local/MKR1000_cloud_and_local.ino +++ b/examples/MKR1000_cloud_and_local/MKR1000_cloud_and_local.ino @@ -114,6 +114,7 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } // Handles message arrived on subscribed topic(s) diff --git a/examples/MKR1000_cloud_api_key/MKR1000_cloud_api_key.ino b/examples/MKR1000_cloud_api_key/MKR1000_cloud_api_key.ino index 3da7b50..8c23d19 100644 --- a/examples/MKR1000_cloud_api_key/MKR1000_cloud_api_key.ino +++ b/examples/MKR1000_cloud_api_key/MKR1000_cloud_api_key.ino @@ -92,6 +92,7 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } // Handles message arrived on subscribed topic(s) diff --git a/examples/Serial/Serial.ino b/examples/Serial/Serial.ino index aaeaf35..93557da 100644 --- a/examples/Serial/Serial.ino +++ b/examples/Serial/Serial.ino @@ -58,4 +58,5 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } diff --git a/examples/WiFi/WiFi.ino b/examples/WiFi/WiFi.ino index 07b38b3..ea1c055 100644 --- a/examples/WiFi/WiFi.ino +++ b/examples/WiFi/WiFi.ino @@ -95,6 +95,7 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } void printWifiStatus() { diff --git a/examples/WiFi_CC3000/WiFi_CC3000.ino b/examples/WiFi_CC3000/WiFi_CC3000.ino index 0894b15..f70a7ce 100644 --- a/examples/WiFi_CC3000/WiFi_CC3000.ino +++ b/examples/WiFi_CC3000/WiFi_CC3000.ino @@ -140,4 +140,5 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } diff --git a/examples/WiFi_CC3000_Due/WiFi_CC3000_Due.ino b/examples/WiFi_CC3000_Due/WiFi_CC3000_Due.ino index 9f8688b..44c5814 100644 --- a/examples/WiFi_CC3000_Due/WiFi_CC3000_Due.ino +++ b/examples/WiFi_CC3000_Due/WiFi_CC3000_Due.ino @@ -135,4 +135,5 @@ int ledControl(String command) { digitalWrite(6,state); return 1; + } diff --git a/examples/Yun/Yun.ino b/examples/Yun/Yun.ino index 71f7742..328486c 100644 --- a/examples/Yun/Yun.ino +++ b/examples/Yun/Yun.ino @@ -67,4 +67,3 @@ int ledControl(String command) { return 1; } - diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..6db63d3 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,105 @@ + +# Created by https://www.gitignore.io/api/python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + + +# End of https://www.gitignore.io/api/python diff --git a/test/http_test.py b/test/http_test.py index 21b2e58..a98b483 100644 --- a/test/http_test.py +++ b/test/http_test.py @@ -118,5 +118,17 @@ def test_function(self): answer = json.loads(curl_call(target,"/digital/6")) self.assertEqual(answer['return_value'],0) + # API-Extension call check + def test_api_extension(self): + + # Call list of sensors + answer = json.loads(curl_call(target, "/aquarium")) + l = [int(val) for val in answer['sensor-ids']] + self.assertEqual(l, [100, 101, 102, 103, 104]) + + # Call set of limit + answer = json.loads(curl_call(target, "/aquarium/water_limit/lower/set/45")) + self.assertEqual(answer['message'], "lower water limit set to 45cm") + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/test/lightweight_test.py b/test/lightweight_test.py index 893301c..8254b09 100644 --- a/test/lightweight_test.py +++ b/test/lightweight_test.py @@ -83,5 +83,17 @@ def test_function(self): answer = curl_call(target,"/digital/6") self.assertEqual(int(answer),0) + # API-Extension call check + def test_api_extension(self): + + # Call list of sensors + answer = curl_call(target, "/aquarium") + l = [int(val) for val in answer.split(',')] + self.assertEqual(l, [100, 101, 102, 103, 104]) + + # Call set of limit + answer = curl_call(target, "/aquarium/water_limit/lower/set/45") + self.assertEqual(answer.strip(), "") + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/test/serial_test.py b/test/serial_test.py index e1a7e26..c43fe6c 100644 --- a/test/serial_test.py +++ b/test/serial_test.py @@ -108,7 +108,7 @@ def test_variable(self): self.assertGreaterEqual(answer['temperature'],0) self.assertLessEqual(answer['temperature'],40) - # Function call check + # Function call check def test_function(self): # Call function @@ -131,5 +131,19 @@ def test_function(self): answer = json.loads(self.serial.readline()) self.assertEqual(answer['return_value'],0) + # API-Extension call check + def test_api_extension(self): + + # Call list of sensors + self.serial.write("/aquarium\r") + answer = json.loads(self.serial.readline()) + l = [int(val) for val in answer['sensor-ids']] + self.assertEqual(l, [100, 101, 102, 103, 104]) + + # Call set of limit + self.serial.write("/aquarium/water_limit/lower/set/45\r") + answer = json.loads(self.serial.readline()) + self.assertEqual(answer['message'], "lower water limit set to 45cm") + if __name__ == '__main__': unittest.main() \ No newline at end of file