|
2 | 2 | #warning **** Included USERMOD_BH1750 **** |
3 | 3 |
|
4 | 4 | #include "wled.h" |
5 | | -#include <BH1750.h> |
| 5 | +#include "BH1750_v2.h" |
6 | 6 |
|
7 | 7 | #ifdef WLED_DISABLE_MQTT |
8 | 8 | #error "This user mod requires MQTT to be enabled." |
9 | 9 | #endif |
10 | 10 |
|
11 | | -// the max frequency to check photoresistor, 10 seconds |
12 | | -#ifndef USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL |
13 | | -#define USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL 10000 |
14 | | -#endif |
15 | | - |
16 | | -// the min frequency to check photoresistor, 500 ms |
17 | | -#ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL |
18 | | -#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500 |
19 | | -#endif |
| 11 | +static bool checkBoundSensor(float newValue, float prevValue, float maxDiff) |
| 12 | +{ |
| 13 | + return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0); |
| 14 | +} |
20 | 15 |
|
21 | | -// how many seconds after boot to take first measurement, 10 seconds |
22 | | -#ifndef USERMOD_BH1750_FIRST_MEASUREMENT_AT |
23 | | -#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000 |
24 | | -#endif |
| 16 | +void Usermod_BH1750::_mqttInitialize() |
| 17 | +{ |
| 18 | + mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness"); |
25 | 19 |
|
26 | | -// only report if difference grater than offset value |
27 | | -#ifndef USERMOD_BH1750_OFFSET_VALUE |
28 | | -#define USERMOD_BH1750_OFFSET_VALUE 1 |
29 | | -#endif |
| 20 | + if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx")); |
| 21 | +} |
30 | 22 |
|
31 | | -class Usermod_BH1750 : public Usermod |
| 23 | +// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. |
| 24 | +void Usermod_BH1750::_createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) |
32 | 25 | { |
33 | | -private: |
34 | | - int8_t offset = USERMOD_BH1750_OFFSET_VALUE; |
35 | | - |
36 | | - unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL; |
37 | | - unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL; |
38 | | - unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT); |
39 | | - unsigned long lastSend = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT); |
40 | | - // flag to indicate we have finished the first readLightLevel call |
41 | | - // allows this library to report to the user how long until the first |
42 | | - // measurement |
43 | | - bool getLuminanceComplete = false; |
44 | | - |
45 | | - // flag set at startup |
46 | | - bool enabled = true; |
47 | | - |
48 | | - // strings to reduce flash memory usage (used more than twice) |
49 | | - static const char _name[]; |
50 | | - static const char _enabled[]; |
51 | | - static const char _maxReadInterval[]; |
52 | | - static const char _minReadInterval[]; |
53 | | - static const char _offset[]; |
54 | | - static const char _HomeAssistantDiscovery[]; |
55 | | - |
56 | | - bool initDone = false; |
57 | | - bool sensorFound = false; |
58 | | - |
59 | | - // Home Assistant and MQTT |
60 | | - String mqttLuminanceTopic; |
61 | | - bool mqttInitialized = false; |
62 | | - bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages |
63 | | - |
64 | | - BH1750 lightMeter; |
65 | | - float lastLux = -1000; |
66 | | - |
67 | | - bool checkBoundSensor(float newValue, float prevValue, float maxDiff) |
68 | | - { |
69 | | - return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0); |
70 | | - } |
| 26 | + String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config"); |
71 | 27 |
|
72 | | - // set up Home Assistant discovery entries |
73 | | - void _mqttInitialize() |
74 | | - { |
75 | | - mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness"); |
| 28 | + StaticJsonDocument<600> doc; |
| 29 | + |
| 30 | + doc[F("name")] = String(serverDescription) + " " + name; |
| 31 | + doc[F("state_topic")] = topic; |
| 32 | + doc[F("unique_id")] = String(mqttClientID) + name; |
| 33 | + if (unitOfMeasurement != "") |
| 34 | + doc[F("unit_of_measurement")] = unitOfMeasurement; |
| 35 | + if (deviceClass != "") |
| 36 | + doc[F("device_class")] = deviceClass; |
| 37 | + doc[F("expire_after")] = 1800; |
| 38 | + |
| 39 | + JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device |
| 40 | + device[F("name")] = serverDescription; |
| 41 | + device[F("identifiers")] = "wled-sensor-" + String(mqttClientID); |
| 42 | + device[F("manufacturer")] = F(WLED_BRAND); |
| 43 | + device[F("model")] = F(WLED_PRODUCT_NAME); |
| 44 | + device[F("sw_version")] = versionString; |
| 45 | + |
| 46 | + String temp; |
| 47 | + serializeJson(doc, temp); |
| 48 | + DEBUG_PRINTLN(t); |
| 49 | + DEBUG_PRINTLN(temp); |
| 50 | + |
| 51 | + mqtt->publish(t.c_str(), 0, true, temp.c_str()); |
| 52 | +} |
| 53 | + |
| 54 | +void Usermod_BH1750::setup() |
| 55 | +{ |
| 56 | + if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; } |
| 57 | + sensorFound = lightMeter.begin(); |
| 58 | + initDone = true; |
| 59 | +} |
76 | 60 |
|
77 | | - if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx")); |
78 | | - } |
| 61 | +void Usermod_BH1750::loop() |
| 62 | +{ |
| 63 | + if ((!enabled) || strip.isUpdating()) |
| 64 | + return; |
79 | 65 |
|
80 | | - // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. |
81 | | - void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) |
82 | | - { |
83 | | - String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config"); |
84 | | - |
85 | | - StaticJsonDocument<600> doc; |
86 | | - |
87 | | - doc[F("name")] = String(serverDescription) + " " + name; |
88 | | - doc[F("state_topic")] = topic; |
89 | | - doc[F("unique_id")] = String(mqttClientID) + name; |
90 | | - if (unitOfMeasurement != "") |
91 | | - doc[F("unit_of_measurement")] = unitOfMeasurement; |
92 | | - if (deviceClass != "") |
93 | | - doc[F("device_class")] = deviceClass; |
94 | | - doc[F("expire_after")] = 1800; |
95 | | - |
96 | | - JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device |
97 | | - device[F("name")] = serverDescription; |
98 | | - device[F("identifiers")] = "wled-sensor-" + String(mqttClientID); |
99 | | - device[F("manufacturer")] = F(WLED_BRAND); |
100 | | - device[F("model")] = F(WLED_PRODUCT_NAME); |
101 | | - device[F("sw_version")] = versionString; |
102 | | - |
103 | | - String temp; |
104 | | - serializeJson(doc, temp); |
105 | | - DEBUG_PRINTLN(t); |
106 | | - DEBUG_PRINTLN(temp); |
107 | | - |
108 | | - mqtt->publish(t.c_str(), 0, true, temp.c_str()); |
109 | | - } |
| 66 | + unsigned long now = millis(); |
110 | 67 |
|
111 | | -public: |
112 | | - void setup() |
| 68 | + // check to see if we are due for taking a measurement |
| 69 | + // lastMeasurement will not be updated until the conversion |
| 70 | + // is complete the the reading is finished |
| 71 | + if (now - lastMeasurement < minReadingInterval) |
113 | 72 | { |
114 | | - if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; } |
115 | | - sensorFound = lightMeter.begin(); |
116 | | - initDone = true; |
| 73 | + return; |
117 | 74 | } |
118 | 75 |
|
119 | | - void loop() |
120 | | - { |
121 | | - if ((!enabled) || strip.isUpdating()) |
122 | | - return; |
| 76 | + bool shouldUpdate = now - lastSend > maxReadingInterval; |
123 | 77 |
|
124 | | - unsigned long now = millis(); |
| 78 | + float lux = lightMeter.readLightLevel(); |
| 79 | + lastMeasurement = millis(); |
| 80 | + getLuminanceComplete = true; |
125 | 81 |
|
126 | | - // check to see if we are due for taking a measurement |
127 | | - // lastMeasurement will not be updated until the conversion |
128 | | - // is complete the the reading is finished |
129 | | - if (now - lastMeasurement < minReadingInterval) |
| 82 | + if (shouldUpdate || checkBoundSensor(lux, lastLux, offset)) |
| 83 | + { |
| 84 | + lastLux = lux; |
| 85 | + lastSend = millis(); |
| 86 | + |
| 87 | + if (WLED_MQTT_CONNECTED) |
130 | 88 | { |
131 | | - return; |
| 89 | + if (!mqttInitialized) |
| 90 | + { |
| 91 | + _mqttInitialize(); |
| 92 | + mqttInitialized = true; |
| 93 | + } |
| 94 | + mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str()); |
| 95 | + DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx")); |
132 | 96 | } |
133 | | - |
134 | | - bool shouldUpdate = now - lastSend > maxReadingInterval; |
135 | | - |
136 | | - float lux = lightMeter.readLightLevel(); |
137 | | - lastMeasurement = millis(); |
138 | | - getLuminanceComplete = true; |
139 | | - |
140 | | - if (shouldUpdate || checkBoundSensor(lux, lastLux, offset)) |
| 97 | + else |
141 | 98 | { |
142 | | - lastLux = lux; |
143 | | - lastSend = millis(); |
144 | | -#ifndef WLED_DISABLE_MQTT |
145 | | - if (WLED_MQTT_CONNECTED) |
146 | | - { |
147 | | - if (!mqttInitialized) |
148 | | - { |
149 | | - _mqttInitialize(); |
150 | | - mqttInitialized = true; |
151 | | - } |
152 | | - mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str()); |
153 | | - DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx")); |
154 | | - } |
155 | | - else |
156 | | - { |
157 | | - DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data")); |
158 | | - } |
159 | | -#endif |
| 99 | + DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data")); |
160 | 100 | } |
161 | 101 | } |
| 102 | +} |
162 | 103 |
|
163 | | - inline float getIlluminance() { |
164 | | - return (float)lastLux; |
165 | | - } |
166 | 104 |
|
167 | | - void addToJsonInfo(JsonObject &root) |
168 | | - { |
169 | | - JsonObject user = root[F("u")]; |
170 | | - if (user.isNull()) |
171 | | - user = root.createNestedObject(F("u")); |
172 | | - |
173 | | - JsonArray lux_json = user.createNestedArray(F("Luminance")); |
174 | | - if (!enabled) { |
175 | | - lux_json.add(F("disabled")); |
176 | | - } else if (!sensorFound) { |
177 | | - // if no sensor |
178 | | - lux_json.add(F("BH1750 ")); |
179 | | - lux_json.add(F("Not Found")); |
180 | | - } else if (!getLuminanceComplete) { |
181 | | - // if we haven't read the sensor yet, let the user know |
182 | | - // that we are still waiting for the first measurement |
183 | | - lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000); |
184 | | - lux_json.add(F(" sec until read")); |
185 | | - return; |
186 | | - } else { |
187 | | - lux_json.add(lastLux); |
188 | | - lux_json.add(F(" lx")); |
189 | | - } |
190 | | - } |
191 | | - |
192 | | - // (called from set.cpp) stores persistent properties to cfg.json |
193 | | - void addToConfig(JsonObject &root) |
194 | | - { |
195 | | - // we add JSON object. |
196 | | - JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname |
197 | | - top[FPSTR(_enabled)] = enabled; |
198 | | - top[FPSTR(_maxReadInterval)] = maxReadingInterval; |
199 | | - top[FPSTR(_minReadInterval)] = minReadingInterval; |
200 | | - top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery; |
201 | | - top[FPSTR(_offset)] = offset; |
202 | | - |
203 | | - DEBUG_PRINTLN(F("BH1750 config saved.")); |
| 105 | +void Usermod_BH1750::addToJsonInfo(JsonObject &root) |
| 106 | +{ |
| 107 | + JsonObject user = root[F("u")]; |
| 108 | + if (user.isNull()) |
| 109 | + user = root.createNestedObject(F("u")); |
| 110 | + |
| 111 | + JsonArray lux_json = user.createNestedArray(F("Luminance")); |
| 112 | + if (!enabled) { |
| 113 | + lux_json.add(F("disabled")); |
| 114 | + } else if (!sensorFound) { |
| 115 | + // if no sensor |
| 116 | + lux_json.add(F("BH1750 ")); |
| 117 | + lux_json.add(F("Not Found")); |
| 118 | + } else if (!getLuminanceComplete) { |
| 119 | + // if we haven't read the sensor yet, let the user know |
| 120 | + // that we are still waiting for the first measurement |
| 121 | + lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000); |
| 122 | + lux_json.add(F(" sec until read")); |
| 123 | + return; |
| 124 | + } else { |
| 125 | + lux_json.add(lastLux); |
| 126 | + lux_json.add(F(" lx")); |
204 | 127 | } |
| 128 | +} |
205 | 129 |
|
206 | | - // called before setup() to populate properties from values stored in cfg.json |
207 | | - bool readFromConfig(JsonObject &root) |
| 130 | +// (called from set.cpp) stores persistent properties to cfg.json |
| 131 | +void Usermod_BH1750::addToConfig(JsonObject &root) |
| 132 | +{ |
| 133 | + // we add JSON object. |
| 134 | + JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname |
| 135 | + top[FPSTR(_enabled)] = enabled; |
| 136 | + top[FPSTR(_maxReadInterval)] = maxReadingInterval; |
| 137 | + top[FPSTR(_minReadInterval)] = minReadingInterval; |
| 138 | + top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery; |
| 139 | + top[FPSTR(_offset)] = offset; |
| 140 | + |
| 141 | + DEBUG_PRINTLN(F("BH1750 config saved.")); |
| 142 | +} |
| 143 | + |
| 144 | +// called before setup() to populate properties from values stored in cfg.json |
| 145 | +bool Usermod_BH1750::readFromConfig(JsonObject &root) |
| 146 | +{ |
| 147 | + // we look for JSON object. |
| 148 | + JsonObject top = root[FPSTR(_name)]; |
| 149 | + if (top.isNull()) |
208 | 150 | { |
209 | | - // we look for JSON object. |
210 | | - JsonObject top = root[FPSTR(_name)]; |
211 | | - if (top.isNull()) |
212 | | - { |
213 | | - DEBUG_PRINT(FPSTR(_name)); |
214 | | - DEBUG_PRINT(F("BH1750")); |
215 | | - DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); |
216 | | - return false; |
217 | | - } |
218 | | - bool configComplete = !top.isNull(); |
219 | | - |
220 | | - configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false); |
221 | | - configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms |
222 | | - configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms |
223 | | - configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false); |
224 | | - configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1); |
225 | | - |
226 | 151 | DEBUG_PRINT(FPSTR(_name)); |
227 | | - if (!initDone) { |
228 | | - DEBUG_PRINTLN(F(" config loaded.")); |
229 | | - } else { |
230 | | - DEBUG_PRINTLN(F(" config (re)loaded.")); |
231 | | - } |
232 | | - |
233 | | - return configComplete; |
234 | | - |
| 152 | + DEBUG_PRINT(F("BH1750")); |
| 153 | + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); |
| 154 | + return false; |
235 | 155 | } |
236 | | - |
237 | | - uint16_t getId() |
238 | | - { |
239 | | - return USERMOD_ID_BH1750; |
| 156 | + bool configComplete = !top.isNull(); |
| 157 | + |
| 158 | + configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false); |
| 159 | + configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms |
| 160 | + configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms |
| 161 | + configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false); |
| 162 | + configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1); |
| 163 | + |
| 164 | + DEBUG_PRINT(FPSTR(_name)); |
| 165 | + if (!initDone) { |
| 166 | + DEBUG_PRINTLN(F(" config loaded.")); |
| 167 | + } else { |
| 168 | + DEBUG_PRINTLN(F(" config (re)loaded.")); |
240 | 169 | } |
241 | 170 |
|
242 | | -}; |
| 171 | + return configComplete; |
| 172 | + |
| 173 | +} |
| 174 | + |
243 | 175 |
|
244 | 176 | // strings to reduce flash memory usage (used more than twice) |
245 | 177 | const char Usermod_BH1750::_name[] PROGMEM = "BH1750"; |
|
0 commit comments