mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
fix: move only the tilt of a tilt-only shade for sun and wind
The three places that act on the sun and wind each set different targets for a tilt-only shade: the sun timer moved its lift, the no-sun and wind timers moved tilt and lift, a group's sun flag moved its lift and only a remote's sun flag moved its tilt. They now share sensorTargets(): a tilt-only shade moves its tilt to the favorite tilt (or fully) and back to 0, and every other shade its lift, as Up and Down already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8276c1c3cd
commit
5f26340d06
5 changed files with 83 additions and 37 deletions
|
|
@ -72,6 +72,20 @@ SensorMove ShadeSensors::onSunFlag(bool sunny, bool windy) const {
|
||||||
if(!sunny && this->noSunDone) return SensorMove::retract;
|
if(!sunny && this->noSunDone) return SensorMove::retract;
|
||||||
return SensorMove::none;
|
return SensorMove::none;
|
||||||
}
|
}
|
||||||
|
SensorTargets sensorTargets(SensorMove move, tilt_types tilt, float myPos, float myTiltPos) {
|
||||||
|
SensorTargets t;
|
||||||
|
if(move == SensorMove::none) return t;
|
||||||
|
const bool extend = move == SensorMove::extend;
|
||||||
|
if(tilt == tilt_types::tiltonly) {
|
||||||
|
t.setTilt = true;
|
||||||
|
t.tilt = extend ? (myTiltPos >= 0 ? myTiltPos : 100.0f) : 0.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
t.setLift = true;
|
||||||
|
t.lift = extend ? (myPos >= 0 ? myPos : 100.0f) : 0.0f;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
bool ShadeSensors::windBlocksDown(uint32_t now) const {
|
bool ShadeSensors::windBlocksDown(uint32_t now) const {
|
||||||
return this->windLast != 0 && now - this->windLast < SOMFY_NO_WIND_REMOTE_TIMEOUT;
|
return this->windLast != 0 && now - this->windLast < SOMFY_NO_WIND_REMOTE_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef SHADE_SENSORS_H
|
#ifndef SHADE_SENSORS_H
|
||||||
#define SHADE_SENSORS_H
|
#define SHADE_SENSORS_H
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "ShadeTypes.h"
|
||||||
|
|
||||||
// Sun and wind automation for a shade with a sun sensor. Kept free of the
|
// Sun and wind automation for a shade with a sun sensor. Kept free of the
|
||||||
// shade, radio and network so it can be unit tested on the PC
|
// shade, radio and network so it can be unit tested on the PC
|
||||||
|
|
@ -49,4 +50,14 @@ struct ShadeSensors {
|
||||||
// Whether a remote Down is ignored because the sensor reported wind lately.
|
// Whether a remote Down is ignored because the sensor reported wind lately.
|
||||||
bool windBlocksDown(uint32_t now) const;
|
bool windBlocksDown(uint32_t now) const;
|
||||||
};
|
};
|
||||||
|
// The targets a sensor move sets. A tilt-only shade only moves its tilt and
|
||||||
|
// every other shade only its lift, as Up and Down do.
|
||||||
|
struct SensorTargets {
|
||||||
|
bool setLift = false;
|
||||||
|
float lift = 0.0f;
|
||||||
|
bool setTilt = false;
|
||||||
|
float tilt = 0.0f;
|
||||||
|
};
|
||||||
|
// myPos and myTiltPos are the favorite, -1 when there is none.
|
||||||
|
SensorTargets sensorTargets(SensorMove move, tilt_types tilt, float myPos, float myTiltPos);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,8 @@ class SomfyShade : public SomfyRemote {
|
||||||
// Repeats for a tilt command, more for a separate tilt motor.
|
// Repeats for a tilt command, more for a separate tilt motor.
|
||||||
uint8_t tiltRepeats() const;
|
uint8_t tiltRepeats() const;
|
||||||
void runToLimit(int8_t dir, bool tilt, uint32_t since);
|
void runToLimit(int8_t dir, bool tilt, uint32_t since);
|
||||||
|
// Sets the targets the sun or wind sends the shade to.
|
||||||
|
void applySensorMove(SensorMove move);
|
||||||
public:
|
public:
|
||||||
uint8_t roomId = 0;
|
uint8_t roomId = 0;
|
||||||
int8_t sortOrder = 0;
|
int8_t sortOrder = 0;
|
||||||
|
|
|
||||||
|
|
@ -152,17 +152,7 @@ void SomfyShade::checkMovement() {
|
||||||
case TxHoldCheck::none:
|
case TxHoldCheck::none:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch(this->sensors.check(sunFlag, isSunny, isWindy, (uint32_t)curTime)) {
|
this->applySensorMove(this->sensors.check(sunFlag, isSunny, isWindy, (uint32_t)curTime));
|
||||||
case SensorMove::extend:
|
|
||||||
this->p_target(this->myPos >= 0 ? this->myPos : 100.0f);
|
|
||||||
break;
|
|
||||||
case SensorMove::retract:
|
|
||||||
if(this->tiltType == tilt_types::tiltonly) this->p_tiltTarget(0.0f);
|
|
||||||
this->p_target(0.0f);
|
|
||||||
break;
|
|
||||||
case SensorMove::none:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!tilt_first && this->direction > 0) {
|
if(!tilt_first && this->direction > 0) {
|
||||||
this->p_currentPos(travelPosition(this->startPos, 1, (uint32_t)(curTime - this->moveStart), downTime));
|
this->p_currentPos(travelPosition(this->startPos, 1, (uint32_t)(curTime - this->moveStart), downTime));
|
||||||
|
|
@ -609,22 +599,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
||||||
const bool isSunny = this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny);
|
const bool isSunny = this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny);
|
||||||
//this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
|
//this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
|
||||||
this->p_sunFlag(true);
|
this->p_sunFlag(true);
|
||||||
switch(this->sensors.onSunFlag(isSunny, isWindy)) {
|
this->applySensorMove(this->sensors.onSunFlag(isSunny, isWindy));
|
||||||
case SensorMove::extend:
|
|
||||||
if(this->tiltType == tilt_types::tiltonly)
|
|
||||||
this->p_tiltTarget(this->myTiltPos >= 0 ? this->myTiltPos : 100.0f);
|
|
||||||
else
|
|
||||||
this->p_target(this->myPos >= 0 ? this->myPos : 100.0f);
|
|
||||||
break;
|
|
||||||
case SensorMove::retract:
|
|
||||||
if(this->tiltType == tilt_types::tiltonly)
|
|
||||||
this->p_tiltTarget(0.0f);
|
|
||||||
else
|
|
||||||
this->p_target(0.0f);
|
|
||||||
break;
|
|
||||||
case SensorMove::none:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
somfy.isDirty = true;
|
somfy.isDirty = true;
|
||||||
this->emitState();
|
this->emitState();
|
||||||
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
|
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
|
||||||
|
|
@ -1007,16 +982,7 @@ void SomfyShade::processInternalCommand(somfy_commands cmd, uint8_t repeat) {
|
||||||
const bool isSunny = this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny);
|
const bool isSunny = this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny);
|
||||||
this->p_sunFlag(true);
|
this->p_sunFlag(true);
|
||||||
//this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
|
//this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
|
||||||
switch(this->sensors.onSunFlag(isSunny, isWindy)) {
|
this->applySensorMove(this->sensors.onSunFlag(isSunny, isWindy));
|
||||||
case SensorMove::extend:
|
|
||||||
this->p_target(this->myPos >= 0 ? this->myPos : 100.0f);
|
|
||||||
break;
|
|
||||||
case SensorMove::retract:
|
|
||||||
this->p_target(0.0f);
|
|
||||||
break;
|
|
||||||
case SensorMove::none:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
somfy.isDirty = true;
|
somfy.isDirty = true;
|
||||||
this->emitState();
|
this->emitState();
|
||||||
}
|
}
|
||||||
|
|
@ -1126,6 +1092,11 @@ void SomfyShade::runToLimit(int8_t dir, bool tilt, uint32_t since) {
|
||||||
// The favorite can't be set where the shade won't stop.
|
// The favorite can't be set where the shade won't stop.
|
||||||
this->settingMyPos = false;
|
this->settingMyPos = false;
|
||||||
}
|
}
|
||||||
|
void SomfyShade::applySensorMove(SensorMove move) {
|
||||||
|
const SensorTargets t = sensorTargets(move, this->tiltType, this->myPos, this->myTiltPos);
|
||||||
|
if(t.setLift) this->p_target(t.lift);
|
||||||
|
if(t.setTilt) this->p_tiltTarget(t.tilt);
|
||||||
|
}
|
||||||
void SomfyShade::setMovement(int8_t dir) {
|
void SomfyShade::setMovement(int8_t dir) {
|
||||||
int8_t currDir = this->direction;
|
int8_t currDir = this->direction;
|
||||||
int8_t currTiltDir = this->tiltDirection;
|
int8_t currTiltDir = this->tiltDirection;
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,49 @@ static void test_timers_run_across_a_millis_rollover() {
|
||||||
TEST_ASSERT_TRUE(s.windBlocksDown(start + 2000));
|
TEST_ASSERT_TRUE(s.windBlocksDown(start + 2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* sensorTargets
|
||||||
|
********************************************************************/
|
||||||
|
#define ASSERT_POS(expected, actual) TEST_ASSERT_FLOAT_WITHIN(0.01f, (expected), (actual))
|
||||||
|
static const float NO_FAVORITE = -1.0f;
|
||||||
|
|
||||||
|
static void test_lift_shade_extends_to_its_favorite() {
|
||||||
|
SensorTargets t = sensorTargets(SensorMove::extend, tilt_types::none, 60.0f, NO_FAVORITE);
|
||||||
|
TEST_ASSERT_TRUE(t.setLift);
|
||||||
|
ASSERT_POS(60.0f, t.lift);
|
||||||
|
TEST_ASSERT_FALSE(t.setTilt);
|
||||||
|
}
|
||||||
|
static void test_lift_shade_extends_fully_without_a_favorite() {
|
||||||
|
SensorTargets t = sensorTargets(SensorMove::extend, tilt_types::integrated, NO_FAVORITE, 30.0f);
|
||||||
|
TEST_ASSERT_TRUE(t.setLift);
|
||||||
|
ASSERT_POS(100.0f, t.lift);
|
||||||
|
TEST_ASSERT_FALSE(t.setTilt);
|
||||||
|
}
|
||||||
|
static void test_lift_shade_retracts_fully() {
|
||||||
|
SensorTargets t = sensorTargets(SensorMove::retract, tilt_types::tiltmotor, 60.0f, 30.0f);
|
||||||
|
TEST_ASSERT_TRUE(t.setLift);
|
||||||
|
ASSERT_POS(0.0f, t.lift);
|
||||||
|
TEST_ASSERT_FALSE(t.setTilt);
|
||||||
|
}
|
||||||
|
static void test_tilt_only_shade_only_moves_its_tilt() {
|
||||||
|
SensorTargets t = sensorTargets(SensorMove::extend, tilt_types::tiltonly, 60.0f, 30.0f);
|
||||||
|
TEST_ASSERT_FALSE(t.setLift);
|
||||||
|
TEST_ASSERT_TRUE(t.setTilt);
|
||||||
|
ASSERT_POS(30.0f, t.tilt);
|
||||||
|
t = sensorTargets(SensorMove::extend, tilt_types::tiltonly, 60.0f, NO_FAVORITE);
|
||||||
|
TEST_ASSERT_FALSE(t.setLift);
|
||||||
|
ASSERT_POS(100.0f, t.tilt);
|
||||||
|
t = sensorTargets(SensorMove::retract, tilt_types::tiltonly, 60.0f, 30.0f);
|
||||||
|
TEST_ASSERT_FALSE(t.setLift);
|
||||||
|
TEST_ASSERT_TRUE(t.setTilt);
|
||||||
|
ASSERT_POS(0.0f, t.tilt);
|
||||||
|
}
|
||||||
|
static void test_no_move_sets_nothing() {
|
||||||
|
SensorTargets t = sensorTargets(SensorMove::none, tilt_types::tiltonly, 60.0f, 30.0f);
|
||||||
|
TEST_ASSERT_FALSE(t.setLift);
|
||||||
|
TEST_ASSERT_FALSE(t.setTilt);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
(void)argc;
|
(void)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
|
|
@ -155,5 +198,10 @@ int main(int argc, char **argv) {
|
||||||
RUN_TEST(test_sun_flag_does_nothing_in_wind);
|
RUN_TEST(test_sun_flag_does_nothing_in_wind);
|
||||||
RUN_TEST(test_clear_forgets_pending_timers);
|
RUN_TEST(test_clear_forgets_pending_timers);
|
||||||
RUN_TEST(test_timers_run_across_a_millis_rollover);
|
RUN_TEST(test_timers_run_across_a_millis_rollover);
|
||||||
|
RUN_TEST(test_lift_shade_extends_to_its_favorite);
|
||||||
|
RUN_TEST(test_lift_shade_extends_fully_without_a_favorite);
|
||||||
|
RUN_TEST(test_lift_shade_retracts_fully);
|
||||||
|
RUN_TEST(test_tilt_only_shade_only_moves_its_tilt);
|
||||||
|
RUN_TEST(test_no_move_sets_nothing);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue