src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
90 / 90
100.0%
Branches:
90 / 102
88.2%
Functions:
4 / 4
100.0%

entryPoints/MqttCommands.cpp
Line Branch Exec Source
1 #include <stdlib.h>
2 #include <string.h>
3 #include "MqttCommands.h"
4
5 const char *const MQTT_COMMAND_TOPICS[MQTT_COMMAND_TOPIC_COUNT] = {
6 "shades/+/target/set",
7 "shades/+/tiltTarget/set",
8 "shades/+/direction/set",
9 "shades/+/mypos/set",
10 "shades/+/myTiltPos/set",
11 "shades/+/sunFlag/set",
12 "shades/+/sunny/set",
13 "shades/+/windy/set",
14 "shades/+/position/set",
15 "shades/+/tiltPosition/set",
16 "groups/+/direction/set",
17 "groups/+/sunFlag/set",
18 "groups/+/sunny/set",
19 "groups/+/windy/set"
20 };
21
22 namespace {
23 struct segment_t {
24 const char *s = "";
25 size_t len = 0;
26
4/4
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 131 times.
✓ Branch 2 taken 133 times.
✓ Branch 3 taken 25 times.
289 bool is(const char *word) const { return this->len == strlen(word) && strncmp(this->s, word, this->len) == 0; }
27 };
28
4/4
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 3 times.
20 bool inRange(int val) { return val >= 0 && val <= 100; }
29 7 somfy_commands directionCommand(int val) {
30
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if(val < 0) return somfy_commands::Up;
31
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
5 if(val > 0) return somfy_commands::Down;
32 1 return somfy_commands::My;
33 }
34 }
35
36 52 bool parseMqttCommand(const char *topic, const uint8_t *payload, size_t length, shade_request_t &req) {
37 // Only the last four segments count, so the root topic may hold slashes or
38 // be empty: type, id, command and "set".
39
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 52 times.
208 segment_t seg[4];
40 52 size_t end = strlen(topic);
41
2/2
✓ Branch 0 taken 203 times.
✓ Branch 1 taken 50 times.
253 for(int i = 3; i >= 0; i--) {
42 203 size_t start = end;
43
4/4
✓ Branch 0 taken 1062 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 863 times.
✓ Branch 3 taken 199 times.
1066 while(start > 0 && topic[start - 1] != '/') start--;
44 203 seg[i].s = topic + start;
45 203 seg[i].len = end - start;
46
2/2
✓ Branch 0 taken 153 times.
✓ Branch 1 taken 50 times.
203 if(i > 0) {
47
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 151 times.
153 if(start == 0) return false;
48 151 end = start - 1;
49 }
50 }
51
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 48 times.
50 if(!seg[3].is("set")) return false;
52
3/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
48 if(seg[1].len == 0 || seg[1].len > 3) return false;
53 47 int id = 0;
54
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 46 times.
98 for(size_t i = 0; i < seg[1].len; i++) {
55
3/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 51 times.
52 if(seg[1].s[i] < '0' || seg[1].s[i] > '9') return false;
56 51 id = id * 10 + (seg[1].s[i] - '0');
57 }
58
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 45 times.
46 if(id > 255) return false;
59 char value[16];
60 45 const size_t n = length < sizeof(value) - 1 ? length : sizeof(value) - 1;
61 45 memcpy(value, payload, n);
62 45 value[n] = '\0';
63 45 const int val = atoi(value);
64 45 const segment_t &cmd = seg[2];
65
66 45 shade_request_t r;
67 45 r.id = static_cast<uint8_t>(id);
68
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 11 times.
45 if(seg[0].is("shades")) {
69
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 26 times.
34 if(cmd.is("target")) {
70
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
8 if(!inRange(val)) return false;
71 6 r.type = shade_request_type_t::shade_command;
72 6 r.target = static_cast<int8_t>(val);
73 }
74
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 23 times.
26 else if(cmd.is("tiltTarget")) {
75
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
3 if(!inRange(val)) return false;
76 2 r.type = shade_request_type_t::tilt_command;
77 2 r.target = static_cast<int8_t>(val);
78 }
79
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 18 times.
23 else if(cmd.is("direction")) {
80 5 r.type = shade_request_type_t::shade_command;
81 5 r.command = directionCommand(val);
82 }
83
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 15 times.
18 else if(cmd.is("mypos")) {
84
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
3 if(!inRange(val)) return false;
85 2 r.type = shade_request_type_t::set_favorite;
86 2 r.position = static_cast<int8_t>(val);
87 }
88
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 13 times.
15 else if(cmd.is("myTiltPos")) {
89
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(!inRange(val)) return false;
90 2 r.type = shade_request_type_t::set_favorite;
91 2 r.tiltPosition = static_cast<int8_t>(val);
92 }
93
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 10 times.
13 else if(cmd.is("sunFlag")) {
94 3 r.type = shade_request_type_t::shade_command;
95
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 r.command = sunFlagCommand(val > 0);
96 }
97
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
10 else if(cmd.is("sunny")) {
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(val < 0) return false;
99 2 r.type = shade_request_type_t::shade_sensor;
100
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 r.sunny = val > 0 ? 1 : 0;
101 }
102
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
8 else if(cmd.is("windy")) {
103
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 if(val < 0) return false;
104 2 r.type = shade_request_type_t::shade_sensor;
105
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 r.windy = val > 0 ? 1 : 0;
106 }
107
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
5 else if(cmd.is("position")) {
108
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(!inRange(val)) return false;
109 2 r.type = shade_request_type_t::set_positions;
110 2 r.position = static_cast<int8_t>(val);
111 }
112
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 else if(cmd.is("tiltPosition")) {
113
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(!inRange(val)) return false;
114 2 r.type = shade_request_type_t::set_positions;
115 2 r.tiltPosition = static_cast<int8_t>(val);
116 }
117 1 else return false;
118 }
119
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 time.
11 else if(seg[0].is("groups")) {
120
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
10 if(cmd.is("direction")) {
121 2 r.type = shade_request_type_t::group_command;
122 2 r.command = directionCommand(val);
123 }
124
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
8 else if(cmd.is("sunFlag")) {
125 2 r.type = shade_request_type_t::group_command;
126
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 r.command = sunFlagCommand(val > 0);
127 }
128
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
6 else if(cmd.is("sunny")) {
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(val < 0) return false;
130 2 r.type = shade_request_type_t::group_sensor;
131
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 r.sunny = val > 0 ? 1 : 0;
132 }
133
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 else if(cmd.is("windy")) {
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(val < 0) return false;
135 2 r.type = shade_request_type_t::group_sensor;
136
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 r.windy = val > 0 ? 1 : 0;
137 }
138 2 else return false;
139 }
140 1 else return false;
141 36 req = r;
142 36 return true;
143 }
144