| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 3 | * 2013-2022 by the Falcon Player Developers. | ||
| 4 | * | ||
| 5 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 6 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 7 | * file for descriptions of what files are covered by each license. | ||
| 8 | * | ||
| 9 | * This source file is covered under the GPL v2 as described in the | ||
| 10 | * included LICENSE.GPL file. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "fpp-pch.h" | ||
| 14 | |||
| 15 | #include "fpp-json.h" | ||
| 16 | |||
| 17 | #include <cmath> | ||
| 18 | |||
| 19 | #include "../common.h" | ||
| 20 | #include "../log.h" | ||
| 21 | #include "../mqtt.h" | ||
| 22 | |||
| 23 | #include "MQTTOutput.h" | ||
| 24 | |||
| 25 | ///////////////////////////////////////////////////////////////////////////// | ||
| 26 | #include "Plugin.h" | ||
| 27 | class MQTTPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin { | ||
| 28 | public: | ||
| 29 | 15 | MQTTPlugin() : | |
| 30 |
2/4✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
45 | FPPPlugins::Plugin("MQTT") { |
| 31 | 15 | } | |
| 32 | 15 | virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override { | |
| 33 |
2/6✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
30 | return new MQTTOutput(startChannel, channelCount); |
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | extern "C" { | ||
| 38 | 15 | FPPPlugins::Plugin* createPlugin() { | |
| 39 |
2/6✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
30 | return new MQTTPlugin(); |
| 40 | } | ||
| 41 | } | ||
| 42 | /* | ||
| 43 | * | ||
| 44 | */ | ||
| 45 | 15 | MQTTOutput::MQTTOutput(unsigned int startChannel, unsigned int channelCount) : | |
| 46 | ThreadedChannelOutput(startChannel, channelCount), | ||
| 47 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
30 | m_payload("%R%,%G%,%B%"), |
| 48 | 15 | m_type(OutputType::THREE_CHAN), | |
| 49 | 15 | m_r(0x00), | |
| 50 | 15 | m_g(0x00), | |
| 51 | 15 | m_b(0x00), | |
| 52 | 15 | m_w(0x00) { | |
| 53 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | LogDebug(VB_CHANNELOUT, "MQTTOutput::MQTTOutput(%u, %u)\n", |
| 54 | startChannel, channelCount); | ||
| 55 | |||
| 56 | 15 | m_useDoubleBuffer = 1; | |
| 57 | 15 | } | |
| 58 | |||
| 59 | /* | ||
| 60 | * | ||
| 61 | */ | ||
| 62 | 60 | MQTTOutput::~MQTTOutput() { | |
| 63 | 30 | LogDebug(VB_CHANNELOUT, "MQTTOutput::~MQTTOutput()\n"); | |
| 64 | 60 | } | |
| 65 | |||
| 66 | 15 | int MQTTOutput::Init(Json::Value config) { | |
| 67 | 15 | LogDebug(VB_CHANNELOUT, "MQTTOutput::Init()\n"); | |
| 68 | |||
| 69 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
|
15 | if (!mqtt) { |
| 70 | 1 | LogWarn(VB_CHANNELOUT, "MQTT Not Configured, cannot start MQTT Output"); | |
| 71 | 1 | return false; | |
| 72 | } | ||
| 73 | |||
| 74 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (config.isMember("topic")) { |
| 75 |
2/4✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
14 | m_topic = config["topic"].asString(); |
| 76 | } | ||
| 77 | // A publish topic may not contain the subscription wildcards: the library | ||
| 78 | // refused every publish, and each refusal was logged, up to once a frame. | ||
| 79 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
|
14 | if (m_topic.find_first_of("+#") != std::string::npos) { |
| 80 | 3 | LogErr(VB_CHANNELOUT, "MQTT Output topic '%s' contains a wildcard (+ or #); not starting it\n", m_topic.c_str()); | |
| 81 | 3 | return false; | |
| 82 | } | ||
| 83 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
|
11 | if (config.isMember("payload")) { |
| 84 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | auto payload = config["payload"].asString(); |
| 85 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
|
6 | if (!payload.empty()) { |
| 86 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | m_payload = payload; |
| 87 | } | ||
| 88 | 6 | } | |
| 89 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
|
11 | if (config.isMember("channelType")) { |
| 90 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | auto type = config["channelType"].asString(); |
| 91 |
3/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
|
6 | if (type == "RGBW") { |
| 92 | 1 | m_type = OutputType::FOUR_CHAN; | |
| 93 |
3/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
|
5 | } else if (type == "RGB") { |
| 94 | 4 | m_type = OutputType::THREE_CHAN; | |
| 95 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | } else if (type == "Single") { |
| 96 | 1 | m_type = OutputType::ONE_CHAN; | |
| 97 | } | ||
| 98 | 6 | } | |
| 99 | |||
| 100 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | return ThreadedChannelOutput::Init(config); |
| 101 | } | ||
| 102 | |||
| 103 | 22 | void MQTTOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) { | |
| 104 | 22 | addRange(m_startChannel, m_startChannel + (int)m_type - 1); | |
| 105 | 22 | } | |
| 106 | |||
| 107 | /* | ||
| 108 | * | ||
| 109 | */ | ||
| 110 | 11 | int MQTTOutput::Close(void) { | |
| 111 | 11 | LogDebug(VB_CHANNELOUT, "MQTTOutput::Close()\n"); | |
| 112 | |||
| 113 | 11 | return ThreadedChannelOutput::Close(); | |
| 114 | } | ||
| 115 | |||
| 116 | /* | ||
| 117 | * | ||
| 118 | */ | ||
| 119 | 1069 | int MQTTOutput::RawSendData(unsigned char* channelData) { | |
| 120 |
1/2✓ Branch 0 taken 1069 times.
✗ Branch 1 not taken.
|
1069 | LogExcess(VB_CHANNELOUT, "MQTTOutput::RawSendData(%p)\n", channelData); |
| 121 | |||
| 122 |
6/8✓ Branch 0 taken 1069 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1069 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1068 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1068 times.
|
1069 | if (!mqtt || !mqtt->IsConnected()) { |
| 123 | 1 | return 0; | |
| 124 | } | ||
| 125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1068 times.
|
1068 | if (m_topic.empty()) { |
| 126 | ✗ | return 0; | |
| 127 | } | ||
| 128 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1068 times.
|
1068 | if (m_payload.empty()) { |
| 129 | ✗ | return 0; | |
| 130 | } | ||
| 131 | |||
| 132 | 1068 | uint8_t r = 0x00; | |
| 133 | 1068 | uint8_t g = 0x00; | |
| 134 | 1068 | uint8_t b = 0x00; | |
| 135 | 1068 | uint8_t w = 0x00; | |
| 136 | |||
| 137 | // The double buffer that ThreadedOutputBase performs only copies/buffers | ||
| 138 | // data from the startChannel for the required length. The channelData | ||
| 139 | // passed in is already starting at startChannel | ||
| 140 |
3/4✓ Branch 0 taken 128 times.
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 812 times.
✗ Branch 3 not taken.
|
1068 | switch (m_type) { |
| 141 | 128 | case OutputType::ONE_CHAN: | |
| 142 | 128 | w = channelData[0]; | |
| 143 | 128 | break; | |
| 144 | 128 | case OutputType::FOUR_CHAN: | |
| 145 | 128 | w = channelData[3]; | |
| 146 | 940 | case OutputType::THREE_CHAN: | |
| 147 | 940 | r = channelData[0]; | |
| 148 | 940 | g = channelData[1]; | |
| 149 | 940 | b = channelData[2]; | |
| 150 | 940 | break; | |
| 151 | } | ||
| 152 | |||
| 153 |
8/8✓ Branch 0 taken 1020 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 1017 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1016 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1007 times.
✓ Branch 7 taken 9 times.
|
1068 | if (r == m_r && g == m_g && b == m_b && w == m_w) { |
| 154 | 1007 | return m_channelCount; | |
| 155 | } | ||
| 156 | |||
| 157 | 61 | m_r = r; | |
| 158 | 61 | m_g = g; | |
| 159 | 61 | m_b = b; | |
| 160 | 61 | m_w = w; | |
| 161 | |||
| 162 |
1/2✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
|
61 | std::string payload = m_payload; |
| 163 | |||
| 164 |
1/2✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
|
61 | ReplaceValues(payload, r, g, b, w); |
| 165 | |||
| 166 |
1/2✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
|
61 | mqtt->PublishRaw(m_topic, payload); |
| 167 | |||
| 168 | 61 | return m_channelCount; | |
| 169 | } | ||
| 170 | |||
| 171 | /* | ||
| 172 | * | ||
| 173 | */ | ||
| 174 | 22 | void MQTTOutput::DumpConfig(void) { | |
| 175 | 22 | LogDebug(VB_CHANNELOUT, "MQTTOutput::DumpConfig()\n"); | |
| 176 | |||
| 177 | 22 | LogDebug(VB_CHANNELOUT, " Topic : %s\n", m_topic.c_str()); | |
| 178 | 22 | LogDebug(VB_CHANNELOUT, " Payload : %s\n", m_payload.c_str()); | |
| 179 | 22 | LogDebug(VB_CHANNELOUT, " Type : %d\n", (int)m_type); | |
| 180 | |||
| 181 | 22 | ThreadedChannelOutput::DumpConfig(); | |
| 182 | 22 | } | |
| 183 | |||
| 184 | 61 | void MQTTOutput::ReplaceValues(std::string& valueStr, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { | |
| 185 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%R%", std::to_string(r)); |
| 186 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%G%", std::to_string(g)); |
| 187 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%B%", std::to_string(b)); |
| 188 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%W%", std::to_string(w)); |
| 189 | |||
| 190 | 61 | int rScale = (r * 100) / 255; | |
| 191 | 61 | int gScale = (g * 100) / 255; | |
| 192 | 61 | int bScale = (b * 100) / 255; | |
| 193 | 61 | int wScale = (w * 100) / 255; | |
| 194 | |||
| 195 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%RS%", std::to_string(rScale)); |
| 196 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%GS%", std::to_string(gScale)); |
| 197 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%BS%", std::to_string(bScale)); |
| 198 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%WS%", std::to_string(wScale)); |
| 199 | |||
| 200 |
8/8✓ Branch 0 taken 51 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 43 times.
|
61 | if (r > 254 || g > 254 || b > 254 || w > 254) { |
| 201 |
3/6✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
|
72 | replaceAll(valueStr, "%SW%", "ON"); |
| 202 | } else { | ||
| 203 |
3/6✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
|
172 | replaceAll(valueStr, "%SW%", "OFF"); |
| 204 | } | ||
| 205 | |||
| 206 | float h, si, sv, i, v; | ||
| 207 | |||
| 208 | // 0..1 floats: integer division made every channel 0 (or 1 at full) | ||
| 209 |
1/2✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
|
61 | RGBtoHSIV(r / 255.0f, g / 255.0f, b / 255.0f, h, si, sv, i, v); |
| 210 | |||
| 211 | 61 | int ih = (h); | |
| 212 | 61 | int isi = (si * 100); | |
| 213 | 61 | int isv = (sv * 100); | |
| 214 | 61 | int ii = (i * 100); | |
| 215 | 61 | int iv = (v * 100); | |
| 216 | |||
| 217 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%H%", std::to_string(ih)); |
| 218 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%S%", std::to_string(isi)); |
| 219 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%SI%", std::to_string(isi)); |
| 220 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%SV%", std::to_string(isv)); |
| 221 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%I%", std::to_string(ii)); |
| 222 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
183 | replaceAll(valueStr, "%V%", std::to_string(iv)); |
| 223 | 61 | } | |
| 224 | |||
| 225 | 61 | void MQTTOutput::RGBtoHSIV(float fR, float fG, float fB, float& fH, float& fSI, float& fSV, float& fI, float& fV) { | |
| 226 | 61 | float M = std::max(std::max(fR, fG), fB); | |
| 227 | 61 | float m = std::min(std::min(fR, fG), fB); | |
| 228 | 61 | float c = M - m; | |
| 229 | 61 | fV = M; | |
| 230 | // fL = (1.0/2.0)*(M+m); | ||
| 231 | 61 | fI = (1.0 / 3.0) * (fR + fG + fB); | |
| 232 | |||
| 233 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 35 times.
|
61 | if (c == 0) { |
| 234 | 26 | fH = 0.0; | |
| 235 | 26 | fSI = 0.0; | |
| 236 | } else { | ||
| 237 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 18 times.
|
35 | if (M == fR) { |
| 238 | 17 | fH = fmod(((fG - fB) / c), 6.0); | |
| 239 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
|
18 | } else if (M == fG) { |
| 240 | 6 | fH = (fB - fR) / c + 2.0; | |
| 241 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | } else if (M == fB) { |
| 242 | 12 | fH = (fR - fG) / c + 4.0; | |
| 243 | } | ||
| 244 | 35 | fH *= 60.0; | |
| 245 |
1/2✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
|
35 | if (fI != 0) { |
| 246 | 35 | fSI = 1.0 - (m / fI); | |
| 247 | } else { | ||
| 248 | ✗ | fSI = 0.0; | |
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 14 times.
|
61 | fSV = M == 0 ? 0 : (M - m) / M; |
| 253 | |||
| 254 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 58 times.
|
61 | if (fH < 0.0) |
| 255 | 3 | fH += 360.0; | |
| 256 | 61 | } | |
| 257 |