...
 
Commits (6)
<?php
/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*
* Host this file somewhere on PHP enabled non-secure HTTP webhost
* and modify the config.h to proxy Maps API requests. Saves a ton
* of memory on the ESP8266.
*
* If you care about your API key being sent across plain HTTP,
* host this or a similar proxy on your LAN, perhaps on a Raspberry Pi.
*/
echo file_get_contents('https://maps.googleapis.com/maps/api/timezone/json?' . $_SERVER['QUERY_STRING']);
?>
\ No newline at end of file
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 = 1;
const uint8_t VersionMinor = 2;
const uint8_t VersionPatch = 0;
const uint8_t VersionMetadata = 0;
const char VersionBranch[] = "develop";
const char VersionSemVer[] = "2.1.0-unstable.3";
const char VersionFullSemVer[] = "2.1.0-unstable.3";
const char VersionCommitDate[] = "2018-04-22";
const char VersionSemVer[] = "2.2.0-unstable.1";
const char VersionFullSemVer[] = "2.2.0-unstable.1";
const char VersionCommitDate[] = "2018-04-29";
#endif
......@@ -41,6 +41,6 @@ const char* TimezoneProxyScriptPath = "/timezone.php";
#endif
const uint8_t InitialisationBrightness = 40;
const uint8_t InitialisationBrightness = 128;
const uint8_t InitialisationFadeTime = 250;
const uint8_t InitialisationBlinkCount = 2;
......@@ -44,6 +44,17 @@ AsyncWebServer server(80);
PCA9685* pwmDriver;
inline void waitForTransition()
{
while (stairs->inTransition())
{
currentTime = millis();
stairs->tick();
delay(1);
}
}
void setup()
{
_dinit();
......@@ -81,10 +92,10 @@ void setup()
for (uint8_t i = 0; i < InitialisationBlinkCount; i++)
{
stairs->set(bottomStep, InitialisationBrightness, InitialisationFadeTime);
while (stairs->inTransition()) { stairs->tick(); delay(1); }
waitForTransition();
stairs->set(bottomStep, 0, InitialisationFadeTime);
while (stairs->inTransition()) { stairs->tick(); delay(1); }
waitForTransition();
}
_dln("Setup :: initializing WiFi");
......
......@@ -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);
}
......@@ -302,6 +304,13 @@ bool lastMotion = false;
void updateMotionTrigger()
{
if (motionTriggerSettingsChanged)
{
initMotionPins();
activeMotionStart = 0;
}
if (!motionTriggerSettings->enabled() || !motionTriggerSettings->triggerCount())
{
activeMotionStart = 0;
......@@ -373,14 +382,24 @@ void checkTriggers()
}
}
bool motionChanged = (activeMotionStart > 0) != lastMotion;
lastMotion = (activeMotionStart > 0);
bool motionChanged = inMotionTrigger != lastMotion;
lastMotion = inMotionTrigger;
if (motionTriggerSettingsChanged)
{
motionChanged = true;
motionTriggerSettingsChanged = false;
}
if (!motionChanged && !timeTriggerChanged)
return;
_d("Triggers :: motionChanged = "); _dln(motionChanged);
_d("Triggers :: timeTriggerChanged = "); _dln(timeTriggerChanged);
if (motionChanged)
{
if (inMotionTrigger)
......
......@@ -52,6 +52,7 @@ bool MotionTriggerSettings::fromJson(char* data, bool* changed)
enabled(root["enabled"]);
enabledDuringDay(root["enabledDuringDay"]);
JsonVariant jsonEnabledDuringTimeTrigger = root["enabledDuringTimeTrigger"];
if (jsonEnabledDuringTimeTrigger.success())
......
function startApp()
{
// Source: https://github.com/axios/axios/issues/164
axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) {
var config = err.config;
// If config does not exist or the retry option is not set, reject
if(!config || !config.retry) return Promise.reject(err);
// Set the variable for keeping track of the retry count
config.__retryCount = config.__retryCount || 0;
// Check if we've maxed out the total number of retries
if(config.__retryCount >= config.retry) {
// Reject with the error
return Promise.reject(err);
}
// Increase the retry count
config.__retryCount += 1;
// Create new promise to handle exponential backoff
var backoff = new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, config.retryDelay || 1);
});
// Return the promise in which recalls axios to retry the request
return backoff.then(function() {
return axios(config);
});
});
Vue.component('check', {
template: '<div class="check" :class="{ checked: value, disabled: disabled }" @keydown="handleKeyDown" @click="handleClick" tabindex="0"><div class="control"><div class="inner"></div></div><div class="label">{{ title }}</div></div>',
props: {
......@@ -119,7 +150,7 @@ function startApp()
});
var i18n = new VueI18n({
locale: navigator.language,
locale: navigator.language.split('-')[0],
fallbackLocale: 'en',
messages: messages
});
......@@ -338,7 +369,7 @@ function startApp()
loadStatus: function()
{
var self = this;
return axios.get('/api/status')
return axios.get('/api/status', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (typeof response.data == 'object')
......@@ -350,7 +381,7 @@ function startApp()
loadConnection: function()
{
var self = this;
return axios.get('/api/connection')
return axios.get('/api/connection', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (typeof response.data == 'object')
......@@ -362,7 +393,7 @@ function startApp()
loadSystem: function()
{
var self = this;
return axios.get('/api/system')
return axios.get('/api/system', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (typeof response.data == 'object')
......@@ -374,7 +405,7 @@ function startApp()
loadTimeTriggers: function()
{
var self = this;
return axios.get('/api/triggers/time')
return axios.get('/api/triggers/time', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (typeof response.data == 'object')
......@@ -419,7 +450,7 @@ function startApp()
loadMotionTriggers: function()
{
var self = this;
return axios.get('/api/triggers/motion')
return axios.get('/api/triggers/motion', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (typeof response.data == 'object')
......@@ -431,7 +462,7 @@ function startApp()
loadSteps: function()
{
var self = this;
return axios.get('/api/steps/values')
return axios.get('/api/steps/values', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (Array.isArray(response.data))
......@@ -481,7 +512,7 @@ function startApp()
ip: self.connection.ip,
subnetmask: self.connection.subnetmask,
gateway: self.connection.gateway,
})
}, { retry: 10, retryDelay: 1000 })
.then(function(response)
{
})
......@@ -499,7 +530,7 @@ function startApp()
self.saving = true;
axios.post('/api/system', self.system)
axios.post('/api/system', self.system, { retry: 10, retryDelay: 1000 })
.then(function(response)
{
self.showNotification(i18n.t('rebootPending'));
......@@ -595,7 +626,7 @@ function startApp()
var self = this;
if (!self.saving)
{
axios.get('/api/connection/status')
axios.get('/api/connection/status', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (typeof response.data == 'object')
......@@ -693,7 +724,9 @@ function startApp()
axios.post('/api/steps/values', {
transitionTime: 1000,
values: steps
values: steps,
retry: 10,
retryDelay: 1000
})
.then(function(response)
{
......@@ -761,7 +794,7 @@ function startApp()
});
}
axios.post('/api/triggers/time', timeSettings)
axios.post('/api/triggers/time', timeSettings, { retry: 10, retryDelay: 1000 })
.then(function(response)
{
})
......@@ -806,7 +839,7 @@ function startApp()
self.saving = true;
axios.post('/api/triggers/motion', self.triggers.motion)
axios.post('/api/triggers/motion', self.triggers.motion, { retry: 10, retryDelay: 1000 })
.then(function(response)
{
})
......@@ -857,7 +890,7 @@ function startApp()
{
var self = this;
axios.get('/api/steps')
axios.get('/api/steps', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
if (typeof response.data == 'object')
......@@ -945,7 +978,7 @@ function startApp()
count: self.calibration.count,
useCurve: self.calibration.useCurve,
ranges: self.calibration.ranges
})
}, { retry: 10, retryDelay: 1000 })
.then(function(response)
{
})
......@@ -960,7 +993,7 @@ function startApp()
{
var self = this;
return axios.get('/api/stacktrace/delete')
return axios.get('/api/stacktrace/delete', { retry: 10, retryDelay: 1000 })
.then(function(response)
{
self.status.resetReason = 0;
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -54,10 +54,8 @@
{{ $t('error.stackTrace') }}
</p>
<div v-if="status.stackTrace">
<a class="button button-primary" href="/api/stacktrace/get">{{ $t('error.stackTraceDownload') }}</a>
<a class="button" @click="deleteStackTrace">{{ $t('error.stackTraceDelete') }}</a>
</div>
<a class="button button-primary" href="/api/stacktrace/get" v-if="status.stackTrace">{{ $t('error.stackTraceDownload') }}</a>
<a class="button" @click="deleteStackTrace">{{ $t('error.stackTraceDelete') }}</a>
</div>
......@@ -185,7 +183,9 @@
<option value="2">{{ $t('triggers.motionEnabledDuringTimeTriggerOnly') }}</option>
</select>
<check v-model.boolean="triggers.motion.enabledDuringDay" :title="$t('triggers.motionEnabledDuringDay')"></check>
<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">
......
......@@ -168,7 +168,7 @@ var messages = {
},
stackTrace: 'A stack trace is available. Please send it to your nearest developer and/or delete it from this Stairs device to remove this message.',
stackTraceDownload: 'Download',
stackTraceDelete: 'Remove',
stackTraceDelete: 'Hide',
stackTraceDeleteError: 'Could not remove stack trace'
},
......@@ -354,7 +354,7 @@ var messages = {
},
stackTrace: 'Een stack trace is beschikbaar. Stuur het naar de dichtsbijzijnde ontwikkelaar en/of verwijder het van deze Trap module om dit bericht te verbergen.',
stackTraceDownload: 'Downloaden',
stackTraceDelete: 'Verwijderen',
stackTraceDelete: 'Verbergen',
stackTraceDeleteError: 'Kan stack trace niet verwijderen'
},
......
......@@ -127,13 +127,19 @@ button, input
font-family: 'Verdana', 'Arial', sans-serif;
}
input
@mixin removeSafariStyling
{
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input
{
@include removeSafariStyling;
}
button, .button, input[type=submit]
{
@extend %outset;
......@@ -306,6 +312,11 @@ a.button
&.disabled
{
cursor: not-allowed;
.label
{
color: $inputDisabledTextColor;
}
}
}
......