85 mqtt_last_mid_sent = mid; |
132 mqtt_last_mid_sent = mid; |
86 } |
133 } |
87 |
134 |
88 |
135 |
89 |
136 |
|
137 void my_subscribe_callback(struct mosquitto *my_mosq, void *userdata, int mid, int qos_count, const int *granted_qos) |
|
138 { |
|
139 int i; |
|
140 |
|
141 syslog(LOG_NOTICE, "Subscribed (mid: %d): %d", mid, granted_qos[0]); |
|
142 for (i = 1; i < qos_count; i++) { |
|
143 syslog(LOG_NOTICE, " %d", granted_qos[i]); |
|
144 } |
|
145 } |
|
146 |
|
147 |
|
148 |
90 void my_log_callback(struct mosquitto *my_mosq, void *obj, int level, const char *str) |
149 void my_log_callback(struct mosquitto *my_mosq, void *obj, int level, const char *str) |
91 { |
150 { |
92 syslog(LOG_NOTICE, "MQTT: %s", str); |
151 syslog(LOG_NOTICE, "MQTT: %s", str); |
93 if (debug) |
152 if (debug) |
94 fprintf(stdout, "MQTT: %s\n", str); |
153 fprintf(stdout, "MQTT: %s\n", str); |
95 } |
154 } |
96 |
155 |
97 |
156 |
98 |
157 |
|
158 void my_message_callback(struct mosquitto *my_mosq, void *userdata, const struct mosquitto_message *message) |
|
159 { |
|
160 if (message->payloadlen) { |
|
161 syslog(LOG_NOTICE, "MQTT: message callback %s :: %d", message->topic, message->payloadlen); |
|
162 // TODO: process subscribed topics here. |
|
163 |
|
164 } else { |
|
165 syslog(LOG_NOTICE, "MQTT: message callback %s (null)", message->topic); |
|
166 } |
|
167 } |
|
168 |
|
169 |
|
170 |
|
171 void publisher(struct mosquitto *my_mosq, char *topic, char *payload, bool retain) { |
|
172 // publish the data |
|
173 if (payload) |
|
174 mosquitto_publish(my_mosq, &mqtt_mid_sent, topic, strlen(payload), payload, mqtt_qos, retain); |
|
175 else |
|
176 mosquitto_publish(my_mosq, &mqtt_mid_sent, topic, 0, NULL, mqtt_qos, retain); |
|
177 } |
|
178 |
|
179 |
|
180 |
|
181 char *unit_data(units_list *unit, bool birth) |
|
182 { |
|
183 char *payload = NULL; |
|
184 char buf[128]; |
|
185 bool comma = false; |
|
186 profiles_list *profile; |
|
187 prof_step *pstep; |
|
188 |
|
189 payload = xstrcat(payload, (char *)"{"); |
|
190 if (birth || unit->mqtt_flag & MQTT_FLAG_MODE) { |
|
191 // Also send these on mode change |
|
192 payload = xstrcat(payload, (char *)"\"uuid\":\""); |
|
193 payload = xstrcat(payload, unit->uuid); |
|
194 payload = xstrcat(payload, (char *)"\",\"alias\":\""); |
|
195 payload = xstrcat(payload, unit->alias); |
|
196 payload = xstrcat(payload, (char *)"\",\"name\":\""); |
|
197 payload = xstrcat(payload, unit->name); |
|
198 payload = xstrcat(payload, (char *)"\""); |
|
199 comma = true; |
|
200 } |
|
201 if (birth || unit->mqtt_flag & MQTT_FLAG_AIR) { |
|
202 if (comma) |
|
203 payload = xstrcat(payload, (char *)","); |
|
204 if (unit->air_address) { |
|
205 payload = xstrcat(payload, (char *)"\"air\":{\"address\":\""); |
|
206 payload = xstrcat(payload, unit->air_address); |
|
207 payload = xstrcat(payload, (char *)"\",\"state\":\""); |
|
208 payload = xstrcat(payload, (char *)TEMPSTATE[unit->air_state]); |
|
209 payload = xstrcat(payload, (char *)"\",\"temperature\":"); |
|
210 sprintf(buf, "%.3f", unit->air_temperature / 1000.0); |
|
211 payload = xstrcat(payload, buf); |
|
212 payload = xstrcat(payload, (char *)"}"); |
|
213 } else { |
|
214 payload = xstrcat(payload, (char *)"\"air\":null"); |
|
215 } |
|
216 comma = true; |
|
217 } |
|
218 if (birth || unit->mqtt_flag & MQTT_FLAG_BEER) { |
|
219 if (comma) |
|
220 payload = xstrcat(payload, (char *)","); |
|
221 if (unit->beer_address) { |
|
222 payload = xstrcat(payload, (char *)"\"beer\":{\"address\":\""); |
|
223 payload = xstrcat(payload, unit->beer_address); |
|
224 payload = xstrcat(payload, (char *)"\",\"state\":\""); |
|
225 payload = xstrcat(payload, (char *)TEMPSTATE[unit->beer_state]); |
|
226 payload = xstrcat(payload, (char *)"\",\"temperature\":"); |
|
227 sprintf(buf, "%.3f", unit->beer_temperature / 1000.0); |
|
228 payload = xstrcat(payload, buf); |
|
229 payload = xstrcat(payload, (char *)"}"); |
|
230 } else { |
|
231 payload = xstrcat(payload, (char *)"\"beer\":null"); |
|
232 } |
|
233 comma = true; |
|
234 } |
|
235 if (birth || unit->mqtt_flag & MQTT_FLAG_HEATER) { |
|
236 if (comma) |
|
237 payload = xstrcat(payload, (char *)","); |
|
238 if (unit->heater_address) { |
|
239 payload = xstrcat(payload, (char *)"\"heater\":{\"address\":\""); |
|
240 payload = xstrcat(payload, unit->heater_address); |
|
241 payload = xstrcat(payload, (char *)"\",\"state\":"); |
|
242 sprintf(buf, "%d", unit->heater_state); |
|
243 payload = xstrcat(payload, buf); |
|
244 payload = xstrcat(payload, (char *)"}"); |
|
245 } else { |
|
246 payload = xstrcat(payload, (char *)"\"heater\":null"); |
|
247 } |
|
248 comma = true; |
|
249 } |
|
250 if (birth || unit->mqtt_flag & MQTT_FLAG_COOLER) { |
|
251 if (comma) |
|
252 payload = xstrcat(payload, (char *)","); |
|
253 if (unit->cooler_address) { |
|
254 payload = xstrcat(payload, (char *)"\"cooler\":{\"address\":\""); |
|
255 payload = xstrcat(payload, unit->cooler_address); |
|
256 payload = xstrcat(payload, (char *)"\",\"state\":"); |
|
257 sprintf(buf, "%d", unit->cooler_state); |
|
258 payload = xstrcat(payload, buf); |
|
259 payload = xstrcat(payload, (char *)"}"); |
|
260 } else { |
|
261 payload = xstrcat(payload, (char *)"\"cooler\":null"); |
|
262 } |
|
263 comma = true; |
|
264 } |
|
265 if (birth || unit->mqtt_flag & MQTT_FLAG_FAN) { |
|
266 if (comma) |
|
267 payload = xstrcat(payload, (char *)","); |
|
268 if (unit->fan_address) { |
|
269 payload = xstrcat(payload, (char *)"\"fan\":{\"address\":\""); |
|
270 payload = xstrcat(payload, unit->fan_address); |
|
271 payload = xstrcat(payload, (char *)"\",\"state\":"); |
|
272 sprintf(buf, "%d", unit->fan_state); |
|
273 payload = xstrcat(payload, buf); |
|
274 payload = xstrcat(payload, (char *)"}"); |
|
275 } else { |
|
276 payload = xstrcat(payload, (char *)"\"fan\":null"); |
|
277 } |
|
278 comma = true; |
|
279 } |
|
280 if (birth || unit->mqtt_flag & MQTT_FLAG_DOOR) { |
|
281 if (comma) |
|
282 payload = xstrcat(payload, (char *)","); |
|
283 if (unit->door_address) { |
|
284 payload = xstrcat(payload, (char *)"\"door\":{\"address\":\""); |
|
285 payload = xstrcat(payload, unit->door_address); |
|
286 payload = xstrcat(payload, (char *)"\",\"state\":"); |
|
287 sprintf(buf, "%d", unit->door_state); |
|
288 payload = xstrcat(payload, buf); |
|
289 payload = xstrcat(payload, (char *)"}"); |
|
290 } else { |
|
291 payload = xstrcat(payload, (char *)"\"door\":null"); |
|
292 } |
|
293 comma = true; |
|
294 } |
|
295 if (birth || unit->mqtt_flag & MQTT_FLAG_LIGHT) { |
|
296 if (comma) |
|
297 payload = xstrcat(payload, (char *)","); |
|
298 if (unit->light_address) { |
|
299 payload = xstrcat(payload, (char *)"\"light\":{\"address\":\""); |
|
300 payload = xstrcat(payload, unit->light_address); |
|
301 payload = xstrcat(payload, (char *)"\",\"state\":"); |
|
302 sprintf(buf, "%d", unit->light_state); |
|
303 payload = xstrcat(payload, buf); |
|
304 payload = xstrcat(payload, (char *)"}"); |
|
305 } else { |
|
306 payload = xstrcat(payload, (char *)"\"light\":null"); |
|
307 } |
|
308 comma = true; |
|
309 } |
|
310 if (birth || unit->mqtt_flag & MQTT_FLAG_PSU) { |
|
311 if (comma) |
|
312 payload = xstrcat(payload, (char *)","); |
|
313 if (unit->psu_address) { |
|
314 payload = xstrcat(payload, (char *)"\"psu\":{\"address\":\""); |
|
315 payload = xstrcat(payload, unit->psu_address); |
|
316 payload = xstrcat(payload, (char *)"\",\"state\":"); |
|
317 sprintf(buf, "%d", unit->psu_state); |
|
318 payload = xstrcat(payload, buf); |
|
319 payload = xstrcat(payload, (char *)"}"); |
|
320 } else { |
|
321 payload = xstrcat(payload, (char *)"\"psu\":null"); |
|
322 } |
|
323 comma = true; |
|
324 } |
|
325 if (birth || unit->mqtt_flag & MQTT_FLAG_MODE) { |
|
326 if (comma) |
|
327 payload = xstrcat(payload, (char *)","); |
|
328 payload = xstrcat(payload, (char *)"\"mode\":\""); |
|
329 payload = xstrcat(payload, (char *)UNITMODE[unit->mode]); |
|
330 payload = xstrcat(payload, (char *)"\""); |
|
331 comma = true; |
|
332 } |
|
333 if (birth || unit->mqtt_flag & MQTT_FLAG_SP) { |
|
334 if (unit->mode != UNITMODE_OFF) { |
|
335 if (comma) |
|
336 payload = xstrcat(payload, (char *)","); |
|
337 payload = xstrcat(payload, (char *)"\"setpoint\":{\"low\":"); |
|
338 sprintf(buf, "%.1f", unit->PID_heat->SetP); |
|
339 payload = xstrcat(payload, buf); |
|
340 payload = xstrcat(payload, (char *)",\"high\":"); |
|
341 sprintf(buf, "%.1f", unit->PID_cool->SetP); |
|
342 payload = xstrcat(payload, buf); |
|
343 payload = xstrcat(payload, (char *)"}"); |
|
344 comma = true; |
|
345 } |
|
346 } |
|
347 if (birth || unit->mqtt_flag & MQTT_FLAG_PROFILE || unit->mqtt_flag & MQTT_FLAG_PERCENT) { |
|
348 if (unit->mode == UNITMODE_PROFILE && unit->profile) { |
|
349 for (profile = Config.profiles; profile; profile = profile->next) { |
|
350 if (strcmp(unit->profile, profile->uuid) == 0) { |
|
351 if (comma) |
|
352 payload = xstrcat(payload, (char *)","); |
|
353 payload = xstrcat(payload, (char *)"\"profile\":{\"uuid\":\""); |
|
354 payload = xstrcat(payload, unit->profile); |
|
355 payload = xstrcat(payload, (char *)",\"name\":\""); |
|
356 payload = xstrcat(payload, profile->name); |
|
357 payload = xstrcat(payload, (char *)"\",\"inittemp\":{\"low\":"); |
|
358 sprintf(buf, "%.1f", profile->inittemp_lo); |
|
359 payload = xstrcat(payload, buf); |
|
360 payload = xstrcat(payload, (char *)",\"high\":"); |
|
361 sprintf(buf, "%.1f", profile->inittemp_hi); |
|
362 payload = xstrcat(payload, buf); |
|
363 payload = xstrcat(payload, (char *)"},\"fridgemode\":"); |
|
364 sprintf(buf, "%d", profile->fridge_mode); |
|
365 payload = xstrcat(payload, buf); |
|
366 comma = false; |
|
367 if (profile->steps) { |
|
368 payload = xstrcat(payload, (char *)",\"steps\":["); |
|
369 for (pstep = profile->steps; pstep; pstep = pstep->next) { |
|
370 if (comma) |
|
371 payload = xstrcat(payload, (char *)","); |
|
372 payload = xstrcat(payload, (char *)"{\"resttime\":"); |
|
373 sprintf(buf, "%d", pstep->resttime); |
|
374 payload = xstrcat(payload, buf); |
|
375 payload = xstrcat(payload, (char *)",\"steptime\":"); |
|
376 sprintf(buf, "%d", pstep->steptime); |
|
377 payload = xstrcat(payload, buf); |
|
378 payload = xstrcat(payload, (char *)",\"target\":{\"low\":"); |
|
379 sprintf(buf, "%.1f", pstep->target_lo); |
|
380 payload = xstrcat(payload, buf); |
|
381 payload = xstrcat(payload, (char *)",\"high\":"); |
|
382 sprintf(buf, "%.1f", pstep->target_hi); |
|
383 payload = xstrcat(payload, buf); |
|
384 payload = xstrcat(payload, (char *)"},\"fridgemode\":"); |
|
385 sprintf(buf, "%d", pstep->fridge_mode); |
|
386 payload = xstrcat(payload, buf); |
|
387 payload = xstrcat(payload, (char *)"}"); |
|
388 comma = true; |
|
389 } |
|
390 payload = xstrcat(payload, (char *)"]"); |
|
391 } else { |
|
392 payload = xstrcat(payload, (char *)",\"steps\":null"); |
|
393 } |
|
394 payload = xstrcat(payload, (char *)"}"); |
|
395 break; |
|
396 } |
|
397 } |
|
398 } else { |
|
399 if (comma) |
|
400 payload = xstrcat(payload, (char *)","); |
|
401 payload = xstrcat(payload, (char *)"\"profile\":null"); |
|
402 } |
|
403 } |
|
404 payload = xstrcat(payload, (char *)"}"); |
|
405 |
|
406 return payload; |
|
407 } |
|
408 |
|
409 |
|
410 |
|
411 void publishDBirth(void) |
|
412 { |
|
413 char *payload = NULL; |
|
414 units_list *unit; |
|
415 int comma = FALSE; |
|
416 |
|
417 payload = payload_header(); |
|
418 payload = xstrcat(payload, (char *)"{\"units\":["); |
|
419 for (unit = Config.units; unit; unit = unit->next) { |
|
420 if (comma) |
|
421 payload = xstrcat(payload, (char *)","); |
|
422 payload = xstrcat(payload, unit_data(unit, true)); |
|
423 comma = TRUE; |
|
424 } |
|
425 payload = xstrcat(payload, (char *)"]}}"); |
|
426 publisher(mosq, topic_base((char *)"DBIRTH"), payload, true); |
|
427 free(payload); |
|
428 payload = NULL; |
|
429 } |
|
430 |
99 #endif |
431 #endif |
100 |
432 |
101 |
433 |
|
434 void publishDData(units_list *unit) |
|
435 { |
|
436 #ifdef HAVE_MOSQUITTO_H |
|
437 |
|
438 char *payload = NULL, *topic = NULL; |
|
439 |
|
440 if (mqtt_use) { |
|
441 payload = payload_header(); |
|
442 payload = xstrcat(payload, unit_data(unit, false)); |
|
443 payload = xstrcat(payload, (char *)"}"); |
|
444 topic = xstrcat(topic_base((char *)"DDATA"), (char *)"/"); |
|
445 topic = xstrcat(topic, unit->alias); |
|
446 publisher(mosq, topic, payload, false); |
|
447 free(payload); |
|
448 payload = NULL; |
|
449 free(topic); |
|
450 topic = NULL; |
|
451 } |
|
452 #endif |
|
453 } |
|
454 |
|
455 |
|
456 |
|
457 void publishNData(bool birth, int flag) |
|
458 { |
|
459 #ifdef HAVE_MOSQUITTO_H |
|
460 char *payload = NULL, buf[64]; |
|
461 struct utsname ubuf; |
|
462 bool comma = false; |
|
463 |
|
464 payload = payload_header(); |
|
465 payload = xstrcat(payload, (char *)"{"); |
|
466 |
|
467 if (birth || flag & MQTT_NODE_CONTROL) { |
|
468 payload = xstrcat(payload, (char *)"\"nodecontrol\":{\"reboot\":false,\"rebirth\":false,\"nextserver\":false,\"scanrate\":3000}"); |
|
469 comma = true; |
|
470 } |
|
471 |
|
472 if (birth) { |
|
473 if (comma) |
|
474 payload = xstrcat(payload, (char *)","); |
|
475 payload = xstrcat(payload, (char *)"\"properties\":{\"hardwaremake\":\"Unknown\",\"hardwaremodel\":\"Unknown\""); |
|
476 if (uname(&ubuf) == 0) { |
|
477 payload = xstrcat(payload, (char *)",\"os\":\""); |
|
478 payload = xstrcat(payload, ubuf.sysname); |
|
479 payload = xstrcat(payload, (char *)"\",\"os_version\":\""); |
|
480 payload = xstrcat(payload, ubuf.release); |
|
481 payload = xstrcat(payload, (char *)"\""); |
|
482 } else { |
|
483 payload = xstrcat(payload, (char *)",\"os\":\"Unknown\",\"os_version\":\"Unknown\""); |
|
484 } |
|
485 |
|
486 payload = xstrcat(payload, (char *)",\"FW\":\""); |
|
487 payload = xstrcat(payload, (char *)VERSION); |
|
488 payload = xstrcat(payload, (char *)"\"}"); |
|
489 comma = true; |
|
490 } |
|
491 |
|
492 if (birth || flag & MQTT_NODE_HT) { |
|
493 if (Config.temp_address || Config.hum_address) { |
|
494 if (comma) |
|
495 payload = xstrcat(payload, (char *)","); |
|
496 payload = xstrcat(payload, (char *)"\"HT\":{"); |
|
497 if (Config.temp_address) { |
|
498 payload = xstrcat(payload, (char *)"\"temperature\":"); |
|
499 sprintf(buf, "%.1f", Config.temp_value / 1000.0); |
|
500 payload = xstrcat(payload, buf); |
|
501 } |
|
502 if (Config.temp_address && Config.hum_address) |
|
503 payload = xstrcat(payload, (char *)","); |
|
504 if (Config.hum_address) { |
|
505 payload = xstrcat(payload, (char *)"\"humidity\":"); |
|
506 sprintf(buf, "%.1f", Config.hum_value / 1000.0); |
|
507 payload = xstrcat(payload, buf); |
|
508 } |
|
509 payload = xstrcat(payload, (char *)"}"); |
|
510 } |
|
511 } |
|
512 payload = xstrcat(payload, (char *)"}}"); |
|
513 |
|
514 if (birth) |
|
515 publisher(mosq, topic_base((char *)"NBIRTH"), payload, true); |
|
516 else |
|
517 publisher(mosq, topic_base((char *)"NDATA"), payload, false); |
|
518 |
|
519 free(payload); |
|
520 payload = NULL; |
|
521 #endif |
|
522 } |
|
523 |
|
524 |
|
525 |
|
526 void publishBirth(void) |
|
527 { |
|
528 publishNData(true, 0); |
|
529 publishDBirth(); |
|
530 } |
|
531 |
|
532 |
|
533 |
|
534 |
102 void mqtt_connect(void) |
535 void mqtt_connect(void) |
103 { |
536 { |
104 #ifdef HAVE_MOSQUITTO_H |
537 #ifdef HAVE_MOSQUITTO_H |
105 char *id = NULL; |
538 char *id = NULL; |
106 char err[1024]; |
539 char err[1024]; |
107 int rc; |
540 int rc; |
108 |
541 |
109 /* |
542 /* |
110 * Initialize mosquitto communication |
543 * Initialize mosquitto communication |
111 */ |
544 */ |
112 gethostname(my_hostname, 255); |
545 gethostname(my_hostname, 255); |