...
 
Commits (8)
......@@ -269,7 +269,7 @@ enabled: whether or not this trigger is enabled
```json
{
"enabled": true,
"enabledDuringTimeTrigger": true,
"enabledDuringTimeTrigger": 0,
"enabledDuringDay": false,
"transitionTime": 1000,
"delay": 30000,
......
......@@ -26,7 +26,7 @@ To get started:
1. Install [Node.js](https://nodejs.org/en/)
1. Install the [GitVersion command line](http://gitversion.readthedocs.io/en/stable/usage/command-line/) tool
1. Open a command line and navigate to the Stairs folder
1. Run ```npm update``` to install all the dependencies
1. Run ```npm install``` to install all the dependencies
### Compiling the assets
......
......@@ -207,7 +207,7 @@ app.post('/api/triggers/time', function(req, res)
var motionTriggers = {
enabled: true,
enabledDuringTimeTrigger: false,
enabledDuringTimeTrigger: 0,
enabledDuringDay: true,
transitionTime: 1000,
delay: 30000,
......
......@@ -207,7 +207,7 @@ gulp.task('embedVersion', function(cb)
headerFile += "const uint8_t VersionMajor = " + version.Major + ";\r\n";
headerFile += "const uint8_t VersionMinor = " + version.Minor + ";\r\n";
headerFile += "const uint8_t VersionPatch = " + version.Patch + ";\r\n";
headerFile += "const uint8_t VersionMetadata = " + version.BuildMetaData + ";\r\n";
headerFile += "const uint8_t VersionMetadata = " + (version.BuildMetaData ? version.BuildMetaData : '0') + ";\r\n";
headerFile += "const char VersionBranch[] = \"" + version.BranchName + "\";\r\n";
......
This diff is collapsed.
......@@ -35,5 +35,8 @@
"vue": "^2.5.13",
"vue-i18n": "^7.3.3",
"yargs": "^10.0.3"
},
"dependencies": {
"npm": "^5.8.0"
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -2,12 +2,12 @@
#define __assets_version
const uint8_t VersionMajor = 2;
const uint8_t VersionMinor = 0;
const uint8_t VersionMinor = 1;
const uint8_t VersionPatch = 0;
const uint8_t VersionMetadata = 0;
const char VersionBranch[] = "master";
const char VersionSemVer[] = "2.0.0";
const char VersionFullSemVer[] = "2.0.0+0";
const char VersionCommitDate[] = "2018-02-17";
const char VersionSemVer[] = "2.1.0";
const char VersionFullSemVer[] = "2.1.0";
const char VersionCommitDate[] = "2018-04-29";
#endif
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "./config.h"
#ifdef SerialDebug
const uint32_t SerialDebugBaudrate = 115200;
const uint32_t SerialDebugStartupDelay = 2000;
#endif
const char* ConnectionSettingsFile = "/connection.json";
const char* SystemSettingsFile = "/system.json";
const char* StepsSettingsFile = "/steps.json";
const char* TimeTriggerSettingsFile = "/timetriggers.json";
const char* MotionTriggerSettingsFile = "/motiontriggers.json";
const char* DefaultAPSSIDPrefix = "Stairs-";
const char* DefaultNTPServer = "pool.ntp.org";
// Timeout when in AP + station mode (otherwise trying to connect
// to the STA will block the AP)
const uint32_t StationModeTimeout = 30000;
const uint16_t APButtonHoldTime = 2000;
// Only used if the timezone has not been succesfully retrieved yet, otherwise
// the configurable NTP interval is used
const uint32_t TimezoneRetryInterval = 60000;
#ifdef MapsAPIViaProxyScript
const char* TimezoneProxyScriptHost = "api.x2software.net";
const char* TimezoneProxyScriptPath = "/timezone.php";
#endif
const uint8_t InitialisationBrightness = 128;
const uint8_t InitialisationFadeTime = 250;
const uint8_t InitialisationBlinkCount = 2;
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#ifndef __config
#define __config
......@@ -14,32 +20,27 @@
#ifdef SerialDebug
static const uint32_t SerialDebugBaudrate = 115200;
static const uint32_t SerialDebugStartupDelay = 2000;
extern const uint32_t SerialDebugBaudrate;
extern const uint32_t SerialDebugStartupDelay;
#endif
static const char* ConnectionSettingsFile = "/connection.json";
static const char* SystemSettingsFile = "/system.json";
static const char* StepsSettingsFile = "/steps.json";
static const char* TimeTriggerSettingsFile = "/timetriggers.json";
static const char* MotionTriggerSettingsFile = "/motiontriggers.json";
extern const char* ConnectionSettingsFile;
extern const char* SystemSettingsFile;
extern const char* StepsSettingsFile;
extern const char* TimeTriggerSettingsFile;
extern const char* MotionTriggerSettingsFile;
static const char* DefaultAPSSIDPrefix = "Stairs-";
extern const char* DefaultAPSSIDPrefix;
static const char* DefaultNTPServer = "pool.ntp.org";
extern const char* DefaultNTPServer;
// Timeout when in AP + station mode (otherwise trying to connect
// to the STA will block the AP)
static const uint32_t StationModeTimeout = 30000;
extern const uint32_t StationModeTimeout;
extern const uint16_t APButtonHoldTime;
static const uint16_t APButtonHoldTime = 2000;
// Only used if the timezone has not been succesfully retrieved yet, otherwise
// the configurable NTP interval is used
static const uint32_t TimezoneRetryInterval = 60000;
extern const uint32_t TimezoneRetryInterval;
// SSL takes quite a bit of memory (and I haven't been optimizing much),
......@@ -60,8 +61,13 @@ static const uint32_t TimezoneRetryInterval = 60000;
#define MapsAPIViaProxyScript
#ifdef MapsAPIViaProxyScript
static const char* TimezoneProxyScriptHost = "api.x2software.net";
static const char* TimezoneProxyScriptPath = "/timezone.php";
extern const char* TimezoneProxyScriptHost;
extern const char* TimezoneProxyScriptPath;
#endif
extern const uint8_t InitialisationBrightness;
extern const uint8_t InitialisationFadeTime;
extern const uint8_t InitialisationBlinkCount;
#endif
\ No newline at end of file
......@@ -44,6 +44,17 @@ AsyncWebServer server(80);
PCA9685* pwmDriver;
inline void waitForTransition()
{
while (stairs->inTransition())
{
currentTime = millis();
stairs->tick();
delay(1);
}
}
void setup()
{
_dinit();
......@@ -75,21 +86,17 @@ void setup()
stairs = new Stairs();
stairs->init(pwmDriver);
/*
_dln("Setup :: starting initialization sequence");
stairs->set(0, 255);
delay(300);
uint8_t bottomStep = stepsSettings->count() - 1;
uint8_t stepCount = stepsSettings->count();
for (int step = 1; step < stepCount; step++)
for (uint8_t i = 0; i < InitialisationBlinkCount; i++)
{
stairs->set(step - 1, 0);
stairs->set(step, 255);
delay(300);
}
stairs->set(bottomStep, InitialisationBrightness, InitialisationFadeTime);
waitForTransition();
stairs->set(stepCount - 1, 0);
*/
stairs->set(bottomStep, 0, InitialisationFadeTime);
waitForTransition();
}
_dln("Setup :: initializing WiFi");
WiFi.persistent(false);
......
......@@ -41,6 +41,9 @@ void wifiEvent(WiFiEvent_t event)
case WIFI_EVENT_SOFTAPMODE_STADISCONNECTED:
_dln("WiFi:: soft AP mode: station disconnected"); break;
default:
break;
}
}
......
......@@ -66,7 +66,8 @@ void parseResponse()
return;
}
timezoneOffset = root["rawOffset"];
timezoneOffset = root["rawOffset"].as<uint32_t>() + root["dstOffset"].as<uint32_t>();
hasTimezone = true;
}
......@@ -284,6 +285,7 @@ void updateTimeTrigger()
{
case RelativeToSunrise: _d("sunrise "); break;
case RelativeToSunset: _d("sunset "); break;
default: break;
}
_dln(activeTimeTrigger->time);
}
......@@ -358,9 +360,30 @@ void checkTriggers()
bool timeTriggerChanged = activeTimeTrigger != lastTimeTrigger;
lastTimeTrigger = activeTimeTrigger;
bool inMotionTrigger = (activeMotionStart > 0) && (!inTimeTrigger || motionTriggerSettings->enabledDuringTimeTrigger());
bool motionChanged = (activeMotionStart > 0) != lastMotion;
lastMotion = (activeMotionStart > 0);
bool inMotionTrigger = false;
if (activeMotionStart > 0)
{
switch (motionTriggerSettings->enabledDuringTimeTrigger())
{
case Never:
inMotionTrigger = !inTimeTrigger;
break;
case Always:
inMotionTrigger = true;
break;
case Only:
inMotionTrigger = inTimeTrigger;
break;
}
}
bool motionChanged = inMotionTrigger != lastMotion;
lastMotion = inMotionTrigger;
if (motionTriggerSettingsChanged)
{
......@@ -373,6 +396,10 @@ void checkTriggers()
return;
_d("Triggers :: motionChanged = "); _dln(motionChanged);
_d("Triggers :: timeTriggerChanged = "); _dln(timeTriggerChanged);
if (motionChanged)
{
if (inMotionTrigger)
......
......@@ -21,6 +21,9 @@ extern "C" {
}
bool clearedResetReason = false;
void handleStatus(AsyncWebServerRequest *request)
{
_dln("API :: status");
......@@ -42,7 +45,7 @@ void handleStatus(AsyncWebServerRequest *request)
root["timeOffset"] = 0;
}
root["resetReason"] = ESP.getResetInfoPtr()->reason;
root["resetReason"] = clearedResetReason ? 0 : ESP.getResetInfoPtr()->reason;
root["stackTrace"] = SaveCrash.count() > 0;
AsyncResponseStream *response = request->beginResponseStream("application/json");
......@@ -186,6 +189,7 @@ void handleDeleteStackTrace(AsyncWebServerRequest *request)
{
_dln("API :: delete stack trace");
clearedResetReason = true;
SaveCrash.clear();
request->send(200);
}
......
......@@ -48,10 +48,10 @@ class StepsSettings : public AbstractJsonSettings
void useCurve(bool value) { mUseCurve = value; }
uint16_t rangeStart(uint8_t step) { return step < MaxStepCount ? mRange[step].start : 0; }
uint16_t rangeStart(uint8_t step, uint16_t value) { if (step < MaxStepCount) mRange[step].start = value; }
void rangeStart(uint8_t step, uint16_t value) { if (step < MaxStepCount) mRange[step].start = value; }
uint16_t rangeEnd(uint8_t step) { return step < MaxStepCount ? mRange[step].end : 0; }
uint16_t rangeEnd(uint8_t step, uint16_t value) { if (step < MaxStepCount) mRange[step].end = value; }
void rangeEnd(uint8_t step, uint16_t value) { if (step < MaxStepCount) mRange[step].end = value; }
};
#endif
\ No newline at end of file
......@@ -17,7 +17,7 @@ void MotionTriggerSettings::toJson(Print &print)
JsonObject& root = jsonBuffer.createObject();
root["enabled"] = enabled();
root["enabledDuringTimeTrigger"] = enabledDuringTimeTrigger();
root["enabledDuringTimeTrigger"] = (uint8_t)enabledDuringTimeTrigger();
root["enabledDuringDay"] = enabledDuringDay();
root["transitionTime"] = transitionTime();
root["delay"] = delay();
......@@ -52,8 +52,28 @@ bool MotionTriggerSettings::fromJson(char* data, bool* changed)
enabled(root["enabled"]);
enabledDuringTimeTrigger(root["enabledDuringTimeTrigger"]);
enabledDuringDay(root["enabledDuringDay"]);
JsonVariant jsonEnabledDuringTimeTrigger = root["enabledDuringTimeTrigger"];
if (jsonEnabledDuringTimeTrigger.success())
{
if (jsonEnabledDuringTimeTrigger.is<bool>())
{
// Backwards compatibility
if (jsonEnabledDuringTimeTrigger.as<bool>())
enabledDuringTimeTrigger(MotionDuringTimeTrigger::Always);
else
enabledDuringTimeTrigger(MotionDuringTimeTrigger::Never);
}
else
{
enabledDuringTimeTrigger((MotionDuringTimeTrigger)jsonEnabledDuringTimeTrigger.as<uint8_t>());
}
}
else
enabledDuringTimeTrigger(MotionDuringTimeTrigger::Never);
transitionTime(root["transitionTime"]);
delay(root["delay"]);
......
......@@ -20,6 +20,14 @@ enum MotionDirection
};
enum MotionDuringTimeTrigger
{
Never = 0,
Always = 1,
Only = 2
};
struct MotionTrigger
{
uint8_t pin;
......@@ -33,7 +41,7 @@ class MotionTriggerSettings : public AbstractJsonSettings
{
private:
bool mEnabled = false;
bool mEnabledDuringTimeTrigger = false;
MotionDuringTimeTrigger mEnabledDuringTimeTrigger = MotionDuringTimeTrigger::Never;
bool mEnabledDuringDay = false;
uint16_t mTransitionTime = 500;
uint32_t mDelay = 30000;
......@@ -52,8 +60,8 @@ class MotionTriggerSettings : public AbstractJsonSettings
bool enabled() { return mEnabled; }
void enabled(bool value) { mEnabled = value; }
bool enabledDuringTimeTrigger() { return mEnabledDuringTimeTrigger; }
void enabledDuringTimeTrigger(bool value) { mEnabledDuringTimeTrigger = value; }
MotionDuringTimeTrigger enabledDuringTimeTrigger() { return mEnabledDuringTimeTrigger; }
void enabledDuringTimeTrigger(MotionDuringTimeTrigger value) { mEnabledDuringTimeTrigger = value; }
bool enabledDuringDay() { return mEnabledDuringDay; }
void enabledDuringDay(bool value) { mEnabledDuringDay = value; }
......
......@@ -39,9 +39,8 @@ DayOfWeek toDayOfWeek(timeDayOfWeek_t timeDay)
case dowThursday: return Thursday;
case dowFriday: return Friday;
case dowSaturday: return Saturday;
default: return Monday;
}
return Monday;
}
......@@ -163,6 +162,10 @@ TimeTrigger* TimeTriggerSettings::getActiveTrigger(tmElements_t &time)
case RelativeToSunset:
triggerTime += sunsetMinutes;
break;
case FixedTime:
// No changes (but eliminates warning)
break;
}
// Check if the current time is after the time set in the trigger, and
......
......@@ -204,7 +204,7 @@ function startApp()
motion: {
enabled: false,
enabledDuringTimeTrigger: false,
enabledDuringTimeTrigger: 0,
enabledDuringDay: false,
transitionTime: null,
delay: null,
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -176,9 +176,16 @@
<check v-model.boolean="triggers.motion.enabled" :title="$t('triggers.motionEnabled')"></check>
<div v-if="triggers.motion.enabled">
<check v-model.boolean="triggers.motion.enabledDuringDay" :title="$t('triggers.motionEnabledDuringDay')"></check>
<check v-model.boolean="triggers.motion.enabledDuringTimeTrigger" :title="$t('triggers.motionEnabledDuringTimeTrigger')"></check>
<label for="motionEnabledDuringTimeTrigger">{{ $t('triggers.motionEnabledDuringTimeTrigger') }}</label>
<select v-model="triggers.motion.enabledDuringTimeTrigger" id="motionEnabledDuringTimeTrigger">
<option value="0">{{ $t('triggers.motionEnabledDuringTimeTriggerNever') }}</option>
<option value="1">{{ $t('triggers.motionEnabledDuringTimeTriggerAlways') }}</option>
<option value="2">{{ $t('triggers.motionEnabledDuringTimeTriggerOnly') }}</option>
</select>
<div>
<check v-model.boolean="triggers.motion.enabledDuringDay" :disabled="triggers.motion.enabledDuringTimeTrigger == 2" :title="$t('triggers.motionEnabledDuringDay')"></check>
</div>
<label for="motionTransitionTime">{{ $t('triggers.motionTransitionTime') }}</label>
<input type="number" id="motionTransitionTime" v-model.number="triggers.motion.transitionTime">
......
......@@ -67,7 +67,10 @@ var messages = {
motionNoData: 'No motion triggers defined yet',
motionEnabled: 'Enable motion triggers',
motionEnabledDuringTimeTrigger: 'Activate even if a time trigger is already active',
motionEnabledDuringTimeTrigger: 'Activate when a time trigger is active',
motionEnabledDuringTimeTriggerNever: 'Do not activate when a time trigger is active',
motionEnabledDuringTimeTriggerAlways: 'Activate even if a time trigger is active',
motionEnabledDuringTimeTriggerOnly: 'Activate only when a time trigger is active',
motionEnabledDuringDay: 'Activate during the day (between sunrise and sunset)',
motionTransitionTime: 'Transition time in milliseconds',
motionDelay: 'Keep on time in milliseconds',
......@@ -250,7 +253,10 @@ var messages = {
motionNoData: 'Nog geen beweging triggers geconfigureerd',
motionEnabled: 'Beweging triggers inschakelen',
motionEnabledDuringTimeTrigger: 'Ook inschakelen als er al een tijd trigger actief is',
motionEnabledDuringTimeTrigger: 'Inschakelen als er een tijd trigger actief is',
motionEnabledDuringTimeTriggerNever: 'Niet activeren als er een tijd trigger actief is',
motionEnabledDuringTimeTriggerAlways: 'Activeer ook als er een tijd trigger actief is',
motionEnabledDuringTimeTriggerOnly: 'Alleen activeren als er een tijd trigger actief is',
motionEnabledDuringDay: 'Ook overdag inschakelen (tussen zonsopgang en zonsondergang)',
motionTransitionTime: 'Transitie tijd in milliseconden',
motionDelay: 'Tijd aan in milliseconden',
......
......@@ -128,6 +128,18 @@ button, input
}
@mixin removeSafariStyling
{
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input
{
@include removeSafariStyling;
}
button, .button, input[type=submit]
{
@extend %outset;
......@@ -265,6 +277,8 @@ a.button
cursor: pointer;
user-select: none;
white-space: nowrap;
margin-top: .5em;
margin-bottom: .5em;
.control
{
......@@ -298,6 +312,11 @@ a.button
&.disabled
{
cursor: not-allowed;
.label
{
color: $inputDisabledTextColor;
}
}
}
......@@ -368,6 +387,7 @@ select
@extend %outset;
background: $selectBackground;
color: $inputTextColor;
font-family: 'Verdana', 'Arial', sans-serif;
padding: .5em;
}
......