Added time trigger settings and corresponding API

Fixed #9: Changing individual step with startTime can fail
This commit is contained in:
Mark van Renswoude 2018-01-10 21:04:55 +01:00
parent 381a77e9ce
commit f220f1b7a1
12 changed files with 334 additions and 5 deletions

7
API.md
View File

@ -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.

View File

@ -8,5 +8,18 @@
],
"completions":[
["t", "{{ \\$t('${1:}') }}"]
]
],
"settings": {
"todoreview": {
"exclude_folders": [
"*.pioenvs*",
"*.piolibdeps*",
"*bin*",
"*node_modules*"
],
"patterns": {
"TODO": "TODO[\\s]*(?P<todo>.*)$",
}
}
}
}

View File

@ -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

View File

@ -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-";

View File

@ -15,6 +15,9 @@ bool systemSettingsChanged = false;
StepsSettings* stepsSettings = new StepsSettings();
bool stepsSettingsChanged = false;
TimeTriggerSettings* timeTriggerSettings = new TimeTriggerSettings();
bool timeTriggerSettingsChanged = false;
Stairs* stairs;

View File

@ -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;

View File

@ -85,6 +85,7 @@ void setup()
connectionSettings->read();
systemSettings->read();
stepsSettings->read();
timeTriggerSettings->read();
pinMode(systemSettings->pinAPButton(), INPUT_PULLUP);
pinMode(systemSettings->pinLEDAP(), OUTPUT);

View File

@ -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);
}

View File

@ -27,6 +27,10 @@ class SystemSettings : CharProperties
char* mMapsAPIKey = nullptr;
// TODO loginRequired
// TODO loginUsername
// TODO loginPassword
public:
void read();
void write();

View File

@ -0,0 +1,148 @@
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "time.h"
#include <string.h>
#include <FS.h>
#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;
}
}
}
}

View File

@ -0,0 +1,68 @@
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#ifndef __settingstriggerstime
#define __settingstriggerstime
#include <TimeLib.h>
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

View File

@ -105,6 +105,8 @@ void Stairs::tick()
applyCurrentValue(step);
}
else
mTick = true;
}
else if (elapsedTime >= stepState->remainingTime)
{