diff --git a/API.md b/API.md index b7f674d..0830764 100644 --- a/API.md +++ b/API.md @@ -6,7 +6,8 @@ - [POST /api/connection](#post-apiconnection) - [GET /api/steps](#get-apisteps) - [POST /api/steps](#post-apisteps) -- [GET /api/geocode/latlong](#get-apigeocodelatlong) +- [GET /api/triggers/time](#get-apitriggerstime) +- [POST /api/triggers/time](#post-apitriggerstime) - [POST /api/firmware](#post-apifirmware) ## GET /api/version @@ -117,6 +118,10 @@ An optional array 'startTime' can be included which specifies the delay, for eac } ``` +## GET /api/triggers/time + +## POST /api/triggers/time + ## POST /api/firmware Uploads new firmware. The bin file should be posted as a multipart/form-data file attachment. Name is not relevant. \ No newline at end of file diff --git a/Stairs.sublime-project b/Stairs.sublime-project index a3375cb..ec7b2ec 100644 --- a/Stairs.sublime-project +++ b/Stairs.sublime-project @@ -8,5 +8,18 @@ ], "completions":[ ["t", "{{ \\$t('${1:}') }}"] - ] + ], + "settings": { + "todoreview": { + "exclude_folders": [ + "*.pioenvs*", + "*.piolibdeps*", + "*bin*", + "*node_modules*" + ], + "patterns": { + "TODO": "TODO[\\s]*(?P.*)$", + } + } + } } \ No newline at end of file diff --git a/src/assets/version.h b/src/assets/version.h index 88dcf43..4f80f74 100644 --- a/src/assets/version.h +++ b/src/assets/version.h @@ -4,10 +4,10 @@ const uint8_t VersionMajor = 2; const uint8_t VersionMinor = 0; const uint8_t VersionPatch = 0; -const uint8_t VersionMetadata = 11; +const uint8_t VersionMetadata = 13; const char VersionBranch[] = "release/2.0"; const char VersionSemVer[] = "2.0.0-beta.1"; -const char VersionFullSemVer[] = "2.0.0-beta.1+11"; -const char VersionCommitDate[] = "2018-01-08"; +const char VersionFullSemVer[] = "2.0.0-beta.1+13"; +const char VersionCommitDate[] = "2018-01-10"; #endif diff --git a/src/config.h b/src/config.h index d222da1..05983bc 100644 --- a/src/config.h +++ b/src/config.h @@ -15,6 +15,7 @@ static const uint32_t SerialDebugStartupDelay = 2000; static const char* ConnectionSettingsFile = "/connection.json"; static const char* SystemSettingsFile = "/system.json"; static const char* StepSettingsFile = "/stepsettings.dat"; +static const char* TimeTriggerSettingsFile = "/timetriggersettings.dat"; static const char* DefaultAPSSIDPrefix = "Stairs-"; diff --git a/src/global.cpp b/src/global.cpp index 46cd07c..a6d1bb7 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -15,6 +15,9 @@ bool systemSettingsChanged = false; StepsSettings* stepsSettings = new StepsSettings(); bool stepsSettingsChanged = false; +TimeTriggerSettings* timeTriggerSettings = new TimeTriggerSettings(); +bool timeTriggerSettingsChanged = false; + Stairs* stairs; diff --git a/src/global.h b/src/global.h index 26cf047..b8fb847 100644 --- a/src/global.h +++ b/src/global.h @@ -13,6 +13,7 @@ #include "settings/connection.h" #include "settings/system.h" #include "settings/steps.h" +#include "settings/triggers/time.h" #include "stairs.h" extern ConnectionSettings* connectionSettings; @@ -24,6 +25,10 @@ extern bool systemSettingsChanged; extern StepsSettings* stepsSettings; extern bool stepsSettingsChanged; +extern TimeTriggerSettings* timeTriggerSettings; +extern bool timeTriggerSettingsChanged; + + extern Stairs* stairs; extern bool shouldReboot; diff --git a/src/main.cpp b/src/main.cpp index 90ef3db..0bc1b04 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -85,6 +85,7 @@ void setup() connectionSettings->read(); systemSettings->read(); stepsSettings->read(); + timeTriggerSettings->read(); pinMode(systemSettings->pinAPButton(), INPUT_PULLUP); pinMode(systemSettings->pinLEDAP(), OUTPUT); diff --git a/src/server/api.cpp b/src/server/api.cpp index 8639a1a..8846540 100644 --- a/src/server/api.cpp +++ b/src/server/api.cpp @@ -13,6 +13,7 @@ #include "../debug.h" #include "../global.h" #include "../settings/connection.h" +#include "../settings/triggers/time.h" void handleGetSteps(AsyncWebServerRequest *request) @@ -64,8 +65,86 @@ void handlePostSteps(AsyncWebServerRequest *request, uint8_t *data, size_t len, } +void handleGetTimeTriggers(AsyncWebServerRequest *request) +{ + _dln("API :: get time triggers"); + + uint8_t count = timeTriggerSettings->triggerCount(); + + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(count) + JSON_OBJECT_SIZE(2) + count*JSON_OBJECT_SIZE(5)); + JsonObject& root = jsonBuffer.createObject(); + root["transitionTime"] = timeTriggerSettings->transitionTime(); + + JsonArray& jsonTriggers = root.createNestedArray("triggers"); + + for (uint8_t i = 0; i < count; i++) + { + TimeTrigger* trigger = timeTriggerSettings->trigger(i); + + JsonObject& jsonTrigger = jsonTriggers.createNestedObject(); + jsonTrigger["timeofDay"] = trigger->timeOfDay; + jsonTrigger["daysOfWeek"] = trigger->daysOfWeek; + jsonTrigger["brightness"] = trigger->brightness; + jsonTrigger["triggerType"] = (uint8_t)trigger->triggerType; + jsonTrigger["enabled"] = trigger->enabled; + } + + AsyncResponseStream *response = request->beginResponseStream("application/json"); + root.printTo(*response); + + request->send(response); +} + + +void handlePostTimeTriggers(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) +{ + _dln("API :: post time triggers"); + + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(10) + JSON_OBJECT_SIZE(2) + 10*JSON_OBJECT_SIZE(5) + 510); + JsonObject& root = jsonBuffer.parseObject((char*)data); + if (!root.success()) + { + request->send(400); + return; + } + + JsonArray& jsonTriggers = root["triggers"]; + if (jsonTriggers.size() > 255) + { + request->send(400); + return; + } + + timeTriggerSettings->transitionTime(root["transitionTime"]); + timeTriggerSettings->beginSetTriggers(jsonTriggers.size()); + + TimeTrigger trigger; + for (uint8_t i = 0; i < jsonTriggers.size(); i++) + { + JsonObject& jsonTrigger = jsonTriggers[i]; + + trigger.timeOfDay = jsonTrigger["timeOfDay"]; + trigger.daysOfWeek = jsonTrigger["daysOfWeek"]; + trigger.brightness = jsonTrigger["brightness"]; + trigger.triggerType = (TimeTriggerType)(uint8_t)jsonTrigger["triggerType"]; + trigger.enabled = jsonTrigger["enabled"]; + + timeTriggerSettings->setTrigger(i, &trigger); + } + + timeTriggerSettings->endSetTriggers(); + timeTriggerSettings->write(); + timeTriggerSettingsChanged = true; + + request->send(200); +} + + void registerAPIRoutes(AsyncWebServer* server) { server->on("/api/steps", HTTP_GET, handleGetSteps); server->on("/api/steps", HTTP_POST, devNullRequest, devNullFileUpload, handlePostSteps); + + server->on("/api/triggers/time", HTTP_GET, handleGetTimeTriggers); + server->on("/api/triggers/time", HTTP_POST, devNullRequest, devNullFileUpload, handlePostTimeTriggers); } \ No newline at end of file diff --git a/src/settings/system.h b/src/settings/system.h index 2232968..4115593 100644 --- a/src/settings/system.h +++ b/src/settings/system.h @@ -27,6 +27,10 @@ class SystemSettings : CharProperties char* mMapsAPIKey = nullptr; + // TODO loginRequired + // TODO loginUsername + // TODO loginPassword + public: void read(); void write(); diff --git a/src/settings/triggers/time.cpp b/src/settings/triggers/time.cpp new file mode 100644 index 0000000..cd79a8b --- /dev/null +++ b/src/settings/triggers/time.cpp @@ -0,0 +1,148 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#include "time.h" +#include +#include +#include "../../debug.h" + + +timeDayOfWeek_t toTimeDayOfWeek(DayOfWeek day) +{ + switch (day) + { + case Monday: return dowMonday; + case Tuesday: return dowTuesday; + case Wednesday: return dowWednesday; + case Thursday: return dowThursday; + case Friday: return dowFriday; + case Saturday: return dowSaturday; + case Sunday: return dowSunday; + } + + return dowInvalid; +} + + +DayOfWeek toDayOfWeek(timeDayOfWeek_t timeDay) +{ + switch (timeDay) + { + case dowSunday: return Sunday; + case dowMonday: return Monday; + case dowTuesday: return Tuesday; + case dowWednesday: return Wednesday; + case dowThursday: return Thursday; + case dowFriday: return Friday; + case dowSaturday: return Saturday; + } + + return Monday; +} + + +struct Header +{ + uint8_t version; + uint8_t triggerCount; + uint16_t transitionTime; +}; + + +void TimeTriggerSettings::read() +{ + _dln("TimeTriggerSettings :: Loading time triggers"); + File f = SPIFFS.open(TimeTriggerSettingsFile, "r"); + if (!f) + return; + + if (!f.available()) + return; + + Header header; + f.readBytes((char*)&header, sizeof(Header)); + + if (header.version != 1) + return; + + mTransitionTime = header.transitionTime; + beginSetTriggers(header.triggerCount); + + if (header.triggerCount > 0) + f.readBytes((char*)&mTriggers, header.triggerCount * sizeof(TimeTrigger)); + + endSetTriggers(); + + f.close(); +} + + +void TimeTriggerSettings::write() +{ + _dln("TimeTriggerSettings :: Saving time triggers"); + File f = SPIFFS.open(TimeTriggerSettingsFile, "w"); + if (!f) + return; + + Header header; + header.version = 1; + header.transitionTime = mTransitionTime; + header.triggerCount = mTriggerCount; + + f.write((uint8_t*)&header, sizeof(Header)); + f.write((uint8_t*)&mTriggers, header.triggerCount * sizeof(TimeTrigger)); + f.close(); +} + + +void TimeTriggerSettings::beginSetTriggers(uint8_t count) +{ + if (mTriggers != nullptr) + delete [] mTriggers; + + mTriggers = new TimeTrigger[count]; + mTriggerCount = count; +} + + +void TimeTriggerSettings::setTrigger(uint8_t index, TimeTrigger* value) +{ + memcpy(&mTriggers[index], value, sizeof(TimeTrigger)); +} + + +void TimeTriggerSettings::endSetTriggers() +{ + // Sort triggers by time of day + // Based on the Comb sort implementation by Rob Tillaart + // http://forum.arduino.cc/index.php?topic=280486.0 + uint8_t i, j; + uint8_t gap; + bool swapped = true; + TimeTrigger temp; + + gap = mTriggerCount; + while (gap > 1 || swapped) + { + if (gap > 1) + { + gap = gap * 10/13; + if (gap == 9 || gap == 10) gap = 11; + } + + swapped = false; + for (i = 0, j = gap; j < mTriggerCount; i++, j++) + { + if (mTriggers[i].timeOfDay > mTriggers[j].timeOfDay) + { + temp = mTriggers[i]; + mTriggers[i] = mTriggers[j]; + mTriggers[j] = temp; + swapped = true; + } + } + } +} \ No newline at end of file diff --git a/src/settings/triggers/time.h b/src/settings/triggers/time.h new file mode 100644 index 0000000..ca51c19 --- /dev/null +++ b/src/settings/triggers/time.h @@ -0,0 +1,68 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#ifndef __settingstriggerstime +#define __settingstriggerstime + +#include + +enum DayOfWeek +{ + Monday = 1, + Tuesday = 2, + Wednesday = 4, + Thursday = 8, + Friday = 16, + Saturday = 32, + Sunday = 64 +}; + + +extern timeDayOfWeek_t toTimeDayOfWeek(DayOfWeek day); +extern DayOfWeek toDayOfWeek(timeDayOfWeek_t timeDay); + + +enum TimeTriggerType +{ + FixedTime = 0, + RelativeToSunrise = 1, + RelativeToSunset = 2 +}; + + +struct TimeTrigger +{ + uint16_t timeOfDay; + uint8_t daysOfWeek; + uint8_t brightness; + TimeTriggerType triggerType; + bool enabled; +}; + + +class TimeTriggerSettings +{ + private: + uint16_t mTransitionTime = 0; + uint8_t mTriggerCount = 0; + TimeTrigger* mTriggers = nullptr; + + public: + void read(); + void write(); + + uint16_t transitionTime() { return mTransitionTime; } + void transitionTime(uint16_t value) { mTransitionTime = value; } + + uint8_t triggerCount() { return mTriggerCount; } + TimeTrigger* trigger(uint8_t index) { return &mTriggers[index]; } + + void beginSetTriggers(uint8_t count); + void setTrigger(uint8_t index, TimeTrigger* value); + void endSetTriggers(); +}; + +#endif \ No newline at end of file diff --git a/src/stairs.cpp b/src/stairs.cpp index f440e92..b8dcdf8 100644 --- a/src/stairs.cpp +++ b/src/stairs.cpp @@ -105,6 +105,8 @@ void Stairs::tick() applyCurrentValue(step); } + else + mTick = true; } else if (elapsedTime >= stepState->remainingTime) {