src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
583 / 811
71.9%
Branches:
448 / 838
53.5%
Functions:
41 / 43
95.3%

ShadeBehaviour.cpp
Line Branch Exec Source
1 #include "esp_log.h"
2 #include "ShadeBehaviour.h"
3
4 static const char *TAG = "Somfy";
5
6 #define SETMY_REPEATS 35
7 #define TILT_REPEATS 15
8
9 // How long a shade's simulated movement waits for its command to go on air
10 // before it starts anyway in case the event was lost.
11 #define TX_PENDING_TIMEOUT 30000
12
13 55 ShadeBehaviour::ShadeBehaviour(const ShadeSettings &settings, const RemoteSettings &remote, somfy_frame_t &lastFrame,
14 55 MotorLink &link, ShadeEvents &events, const uint8_t &shadeId, const char *name)
15 55 : settings(settings), remote(remote), lastFrame(lastFrame), link(link), events(events), shadeId(shadeId), name(name) {}
16 ✗ void ShadeBehaviour::clear() {
17 ✗ this->moveStart = 0;
18 ✗ this->tiltStart = 0;
19 ✗ this->sensors.clear();
20 ✗ this->startPos = 0.0f;
21 ✗ this->startTiltPos = 0.0f;
22 ✗ this->settingMyPos = false;
23 ✗ this->settingPos = false;
24 ✗ this->settingTiltPos = false;
25 ✗ this->awaitMy = 0;
26 ✗ this->s.currentPos = 0.0f;
27 ✗ this->s.currentTiltPos = 0.0f;
28 ✗ this->s.direction = 0;
29 ✗ this->s.tiltDirection = 0;
30 ✗ this->s.target = 0.0f;
31 ✗ this->s.tiltTarget = 0.0f;
32 ✗ this->s.myPos = -1.0f;
33 ✗ this->s.myTiltPos = -1.0f;
34 ✗ }
35 19 void ShadeBehaviour::setPosition(float pos, float tilt) {
36 // Through the setters, so MQTT hears about it too.
37
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if(pos >= 0.0f) {
38 19 this->p_target(pos);
39 19 this->p_currentPos(pos);
40 }
41
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if(tilt >= 0.0f) {
42 ✗ this->p_tiltTarget(tilt);
43 ✗ this->p_currentTiltPos(tilt);
44 }
45 19 }
46 32 void ShadeBehaviour::restore(float pos, float tiltPos, float myPos, float myTiltPos) {
47 32 this->s.currentPos = pos;
48 32 this->s.currentTiltPos = tiltPos;
49 32 this->s.myPos = myPos;
50 32 this->s.myTiltPos = myTiltPos;
51 32 this->s.target = floor(pos);
52 32 this->s.tiltTarget = floor(tiltPos);
53 32 }
54 1 void ShadeBehaviour::clearTilt() {
55 1 this->s.myTiltPos = -1;
56 1 this->s.currentTiltPos = 0;
57 1 }
58
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 138 times.
139 somfy_commands ShadeBehaviour::transformCommand(somfy_commands cmd) const { return this->remote.flipCommands ? flipCommand(cmd) : cmd; }
59 41 send_result_t ShadeBehaviour::transmit(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint16_t delayMs, bool priority, uint32_t now) {
60
1/2
✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
41 const send_result_t result = this->link.send(cmd, repeat, stepSize, delayMs, priority);
61
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 39 times.
41 if(!result.sent) return result;
62 // The link has put the frame in lastFrame and every other shade has heard
63 // it. The shade hears it too, as if a remote had sent it.
64
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 this->onFrame(this->lastFrame, true, now);
65 // An idle shade this sets moving waits for the frame to go on air. Stops
66 // are sent ahead of everything else so they don't need to.
67
10/12
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 8 times.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 29 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 29 times.
✓ Branch 10 taken 10 times.
✓ Branch 12 taken 29 times.
✗ Branch 13 not taken.
39 if(result.ticket != 0 && !priority && startsMovement(this->lastFrame.cmd)) this->onTxQueued(result.ticket, now);
68 39 return result;
69 }
70 495 bool ShadeBehaviour::isAtTarget() const {
71 495 float epsilon = .00001;
72
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 484 times.
495 if(this->settings.tiltType == tilt_types::tiltonly) return fabs(this->s.currentTiltPos - this->s.tiltTarget) < epsilon;
73
2/2
✓ Branch 0 taken 475 times.
✓ Branch 1 taken 9 times.
484 else if(this->settings.tiltType == tilt_types::none) return fabs(this->s.currentPos - this->s.target) < epsilon;
74
3/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
9 return fabs(this->s.currentPos - this->s.target) < epsilon && fabs(this->s.currentTiltPos - this->s.tiltTarget) < epsilon;
75 }
76 16350 void ShadeBehaviour::tick(uint32_t now) {
77 16350 const uint32_t curTime = now;
78 16350 const bool sunFlag = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::SunFlag);
79 16350 const bool isSunny = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Sunny);
80 16350 const bool isWindy = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Windy);
81 // We need to first evaluate the sensor flags as these could be triggering movement from previous sensor inputs. So
82 // we must check this before setting the directional items or it will not get processed until the next loop.
83 16350 int32_t downTime = (int32_t)this->settings.downTime;
84 16350 int32_t upTime = (int32_t)this->settings.upTime;
85 16350 int32_t tiltTime = (int32_t)this->settings.tiltTime;
86
2/4
✓ Branch 0 taken 16350 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16350 times.
16350 if(this->settings.shadeType == shade_types::drycontact || this->settings.shadeType == shade_types::drycontact2) downTime = upTime = tiltTime = 1;
87
88
89 // We are checking movement for essentially 3 types of motors.
90 // If this is an integrated tilt we need to first tilt in the direction we are moving then move. We know
91 // what needs to be done by the tilt type. Set a tilt first flag to indicate whether we should be tilting or
92 // moving. If this is only a tilt action then the regular tilt action should operate fine.
93 16350 int8_t currDir = this->s.direction;
94 16350 int8_t currTiltDir = this->s.tiltDirection;
95
4/4
✓ Branch 0 taken 12170 times.
✓ Branch 1 taken 4180 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 12150 times.
16350 this->p_direction(this->s.currentPos == this->s.target ? 0 : this->s.currentPos > this->s.target ? -1 : 1);
96
7/10
✓ Branch 0 taken 2040 times.
✓ Branch 1 taken 14310 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2040 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1790 times.
✓ Branch 7 taken 250 times.
✓ Branch 8 taken 400 times.
✓ Branch 9 taken 1390 times.
16350 bool tilt_first = this->settings.tiltType == tilt_types::integrated && ((this->s.direction == -1 && this->s.currentTiltPos != 0.0f) || (this->s.direction == 1 && this->s.currentTiltPos != 100.0f));
97
98
4/4
✓ Branch 0 taken 3190 times.
✓ Branch 1 taken 13160 times.
✓ Branch 2 taken 1820 times.
✓ Branch 3 taken 1370 times.
16350 this->p_tiltDirection(this->s.currentTiltPos == this->s.tiltTarget ? 0 : this->s.currentTiltPos > this->s.tiltTarget ? -1 : 1);
99
2/2
✓ Branch 0 taken 400 times.
✓ Branch 1 taken 15950 times.
16350 if(tilt_first) this->p_tiltDirection(this->s.direction);
100
2/2
✓ Branch 0 taken 11770 times.
✓ Branch 1 taken 4180 times.
15950 else if(this->s.direction != 0) this->p_tiltDirection(0);
101 16350 uint8_t currPos = floor(this->s.currentPos);
102 16350 uint8_t currTiltPos = floor(this->s.currentTiltPos);
103
2/2
✓ Branch 0 taken 12170 times.
✓ Branch 1 taken 4180 times.
16350 if(this->s.direction != 0) this->s.lastMovement = this->s.direction;
104
7/8
✓ Branch 0 taken 4180 times.
✓ Branch 1 taken 12170 times.
✓ Branch 2 taken 2391 times.
✓ Branch 3 taken 1789 times.
✓ Branch 5 taken 3199 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 13150 times.
✗ Branch 8 not taken.
16350 switch(this->tx.hold.check(this->s.direction != 0 || this->s.tiltDirection != 0, (uint32_t)curTime, TX_PENDING_TIMEOUT)) {
105 3199 case TxHoldCheck::holding:
106 // The command that set us moving hasn't gone on air yet so the motor
107 // hasn't started. Hold the simulated position until it does.
108 3199 this->moveStart = this->tiltStart = curTime;
109
3/4
✓ Branch 0 taken 3197 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3197 times.
3199 if(currDir != this->s.direction || currTiltDir != this->s.tiltDirection) this->emitState();
110 3199 return;
111 1 case TxHoldCheck::expired:
112 ESP_LOGW(TAG, "[Shade %u] Timed out waiting for the radio, moving anyway", this->shadeId);
113 1 break;
114 13150 case TxHoldCheck::none:
115 13150 break;
116 }
117 13151 this->applySensorMove(this->sensors.check(sunFlag, isSunny, isWindy, (uint32_t)curTime));
118
119
4/4
✓ Branch 0 taken 12751 times.
✓ Branch 1 taken 400 times.
✓ Branch 2 taken 8551 times.
✓ Branch 3 taken 4200 times.
13151 if(!tilt_first && this->s.direction > 0) {
120 8551 this->p_currentPos(travelPosition(this->startPos, 1, (uint32_t)(curTime - this->moveStart), downTime));
121
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 8536 times.
8551 if(this->s.currentPos >= this->s.target) {
122 15 this->p_currentPos(this->s.target);
123 //if(this->settingMyPos) Serial.printf("IsAtTarget: %d %f=%f\n", this->isAtTarget(), this->s.currentPos, this->s.target);
124 // If we need to stop the shade do this before we indicate that we are
125 // not moving otherwise the my function will kick in.
126
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 7 times.
15 if(this->settingPos) {
127 // At the end limit the motor stops by itself. If the stop doesn't go
128 // out the shade is now running on to the limit so leave the tilt be.
129
5/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 1 time.
8 if(this->s.target == 100.0 || this->sendStop(1, false, now)) {
130
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 6 times.
7 if(!isAtTarget()) {
131 ESP_LOGD(TAG, "We are not at our tilt target: %.2f", this->s.tiltTarget);
132 // We now need to move the tilt to the position we requested.
133 1 this->moveToTiltTarget(this->s.tiltTarget, now);
134 }
135 }
136 }
137 15 this->p_direction(0);
138 15 this->settingPos = false;
139 15 this->tiltStart = curTime;
140 15 this->startTiltPos = this->s.currentTiltPos;
141
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 2 times.
15 if(this->isAtTarget()) this->commitShadePosition();
142 }
143 }
144
4/4
✓ Branch 0 taken 4200 times.
✓ Branch 1 taken 400 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 4180 times.
4600 else if(!tilt_first && this->s.direction < 0) {
145 // Shades often move slower up so this uses its own travel time.
146 20 this->p_currentPos(travelPosition(this->startPos, -1, (uint32_t)(curTime - this->moveStart), upTime));
147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if(this->s.currentPos <= this->s.target) {
148 ✗ this->p_currentPos(this->s.target);
149 //if(this->settingMyPos) Serial.printf("IsAtTarget: %d %f=%f\n", this->isAtTarget(), this->s.currentPos, this->s.target);
150
151 // If we need to stop the shade do this before we indicate that we are
152 // not moving otherwise the my function will kick in.
153
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->settingPos) {
154 // At the end limit the motor stops by itself. If the stop doesn't go
155 // out the shade is now running on to the limit so leave the tilt be.
156
0/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ if(this->s.target == 0.0 || this->sendStop(-1, false, now)) {
157
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(!isAtTarget()) {
158 ESP_LOGD(TAG, "We are not at our tilt target: %.2f", this->s.tiltTarget);
159 // We now need to move the tilt to the position we requested.
160 ✗ this->moveToTiltTarget(this->s.tiltTarget, now);
161 }
162 }
163 }
164 ✗ this->p_direction(0);
165 ✗ this->settingPos = false;
166 ✗ this->tiltStart = curTime;
167 ✗ this->startTiltPos = this->s.currentTiltPos;
168
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(this->isAtTarget()) this->commitShadePosition();
169 }
170 }
171
2/2
✓ Branch 0 taken 1570 times.
✓ Branch 1 taken 11581 times.
13151 if(this->s.tiltDirection > 0) {
172
2/2
✓ Branch 0 taken 400 times.
✓ Branch 1 taken 1170 times.
1570 if(tilt_first) this->moveStart = curTime;
173 1570 this->p_currentTiltPos(travelPosition(this->startTiltPos, 1, (uint32_t)(curTime - this->tiltStart), tiltTime));
174
2/2
✓ Branch 0 taken 400 times.
✓ Branch 1 taken 1170 times.
1570 if(tilt_first) {
175
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 398 times.
400 if(this->s.currentTiltPos >= 100.0f) {
176 2 this->p_currentTiltPos(100.0f);
177 2 this->moveStart = curTime;
178 2 this->startPos = this->s.currentPos;
179 //this->p_tiltDirection(0);
180 //Serial.println("Setting tiltDirection to 0 (tilt_first)");
181 }
182 }
183
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1170 times.
1170 else if(this->s.currentTiltPos >= this->s.tiltTarget) {
184 ✗ this->p_currentTiltPos(this->s.tiltTarget);
185 // If we need to stop the shade do this before we indicate that we are
186 // not moving otherwise the my function will kick in.
187
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->settingTiltPos) {
188
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->settings.tiltType == tilt_types::integrated) {
189 // If this is an integrated tilt mechanism the we will simply let it finish. If it is not then we will stop it.
190 //Serial.printf("Sending My -- tiltTarget: %.2f, tiltDirection: %d\n", this->s.tiltTarget, this->s.tiltDirection);
191
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(this->s.tiltTarget != 100.0f || this->s.currentPos != 100.0f) this->sendStop(1, true, now);
192 }
193 else {
194 // This is a tilt motor so let it complete if it is going to 100.
195
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.tiltTarget != 100.0f) this->sendStop(1, true, now);
196 }
197 }
198 ✗ this->p_tiltDirection(0);
199 ✗ this->settingTiltPos = false;
200
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(this->isAtTarget()) this->commitShadePosition();
201 }
202 }
203
2/2
✓ Branch 0 taken 1221 times.
✓ Branch 1 taken 10360 times.
11581 else if(this->s.tiltDirection < 0) {
204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1221 times.
1221 if(tilt_first) this->moveStart = curTime;
205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1221 times.
1221 if(tiltTime == 0) this->p_tiltDirection(0);
206 1221 this->p_currentTiltPos(travelPosition(this->startTiltPos, -1, (uint32_t)(curTime - this->tiltStart), tiltTime));
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1221 times.
1221 if(tilt_first) {
208
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.currentTiltPos <= 0.0f) {
209 ✗ this->p_currentTiltPos(0.0f);
210 ✗ this->moveStart = curTime;
211 ✗ this->startPos = this->s.currentPos;
212 //this->p_tiltDirection(0);
213 }
214 }
215
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1219 times.
1221 else if(this->s.currentTiltPos <= this->s.tiltTarget) {
216 2 this->p_currentTiltPos(this->s.tiltTarget);
217 // If we need to stop the shade do this before we indicate that we are
218 // not moving otherwise the my function will kick in.
219
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if(this->settingTiltPos) {
220
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if(this->settings.tiltType == tilt_types::integrated) {
221 // If this is an integrated tilt mechanism the we will simply let it finish. If it is not then we will stop it.
222 //Serial.printf("Sending My -- tiltTarget: %.2f, tiltDirection: %d\n", this->s.tiltTarget, this->s.tiltDirection);
223
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 if(this->s.tiltTarget != 0.0 || this->s.currentPos != 0.0) this->sendStop(-1, true, now);
224 }
225 else {
226 // This is a tilt motor so let it complete if it is going to 0.
227
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.tiltTarget != 0.0) this->sendStop(-1, true, now);
228 }
229 }
230 2 this->p_tiltDirection(0);
231 2 this->settingTiltPos = false;
232 ESP_LOGI(TAG, "Stopping at tilt position");
233
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if(this->isAtTarget()) this->commitShadePosition();
234 }
235 }
236
6/6
✓ Branch 0 taken 400 times.
✓ Branch 1 taken 12751 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 399 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 13150 times.
13151 if(this->settingMyPos && this->isAtTarget()) {
237 // Set this position before sending the command. If you don't the processFrame function
238 // will send the shade back to its original My position.
239 1 const float oldMyPos = this->s.myPos;
240 1 const float oldMyTiltPos = this->s.myTiltPos;
241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->settings.tiltType != tilt_types::none) {
242
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(this->s.myTiltPos == this->s.currentTiltPos && this->s.myPos == this->s.currentPos) this->s.myPos = this->s.myTiltPos = -1;
243 else {
244 ✗ this->p_myPos(this->s.currentPos);
245 ✗ this->p_myTiltPos(this->s.currentTiltPos);
246 }
247 }
248 else {
249 1 this->p_myTiltPos(-1);
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->s.myPos == this->s.currentPos) this->p_myPos(-1);
251 1 else this->p_myPos(this->s.currentPos);
252 }
253 // Give the motor time to settle at the target before it is told to store it.
254 1 this->sendSetMy(200, oldMyPos, oldMyTiltPos, now);
255 1 this->settingMyPos = false;
256 1 this->emitState();
257 }
258
8/8
✓ Branch 0 taken 13110 times.
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 12269 times.
✓ Branch 3 taken 841 times.
✓ Branch 4 taken 12240 times.
✓ Branch 5 taken 29 times.
✓ Branch 6 taken 608 times.
✓ Branch 7 taken 11632 times.
13150 else if(currDir != this->s.direction || currPos != floor(this->s.currentPos) || currTiltDir != this->s.tiltDirection || currTiltPos != floor(this->s.currentTiltPos)) {
259 // We need to emit on the socket that our state has changed.
260 1518 this->emitState();
261 }
262 }
263
264
265 // State Setters. Each marks what clients see as changed when it does change;
266 // the change fan-out publishes it once per loop() pass.
267 8638 float ShadeBehaviour::p_currentPos(float pos) {
268 8638 float old = this->s.currentPos;
269 8638 this->s.currentPos = pos;
270
2/2
✓ Branch 0 taken 898 times.
✓ Branch 1 taken 7740 times.
8638 if(floor(old) != floor(pos)) this->events.onChanged(SHADE_CHANGED_POSITION);
271 8638 return old;
272 }
273 2795 float ShadeBehaviour::p_currentTiltPos(float pos) {
274 2795 float old = this->s.currentTiltPos;
275 2795 this->s.currentTiltPos = pos;
276
2/2
✓ Branch 0 taken 617 times.
✓ Branch 1 taken 2178 times.
2795 if(floor(old) != floor(pos)) this->events.onChanged(SHADE_CHANGED_TILT_POSITION);
277 2795 return old;
278 }
279 8 bool ShadeBehaviour::p_flag(somfy_flags_t flag, bool val) {
280 8 bool old = !!(this->s.stateFlags & static_cast<uint8_t>(flag));
281
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if(val)
282 4 this->s.stateFlags |= static_cast<uint8_t>(flag);
283 else
284 4 this->s.stateFlags &= ~(static_cast<uint8_t>(flag));
285 8 return old;
286 }
287 4 bool ShadeBehaviour::p_sunFlag(bool val) {
288 4 bool old = this->p_flag(somfy_flags_t::SunFlag, val);
289
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(old != val) this->events.onChanged(SHADE_CHANGED_SUN_FLAG);
290 4 return old;
291 }
292 2 bool ShadeBehaviour::p_windy(bool val) {
293 2 bool old = this->p_flag(somfy_flags_t::Windy, val);
294
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(old != val) this->events.onChanged(SHADE_CHANGED_WINDY);
295 2 return old;
296 }
297 2 bool ShadeBehaviour::p_sunny(bool val) {
298 2 bool old = this->p_flag(somfy_flags_t::Sunny, val);
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(old != val) this->events.onChanged(SHADE_CHANGED_SUNNY);
300 2 return old;
301 }
302 16365 int8_t ShadeBehaviour::p_direction(int8_t dir) {
303 16365 int8_t old = this->s.direction;
304
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 16322 times.
16365 if(old != dir) {
305 43 this->s.direction = dir;
306 43 this->events.onChanged(SHADE_CHANGED_DIRECTION);
307 }
308 16365 return old;
309 }
310 28523 int8_t ShadeBehaviour::p_tiltDirection(int8_t dir) {
311 28523 int8_t old = this->s.tiltDirection;
312
2/2
✓ Branch 0 taken 1229 times.
✓ Branch 1 taken 27294 times.
28523 if(old != dir) {
313 1229 this->s.tiltDirection = dir;
314 1229 this->events.onChanged(SHADE_CHANGED_TILT_DIRECTION);
315 }
316 28523 return old;
317 }
318 97 float ShadeBehaviour::p_target(float target) {
319 97 float old = this->s.target;
320
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 23 times.
97 if(old != target) {
321 74 this->s.target = target;
322
1/2
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
74 if(this->transformPosition(old) != this->transformPosition(target))
323 74 this->events.onChanged(SHADE_CHANGED_TARGET);
324 }
325 97 return old;
326 }
327 36 float ShadeBehaviour::p_tiltTarget(float target) {
328 36 float old = this->s.tiltTarget;
329
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 10 times.
36 if(old != target) {
330 26 this->s.tiltTarget = target;
331
1/2
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
26 if(this->transformPosition(old) != this->transformPosition(target))
332 26 this->events.onChanged(SHADE_CHANGED_TILT_TARGET);
333 }
334 36 return old;
335 }
336 6 float ShadeBehaviour::p_myPos(float pos) {
337 6 float old = this->s.myPos;
338
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 time.
6 if(old != pos) {
339 5 this->s.myPos = pos;
340
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 if(this->transformPosition(old) != this->transformPosition(pos))
341 5 this->events.onChanged(SHADE_CHANGED_MY_POS);
342 }
343 6 return old;
344 }
345 5 float ShadeBehaviour::p_myTiltPos(float pos) {
346 5 float old = this->s.myTiltPos;
347
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
5 if(old != pos) {
348 1 this->s.myTiltPos = pos;
349
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 if(this->transformPosition(old) != this->transformPosition(pos))
350 1 this->events.onChanged(SHADE_CHANGED_MY_TILT_POS);
351 }
352 5 return old;
353 }
354 71 bool ShadeBehaviour::isIdle() const {
355
6/6
✓ Branch 1 taken 65 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 59 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 58 times.
✓ Branch 6 taken 1 time.
71 return this->isAtTarget() && this->s.direction == 0 && this->s.tiltDirection == 0;
356 }
357 760 void ShadeBehaviour::onWaitingFrame(uint32_t now) {
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 760 times.
760 if(this->shadeId == 255) {
359 ✗ this->lastFrame.await = 0;
360 ✗ return;
361 }
362
2/2
✓ Branch 0 taken 454 times.
✓ Branch 1 taken 306 times.
760 if(this->lastFrame.processed) return;
363 // Wrap-safe: await is now + 500 when the press began.
364
3/4
✓ Branch 0 taken 306 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 300 times.
306 if(this->lastFrame.await > 0 && static_cast<int32_t>(now - this->lastFrame.await) > 0) {
365 6 somfy_commands cmd = this->transformCommand(this->lastFrame.cmd);
366
2/3
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
6 switch(cmd) {
367 // A step acts as soon as it arrives (onFrame), so it is never waiting here.
368 2 case somfy_commands::Down:
369 case somfy_commands::Up:
370
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(this->settings.tiltType == tilt_types::tiltmotor) { // Theoretically this should get here unless it does have a tilt motor.
371
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if(this->lastFrame.repeats >= TILT_REPEATS) {
372
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 int8_t dir = this->lastFrame.cmd == somfy_commands::Up ? -1 : 1;
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 this->p_tiltTarget(dir > 0 ? 100.0f : 0.0f);
374 1 this->setTiltMovement(dir, now);
375 1 this->lastFrame.processed = true;
376 ESP_LOGD(TAG, "%s Processing tilt %s after %d repeats",
377 this->name, translateSomfyCommand(this->lastFrame.cmd).c_str(), this->lastFrame.repeats);
378 1 this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
379 }
380 else {
381
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 int8_t dir = this->lastFrame.cmd == somfy_commands::Up ? -1 : 1;
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 this->p_target(dir > 0 ? 100 : 0);
383 1 this->setMovement(dir, now);
384 1 this->lastFrame.processed = true;
385 1 this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
386 }
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(this->lastFrame.repeats > TILT_REPEATS + 2) {
388 ✗ this->lastFrame.processed = true;
389 ✗ this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
390 }
391 }
392
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else if(this->settings.tiltType == tilt_types::euromode) {
393
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->lastFrame.repeats >= TILT_REPEATS) {
394
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ int8_t dir = this->lastFrame.cmd == somfy_commands::Up ? -1 : 1;
395
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->p_target(dir > 0 ? 100.0f : 0.0f);
396 ✗ this->setMovement(dir, now);
397 ✗ this->lastFrame.processed = true;
398 ESP_LOGD(TAG, "%s Processing %s after %d repeats",
399 this->name, translateSomfyCommand(this->lastFrame.cmd).c_str(), this->lastFrame.repeats);
400 ✗ this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
401 }
402 else {
403
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ int8_t dir = this->lastFrame.cmd == somfy_commands::Up ? -1 : 1;
404
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->p_tiltTarget(dir > 0 ? 100 : 0);
405 ✗ this->setTiltMovement(dir, now);
406 ✗ this->lastFrame.processed = true;
407 ✗ this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
408 }
409
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->lastFrame.repeats > TILT_REPEATS + 2) {
410 ✗ this->lastFrame.processed = true;
411 ✗ this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
412 }
413 }
414 2 break;
415 4 case somfy_commands::My:
416
5/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 3 times.
4 if(this->lastFrame.repeats >= SETMY_REPEATS && this->isIdle()) {
417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(floor(this->s.myPos) == floor(this->s.currentPos)) {
418 // We are clearing it.
419 ✗ this->p_myPos(-1);
420 ✗ this->p_myTiltPos(-1);
421 }
422 else {
423 1 this->p_myPos(this->s.currentPos);
424 1 this->p_myTiltPos(this->s.currentTiltPos);
425 }
426 1 this->commitMyPosition();
427 1 this->lastFrame.processed = true;
428 1 this->emitState();
429 }
430
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 else if(this->isIdle()) {
431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(this->settings.simMy)
432 ✗ this->moveToMyPosition(now); // Call out like this (instead of move to target) so that we don't get some of the goofy tilt only problems.
433 else {
434
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
3 if(this->s.myPos >= 0.0f && this->s.myPos <= 100.0f) this->p_target(this->s.myPos);
435
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
3 if(this->s.myTiltPos >= 0.0f && this->s.myTiltPos <= 100.0f) this->p_tiltTarget(this->s.myTiltPos);
436 3 this->pinTiltOnlyLift();
437 }
438 3 this->setMovement(0, now);
439 3 this->lastFrame.processed = true;
440 3 this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
441 }
442 else {
443 ✗ this->p_target(this->s.currentPos);
444 ✗ this->p_tiltTarget(this->s.currentTiltPos);
445 ✗ this->pinTiltOnlyLift();
446 }
447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(this->lastFrame.repeats > SETMY_REPEATS + 2) this->lastFrame.processed = true;
448 4 if(this->lastFrame.processed) {
449 ESP_LOGD(TAG, "%s Processing MY after %d repeats", this->name, this->lastFrame.repeats);
450 }
451 4 break;
452 ✗ default:
453 ✗ break;
454 }
455 }
456 }
457 133 void ShadeBehaviour::onFrame(somfy_frame_t &frame, bool internal, uint32_t now) {
458 // The reason why we are processing all frames here is so
459 // any linked remotes that may happen to be on the same ESPSomfy RTS
460 // device can trigger the appropriate actions.
461
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
133 if(this->shadeId == 255) return;
462 133 const uint32_t curTime = now;
463 133 this->lastFrame.copy(frame);
464 133 int8_t dir = 0;
465 133 this->moveStart = this->tiltStart = curTime;
466 133 this->startPos = this->s.currentPos;
467 133 this->startTiltPos = this->s.currentTiltPos;
468 // If the command is coming from a remote then we are aborting all these positioning operations.
469
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 39 times.
133 if(!internal) this->settingMyPos = this->settingPos = this->settingTiltPos = false;
470 133 somfy_commands cmd = this->transformCommand(frame.cmd);
471 // At this point we are not processing the combo buttons
472 // will need to see what the shade does when you press both.
473
9/13
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 23 times.
✓ Branch 5 taken 22 times.
✓ Branch 6 taken 57 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 25 times.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 1 time.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
133 switch(cmd) {
474 2 case somfy_commands::Sensor:
475 2 this->lastFrame.processed = true;
476
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if(this->settings.shadeType == shade_types::drycontact || this->settings.shadeType == shade_types::drycontact2) return;
477 {
478 2 const uint8_t prevFlags = this->s.stateFlags;
479 2 const bool wasSunny = prevFlags & static_cast<uint8_t>(somfy_flags_t::Sunny);
480 2 const bool wasWindy = prevFlags & static_cast<uint8_t>(somfy_flags_t::Windy);
481 2 const uint16_t status = frame.rollingCode << 4;
482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (status & static_cast<uint8_t>(somfy_flags_t::Sunny))
483 ✗ this->p_sunny(true);
484 //this->flags |= static_cast<uint8_t>(somfy_flags_t::Sunny);
485 else
486 2 this->p_sunny(false);
487 //this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Sunny));
488
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status & static_cast<uint8_t>(somfy_flags_t::Windy))
489 2 this->p_windy(true);
490 //this->flags |= static_cast<uint8_t>(somfy_flags_t::Windy);
491 else
492 ✗ this->p_windy(false);
493 //this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Windy));
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(frame.rollingCode & static_cast<uint8_t>(somfy_flags_t::DemoMode))
495 ✗ this->s.stateFlags |= static_cast<uint8_t>(somfy_flags_t::DemoMode);
496 else
497 2 this->s.stateFlags &= ~(static_cast<uint8_t>(somfy_flags_t::DemoMode));
498 2 const bool isSunny = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Sunny);
499 2 const bool isWindy = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Windy);
500 2 this->sensors.onSensorFrame(wasSunny, wasWindy, isSunny, isWindy, (uint32_t)curTime);
501 2 this->emitState();
502 2 this->events.onSensorFlags();
503 }
504 2 break;
505 ✗ case somfy_commands::Prog:
506 case somfy_commands::MyUp:
507 case somfy_commands::MyDown:
508 case somfy_commands::MyUpDown:
509 case somfy_commands::UpDown:
510 ✗ this->lastFrame.processed = true;
511
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(this->settings.shadeType == shade_types::drycontact || this->settings.shadeType == shade_types::drycontact2) return;
512
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
513 ✗ break;
514
515 1 case somfy_commands::Flag:
516 1 this->lastFrame.processed = true;
517
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if(this->settings.shadeType == shade_types::drycontact || this->settings.shadeType == shade_types::drycontact2) return;
518
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->lastFrame.rollingCode & 0x8000) return; // Some sensors send bogus frames with a rollingCode >= 32768 that cause them to change the state.
519 1 this->p_sunFlag(false);
520 //this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SunFlag));
521 1 this->events.onSaveWanted();
522 1 this->emitState();
523
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
524 1 this->events.onSensorFlags();
525 1 break;
526 1 case somfy_commands::SunFlag:
527
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if(this->settings.shadeType == shade_types::drycontact || this->settings.shadeType == shade_types::drycontact2) return;
528
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->lastFrame.rollingCode & 0x8000) return; // Some sensors send bogus frames with a rollingCode >= 32768 that cause them to change the state.
529 {
530 1 const bool isWindy = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Windy);
531 1 const bool isSunny = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Sunny);
532 //this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
533 1 this->p_sunFlag(true);
534 1 this->applySensorMove(this->sensors.onSunFlag(isSunny, isWindy));
535 1 this->events.onSaveWanted();
536 1 this->emitState();
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
538 1 this->events.onSensorFlags();
539 }
540 1 break;
541 23 case somfy_commands::Up:
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if(this->settings.shadeType == shade_types::drycontact) {
543 ✗ this->lastFrame.processed = true;
544 ✗ return;
545 }
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 else if(this->settings.shadeType == shade_types::drycontact2) {
547
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->lastFrame.processed) return;
548 ✗ this->lastFrame.processed = true;
549
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.currentPos != 0.0f) this->p_target(0);
550
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
551 ✗ return;
552 }
553
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
23 if(this->settings.tiltType == tilt_types::tiltmotor || this->settings.tiltType == tilt_types::euromode) {
554 // Wait another half second just in case we are potentially processing a tilt.
555
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if(!internal) this->lastFrame.await = curTime + 500;
556 ✗ else this->lastFrame.processed = true;
557 }
558 else {
559 // If from a remote we will simply be going up.
560 6 this->pinTiltOnlyLift();
561
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
6 if(this->settings.tiltType == tilt_types::tiltonly && !internal) this->p_tiltTarget(0.0f);
562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if(!internal) {
563
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->settings.tiltType != tilt_types::tiltonly) this->p_target(0.0f);
564 ✗ this->p_tiltTarget(0.0f);
565 }
566 6 this->lastFrame.processed = true;
567
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
568 }
569 23 break;
570 22 case somfy_commands::Down:
571
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if(this->settings.shadeType == shade_types::drycontact) {
572 ✗ this->lastFrame.processed = true;
573 ✗ return;
574 }
575
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 else if(this->settings.shadeType == shade_types::drycontact2) {
576
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->lastFrame.processed) return;
577 ✗ this->lastFrame.processed = true;
578
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.currentPos != 100.0f) this->p_target(100);
579
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
580 ✗ return;
581 }
582
2/2
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 1 time.
22 if (!this->sensors.windBlocksDown((uint32_t)curTime)) {
583
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
21 if(this->settings.tiltType == tilt_types::tiltmotor || this->settings.tiltType == tilt_types::euromode) {
584 // Wait another half seccond just in case we are potentially processing a tilt.
585
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(!internal) this->lastFrame.await = curTime + 500;
586 ✗ else this->lastFrame.processed = true;
587 }
588 else {
589 21 this->lastFrame.processed = true;
590 21 this->pinTiltOnlyLift();
591
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 17 times.
21 if(!internal) {
592
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(this->settings.tiltType != tilt_types::tiltonly) this->p_target(100.0f);
593
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(this->settings.tiltType != tilt_types::none) this->p_tiltTarget(100.0f);
594 }
595 }
596
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 4 times.
21 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
597 }
598 22 break;
599 57 case somfy_commands::My:
600
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if(this->settings.shadeType == shade_types::drycontact2) return;
601
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 55 times.
57 if(this->isToggle()) { // This is a one button device
602
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(this->lastFrame.processed) return;
603 2 this->lastFrame.processed = true;
604
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(!this->isIdle()) this->p_target(this->s.currentPos);
605
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 else if(this->s.currentPos == 100.0f) this->p_target(0.0f);
606
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 else if(this->s.currentPos == 0.0f) this->p_target(100.0f);
607
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else this->p_target(this->s.lastMovement == -1 ? 100 : 0);
608
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
609 2 return;
610 }
611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
55 else if(this->settings.shadeType == shade_types::drycontact) {
612 // In this case we need to toggle the contact but we only should do this if
613 // this is not a repeat.
614
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->lastFrame.processed) return;
615 ✗ this->lastFrame.processed = true;
616
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.currentPos == 100.0f) this->p_target(0);
617
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else if(this->s.currentPos == 0.0f) this->p_target(100);
618
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else this->p_target(this->s.lastMovement == -1 ? 100 : 0);
619
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
620 ✗ return;
621 }
622
2/2
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 11 times.
55 if(this->isIdle()) {
623
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 5 times.
44 if(!internal) {
624 // This frame is coming from a remote. We are potentially setting
625 // the my position.
626 39 this->lastFrame.await = curTime + 500;
627 }
628 else {
629
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if(this->lastFrame.processed) return;
630 ESP_LOGI(TAG, "Moving to My target");
631 5 this->lastFrame.processed = true;
632 5 this->pinTiltOnlyLift();
633
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
5 if(this->s.myTiltPos >= 0.0f && this->s.myTiltPos <= 100.0f) this->p_tiltTarget(this->s.myTiltPos);
634
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
5 if(this->s.myPos >= 0.0f && this->s.myPos <= 100.0f && this->settings.tiltType != tilt_types::tiltonly) this->p_target(this->s.myPos);
635
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
636 }
637 }
638 else {
639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if(this->lastFrame.processed) return;
640 11 this->lastFrame.processed = true;
641 11 this->pinTiltOnlyLift();
642
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9 times.
11 if(!internal) {
643
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if(this->settings.tiltType != tilt_types::tiltonly) this->p_target(this->s.currentPos);
644 2 this->p_tiltTarget(this->s.currentTiltPos);
645 }
646
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
647 }
648 55 break;
649 ✗ case somfy_commands::StepUp:
650
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->lastFrame.processed) return;
651 ✗ this->lastFrame.processed = true;
652
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(this->settings.shadeType == shade_types::drycontact || this->settings.shadeType == shade_types::drycontact2) return;
653 ✗ dir = 0;
654 // The number of steps the frame asks for.
655
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ if(!this->applyStep(-1, frame.stepSize == 0 ? 1 : frame.stepSize)) return;
656
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
657 ✗ break;
658 25 case somfy_commands::StepDown:
659
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
25 if(this->lastFrame.processed) return;
660 5 this->lastFrame.processed = true;
661
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if(this->settings.shadeType == shade_types::drycontact || this->settings.shadeType == shade_types::drycontact2) return;
662 5 dir = 1;
663 // The number of steps the frame asks for.
664
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 if(!this->applyStep(1, frame.stepSize == 0 ? 1 : frame.stepSize)) return;
665
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
666 5 break;
667 1 case somfy_commands::Toggle:
668
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->lastFrame.processed) return;
669 1 this->lastFrame.processed = true;
670
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if(!this->isIdle()) this->p_target(this->s.currentPos);
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 else if(this->s.currentPos == 100.0f) this->p_target(0);
672
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 else if(this->s.currentPos == 0.0f) this->p_target(100);
673
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else this->p_target(this->s.lastMovement == -1 ? 100 : 0);
674
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
675 1 break;
676 1 case somfy_commands::Stop:
677
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->lastFrame.processed) return;
678 1 this->lastFrame.processed = true;
679 1 this->p_target(this->s.currentPos);
680 1 this->p_tiltTarget(this->s.currentTiltPos);
681
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
682 1 break;
683 ✗ case somfy_commands::Favorite:
684
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->lastFrame.processed) return;
685 ✗ this->lastFrame.processed = true;
686
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->settings.simMy) {
687 ✗ this->moveToMyPosition(now);
688 }
689 else {
690
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(this->s.myTiltPos >= 0.0f && this->s.myTiltPos <= 100.0f) this->p_tiltTarget(this->s.myTiltPos);
691
0/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ if(this->s.myPos >= 0.0f && this->s.myPos <= 100.0f && this->settings.tiltType != tilt_types::tiltonly) this->p_target(this->s.myPos);
692
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
693 }
694 ✗ break;
695 ✗ default:
696 ✗ dir = 0;
697 ✗ break;
698 }
699 //if(dir == 0 && this->settings.tiltType == tilt_types::tiltmotor && this->s.tiltDirection != 0) this->setTiltMovement(0, now);
700 111 this->setMovement(dir, now);
701 }
702 21 void ShadeBehaviour::onGroupCommand(somfy_commands cmd, uint8_t repeat, uint32_t now) {
703 // The reason why we are processing all frames here is so
704 // any linked remotes that may happen to be on the same ESPSomfy RTS
705 // device can trigger the appropriate actions.
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if(this->shadeId == 255) return;
707 21 const uint32_t curTime = now;
708 21 int8_t dir = 0;
709 21 this->moveStart = this->tiltStart = curTime;
710 21 this->startPos = this->s.currentPos;
711 21 this->startTiltPos = this->s.currentTiltPos;
712 // If the command is coming from a remote then we are aborting all these positioning operations.
713
6/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
21 switch(cmd) {
714 2 case somfy_commands::Up:
715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(this->settings.tiltType == tilt_types::tiltmotor) {
716
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(repeat >= TILT_REPEATS)
717 ✗ this->p_tiltTarget(0.0f);
718 else
719 ✗ this->p_target(0.0f);
720 }
721
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if(this->settings.tiltType == tilt_types::tiltonly) {
722 2 this->p_target(100.0f);
723 2 this->p_currentPos(100.0f);
724 2 this->p_tiltTarget(0.0f);
725 }
726 else {
727 ✗ this->p_target(0.0f);
728 ✗ this->p_tiltTarget(0.0f);
729 }
730 2 break;
731 12 case somfy_commands::Down:
732
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 if (!this->sensors.windBlocksDown((uint32_t)curTime)) {
733
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(this->settings.tiltType == tilt_types::tiltmotor) {
734
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(repeat >= TILT_REPEATS)
735 ✗ this->p_tiltTarget(100.0f);
736 else
737 ✗ this->p_target(100.0f);
738 }
739
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 time.
12 else if(this->settings.tiltType == tilt_types::tiltonly) {
740 11 this->p_target(100.0f);
741 11 this->p_currentPos(100.0f);
742 11 this->p_tiltTarget(100.0f);
743 }
744 else {
745 1 this->p_target(100.0f);
746
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->settings.tiltType != tilt_types::none) this->p_tiltTarget(100.0f);
747 }
748 }
749 12 break;
750 2 case somfy_commands::My:
751
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 if(this->isIdle()) {
752 ESP_LOGD(TAG, "Shade #%d is idle", this->shadeId);
753
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->settings.simMy) {
754 ✗ this->moveToMyPosition(now);
755 }
756 else {
757
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 if(this->s.myTiltPos >= 0.0f && this->s.myTiltPos <= 100.0f) this->p_tiltTarget(this->s.myTiltPos);
758
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if(this->s.myPos >= 0.0f && this->s.myPos <= 100.0f && this->settings.tiltType != tilt_types::tiltonly) this->p_target(this->s.myPos);
759 1 this->pinTiltOnlyLift();
760 }
761 }
762 else {
763
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if(this->settings.tiltType == tilt_types::tiltonly) this->pinTiltOnlyLift();
764 ✗ else this->p_target(this->s.currentPos);
765 1 this->p_tiltTarget(this->s.currentTiltPos);
766 }
767 2 break;
768 ✗ case somfy_commands::StepUp:
769 // A group step is always one step.
770
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(!this->applyStep(-1, 1)) return;
771 ✗ break;
772 2 case somfy_commands::StepDown:
773 2 dir = 1;
774
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(!this->applyStep(1, 1)) return;
775 2 break;
776 1 case somfy_commands::Flag:
777 1 this->p_sunFlag(false);
778 // Every group works its flags out again, as for a Flag frame.
779 1 this->events.onSensorFlags();
780
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if(this->settings.sunSensor) {
781 1 this->events.onSaveWanted();
782 1 this->emitState();
783 }
784 else {
785 ESP_LOGW(TAG, "Shade does not have sensor %d", joinShadeFlags(this->settings, false, this->s.stateFlags));
786 }
787 1 break;
788 2 case somfy_commands::SunFlag:
789
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if(this->settings.sunSensor) {
790 1 const bool isWindy = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Windy);
791 1 const bool isSunny = this->s.stateFlags & static_cast<uint8_t>(somfy_flags_t::Sunny);
792 1 this->p_sunFlag(true);
793 //this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
794 1 this->applySensorMove(this->sensors.onSunFlag(isSunny, isWindy));
795 1 this->events.onSaveWanted();
796 1 this->emitState();
797 1 this->events.onSensorFlags();
798 }
799 else
800 ESP_LOGW(TAG, "Shade does not have sensor %d", joinShadeFlags(this->settings, false, this->s.stateFlags));
801 2 break;
802 ✗ default:
803 ✗ dir = 0;
804 ✗ break;
805 }
806 21 this->setMovement(dir, now);
807 }
808 1 void ShadeBehaviour::setTiltMovement(int8_t dir, uint32_t now) {
809 1 int8_t currDir = this->s.tiltDirection;
810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(dir == 0) {
811 // The shade tilt is stopped.
812 ✗ this->startTiltPos = this->s.currentTiltPos;
813 ✗ this->tiltStart = 0;
814 ✗ this->p_tiltDirection(dir);
815
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(currDir != dir) {
816 ✗ this->commitTiltPosition();
817 }
818 }
819
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 else if(this->s.tiltDirection != dir) {
820 1 this->tiltStart = now;
821 1 this->startTiltPos = this->s.currentTiltPos;
822 1 this->p_tiltDirection(dir);
823 }
824
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if(this->s.tiltDirection != currDir) {
825 1 this->emitState();
826 }
827 1 }
828 29 void ShadeBehaviour::onTxQueued(uint32_t ticket, uint32_t now) {
829 // This runs before the caller sets the new target so direction still says
830 // whether the shade was moving. A reversal is simulated as soon as it is
831 // queued even though the motor turns once it is on air. See TxHold::mark
832 // for which of several queued commands is held.
833
4/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 24 times.
29 this->tx.hold.mark(ticket, this->s.direction != 0 || this->s.tiltDirection != 0, now);
834 29 }
835 25 void ShadeBehaviour::onTxEvent(const somfy_tx_event_t &evt) {
836
5/6
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 20 times.
✓ Branch 5 taken 25 times.
✗ Branch 6 not taken.
25 const ShadeTxActions act = this->tx.onEvent(evt, this->s.direction != 0 || this->s.tiltDirection != 0);
837
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 10 times.
25 if(act.startMovement) {
838 // The motor starts now so the simulated movement does too.
839 15 this->moveStart = this->tiltStart = act.startAt;
840 }
841
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 23 times.
25 if(act.cancelMovement) {
842 // The motor never got the command so it hasn't moved.
843 ESP_LOGW(TAG, "[Shade %u] Command dropped by the radio, staying at %.2f%%", this->shadeId, this->s.currentPos);
844 2 this->settingPos = this->settingTiltPos = this->settingMyPos = false;
845
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 this->p_target(this->s.currentPos);
846
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 this->p_tiltTarget(this->s.currentTiltPos);
847
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 this->emitState();
848 }
849
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 24 times.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
25 if(act.commitMy) this->commitMyPosition();
850
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 24 times.
25 if(act.restoreMy) {
851 ESP_LOGW(TAG, "[Shade %u] Favorite not sent in full, keeping the previous one", this->shadeId);
852
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 this->p_myPos(act.myOldPos);
853
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 this->p_myTiltPos(act.myOldTiltPos);
854
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 this->emitState();
855 }
856
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 23 times.
25 if(act.runToLimit) {
857 ESP_LOGW(TAG, "[Shade %u] Stop dropped by the radio, running on to the limit", this->shadeId);
858
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 this->runToLimit(act.limitDir, act.limitTilt, act.limitSince);
859
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 this->emitState();
860 }
861 25 }
862 9 bool ShadeBehaviour::sendStop(int8_t dir, bool tilt, uint32_t now) {
863 // Stops jump the radio queue so the motor stops as close to the target as it can.
864
3/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 time.
9 if(const send_result_t sent = this->transmit(somfy_commands::My, this->remote.repeats, 0, 0, true, now)) {
865
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 this->tx.stopSent(sent.ticket, now, dir, tilt);
866 8 return true;
867 }
868 ESP_LOGW(TAG, "[Shade %u] Stop not sent, running on to the limit", this->shadeId);
869 1 this->runToLimit(dir, tilt, now);
870 1 return false;
871 }
872 3 bool ShadeBehaviour::sendSetMy(uint16_t delayMs, float oldPos, float oldTiltPos, uint32_t now) {
873 // The new favorite is already in myPos/myTiltPos. It is committed once the
874 // whole long press is on air, see processTxEvent.
875
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 if(const send_result_t sent = this->transmit(somfy_commands::My, SETMY_REPEATS, 0, delayMs, false, now)) {
876
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 this->tx.setMySent(sent.ticket, oldPos, oldTiltPos);
877 // Without the radio (GPIO remotes) there is nothing to wait for.
878
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3 if(sent.ticket == 0) this->commitMyPosition();
879 3 return true;
880 }
881 // The motor never learned it so keep the favorite it has.
882 ✗ this->p_myPos(oldPos);
883 ✗ this->p_myTiltPos(oldTiltPos);
884 ✗ return false;
885 }
886 49 void ShadeBehaviour::pinTiltOnlyLift() {
887
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 15 times.
49 if(this->settings.tiltType != tilt_types::tiltonly) return;
888 15 this->p_target(100.0f);
889 15 this->p_currentPos(100.0f);
890 }
891 3 void ShadeBehaviour::runToLimit(int8_t dir, bool tilt, uint32_t since) {
892 // The motor didn't get a stop at the target so it keeps going until its end
893 // limit. Carry on simulating that from where and when it should have stopped.
894
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(tilt) {
895 ✗ this->startTiltPos = this->s.currentTiltPos;
896 ✗ this->tiltStart = since;
897 ✗ this->settingTiltPos = false;
898 ✗ this->p_tiltTarget(limitTarget(dir));
899 }
900 else {
901 3 this->startPos = this->s.currentPos;
902 3 this->moveStart = since;
903 3 this->settingPos = false;
904 3 this->p_target(limitTarget(dir));
905 }
906 // The favorite can't be set where the shade won't stop.
907 3 this->settingMyPos = false;
908 3 }
909 13153 void ShadeBehaviour::applySensorMove(SensorMove move) {
910
1/2
✓ Branch 1 taken 13153 times.
✗ Branch 2 not taken.
13153 const SensorTargets t = sensorTargets(move, this->settings.tiltType, this->s.myPos, this->s.myTiltPos);
911
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13150 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
13153 if(t.setLift) this->p_target(t.lift);
912
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13153 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
13153 if(t.setTilt) this->p_tiltTarget(t.tilt);
913 13153 }
914 7 bool ShadeBehaviour::applyStep(int8_t dir, uint8_t frameStepSize) {
915 7 const StepTargets t = stepTargets(this->settings.tiltType, dir, this->s.currentPos, this->s.currentTiltPos,
916
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 this->settings.upTime, this->settings.downTime, this->settings.tiltTime, this->settings.stepSize, frameStepSize);
917
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if(t.action == StepAction::ignore) return false;
918
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 if(t.setLift) this->p_target(t.lift);
919
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
7 if(t.setTilt) this->p_tiltTarget(t.tilt);
920 7 return true;
921 }
922 137 void ShadeBehaviour::setMovement(int8_t dir, uint32_t now) {
923 137 int8_t currDir = this->s.direction;
924 137 int8_t currTiltDir = this->s.tiltDirection;
925
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 8 times.
137 if(dir == 0) {
926
4/4
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 107 times.
129 if(currDir != dir || currTiltDir != dir) this->commitShadePosition();
927 }
928 else {
929 8 this->tiltStart = this->moveStart = now;
930 8 this->startPos = this->s.currentPos;
931 8 this->startTiltPos = this->s.currentTiltPos;
932 }
933
2/4
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 137 times.
137 if(this->s.direction != currDir || currTiltDir != this->s.tiltDirection) {
934 ✗ this->emitState();
935 }
936 137 }
937 3 bool ShadeBehaviour::setMyPosition(int8_t pos, int8_t tilt, uint32_t now) {
938
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if(!this->isIdle()) return true; // Don't do this if it is moving.
939 3 bool sent = true;
940
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(this->settings.tiltType == tilt_types::tiltonly) {
941 ✗ this->p_myPos(-1.0f);
942
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(tilt != floor(this->s.currentTiltPos)) {
943 // The favorite is set once we get there, if the move went out.
944
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(tilt == floor(this->s.myTiltPos))
945 ✗ sent = this->settingMyPos = this->moveToMyPosition(now);
946 else
947 ✗ sent = this->settingMyPos = this->moveToTarget(100, tilt, now);
948 }
949
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else if(tilt == floor(this->s.myTiltPos)) {
950 // Of so we need to clear the my position. These motors are finicky so send
951 // a my command to ensure we are actually at the my position then send the clear
952 // command. There really is no other way to do this.
953
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.currentTiltPos != this->s.myTiltPos) {
954 ✗ sent = this->settingMyPos = this->moveToMyPosition(now);
955 }
956
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ else if((sent = this->transmit(somfy_commands::My, this->remote.repeats, 0, 0, false, now).sent)) {
957 ✗ this->settingPos = false;
958 ✗ this->settingMyPos = true;
959 }
960 }
961 ✗ else return this->storeMyPosition(-1.0f, this->s.currentTiltPos, now);
962 ✗ this->commitMyPosition();
963 ✗ this->emitState();
964 }
965
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 else if(this->settings.tiltType != tilt_types::none) {
966
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(tilt < 0) tilt = 0;
967
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(pos != floor(this->s.currentPos) || tilt != floor(this->s.currentTiltPos)) {
968 // The favorite is set once we get there, if the move went out.
969
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(pos == floor(this->s.myPos) && tilt == floor(this->s.myTiltPos))
970 ✗ sent = this->settingMyPos = this->moveToMyPosition(now);
971 else
972 ✗ sent = this->settingMyPos = this->moveToTarget(pos, tilt, now);
973 }
974
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ else if(pos == floor(this->s.myPos) && tilt == floor(this->s.myTiltPos)) {
975 // Of so we need to clear the my position. These motors are finicky so send
976 // a my command to ensure we are actually at the my position then send the clear
977 // command. There really is no other way to do this.
978
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(this->s.currentPos != this->s.myPos || this->s.currentTiltPos != this->s.myTiltPos) {
979 ✗ sent = this->settingMyPos = this->moveToMyPosition(now);
980 }
981
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ else if((sent = this->transmit(somfy_commands::My, this->remote.repeats, 0, 0, false, now).sent)) {
982 ✗ this->settingPos = false;
983 ✗ this->settingMyPos = true;
984 }
985 }
986 ✗ else return this->storeMyPosition(this->s.currentPos, this->s.currentTiltPos, now);
987 ✗ this->commitMyPosition();
988 ✗ this->emitState();
989 }
990 else {
991
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 if(pos != floor(this->s.currentPos)) {
992 // The favorite is set once we get there, if the move went out.
993
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(pos == floor(this->s.myPos))
994 ✗ sent = this->settingMyPos = this->moveToMyPosition(now);
995 else
996 1 sent = this->settingMyPos = this->moveToTarget(pos, -1.0f, now);
997 }
998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 else if(pos == floor(this->s.myPos)) {
999 // Of so we need to clear the my position. These motors are finicky so send
1000 // a my command to ensure we are actually at the my position then send the clear
1001 // command. There really is no other way to do this.
1002
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.myPos != this->s.currentPos) {
1003 ✗ sent = this->settingMyPos = this->moveToMyPosition(now);
1004 }
1005
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ else if((sent = this->transmit(somfy_commands::My, this->remote.repeats, 0, 0, false, now).sent)) {
1006 ✗ this->settingPos = false;
1007 ✗ this->settingMyPos = true;
1008 }
1009 }
1010 2 else sent = this->storeMyPosition(this->s.currentPos, -1.0f, now);
1011 }
1012 3 return sent;
1013 }
1014 2 bool ShadeBehaviour::storeMyPosition(float pos, float tiltPos, uint32_t now) {
1015 // Set it before sending so processFrame doesn't head for the old one. It is
1016 // only committed once the motor was told about it.
1017 2 const float oldPos = this->s.myPos;
1018 2 const float oldTiltPos = this->s.myTiltPos;
1019 2 this->p_myPos(pos);
1020 2 this->p_myTiltPos(tiltPos);
1021 2 const bool sent = this->sendSetMy(0, oldPos, oldTiltPos, now);
1022 2 this->emitState();
1023 2 return sent;
1024 }
1025 2 bool ShadeBehaviour::moveToMyPosition(uint32_t now) {
1026
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(!this->isIdle()) return true;
1027 ESP_LOGI(TAG, "Moving to My Position");
1028
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if(this->settings.tiltType == tilt_types::tiltonly) {
1029 1 this->p_currentPos(100.0f);
1030 1 this->p_myPos(-1.0f);
1031 }
1032
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(this->s.currentPos == this->s.myPos) {
1033
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->settings.tiltType != tilt_types::none) {
1034
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(this->s.currentTiltPos == this->s.myTiltPos) return true; // Nothing to see here since we are already here.
1035 }
1036 else
1037 ✗ return true;
1038 }
1039
4/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
2 if(this->s.myPos == -1 && (this->settings.tiltType == tilt_types::none || this->s.myTiltPos == -1)) return true;
1040 2 this->settingPos = false;
1041
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if(this->settings.simMy) {
1042 ESP_LOGI(TAG, "Moving to simulated favorite");
1043
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if(!this->moveToTarget(this->s.myPos, this->s.myTiltPos, now)) return false;
1044 }
1045
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 else if(!this->transmit(somfy_commands::My, this->remote.repeats, 0, 0, false, now).sent) return false;
1046
4/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
2 if(this->settings.tiltType != tilt_types::tiltonly && this->s.myPos >= 0.0f && this->s.myPos <= 100.0f) this->p_target(this->s.myPos);
1047
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
2 if(this->s.myTiltPos >= 0.0f && this->s.myTiltPos <= 100.0f) this->p_tiltTarget(this->s.myTiltPos);
1048 2 return true;
1049 }
1050 19 bool ShadeBehaviour::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint32_t now) {
1051 ESP_LOGD(TAG, "Send command start");
1052 // This sendCommand function will always be called externally. sendCommand at the remote level
1053 // is expected to be called internally when the motor needs commanded.
1054 // If same direction command sent while already moving, stop the state tracking.
1055 // The real motor stops on its own when it receives the same direction again.
1056
5/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 10 times.
19 if((cmd == somfy_commands::Up && this->s.direction == -1) ||
1057
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
9 (cmd == somfy_commands::Down && this->s.direction == 1)) {
1058 ESP_LOGD(TAG, "Same command as dir");
1059
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if(!this->transmit(cmd, repeat, 0, 0, false, now).sent) return false;
1060 1 this->p_target(this->s.currentPos);
1061 1 this->p_tiltTarget(this->s.currentTiltPos);
1062 1 this->setMovement(0, now);
1063 1 return true;
1064 }
1065 // The simulated state is only moved for commands the radio took.
1066
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
18 if(cmd == somfy_commands::Up) {
1067
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(this->settings.tiltType == tilt_types::euromode) {
1068 // In euromode we need to long press for 2 seconds on the
1069 // up command.
1070
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(!this->transmit(cmd, TILT_REPEATS, 0, 0, false, now).sent) return false;
1071 ✗ this->p_target(0.0f);
1072 }
1073 else {
1074
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if(!this->transmit(cmd, repeat, 0, 0, false, now).sent) return false;
1075
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if(this->settings.tiltType == tilt_types::tiltonly) {
1076 2 this->p_target(100.0f);
1077 2 this->p_tiltTarget(0.0f);
1078 2 this->p_currentPos(100.0f);
1079 }
1080 1 else this->p_target(0.0f);
1081
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(this->settings.tiltType == tilt_types::integrated) this->p_tiltTarget(0.0f);
1082 }
1083 }
1084
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 7 times.
15 else if(cmd == somfy_commands::Down) {
1085
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(this->settings.tiltType == tilt_types::euromode) {
1086 // In euromode we need to long press for 2 seconds on the
1087 // down command.
1088
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(!this->transmit(cmd, TILT_REPEATS, 0, 0, false, now).sent) return false;
1089 ✗ this->p_target(100.0f);
1090 }
1091 else {
1092
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 7 times.
8 if(!this->transmit(cmd, repeat, 0, 0, false, now).sent) return false;
1093
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if(this->settings.tiltType == tilt_types::tiltonly) {
1094 2 this->p_target(100.0f);
1095 2 this->p_tiltTarget(100.0f);
1096 2 this->p_currentPos(100.0f);
1097 }
1098 5 else this->p_target(100.0f);
1099
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if(this->settings.tiltType == tilt_types::integrated) this->p_tiltTarget(100.0f);
1100 }
1101 }
1102
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 else if(cmd == somfy_commands::My) {
1103
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
2 if(this->isToggle() || this->settings.shadeType == shade_types::drycontact) {
1104
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(!this->transmit(cmd, repeat, 0, 0, false, now).sent) return false;
1105 }
1106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 else if(this->settings.shadeType == shade_types::drycontact2){
1107 ESP_LOGD(TAG, "Send command start 1");
1108 ✗ return true;
1109 }
1110
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 else if(this->isIdle()) {
1111 1 bool sent = this->moveToMyPosition(now);
1112 ESP_LOGD(TAG, "Send command end 2");
1113 1 return sent;
1114 }
1115 else {
1116
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if(!this->transmit(cmd, repeat, 0, 0, false, now).sent) return false;
1117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if(this->settings.tiltType != tilt_types::tiltonly) this->p_target(this->s.currentPos);
1118 1 this->p_tiltTarget(this->s.currentTiltPos);
1119 1 this->pinTiltOnlyLift();
1120 }
1121 }
1122
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 else if(cmd == somfy_commands::Toggle) {
1123
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
2 if(this->remote.bitLength != 80) { if(!this->transmit(somfy_commands::My, repeat, stepSize, 0, false, now).sent) return false; }
1124
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 else if(!this->transmit(somfy_commands::Toggle, repeat, 0, 0, false, now).sent) return false;
1125 }
1126
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
3 else if(this->isToggle() && cmd == somfy_commands::Prog) {
1127
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if(!this->transmit(somfy_commands::Toggle, repeat, stepSize, 0, false, now).sent) return false;
1128 }
1129 else {
1130
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if(!this->transmit(cmd, repeat, stepSize, 0, false, now).sent) return false;
1131 }
1132 ESP_LOGD(TAG, "Send command end");
1133 16 return true;
1134 }
1135
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 uint8_t ShadeBehaviour::tiltRepeats() const { return this->settings.tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->remote.repeats; }
1136 ✗ bool ShadeBehaviour::sendTiltCommand(somfy_commands cmd, uint32_t now) {
1137 float target;
1138
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(cmd == somfy_commands::Up) target = 0.0f;
1139
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else if(cmd == somfy_commands::Down) target = 100.0f;
1140
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else if(cmd == somfy_commands::My) target = this->s.currentTiltPos;
1141 ✗ else return true;
1142
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if(!this->transmit(cmd, this->tiltRepeats(), 0, 0, false, now).sent) return false;
1143 ✗ this->p_tiltTarget(target);
1144 ✗ return true;
1145 }
1146 1 bool ShadeBehaviour::moveToTiltTarget(float target, uint32_t now) {
1147 1 somfy_commands cmd = somfy_commands::My;
1148
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if(target < this->s.currentTiltPos)
1149 1 cmd = somfy_commands::Up;
1150
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else if(target > this->s.currentTiltPos)
1151 ✗ cmd = somfy_commands::Down;
1152
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 if(target >= 0.0f && target <= 100.0f) {
1153 // Only send a command if the lift is not moving.
1154
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if(this->s.currentPos == this->s.target || this->settings.tiltType == tilt_types::tiltmotor) {
1155
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if(cmd != somfy_commands::My) {
1156 ESP_LOGI(TAG, "Moving Tilt to %.2f%% from %.2f%% using %s",
1157 target, this->s.currentTiltPos, translateSomfyCommand(cmd).c_str());
1158
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if(!this->transmit(cmd, this->tiltRepeats(), 0, 0, false, now).sent) return false;
1159 }
1160 // If the blind is currently moving then the command to stop it
1161 // will occur on its own when the tilt target is set.
1162 }
1163 1 this->p_tiltTarget(target);
1164 }
1165
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if(cmd != somfy_commands::My) this->settingTiltPos = true;
1166 1 return true;
1167 }
1168 9 bool ShadeBehaviour::moveToTarget(float pos, float tilt, uint32_t now) {
1169 ESP_LOGI(TAG, "[Shade %u] moveToTarget(pos=%.2f, tilt=%.2f) settingPos=%d direction=%d currentTarget=%.2f currentPos=%.2f",
1170 this->shadeId, pos, tilt, this->settingPos, this->s.direction, this->s.target, this->s.currentPos);
1171 9 somfy_commands cmd = somfy_commands::My;
1172
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if(this->isToggle()) {
1173 // Overload this as we cannot seek a position on a garage door or single button device.
1174 ✗ this->p_target(pos);
1175 ✗ this->p_currentPos(pos);
1176 ✗ this->emitState();
1177 ✗ return true;
1178 }
1179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(this->settings.tiltType == tilt_types::tiltonly) {
1180 // A tilt only shade is always at 100%.
1181 ✗ this->p_target(100.0f);
1182 ✗ this->p_myPos(-1.0f);
1183 ✗ this->p_currentPos(100.0f);
1184 ✗ pos = 100;
1185
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if(tilt < this->s.currentTiltPos) cmd = somfy_commands::Up;
1186
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ else if(tilt > this->s.currentTiltPos) cmd = somfy_commands::Down;
1187 }
1188 else {
1189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(pos < this->s.currentPos)
1190 ✗ cmd = somfy_commands::Up;
1191
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 else if(pos > this->s.currentPos)
1192 9 cmd = somfy_commands::Down;
1193
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ else if(tilt >= 0 && tilt < this->s.currentTiltPos)
1194 ✗ cmd = somfy_commands::Up;
1195
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ else if(tilt >= 0 && tilt > this->s.currentTiltPos)
1196 ✗ cmd = somfy_commands::Down;
1197 }
1198
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if(cmd != somfy_commands::My) {
1199 if(tilt >= 0) {
1200 ESP_LOGI(TAG, "Moving to %.2f%% from %.2f%% tilt %.2f%% from %.2f%% using %s",
1201 pos, this->s.currentPos, tilt, this->s.currentTiltPos, translateSomfyCommand(cmd).c_str());
1202 } else {
1203 ESP_LOGI(TAG, "Moving to %.2f%% from %.2f%% using %s",
1204 pos, this->s.currentPos, translateSomfyCommand(cmd).c_str());
1205 }
1206
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
9 if(!this->transmit(cmd, this->settings.tiltType == tilt_types::euromode ? TILT_REPEATS : this->remote.repeats, 0, 0, false, now).sent) return false;
1207 9 this->settingPos = true;
1208 9 this->p_target(pos);
1209
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
9 if(tilt >= 0) {
1210 1 this->p_tiltTarget(tilt);
1211 1 this->settingTiltPos = true;
1212 }
1213 }
1214 9 return true;
1215 }
1216