diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53c35fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +ble_sensor_mqtt_pub +.vscode diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dcab913 --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +.POSIX: +.SUFFIXES: + +CC = gcc +#CFLAGS = -Wall -Wextra -pedantic -std=c99 -O2 +CFLAGS = -Wall -Wextra -O2 + +all: ble_sensor_mqtt_pub + +ble_sensor_mqtt_pub : ble_sensor_mqtt_pub.c + $(CC) $(CFLAGS) $< -lyaml -lbluetooth -lpaho-mqtt3c -o $@ + + +.PHONY : install +install: + install -m 755 ble_sensor_mqtt_pub /usr/bin/ + install -m 644 ble_sensor_mqtt_pub.service /etc/systemd/system/ +ifeq (,$(wildcard /etc/ble_sensor_mqtt_pub.yaml)) + install -m 600 ble_sensor_mqtt_pub.yaml /etc/ + # Config using /etc/ble_sensor_mqtt_pub.yaml +else + # Leaving existing config at /etc/ble_sensor_mqtt_pub.yaml +endif + # Start service with 'systemctl start ble_sensor_mqtt_pub' + # Set service to start at boot with 'systemctl enable ble_sensor_mqtt_pub' + +.PHONY : uninstall +uninstall: + systemctl stop ble_sensor_mqtt_pub + systemctl disable ble_sensor_mqtt_pub + rm -f /usr/bin/ble_sensor_mqtt_pub + rm -f /etc/systemd/system/ble_sensor_mqtt_pub.service + # Leaving config file if it exists + +.PHONY : clean +clean : + rm ble_sensor_mqtt_pub diff --git a/README.md b/README.md index 7f5af4c..3571093 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Read Bluetooth Advertising Packets from BLE temperature sensors and publish data This program decodes the bluetooth advertising packets for the following BLE temperature and humidity sensors: ``` -// 1 = Xiaomi LYWSD03MMC-ATC https://github.com/atc1441/ATC_MiThermometer +// 1 = Xiaomi LYWSD03MMC-ATC https://github.com/atc1441/ATC_MiThermometer or https://github.com/pvvx/ATC_MiThermometer // 2 = Govee H5052 (type 4 advertising packets) // 3 = Govee H5072 // 4 = Govee H5102 @@ -11,26 +11,33 @@ This program decodes the bluetooth advertising packets for the following BLE tem // 6 = Govee H5074 (type 4 advertising packets) // 99 = Display raw type 0 and type 4 advertising packets for this BLE MAC address ``` -The program uses the bluetooth and mqtt client libraries, steps to image Raspberry Pi and install necessary libraries to compile program are show at bottom of this readme: +When using the LYWSD03MMC you need to flash one of the custom firmwares above. Both the atc1441 and pvvx custom formats are supported. With pvvx custom you get more resolution for humidity. -To compile: +The program uses the bluetooth and mqtt client libraries, steps to image Raspberry Pi and install necessary libraries to compile program are show at bottom of this readme. +Presuming reasonably modern systemd based linux system you can build and run like this: ``` -gcc -o ble_sensor_mqtt_pub ble_sensor_mqtt_pub.c -lbluetooth -l paho-mqtt3c +sudo apt install libssl-dev libbluetooth-dev libyaml-dev +git clone https://github.com/eclipse/paho.mqtt.c.git +cd paho.mqtt.c/ +make +sudo make install +cd .. + +git clone https://github.com/deepcoder/bluetooth-temperature-sensors.git +cd bluetooth-temperature-sensors +make +sudo make install + +#edit for your config /etc/bluetooth-temperature-sensors.yaml +systemctl start ble_sensor_mqtt_pub + +# if you want to start at boot +systemctl enable ble_sensor_mqtt_pub ``` -To run: -``` -sudo ble_sensor_mqtt_pub - -bluetooth adapter = integer number of bluetooth devices, run hciconfig to see your adapters, 1st adapter is referenced as 0 in this program -scan type = 0 for passive, 1 for active advertising scan, some BLE sensors only share data on type 4 response active advertising packets -scan window = integer number that is multiplied by 0.625 to set advertising scanning window in milliseconds. Try 100 to start. -scan interval = integer number that is multiplied by 0.625 to set advertising scanning interval in milliseconds. Try 1000 to start. -BLE scanning requires root equivalent rights, therefore sudo is necessary. - ``` -## Example JSON published to MQTT topic: +## Example JSON published to MQTT topic if using legacy publishing (won't work with HA auto configuration): ``` topic: homeassistant/sensor/ble-temp/A4:C1:38:22:13:D0 @@ -38,6 +45,16 @@ homeassistant/sensor/ble-temp/A4:C1:38:22:13:D0 payload: {"timestamp":"20201206025836","mac-address":"A4:C1:38:22:13:D0","rssi":-69,"temperature":64.4,"units":"F","temperature-celsius":18.0,"humidity":44.0,"battery-pct":93,"sensor-name":"","location":"H5072 Kitchen","sensor-type":"3"} ``` + +## Example JSON published to MQTT topic if using new style publishing: +``` +topic: +homeassistant/sensor/ble-temp/th_kitchen/state + +payload: +{"timestamp":"20201206025836","mac":"A4:C1:38:22:13:D0","rssi":-69,"tempf":64.4,"units":"F","tempc":18.0,"humidity":44.0,"batterypct":93,"name":"Kitchen Temp/Hum","location":"Kitchen","type":"3"} +``` + At the top of each hour the program will publish a count of the total number of advertising packets seen for each sensor in the prior hour to MQTT. This is useful to check the bluetooth frequency reception for each sensor as well as the quality and frequency of readings for each sensor type. The sub topic for this is: ``` $SYS/hour-stats @@ -107,20 +124,63 @@ Example: ## Configuration file: -Please note the program is very UNFORGIVING of format mistakes in this file! Lines must match format, the third line of column headers is required. See included configuration file, this sets MQTT server, MQTT base topic and details about each BLE sensor. Key info you need to have is MAC Address of each sensor and the device type of each. +The configuration file is normal YAML. The included sample config has more detail but here is an example config with 4 sensors. ``` -tcp://172.148.5.11:1883 -homeassistant/sensor/ble-temp/ -mac address, type, location -DD:C1:38:70:0C:24, 1, Living Room -DD:C1:38:AC:77:44, 3, Shared Bathroom -DD:12:1D:22:80:77, 6, Attic -DD:C1:38:AC:28:A2, 99, LYWSD03MMC test +mqtt_server_url: "tcp://172.148.5.11:1883" +mqtt_base_topic: "homeassistant/sensor/ble-temp/" + +mqtt_username: "mosquitto" +mqtt_password: "mosquitto_pass" + +bluetooth_adapter: 0 +scan_type: 1 +scan_window: 100 +scan_interval: 1000 +logging_level: 3 + +publish_type: 1 +auto_configure: 1 +auto_conf_stats: 1 +auto_conf_tempc: 1 +auto_conf_tempf: 0 +auto_conf_hum: 1 +auto_conf_battery: 1 +auto_conf_voltage: 0 +auto_conf_signal: 1 + +sensors: + - name: "Living Room Temp/Hum" + unique: "th_living_room" + location: "Living Room" + type: 1 + mac: "DD:C1:38:70:0C:24" + + - name: "Shared Bathroom Temp/Hum" + unique: "th_bathroom" + location: "Shared Bathroom" + type: 3 + mac: "DD:C1:38:AC:77:44" + + - name: "Attic Temp/Hum" + unique: "th_attic" + location: "Attic" + type: 3 + mac: "DD:12:1D:22:80:77" + + - name: "LYWSD03MMC test" + unique: "th_test" + location: "Garage" + type: 99 + mac: "DD:C1:38:AC:28:A2" ``` -## Example Home Assistant MQTT sensor configuration: +## Use auto device configuration with Home Assistant +There is full support for Home Assintant's MQTT Discovery features (https://www.home-assistant.io/docs/mqtt/discovery). This will allow auto creation of all configured entities in HA as well as grouping them together properly into Devices (which doesn't seem possible to do currently without using auto discovery). Make sure the MQTT integration is enabled either via YAML or the UI. Make sure both publish_type & auto_configure are set to 1 and then pick which entities you'd like created for each sensor from the auto_conf_* options. Enabling auto_conf_battery will integrate with HA's battery function for devices (auto_conf_voltage will only be informational). + + +## Example Home Assistant manual MQTT sensor configuration: ``` - platform: mqtt name: "BLE Temperature Reading Hourly Stats" @@ -227,18 +287,21 @@ RAM : 1024Mb git clone https://github.com/deepcoder/bluetooth-temperature-sensors.git -# install libssl-dev if not already installed +# install libs if not already installed sudo apt-get install libssl-dev sudo apt-get install libbluetooth-dev +sudo apt-get install libyaml-dev + git clone https://github.com/eclipse/paho.mqtt.c.git cd paho.mqtt.c/ make sudo make install cd /home/pi/bluetooth-temperature-sensors -gcc -o ble_sensor_mqtt_pub ble_sensor_mqtt_pub.c -lbluetooth -l paho-mqtt3c +make +sudo make install pi@pi-ble-02:~/bluetooth-temperature-sensors $ lsusb Bus 001 Device 005: ID 0a5c:21e8 Broadcom Corp. BCM20702A0 Bluetooth 4.0 @@ -250,44 +313,24 @@ hci0: Type: Primary Bus: USB RX bytes:980 acl:0 sco:0 events:51 errors:0 TX bytes:2446 acl:0 sco:0 commands:51 errors:0 -pi@pi-ble-02:~/bluetooth-temperature-sensors $ sudo ./ble_sensor_mqtt_pub 0 1 100 1000 -ble_sensor_mqtt_pub v 2.11 -1 Bluetooth adapter(s) in system. -Reading configuration file : ble_sensor_mqtt_pub.csv -MQTT server : tcp://172.168.2.22:1883 -MQTT topic : homeassistant/sensor/ble-temp/ -Header |MAC Address |Type|Location | -Unit : 0 |58:2D:34:3B:44:16| 99|MJ_HT_V1_LYWSDCGQ | -Total devices in configuration file : 1 -MQTT client name : ble_sensor_mqtt_pub-2F:31:FC:13:02:00 -Bluetooth Adapter : 0 has MAC address : 2F:31:FC:13:02:00 -Advertising scan type (0=passive, 1=active): 1 -Advertising scan window : 100, 62.5 ms -Advertising scan interval : 1000, 625.0 ms -Scanning.... -current hour (GMT) = 23 -last hour (GMT) = 22 +# Edit config file +pi@pi-ble-02:~/bluetooth-temperature-sensors $ sudo nano /etc/ble_sensor_mqtt_pub.yaml + +# Run in the foreground +pi@pi-ble-02:~/bluetooth-temperature-sensors $ sudo /usr/bin/ble_sensor_mqtt_pub /etc/ble_sensor_mqtt_pub.yaml + +# Start the service +pi@pi-ble-02:~/bluetooth-temperature-sensors $ sudo systemctl start ble_sensor_mqtt_pub + +# Check status of the service +pi@pi-ble-02:~/bluetooth-temperature-sensors $ sudo systemctl status ble_sensor_mqtt_pub + +# Follow the log file, ctrl-c to exit +pi@pi-ble-02:~/bluetooth-temperature-sensors $ sudo journalctl -efu ble_sensor_mqtt_pub + +# Enable service to run at boot +pi@pi-ble-02:~/bluetooth-temperature-sensors $ sudo systemctl enable ble_sensor_mqtt_pub -========= -Current local time and date: Sun Dec 6 15:30:29 2020 -mac address = 58:2D:34:3B:44:16 location = MJ_HT_V1_LYWSDCGQ device type = 99 advertising_packet_type = 000 -==>0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 -==>0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 -==> 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 -==> 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 -==>043E230201000056723B342D5817020106131695FE5020AA015456723B342D58061002CD01B9 -==>__________ad________________________mmmmmmmmmmmmtttthhbbzbzbccrr -rssi = -71 -========= -Current local time and date: Sun Dec 6 15:30:29 2020 -mac address = 58:2D:34:3B:44:16 location = MJ_HT_V1_LYWSDCGQ device type = 99 advertising_packet_type = 004 -==>0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 -==>0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 -==> 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 -==> 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 -==>043E260201040056723B342D581A09094D4A5F48545F563105030F180A180916FFFFC3FB2C6D28B1B9 -==>__________ad________________________mmmmmmmmmmmmtttthhbbzbzbccrr -rssi = -71 ``` ## Comparison of number of sensor reading There is not much that can be done to control the number of sensor reading taken and then collected for each of the BLE devices. These graphs show a comparison of some of the sensors over a 24 hour period. Some sensors take readings often, others much less often. The Govee 5074 appears to takes both temperature and humidity readings several times per minute, where as sensors like the Govee 5075 go multiple minutes between readings. It is difficult to gauge the specific sampling rate. Note that some sensors seems to sample temperature and humidity at different intervals, for example the Xiaomi LYWSD03MMC w/ custom firmware does this. In addition to sampling rate of the sensor, you have to account for how many of the BLE advertising packets you are collecting. If a sensor has a poor RF signal, even if it is collecting and transmitting samples at one rate, your BLE collecting device might only be capturing a subset of the packets. In addition to RF signal considerations, the BLE parameters for scan window and scan interval will effect the number of advertising packets and therefor the number of samples you receive. The table below shows that for the test sensors, with BLE scan window of 125 ms and scan interval of 312.5 ms, the collector Raspberry PI captured on the order of 300 to 500 advertising packets per hour. Again, it is difficult to deduce the number of samples per hour each sensor was taking. diff --git a/ble_sensor_mqtt_pub.c b/ble_sensor_mqtt_pub.c index d1c0c00..bac3629 100644 --- a/ble_sensor_mqtt_pub.c +++ b/ble_sensor_mqtt_pub.c @@ -1,6 +1,6 @@ // ble_sensor_mqtt_pub.c // gcc -o ble_sensor_mqtt_pub ble_sensor_mqtt_pub.c -l bluetooth -l paho-mqtt3c -// 202102030607 +// 202102030607 // // decode BLE temperature sensor temperature and humidity data from BLE advertising packets // and publish to MQTT @@ -16,15 +16,20 @@ // Intel Edison Playground // Copyright (c) 2015 Damian Kołakowski. All rights reserved. // +// YAML config parsing based on: +// https://github.com/smavros/yaml-to-struct +// -#define VERSION_MAJOR 2 -#define VERSION_MINOR 13 +#define VERSION_MAJOR 3 +#define VERSION_MINOR 0 // why is it so hard to get the base name of the program withOUT the .c extension!!!!!!! #define PROGRAM_NAME "ble_sensor_mqtt_pub" // program configuration file, // holds list of BLE sensors to track -#define CONFIGURATION_FILE "ble_sensor_mqtt_pub.csv" +#define CONFIGURATION_FILE "/etc/ble_sensor_mqtt_pub.yaml" + +#define MAX_SENSORS 64 #include #include @@ -35,7 +40,7 @@ #include #include #include -#include +#include #include #include #include @@ -45,6 +50,7 @@ #include #include #include +#include #include "MQTTClient.h" // logging setup @@ -65,10 +71,9 @@ // LOG_DEBUG // A message useful for debugging programs. -int logging_level = LOG_INFO; +// int logging_level = LOG_INFO; // int logging_level = LOG_ERR; -// int logging_level = LOG_DEBUG; - +int logging_level = LOG_DEBUG; #define RSYSLOG_ADDRESS "192.168.2.5" #define LOGMESSAGESIZE 512 @@ -81,8 +86,8 @@ char mqtt_server_address[128]; #define MQTTCLIENTIDSIZE 128 char z_client_id_mqtt[MQTTCLIENTIDSIZE]; -#define QOS 1 -#define TIMEOUT 10000L +#define QOS 1 +#define TIMEOUT 10000L // MONITOR THIS AS YOU ADD MORE UNITS!!!!!!!!!!!!!!!!! #define MAXIMUM_JSON_MESSAGE 2048 @@ -92,13 +97,13 @@ char z_client_id_mqtt[MQTTCLIENTIDSIZE]; // base topic: // each sensor with publish it's data under this base, example: // homeassistant/sensor/ble-temp/A4:C1:38:DB:64:96 -char topic_base[128]; +//char topic_base[128]; //const char topic_base[] = "homeassistant/sensor/ble-temp/"; // under the base topic, this sub topic will publish statistics // topic for hourly statistics const char topic_statistics[] = "$SYS/hour-stats"; -struct hci_request ble_hci_request(uint16_t ocf, int clen, void * status, void * cparam) +struct hci_request ble_hci_request(uint16_t ocf, int clen, void *status, void *cparam) { struct hci_request rq; memset(&rq, 0, sizeof(rq)); @@ -111,49 +116,104 @@ struct hci_request ble_hci_request(uint16_t ocf, int clen, void * status, void * return rq; } +typedef struct +{ + int type; + char mac[19]; + char location[64]; + char name[64]; + char unique[64]; + char my_id[64]; + char make[64]; + char model[64]; + int readings_per_hour; +} sensor_t; + +typedef struct +{ + char mqtt_server_url[128]; + char mqtt_base_topic[128]; + char mqtt_username[64]; + char mqtt_password[64]; + int bluetooth_adapter; + int scan_type; + int scan_window; + int scan_interval; + int publish_type; + int auto_configure; + int auto_conf_stats; + int auto_conf_tempf; + int auto_conf_tempc; + int auto_conf_hum; + int auto_conf_battery; + int auto_conf_voltage; + int auto_conf_signal; + char syslog_address[64]; + int logging_level; + sensor_t sensors[MAX_SENSORS]; +} config_t; + +/* Global parser */ +unsigned int parser(config_t *config, char **argv); + +/* Parser utilities */ +void init_prs(FILE *fp, yaml_parser_t *parser); +void parse_next(yaml_parser_t *parser, yaml_event_t *event); +void clean_prs(FILE *fp, yaml_parser_t *parser, yaml_event_t *event); + +/* Parser actions */ +void event_switch(bool *seq_status, unsigned int *map_seq, config_t *config, + yaml_parser_t *parser, yaml_event_t *event, FILE *fp); +void to_data(bool *seq_status, unsigned int *map_seq, config_t *config, + yaml_parser_t *parser, yaml_event_t *event, FILE *fp); +void to_data_from_map(char *buf, unsigned int *map_seq, config_t *config, + yaml_parser_t *parser, yaml_event_t *event, FILE *fp); + +/* Post parsing utilities */ +void print_data(unsigned int sensor_count, config_t *config); + // returns a structure with info about each bluetooth adapter found on system and returns number of adapters found static int hci_devlist(struct hci_dev_info **di, int *num) { - int i; + int i; - if((*di = malloc(HCI_MAX_DEV * sizeof(**di))) == NULL) - { - printf("Couldn't allocated memory for hci_devlist: %s", strerror(errno)); - exit(1); - } + if ((*di = malloc(HCI_MAX_DEV * sizeof(**di))) == NULL) + { + printf("Couldn't allocated memory for hci_devlist: %s", strerror(errno)); + exit(1); + } - for(i = *num = 0; i < HCI_MAX_DEV; i++) - if (hci_devinfo(i, &(*di)[*num]) == 0) - (*num)++; + for (i = *num = 0; i < HCI_MAX_DEV; i++) + if (hci_devinfo(i, &(*di)[*num]) == 0) + (*num)++; - return 0; + return 0; } char advertising_packet_type_desc[9][30] = { - "ADV_IND 0 (0000)", - "ADV_DIRECT_IND 1 (0001)", - "ADV_NONCONN_IND 2 (0010)", - "SCAN_REQ 3 (0011)", - "SCAN_RSP 4 (0100)", - "CONNECT_REQ 5 (0101)", - "ADV_SCAN_IND 6 (0110)", - "ADV_EXT_IND 7 (0111)", - "AUX_CONNECT_RSP 8 (1000)" - }; + "ADV_IND 0 (0000)", + "ADV_DIRECT_IND 1 (0001)", + "ADV_NONCONN_IND 2 (0010)", + "SCAN_REQ 3 (0011)", + "SCAN_RSP 4 (0100)", + "CONNECT_REQ 5 (0101)", + "ADV_SCAN_IND 6 (0110)", + "ADV_EXT_IND 7 (0111)", + "AUX_CONNECT_RSP 8 (1000)"}; // used for printing packet info during debugging // https://stackoverflow.com/questions/111928/is-there-a-printf-converter-to-print-in-binary-format #define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" -#define BYTE_TO_BINARY(byte) \ - (byte & 0x80 ? '1' : '0'), \ - (byte & 0x40 ? '1' : '0'), \ - (byte & 0x20 ? '1' : '0'), \ - (byte & 0x10 ? '1' : '0'), \ - (byte & 0x08 ? '1' : '0'), \ - (byte & 0x04 ? '1' : '0'), \ - (byte & 0x02 ? '1' : '0'), \ - (byte & 0x01 ? '1' : '0') +#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0') // this function sends a log message to a remote syslog server // call with: @@ -162,21 +222,21 @@ char advertising_packet_type_desc[9][30] = // program name that is sending log message // message -void send_remote_syslog_message(int log_level, char *hostname, char *program_name, char *message) +void send_remote_syslog_message(int log_level, char *hostname, char *program_name, char *message) { - int sockfd, portno, n; + int sockfd, n; int serverlen; int message_length; struct sockaddr_in serveraddr; struct hostent *server; - #define LOGBUFFERSIZE 1024 +#define LOGBUFFERSIZE 1024 char syslogbuf[LOGBUFFERSIZE]; - #define RSYSLOGPORT 514 +#define RSYSLOGPORT 514 // socket: create the socket sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) - { + { fprintf(stderr, "ERROR opening socket for remote syslog write"); exit(1); } @@ -185,15 +245,15 @@ void send_remote_syslog_message(int log_level, char *hostname, char *program_na server = gethostbyname(hostname); if (server == NULL) { - fprintf(stderr,"ERROR, no such host as %s for remote syslog write\n", hostname); + fprintf(stderr, "ERROR, no such host as %s for remote syslog write\n", hostname); exit(1); } // build the server's Internet address - bzero((char *) &serveraddr, sizeof(serveraddr)); + bzero((char *)&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; - bcopy((char *)server->h_addr, - (char *)&serveraddr.sin_addr.s_addr, server->h_length); + bcopy((char *)server->h_addr, + (char *)&serveraddr.sin_addr.s_addr, server->h_length); serveraddr.sin_port = htons(RSYSLOGPORT); // build the syslog message @@ -206,8 +266,8 @@ void send_remote_syslog_message(int log_level, char *hostname, char *program_na n = sendto(sockfd, syslogbuf, strlen(syslogbuf), 0, (struct sockaddr *)&serveraddr, serverlen); if (n < 0) { - fprintf(stderr,"ERROR in sendto for remote syslog write\n"); - exit(1); + fprintf(stderr, "ERROR in sendto for remote syslog write\n"); + exit(1); } return; @@ -235,12 +295,12 @@ void delivered(void *context, MQTTClient_deliveryToken dt) int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) { int i; - char* payloadptr; + char *payloadptr; fprintf(stdout, "Message arrived\n"); fprintf(stdout, " topic: %s\n", topicName); fprintf(stdout, " message: "); payloadptr = message->payload; - for(i=0; ipayloadlen; i++) + for (i = 0; i < message->payloadlen; i++) { putc(*payloadptr++, stdout); } @@ -253,22 +313,21 @@ int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *m // MQTT connection to server lost handler void connlost(void *context, char *cause) { - snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d MQTT Server Connection lost", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "MQTT Server Connection lost, cause: %s\n", cause); exit(1); } // for reading configuration file -// read a field from the input line -char* getfield(char* line, int num) +// read a field from the input line +char *getfield(char *line, int num) { - char* tok; + char *tok; for (tok = strtok(line, ","); - tok && *tok; - tok = strtok(NULL, ",\n")) + tok && *tok; + tok = strtok(NULL, ",\n")) { if (!--num) return tok; @@ -285,33 +344,47 @@ char *trim(char *str) char *frontp = str; char *endp = NULL; - if( str == NULL ) { return NULL; } - if( str[0] == '\0' ) { return str; } + if (str == NULL) + { + return NULL; + } + if (str[0] == '\0') + { + return str; + } len = strlen(str); endp = str + len; // Move the front and back pointers to address the first non-whitespace // characters from each end. - while( isspace((unsigned char) *frontp) ) { ++frontp; } - if( endp != frontp ) + while (isspace((unsigned char)*frontp)) { - while( isspace((unsigned char) *(--endp)) && endp != frontp ) {} + ++frontp; + } + if (endp != frontp) + { + while (isspace((unsigned char)*(--endp)) && endp != frontp) + { + } } - if( frontp != str && endp == frontp ) - *str = '\0'; - else if( str + len - 1 != endp ) - *(endp + 1) = '\0'; + if (frontp != str && endp == frontp) + *str = '\0'; + else if (str + len - 1 != endp) + *(endp + 1) = '\0'; // Shift the string so that it starts at str so that if it's dynamically // allocated, we can still free it on the returned pointer. Note the reuse // of endp to mean the front of the string buffer now. endp = str; - if( frontp != str ) + if (frontp != str) { - while( *frontp ) { *endp++ = *frontp++; } - *endp = '\0'; + while (*frontp) + { + *endp++ = *frontp++; + } + *endp = '\0'; } return str; @@ -328,10 +401,73 @@ int main(int argc, char *argv[]) act.sa_handler = intHandler; sigaction(SIGINT, &act, NULL); + if (argc != 2) + { + snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Start program with a single argument pointing to yaml config file\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); + send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); + syslog(LOG_ERR, "%s", log_message); + fprintf(stderr, "Start program with a single argument pointing to yaml config file\n"); + exit(1); + } + + config_t config; + + int sensor_count; + sensor_count = parser(&config, argv); + + int x; + for (x = 0; x < sensor_count; x++) + { + if (config.publish_type) + { + strcpy(config.sensors[x].my_id, config.sensors[x].unique); + } + else + { + strcpy(config.sensors[x].my_id, config.sensors[x].mac); + } + switch (config.sensors[x].type) + { + case 1: + strcpy(config.sensors[x].make, "Xiaomi"); + strcpy(config.sensors[x].model, "LYWSD03MMC-ATC"); + break; + case 2: + strcpy(config.sensors[x].make, "Govee"); + strcpy(config.sensors[x].model, "H5052"); + break; + case 3: + strcpy(config.sensors[x].make, "Govee"); + strcpy(config.sensors[x].model, "H5072"); + break; + case 4: + strcpy(config.sensors[x].make, "Govee"); + strcpy(config.sensors[x].model, "H5102"); + break; + case 5: + strcpy(config.sensors[x].make, "Govee"); + strcpy(config.sensors[x].model, "H5075"); + break; + case 6: + strcpy(config.sensors[x].make, "Govee"); + strcpy(config.sensors[x].model, "H5074"); + break; + default: + strcpy(config.sensors[x].make, ""); + strcpy(config.sensors[x].model, ""); + } + } + logging_level = config.logging_level; + + if (logging_level > LOG_NOTICE) + { + print_data(sensor_count, &config); + } + int hci_devs_num; struct hci_dev_info *hci_devs; - int ret, hci_return, status; + int ret, status; // bluetooth adapter mac address char bluetooth_adapter_mac[19]; @@ -344,31 +480,22 @@ int main(int argc, char *argv[]) snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Couldn't enumerate HCI devices: %s", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, strerror(errno)); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Couldn't enumerate HCI devices: %s", strerror(errno)); exit(1); } else { - fprintf(stdout,"%u Bluetooth adapter(s) in system.\n", hci_devs_num); - } - - if (argc != 5) - { - snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Start program with four arguments, the bluetooth adapter number, scan type (0=passive, 1=active), BLE scan Window (0 for default), BLE scan Interval (0 for default)\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); - send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); - fprintf(stderr, "Start program with four arguments, the bluetooth adapter number, scan type (0=passive, 1=active), BLE scan Window (0 for default), BLE scan Interval (0 for default)\n"); - exit(1); + fprintf(stdout, "%u Bluetooth adapter(s) in system.\n", hci_devs_num); } // get requested adapter number from command line - bluetooth_adapter_number = atoi(argv[1]); + bluetooth_adapter_number = config.bluetooth_adapter; int ble_scan_type; // 0 = passive, 1 = active scan - - ble_scan_type = atoi(argv[2]); - + + ble_scan_type = config.scan_type; + if (ble_scan_type != 1) { ble_scan_type = 0; @@ -376,28 +503,28 @@ int main(int argc, char *argv[]) //int ble_scan_interval = 65; // value * 0.625 ms, scan every 41 milliseconds //int ble_scan_window = 750; // value * 0.625 ms, window 469 milliseconds - int ble_scan_window = 48; // value * 0.625 ms, window 30 milliseconds + int ble_scan_window = 48; // value * 0.625 ms, window 30 milliseconds int ble_scan_interval = 1500; // value * 0.625 ms, scan every 975 milliseconds - ble_scan_window = atoi(argv[3]); - + ble_scan_window = config.scan_window; + if (ble_scan_window == 0) { ble_scan_window = 48; } - ble_scan_interval = atoi(argv[4]); + ble_scan_interval = config.scan_interval; if (ble_scan_interval == 0) { ble_scan_interval = 1500; } - + if (bluetooth_adapter_number < 0 || bluetooth_adapter_number > hci_devs_num - 1) { snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Enter bluetooth adapter number between 0 and %u !!\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, hci_devs_num - 1); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Enter bluetooth adapter number between 0 and %u !!\n", hci_devs_num - 1); exit(1); } @@ -407,135 +534,21 @@ int main(int argc, char *argv[]) // log startup of program // strcpy(log_message, "test message *****"); - setlogmask (LOG_UPTO (LOG_INFO)); - openlog (PROGRAM_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); + setlogmask(LOG_UPTO(LOG_INFO)); + openlog(PROGRAM_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Starting.", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_INFO, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_INFO, "%s",log_message); + syslog(LOG_INFO, "%s", log_message); // maximum number of sensors - #define MAXIMUM_UNITS 40 + // #define MAXIMUM_UNITS 40 // number of devices read from configuration file int mac_total; - // type of ble temperature sensor - int device_units_type[MAXIMUM_UNITS]; // type of sensor - - // text description of sensor location - char device_units_location[MAXIMUM_UNITS][30]; - - // MAC address of sensor in ASCII - char device_units_mac[MAXIMUM_UNITS][19]; - - // name of device stored within, if supported and if retrieved from BLE advertising packet - char device_units_nice_name[MAXIMUM_UNITS][17]; - - // keep track of number of advertising packets we receive from each unit each hour - int device_units_reading_per_hour[MAXIMUM_UNITS] = { 0 }; - - // used to retrieve the name of device stored in BLE advertising pack - char nice_name [17]; - int nice_name_start; - int nice_name_length; int sensor_data_start; - // read the list of BLE sensors to track from configuration file - - // row in configuration file, starting at 1 - int row_number; - - // index of unit number, starting at zero - int unit_number; - - char fname[256] = CONFIGURATION_FILE; - - fprintf(stdout, "Reading configuration file : %s\n", fname); - - FILE* stream = fopen(fname, "r"); - - char line[1024]; - char* tmp; - char field_01[100]; - char field_02[100]; - char field_03[100]; - row_number = 0; - unit_number = 0; - - while (fgets(line, 1024, stream)) - { - // if line is blank, skip - if( strlen(trim(line)) > 0 ) - { - // NOTE strtok clobbers tmp - row_number = row_number + 1; - - if ( unit_number + 1 > MAXIMUM_UNITS ) - { - snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Too many devices in configuration file, limit is : %d\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, MAXIMUM_UNITS); - send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); - fprintf(stderr, "Too many devices in configuration file, limit is : %d\n", MAXIMUM_UNITS); - exit(1); - - } - - switch (row_number) - { - // row 1 is MQTT server string - case 1: - strcpy(mqtt_server_address, trim(line)); - fprintf(stdout, "MQTT server : %s\n", mqtt_server_address); - break; - - // row 2 is MQTT topic - case 2: - strcpy(topic_base, trim(line)); - fprintf(stdout, "MQTT topic : %s\n", topic_base); - break; - - // row 3 of configuration file must be column header row - case 3: - fprintf(stdout, "Header |MAC Address |Type|Location |\n"); - break; - - // row 4 and beyond must contain sensor unit information - default: - fprintf(stdout, "Unit : %3d ", unit_number); - - // blank the nice name of unit - device_units_nice_name[unit_number][0] = '\0'; - - tmp = strdup(line); - strcpy(field_01, trim(getfield(tmp, 1))); - strcpy(device_units_mac[unit_number], field_01); - fprintf(stdout, "|%-17s", device_units_mac[unit_number]); - free(tmp); - tmp = strdup(line); - strcpy(field_02, trim(getfield(tmp, 2))); - device_units_type[unit_number] = atoi(field_02); - fprintf(stdout, "|%4d", device_units_type[unit_number]); - free(tmp); - tmp = strdup(line); - strcpy(field_03, trim(getfield(tmp, 3))); - strcpy(device_units_location[unit_number], field_03); - fprintf(stdout, "|%-30s|\n", device_units_location[unit_number]); - free(tmp); - - unit_number = unit_number + 1; - } - } - else - { - snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d No blank rows in configuration file are allowed\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); - send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); - fprintf(stderr, "No blank rows in configuration file are allowed\n"); - exit(1); - } - } - // total number of devices read from configuration file - mac_total = unit_number; + mac_total = sensor_count; fprintf(stdout, "Total devices in configuration file : %d\n", mac_total); @@ -553,17 +566,19 @@ int main(int argc, char *argv[]) // set MQTT client ID to program name plus bluetooth mac address, to allow multiple instances on one machine snprintf(z_client_id_mqtt, MQTTCLIENTIDSIZE, "%s-%s", PROGRAM_NAME, bluetooth_adapter_mac); - fprintf(stdout, "MQTT client name : %s\n", z_client_id_mqtt); - MQTTClient_create(&client, mqtt_server_address, z_client_id_mqtt, MQTTCLIENT_PERSISTENCE_NONE, NULL); -// MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL); + fprintf(stdout, "MQTT client name : %s\n", z_client_id_mqtt); + MQTTClient_create(&client, config.mqtt_server_url, z_client_id_mqtt, MQTTCLIENT_PERSISTENCE_NONE, NULL); + // MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL); conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 1; + conn_opts.username = config.mqtt_username; + conn_opts.password = config.mqtt_password; MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered); if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) { snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d failed to connect to MQTT server", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Failed to connect to MQTT server, return code %d\n", rc); exit(1); } @@ -574,13 +589,14 @@ int main(int argc, char *argv[]) snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Bluetooth Adapter : %u has MAC address : %s\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, bluetooth_adapter_number, bluetooth_adapter_mac); send_remote_syslog_message(LOG_INFO, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_INFO, "%s",log_message); + syslog(LOG_INFO, "%s", log_message); fprintf(stdout, "Bluetooth Adapter : %u has MAC address : %s\n", bluetooth_adapter_number, bluetooth_adapter_mac); - if ( bluetooth_device < 0 ) { + if (bluetooth_device < 0) + { snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d failed to open HCI device", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Failed to open HCI device, return code %d\n", bluetooth_device); exit(1); } @@ -589,39 +605,39 @@ int main(int argc, char *argv[]) snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Advertising scan type (0=passive, 1=active): %u\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, ble_scan_type); send_remote_syslog_message(LOG_INFO, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_INFO, "%s",log_message); + syslog(LOG_INFO, "%s", log_message); fprintf(stdout, "Advertising scan type (0=passive, 1=active): %u\n", ble_scan_type); - snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Advertising scan window : %u %.1f ms\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, ble_scan_window, ble_scan_window*0.625); + snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Advertising scan window : %u %.1f ms\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, ble_scan_window, ble_scan_window * 0.625); send_remote_syslog_message(LOG_INFO, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_INFO, "%s",log_message); - fprintf(stdout, "Advertising scan window : %4u, %4.1f ms\n", ble_scan_window, ble_scan_window*0.625); - - snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Advertising scan interval : %u %.1f ms\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, ble_scan_interval, ble_scan_interval*0.625); + syslog(LOG_INFO, "%s", log_message); + fprintf(stdout, "Advertising scan window : %4u, %4.1f ms\n", ble_scan_window, ble_scan_window * 0.625); + + snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Advertising scan interval : %u %.1f ms\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, ble_scan_interval, ble_scan_interval * 0.625); send_remote_syslog_message(LOG_INFO, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_INFO, "%s",log_message); - fprintf(stdout, "Advertising scan interval : %4u, %4.1f ms\n", ble_scan_interval, ble_scan_interval*0.625); - - + syslog(LOG_INFO, "%s", log_message); + fprintf(stdout, "Advertising scan interval : %4u, %4.1f ms\n", ble_scan_interval, ble_scan_interval * 0.625); + le_set_scan_parameters_cp scan_params_cp; memset(&scan_params_cp, 0, sizeof(scan_params_cp)); // BLE PASSIVE OR ACTIVE SCAN *************************************************************************************** - scan_params_cp.type = ble_scan_type; // 0x00 for passive scan, 0x01 for active scan (to get scan response packets) - scan_params_cp.interval = htobs(ble_scan_interval); - scan_params_cp.window = htobs(ble_scan_window); -// scan_params_cp.interval = htobs(0x0010); -// scan_params_cp.window = htobs(0x0010); - scan_params_cp.own_bdaddr_type = 0x00; // Public Device Address (default). - scan_params_cp.filter = 0x00; // Accept all. + scan_params_cp.type = ble_scan_type; // 0x00 for passive scan, 0x01 for active scan (to get scan response packets) + scan_params_cp.interval = htobs(ble_scan_interval); + scan_params_cp.window = htobs(ble_scan_window); + // scan_params_cp.interval = htobs(0x0010); + // scan_params_cp.window = htobs(0x0010); + scan_params_cp.own_bdaddr_type = 0x00; // Public Device Address (default). + scan_params_cp.filter = 0x00; // Accept all. struct hci_request scan_params_rq = ble_hci_request(OCF_LE_SET_SCAN_PARAMETERS, LE_SET_SCAN_PARAMETERS_CP_SIZE, &status, &scan_params_cp); ret = hci_send_req(bluetooth_device, &scan_params_rq, 1000); - if ( ret < 0 ) { + if (ret < 0) + { hci_close_dev(bluetooth_device); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Failed to set scan parameters data", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Failed to set scan parameters data, you must run this program as ROOT, return code %d\n", ret); exit(1); } @@ -631,15 +647,17 @@ int main(int argc, char *argv[]) le_set_event_mask_cp event_mask_cp; memset(&event_mask_cp, 0, sizeof(le_set_event_mask_cp)); int i = 0; - for ( i = 0 ; i < 8 ; i++ ) event_mask_cp.mask[i] = 0xFF; + for (i = 0; i < 8; i++) + event_mask_cp.mask[i] = 0xFF; struct hci_request set_mask_rq = ble_hci_request(OCF_LE_SET_EVENT_MASK, LE_SET_EVENT_MASK_CP_SIZE, &status, &event_mask_cp); ret = hci_send_req(bluetooth_device, &set_mask_rq, 1000); - if ( ret < 0 ) { + if (ret < 0) + { hci_close_dev(bluetooth_device); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Failed to set event mask", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Failed to set event mask, return code %d\n", ret); exit(1); } @@ -648,17 +666,18 @@ int main(int argc, char *argv[]) le_set_scan_enable_cp scan_cp; memset(&scan_cp, 0, sizeof(scan_cp)); - scan_cp.enable = 0x01; // Enable flag. - scan_cp.filter_dup = 0x00; // Filtering disabled. + scan_cp.enable = 0x01; // Enable flag. + scan_cp.filter_dup = 0x00; // Filtering disabled. struct hci_request enable_adv_rq = ble_hci_request(OCF_LE_SET_SCAN_ENABLE, LE_SET_SCAN_ENABLE_CP_SIZE, &status, &scan_cp); ret = hci_send_req(bluetooth_device, &enable_adv_rq, 1000); - if ( ret < 0 ) { + if (ret < 0) + { hci_close_dev(bluetooth_device); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Failed to enable scan", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Failed to enable scan, return code %d\n", ret); exit(1); } @@ -670,26 +689,27 @@ int main(int argc, char *argv[]) hci_filter_set_ptype(HCI_EVENT_PKT, &nf); hci_filter_set_event(EVT_LE_META_EVENT, &nf); ret = setsockopt(bluetooth_device, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)); - if ( ret < 0 ) { + if (ret < 0) + { hci_close_dev(bluetooth_device); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Could not set socket options", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stderr, "Could not set socket options, return code %d\n", ret); exit(1); } snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Scanning....", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_INFO, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_INFO, "%s",log_message); -// fprintf(stdout, "%s v%2d.%02d\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); + syslog(LOG_INFO, "%s", log_message); + // fprintf(stdout, "%s v%2d.%02d\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); fprintf(stdout, "Scanning....\n"); fflush(stdout); // bluetooth advertising packet buffer uint8_t ble_adv_buf[HCI_MAX_EVENT_SIZE]; - evt_le_meta_event * meta_event; - le_advertising_info * adv_info; + evt_le_meta_event *meta_event; + le_advertising_info *adv_info; int bluetooth_adv_packet_length; // create the MQTT topic from the base topic string and the MAC address of sensor @@ -700,6 +720,7 @@ int main(int argc, char *argv[]) // MQTT payload buffer int payload_length; char payload_buffer[MAXIMUM_JSON_MESSAGE]; + // int payload_buff_size = 300; // holds current time of current advertising packet that is received @@ -714,11 +735,11 @@ int main(int argc, char *argv[]) time_t gmt_time_now; struct tm tnp = *gmtime(&gmt_time_now); - time( &gmt_time_now ); - tnp = *gmtime( &gmt_time_now ); + time(&gmt_time_now); + tnp = *gmtime(&gmt_time_now); hour_current = tnp.tm_hour; - if ( hour_current == 0 ) + if (hour_current == 0) { hour_last = 23; } @@ -730,19 +751,331 @@ int main(int argc, char *argv[]) fprintf(stdout, "current hour (GMT) = %d\n", hour_current); fprintf(stdout, "last hour (GMT) = %d\n", hour_last); - // loop until SIGINT received - while ( keep_running ) { + if (config.auto_configure) + { + if (logging_level > LOG_INFO) + { + fprintf(stdout, "=========\n"); + fprintf(stdout, "Begining auto configuration of devices\n"); + } + + if (config.auto_conf_stats) + { + + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"~\":\"%s$SYS/hour-stats\",\"name\":\"BLE Temperature Reading Hourly Stats\",\"uniq_id\":\"ble-tmp-hourly-stats\",\"stat_t\":\"~\",\"unit_of_meas\":\"Pkts\",\"val_tpl\":\"{{value_json.total_adv_packets}}\"}", + config.mqtt_base_topic); + + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } + + // // create the MQTT topic from the base topic string and the MAC address of sensor + // int topic_length; + // char topic_buffer[200]; + // int topic_buffer_size = 200; + + topic_length = snprintf(topic_buffer, topic_buffer_size, "%shourly-stats/config", config.mqtt_base_topic); + + // publish the message and wait for success + pubmsg.payload = payload_buffer; + pubmsg.payloadlen = payload_length; + pubmsg.qos = QOS; + pubmsg.retained = 1; + deliveredtoken = 0; + MQTTClient_publishMessage(client, topic_buffer, &pubmsg, &token); + + // wait for messqge to be delivered to server + // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); + while (deliveredtoken != token) + ; + } + + for (x = 0; x < sensor_count; x++) + { + if (logging_level > LOG_INFO) + { + fprintf(stdout, " Configuring: %s\n", config.sensors[x].my_id); + } + + if (config.sensors[x].type != 99) + { + // configure temp F sensor + if (config.auto_conf_tempf) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"~\":\"%s%s\",\"dev_cla\":\"temperature\",\"name\":\"%s-F\",\"uniq_id\":\"%s-F\",\"stat_t\":\"~/state\",\"unit_of_meas\":\"°F\",\"val_tpl\":\"{{value_json.tempf}}\",\"dev\":{\"name\":\"%s\",\"ids\":\"%s\",\"sa\":\"%s\",\"cns\":[[\"mac\", \"%s\"]],\"mf\":\"%s\",\"mdl\":\"%s\"} }", + config.mqtt_base_topic, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].location, + config.sensors[x].mac, + config.sensors[x].make, + config.sensors[x].model); + + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } + + // // create the MQTT topic from the base topic string and the MAC address of sensor + + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%sF/config", config.mqtt_base_topic, config.sensors[x].my_id); + + // publish the message and wait for success + pubmsg.payload = payload_buffer; + pubmsg.payloadlen = payload_length; + pubmsg.qos = QOS; + pubmsg.retained = 1; + deliveredtoken = 0; + MQTTClient_publishMessage(client, topic_buffer, &pubmsg, &token); + + // wait for messqge to be delivered to server + // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); + while (deliveredtoken != token) + ; + } + + // configure temp C sensor + if (config.auto_conf_tempc) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"~\":\"%s%s\",\"dev_cla\":\"temperature\",\"name\":\"%s-T\",\"uniq_id\":\"%s-T\",\"stat_t\":\"~/state\",\"unit_of_meas\":\"°C\",\"val_tpl\":\"{{value_json.tempc}}\",\"dev\":{\"name\":\"%s\",\"ids\":\"%s\",\"sa\":\"%s\",\"cns\":[[\"mac\", \"%s\"]],\"mf\":\"%s\",\"mdl\":\"%s\"} }", + config.mqtt_base_topic, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].location, + config.sensors[x].mac, + config.sensors[x].make, + config.sensors[x].model); + + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } + + // // create the MQTT topic from the base topic string and the MAC address of sensor + + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%sT/config", config.mqtt_base_topic, config.sensors[x].my_id); + + // publish the message and wait for success + pubmsg.payload = payload_buffer; + pubmsg.payloadlen = payload_length; + pubmsg.qos = QOS; + pubmsg.retained = 1; + deliveredtoken = 0; + MQTTClient_publishMessage(client, topic_buffer, &pubmsg, &token); + + // wait for messqge to be delivered to server + // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); + while (deliveredtoken != token) + ; + } + // configure hum sensor + if (config.auto_conf_hum) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"~\":\"%s%s\",\"dev_cla\":\"humidity\",\"name\":\"%s-H\",\"uniq_id\":\"%s-H\",\"stat_t\":\"~/state\",\"unit_of_meas\":\"%%\",\"val_tpl\":\"{{value_json.humidity}}\",\"dev\":{\"name\":\"%s\",\"ids\":\"%s\",\"sa\":\"%s\",\"cns\":[[\"mac\", \"%s\"]],\"mf\":\"%s\",\"mdl\":\"%s\"} }", + config.mqtt_base_topic, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].location, + config.sensors[x].mac, + config.sensors[x].make, + config.sensors[x].model); + + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } + + // // create the MQTT topic from the base topic string and the MAC address of sensor + + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%sH/config", config.mqtt_base_topic, config.sensors[x].my_id); + + // publish the message and wait for success + pubmsg.payload = payload_buffer; + pubmsg.payloadlen = payload_length; + pubmsg.qos = QOS; + pubmsg.retained = 1; + deliveredtoken = 0; + MQTTClient_publishMessage(client, topic_buffer, &pubmsg, &token); + + // wait for messqge to be delivered to server + // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); + while (deliveredtoken != token) + ; + } + + // configure battery sensor + if (config.auto_conf_battery) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"~\":\"%s%s\",\"dev_cla\":\"battery\",\"name\":\"%s-B\",\"uniq_id\":\"%s-B\",\"stat_t\":\"~/state\",\"unit_of_meas\":\"%%\",\"val_tpl\":\"{{value_json.batterypct}}\",\"dev\":{\"name\":\"%s\",\"ids\":\"%s\",\"sa\":\"%s\",\"cns\":[[\"mac\", \"%s\"]],\"mf\":\"%s\",\"mdl\":\"%s\"} }", + config.mqtt_base_topic, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].location, + config.sensors[x].mac, + config.sensors[x].make, + config.sensors[x].model); + + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } + + // // create the MQTT topic from the base topic string and the MAC address of sensor + + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%sB/config", config.mqtt_base_topic, config.sensors[x].my_id); + + // publish the message and wait for success + pubmsg.payload = payload_buffer; + pubmsg.payloadlen = payload_length; + pubmsg.qos = QOS; + pubmsg.retained = 1; + deliveredtoken = 0; + MQTTClient_publishMessage(client, topic_buffer, &pubmsg, &token); + + // wait for messqge to be delivered to server + // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); + while (deliveredtoken != token) + ; + } + + // configure voltage sensor only an option for sensor type 1 + if (config.auto_conf_voltage && config.sensors[x].type == 1) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"~\":\"%s%s\",\"dev_cla\":\"voltage\",\"name\":\"%s-V\",\"uniq_id\":\"%s-V\",\"stat_t\":\"~/state\",\"unit_of_meas\":\"mV\",\"val_tpl\":\"{{value_json.batterymv}}\",\"dev\":{\"name\":\"%s\",\"ids\":\"%s\",\"sa\":\"%s\",\"cns\":[[\"mac\", \"%s\"]],\"mf\":\"%s\",\"mdl\":\"%s\"} }", + config.mqtt_base_topic, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].location, + config.sensors[x].mac, + config.sensors[x].make, + config.sensors[x].model); + + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } + + // // create the MQTT topic from the base topic string and the MAC address of sensor + // int topic_length; + // char topic_buffer[200]; + // int topic_buffer_size = 200; + + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%sV/config", config.mqtt_base_topic, config.sensors[x].my_id); + + // publish the message and wait for success + pubmsg.payload = payload_buffer; + pubmsg.payloadlen = payload_length; + pubmsg.qos = QOS; + pubmsg.retained = 1; + deliveredtoken = 0; + MQTTClient_publishMessage(client, topic_buffer, &pubmsg, &token); + + // wait for messqge to be delivered to server + // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); + while (deliveredtoken != token) + ; + } + + // configure signal sensor + if (config.auto_conf_signal) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"~\":\"%s%s\",\"dev_cla\":\"signal_strength\",\"name\":\"%s-S\",\"uniq_id\":\"%s-S\",\"stat_t\":\"~/state\",\"unit_of_meas\":\"dBm\",\"val_tpl\":\"{{value_json.rssi}}\",\"dev\":{\"name\":\"%s\",\"ids\":\"%s\",\"sa\":\"%s\",\"cns\":[[\"mac\", \"%s\"]],\"mf\":\"%s\",\"mdl\":\"%s\"} }", + config.mqtt_base_topic, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].name, + config.sensors[x].my_id, + config.sensors[x].location, + config.sensors[x].mac, + config.sensors[x].make, + config.sensors[x].model); + + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } + + // // create the MQTT topic from the base topic string and the MAC address of sensor + // int topic_length; + // char topic_buffer[200]; + // int topic_buffer_size = 200; + + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%sS/config", config.mqtt_base_topic, config.sensors[x].my_id); + + // publish the message and wait for success + pubmsg.payload = payload_buffer; + pubmsg.payloadlen = payload_length; + pubmsg.qos = QOS; + pubmsg.retained = 1; + deliveredtoken = 0; + MQTTClient_publishMessage(client, topic_buffer, &pubmsg, &token); + + // wait for messqge to be delivered to server + // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); + while (deliveredtoken != token) + ; + } + } + } + if (logging_level > LOG_INFO) + { + fprintf(stdout, " Auto configuration complete\n"); + } + fflush(stdout); + } + + // loop until SIGINT received + while (keep_running) + { // check if we have rolled over to a new hour, if so send report of advertising packets receive in last hour - time( &gmt_time_now ); - tnp = *gmtime( &gmt_time_now ); + time(&gmt_time_now); + tnp = *gmtime(&gmt_time_now); if (hour_current != tnp.tm_hour) { hour_current = tnp.tm_hour; // check if new day - if ( hour_current == 0 ) + if (hour_current == 0) { hour_last = 23; } @@ -759,14 +1092,13 @@ int main(int argc, char *argv[]) fprintf(stdout, "current hour (GMT) = %d\n", hour_current); fprintf(stdout, "last hour (GMT) = %d\n", hour_last); - time( &gmt_time_now ); - tnp = *gmtime( &gmt_time_now ); + time(&gmt_time_now); + tnp = *gmtime(&gmt_time_now); // create JSON string with timestamp, count and location for each known device payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, - "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",", - tnp.tm_year+1900, tnp.tm_mon+1, tnp.tm_mday, tnp.tm_hour, tnp.tm_min, tnp.tm_sec - ); + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",", + tnp.tm_year + 1900, tnp.tm_mon + 1, tnp.tm_mday, tnp.tm_hour, tnp.tm_min, tnp.tm_sec); int count_string_length; char count_string_buffer[MAXIMUM_JSON_MESSAGE] = ""; @@ -775,20 +1107,20 @@ int main(int argc, char *argv[]) // this builds a string contains the readings for each device concatenated together int total_advertising_packets = 0; int n; - for( n=0; n <= mac_total - 1; n++ ) + for (n = 0; n <= mac_total - 1; n++) { - count_string_length = snprintf(count_string_buffer, count_string_size, "\"%s\":{\"count\":%d, \"location\":\"%s\"},", device_units_mac[n], device_units_reading_per_hour[n], device_units_location[n]); + count_string_length = snprintf(count_string_buffer, count_string_size, "\"%s\":{\"count\":%d, \"location\":\"%s\"},", config.sensors[n].mac, config.sensors[n].readings_per_hour, config.sensors[n].location); strcat(payload_buffer, count_string_buffer); // if ( n < mac_total - 1 ) // strcat(payload_buffer, ","); - fprintf(stderr, "Location : %s packets received in last hour : %d %s\n", device_units_mac[n], device_units_reading_per_hour[n], device_units_location[n]); - total_advertising_packets = total_advertising_packets + device_units_reading_per_hour[n]; - device_units_reading_per_hour[n] = 0; + fprintf(stderr, "Location : %s packets received in last hour : %d %s\n", config.sensors[n].mac, config.sensors[n].readings_per_hour, config.sensors[n].location); + total_advertising_packets = total_advertising_packets + config.sensors[n].readings_per_hour; + config.sensors[n].readings_per_hour = 0; } // append the total of all advertising packets for all sensors of this type in last hour - count_string_length = snprintf(count_string_buffer, count_string_size, "\"total_adv_packets\":%d}", total_advertising_packets); + count_string_length = snprintf(count_string_buffer, count_string_size, "\"total_adv_packets\":%d}", total_advertising_packets); strcat(payload_buffer, count_string_buffer); // get length of MQTT payload after concatinating all the individual string together @@ -801,14 +1133,14 @@ int main(int argc, char *argv[]) fprintf(stderr, "MQTT payload too long: %d\n", payload_length); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d MQTT payload too long: %d\n", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, payload_length); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); exit(1); } // publish it to a statistics topic under the root topic topic_length = snprintf(topic_buffer, topic_buffer_size, - "%s%s", - topic_base, topic_statistics); + "%s%s", + config.mqtt_base_topic, topic_statistics); // publish the message and wait for success pubmsg.payload = payload_buffer; @@ -820,24 +1152,26 @@ int main(int argc, char *argv[]) // wait for messqge to be delivered to server // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); - while(deliveredtoken != token); - + while (deliveredtoken != token) + ; } // get the bluetooth packet bluetooth_adv_packet_length = read(bluetooth_device, ble_adv_buf, sizeof(ble_adv_buf)); // apparently there can be multiple advertisement packets with the packet received - if ( bluetooth_adv_packet_length >= HCI_EVENT_HDR_SIZE ) { - meta_event = (evt_le_meta_event*)(ble_adv_buf + HCI_EVENT_HDR_SIZE+1); - if ( meta_event->subevent == EVT_LE_ADVERTISING_REPORT ) { + if (bluetooth_adv_packet_length >= HCI_EVENT_HDR_SIZE) + { + meta_event = (evt_le_meta_event *)(ble_adv_buf + HCI_EVENT_HDR_SIZE + 1); + if (meta_event->subevent == EVT_LE_ADVERTISING_REPORT) + { uint8_t reports_count = meta_event->data[0]; - void * offset = meta_event->data + 1; - while ( reports_count-- ) + void *offset = meta_event->data + 1; + while (reports_count--) { // this is the advertising specific data within the packet adv_info = (le_advertising_info *)offset; - + // get the MAC address of the device that sent the advertising packet char addr[18]; ba2str(&(adv_info->bdaddr), addr); @@ -850,7 +1184,7 @@ int main(int argc, char *argv[]) for (i_match = 0; i_match < mac_total; ++i_match) { - if (strcmp(addr, device_units_mac[i_match]) == 0) + if (strcmp(addr, config.sensors[i_match].mac) == 0) { mac_match = 1; // keep a pointer to the mac address we matched @@ -858,8 +1192,6 @@ int main(int argc, char *argv[]) } } - int advertising_packet_type; // type of advertising packet - // found the mac address in our list we are interested in, so decipher it's data // if (1 == 1) if (mac_match == 1) @@ -868,19 +1200,18 @@ int main(int argc, char *argv[]) // different processing based on the device type, each type has different formats of advertising packets // 1 = Xiaomi LYWSD03MMC-ATC - if ( device_units_type[mac_index] == 1 ) + if (config.sensors[mac_index].type == 1) { int advertising_packet_type; // type of advertising packet - - // printf("=========\n"); + //printf("=========\n"); //get the time that we received the scan response packet - time( &rawtime ); - tm = *gmtime( &rawtime ); - time_packet_received = localtime ( &rawtime ); + time(&rawtime); + tm = *gmtime(&rawtime); + time_packet_received = localtime(&rawtime); // printf ( "Current local time and date: %s", asctime (timeinfo) ); - // printf("mac address = %s location = %s ", addr, device_units_location[mac_index]); + // printf("mac address = %s location = %s ", addr, config.sensors[mac_index].location); advertising_packet_type = (unsigned int)ble_adv_buf[5]; // printf("advertising_packet_type = %03d\n", advertising_packet_type); @@ -898,88 +1229,133 @@ int main(int argc, char *argv[]) // data is sent in the ScanRspData field of SCAN_RSP packets. // https://www.libelium.com/forum/libelium_files/bt4_core_spec_adv_data_reference.pdf - - if ( advertising_packet_type == 0) + if (advertising_packet_type == 0) { int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type + int ad_type; // attribute of ADV_IND packet type int8_t rssi_int = adv_info->data[adv_info->length]; if (logging_level == LOG_DEBUG) { fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); fprintf(stdout, "rssi = %03d\n", rssi_int); } // length of packet and subpacket - // printf("full packet length = %3d %02X\n", bluetooth_adv_packet_length, bluetooth_adv_packet_length); - // printf("sub packet length = %3d %02X\n", adv_info->length, adv_info->length); + //printf("full packet length = %3d %02X\n", bluetooth_adv_packet_length, bluetooth_adv_packet_length); + //printf("sub packet length = %3d %02X\n", adv_info->length, adv_info->length); ad_length = (unsigned int)adv_info->data[0]; - // printf("ADV_IND AD Data Length = %3d %02X\n", ad_length, ad_length); + //printf("ADV_IND AD Data Length = %3d %02X\n", ad_length, ad_length); ad_type = (unsigned int)adv_info->data[1]; - // printf("ADV_IND AD Type = %3d %02X\n", ad_type, ad_type); + //printf("ADV_IND AD Type = %3d %02X\n", ad_type, ad_type); + + int16_t temperature_int; + double temperature_celsius; + double temperature_fahrenheit; + int16_t humidity_int; + double humidity; + uint8_t battery_pct_int; + uint16_t battery_mv_int; + uint8_t frame_int; + + // check for pvvx firmware custom format + if (bluetooth_adv_packet_length == 34) + { + if (logging_level == LOG_DEBUG) + { + fprintf(stdout, "Parsing as PVVX Firmware\n"); + } + + temperature_int = (adv_info->data[10]) | (adv_info->data[11] << 8); + temperature_celsius = (double)temperature_int / 100.0; + + temperature_fahrenheit = temperature_celsius * 9.0 / 5.0 + 32.0; - int16_t temperature_int = (adv_info->data[10] << 8) | adv_info->data[11]; - double temperature_celsius = (double)temperature_int / 10.0; + humidity_int = (adv_info->data[12]) | (adv_info->data[13] << 8); + humidity = (double)humidity_int / 100.0; - double temperature_fahrenheit = temperature_celsius * 9.0/5.0 + 32.0; + battery_pct_int = adv_info->data[16]; - uint8_t humidity_int = adv_info->data[12]; + battery_mv_int = (adv_info->data[14]) | (adv_info->data[15] << 8); - uint8_t battery_pct_int = adv_info->data[13]; + frame_int = adv_info->data[17]; + } + else + { + if (logging_level == LOG_DEBUG) + { + fprintf(stdout, "Parsing as ATC Firmware\n"); + } + + temperature_int = (adv_info->data[10] << 8) | adv_info->data[11]; + temperature_celsius = (double)temperature_int / 10.0; - uint16_t battery_mv_int = (adv_info->data[14] << 8) | adv_info->data[15]; + temperature_fahrenheit = temperature_celsius * 9.0 / 5.0 + 32.0; - uint8_t frame_int = adv_info->data[16]; + humidity_int = adv_info->data[12]; + humidity = (double)humidity_int; + + battery_pct_int = adv_info->data[13]; + + battery_mv_int = (adv_info->data[14] << 8) | adv_info->data[15]; + + frame_int = adv_info->data[16]; + } if (logging_level == LOG_DEBUG) { fprintf(stdout, "temp c = %.1f\n", temperature_celsius); fprintf(stdout, "temp f = %.1f\n", temperature_fahrenheit); - fprintf(stdout, "humidity pct = %3d\n", humidity_int); + fprintf(stdout, "humidity pct = %.1f\n", humidity); fprintf(stdout, "battery pct = %3d\n", battery_pct_int); fprintf(stdout, "battery mv = %4d\n", battery_mv_int); fprintf(stdout, "frame = %3d\n", frame_int); } - // count the number of advertising packets we get from each unit - device_units_reading_per_hour[mac_index] = device_units_reading_per_hour[mac_index] + 1; + config.sensors[mac_index].readings_per_hour = config.sensors[mac_index].readings_per_hour + 1; - payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, - "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%i,\"battery-pct\":%i,\"battery-mv\":%i,\"frame\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, \ - addr, rssi_int, temperature_fahrenheit, \ - temperature_celsius, \ - humidity_int, battery_pct_int, battery_mv_int, frame_int, \ - device_units_nice_name[mac_index], \ - device_units_location[mac_index], - device_units_type[mac_index] - ); + if (config.publish_type == 1) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac\":\"%s\",\"rssi\":%d,\"tempf\":%#.1F,\"units\":\"F\",\"tempc\":%#.1F,\"humidity\":%#.1F,\"batterypct\":%i,\"batterymv\":%i,\"frame\":%i,\"name\":\"%s\",\"location\":\"%s\",\"type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_pct_int, battery_mv_int, frame_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s/state", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } + else + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%.0F,\"battery-pct\":%i,\"battery-mv\":%i,\"frame\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_pct_int, battery_mv_int, frame_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } - if (payload_length >= MAXIMUM_JSON_MESSAGE) + if (payload_length >= MAXIMUM_JSON_MESSAGE) // if (payload_length >= payload_buff_size) { - fprintf(stderr, "MQTT payload too long, %d\n", payload_length); - exit(-1); + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); } - // // create the MQTT topic from the base topic string and the MAC address of sensor - // int topic_length; - // char topic_buffer[200]; - // int topic_buffer_size = 200; - - topic_length = snprintf(topic_buffer, topic_buffer_size, - "%s%s", - topic_base, addr); - // publish the message and wait for success pubmsg.payload = payload_buffer; pubmsg.payloadlen = payload_length; @@ -990,59 +1366,53 @@ int main(int argc, char *argv[]) // wait for messqge to be delivered to server // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); - while(deliveredtoken != token); - + while (deliveredtoken != token) + ; } - if ( advertising_packet_type == 4) + if (advertising_packet_type == 4) { - } fflush(stdout); } // end device type 1 // 2 = Govee H5052 - if ( device_units_type[mac_index] == 2 ) + if (config.sensors[mac_index].type == 2) { //get the time that we received the scan response packet - time( &rawtime ); - tm = *gmtime( &rawtime ); - time_packet_received = localtime ( &rawtime ); + time(&rawtime); + tm = *gmtime(&rawtime); + time_packet_received = localtime(&rawtime); int advertising_packet_type; // type of advertising packet advertising_packet_type = (unsigned int)ble_adv_buf[5]; - if ( advertising_packet_type == 0) + if (advertising_packet_type == 0) { - } - + // sensor data is broadcast in type 4 advertising message by the H5052 - if ( advertising_packet_type == 4) + if (advertising_packet_type == 4) { - - int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type - // get rssi - int rssi_int = (signed char) (int8_t)adv_info->data[adv_info->length]; + int rssi_int = (signed char)(int8_t)adv_info->data[adv_info->length]; if (logging_level == LOG_DEBUG) { fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); fprintf(stdout, "rssi = %03d\n", rssi_int); } -// fprintf(stdout, "=========\n"); -// fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); -// fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); -// -// fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); + // fprintf(stdout, "=========\n"); + // fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); + // fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].location); + // + // fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); sensor_data_start = 5; @@ -1055,13 +1425,13 @@ int main(int argc, char *argv[]) double temperature_fahrenheit; double temperature_celsius; - temperature_fahrenheit = (temperature_int / 100.0) * 9.0/5.0 + 32.0; + temperature_fahrenheit = (temperature_int / 100.0) * 9.0 / 5.0 + 32.0; temperature_celsius = (temperature_int / 100.0); double humidity = humidity_int / 100.0; // get battery level percentage - int battery_precentage_int = (signed char) adv_info->data[sensor_data_start + 4]; + int battery_precentage_int = (signed char)adv_info->data[sensor_data_start + 4]; if (logging_level == LOG_DEBUG) { @@ -1073,40 +1443,41 @@ int main(int argc, char *argv[]) // count the number of advertising packets we get from each unit - device_units_reading_per_hour[mac_index] = device_units_reading_per_hour[mac_index] + 1; - - // create JSON formatted string for MQTT payload, now publishing both fahrenheit and celsius temperatures for homekit - // BE CAREFUL of playload length limits!! - // int payload_length; - // char payload_buffer[300]; - // int payload_buff_size = 300; - - payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, - "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, \ - addr, rssi_int, temperature_fahrenheit, \ - temperature_celsius, \ - humidity, battery_precentage_int, \ - device_units_nice_name[mac_index], \ - device_units_location[mac_index], - device_units_type[mac_index] - ); - - if (payload_length >= MAXIMUM_JSON_MESSAGE) - // if (payload_length >= payload_buff_size) + config.sensors[mac_index].readings_per_hour = config.sensors[mac_index].readings_per_hour + 1; + + if (config.publish_type == 1) { - fprintf(stderr, "MQTT payload too long, %d\n", payload_length); - exit(-1); + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac\":\"%s\",\"rssi\":%d,\"tempf\":%#.1F,\"units\":\"F\",\"tempc\":%#.1F,\"humidity\":%#.1F,\"batterypct\":%i,\"name\":\"%s\",\"location\":\"%s\",\"type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s/state", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } + else + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s", config.mqtt_base_topic, config.sensors[mac_index].my_id); } - // // create the MQTT topic from the base topic string and the MAC address of sensor - // int topic_length; - // char topic_buffer[200]; - // int topic_buffer_size = 200; - - topic_length = snprintf(topic_buffer, topic_buffer_size, - "%s%s", - topic_base, addr); + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } // publish the message and wait for success pubmsg.payload = payload_buffer; @@ -1118,8 +1489,8 @@ int main(int argc, char *argv[]) // wait for messqge to be delivered to server // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); - while(deliveredtoken != token); - + while (deliveredtoken != token) + ; } fflush(stdout); @@ -1127,41 +1498,37 @@ int main(int argc, char *argv[]) //end device type 2 // device type 3 = Govee H5072 - if ( device_units_type[mac_index] == 3 ) + if (config.sensors[mac_index].type == 3) { //get the time that we received the scan response packet - time( &rawtime ); - tm = *gmtime( &rawtime ); - time_packet_received = localtime ( &rawtime ); + time(&rawtime); + tm = *gmtime(&rawtime); + time_packet_received = localtime(&rawtime); int advertising_packet_type; // type of advertising packet advertising_packet_type = (unsigned int)ble_adv_buf[5]; // sensor data is broadcast in type 0 advertising message by the H5072 - if ( advertising_packet_type == 0) + if (advertising_packet_type == 0) { - - int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type - // get rssi - int rssi_int = (signed char) (int8_t)adv_info->data[adv_info->length]; + int rssi_int = (signed char)(int8_t)adv_info->data[adv_info->length]; if (logging_level == LOG_DEBUG) { fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); fprintf(stdout, "rssi = %03d\n", rssi_int); } -// fprintf(stdout, "=========\n"); -// fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); -// fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); -// -// fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); + // fprintf(stdout, "=========\n"); + // fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); + // fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); + // + // fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); sensor_data_start = 26; @@ -1170,14 +1537,14 @@ int main(int argc, char *argv[]) if ((adv_info->data[sensor_data_start + 0] & (1 << 7)) != 0) { - below_32 = 1; - msb = adv_info->data[sensor_data_start + 0]; - msb &= ~(1UL << 7); + below_32 = 1; + msb = adv_info->data[sensor_data_start + 0]; + msb &= ~(1UL << 7); } else { - below_32 = 0; - msb = adv_info->data[sensor_data_start + 0]; + below_32 = 0; + msb = adv_info->data[sensor_data_start + 0]; } unsigned int sensor_data = adv_info->data[sensor_data_start + 2] | adv_info->data[sensor_data_start + 1] << 8 | msb << 16; @@ -1189,70 +1556,71 @@ int main(int argc, char *argv[]) // Humidity seems accurate thru range however if (below_32 == 1) { - temperature_int = temperature_int * -1; + temperature_int = temperature_int * -1; } unsigned int humidity_int = (sensor_data % 1000) / 10; int32_t answer; - answer = (((int32_t)((int8_t)adv_info->data[sensor_data_start + 0]))<<16) + (((int32_t)adv_info->data[sensor_data_start + 1])<< 8) + adv_info->data[sensor_data_start + 2]; - + answer = (((int32_t)((int8_t)adv_info->data[sensor_data_start + 0])) << 16) + (((int32_t)adv_info->data[sensor_data_start + 1]) << 8) + adv_info->data[sensor_data_start + 2]; + // convert the integer * 100 value for temperature and humidity to degrees fahrenheit and celsius (for homekit) and humidity percentage double temperature_fahrenheit; double temperature_celsius; - temperature_fahrenheit = temperature_int * 9.0/5.0 + 32.0; + temperature_fahrenheit = temperature_int * 9.0 / 5.0 + 32.0; temperature_celsius = temperature_int; double humidity = humidity_int; // get battery level percentage - int battery_precentage_int = (signed char) adv_info->data[sensor_data_start + 3]; + int battery_precentage_int = (signed char)adv_info->data[sensor_data_start + 3]; if (logging_level == LOG_DEBUG) { fprintf(stdout, "temp c = %.1f\n", temperature_celsius); fprintf(stdout, "temp f = %.1f\n", temperature_fahrenheit); fprintf(stdout, "humidity pct = %.1f\n", humidity); - fprintf(stdout, "battery pct = %3d\n", battery_precentage_int); + fprintf(stdout, "battery pct = %3d\n", battery_precentage_int); } // count the number of advertising packets we get from each unit - device_units_reading_per_hour[mac_index] = device_units_reading_per_hour[mac_index] + 1; - - // create JSON formatted string for MQTT payload, now publishing both fahrenheit and celsius temperatures for homekit - // BE CAREFUL of playload length limits!! - // int payload_length; - // char payload_buffer[300]; - // int payload_buff_size = 300; - - payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, - "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, \ - addr, rssi_int, temperature_fahrenheit, \ - temperature_celsius, \ - humidity, battery_precentage_int, \ - device_units_nice_name[mac_index], \ - device_units_location[mac_index], - device_units_type[mac_index] - ); - - if (payload_length >= MAXIMUM_JSON_MESSAGE) - // if (payload_length >= payload_buff_size) + config.sensors[mac_index].readings_per_hour = config.sensors[mac_index].readings_per_hour + 1; + + if (config.publish_type == 1) { - fprintf(stderr, "MQTT payload too long, %d\n", payload_length); - exit(-1); + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac\":\"%s\",\"rssi\":%d,\"tempf\":%#.1F,\"units\":\"F\",\"tempc\":%#.1F,\"humidity\":%#.1F,\"batterypct\":%i,\"name\":\"%s\",\"location\":\"%s\",\"type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s/state", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } + else + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s", config.mqtt_base_topic, config.sensors[mac_index].my_id); } - // // create the MQTT topic from the base topic string and the MAC address of sensor - // int topic_length; - // char topic_buffer[200]; - // int topic_buffer_size = 200; - - topic_length = snprintf(topic_buffer, topic_buffer_size, - "%s%s", - topic_base, addr); + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } // publish the message and wait for success pubmsg.payload = payload_buffer; @@ -1264,11 +1632,11 @@ int main(int argc, char *argv[]) // wait for messqge to be delivered to server // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); - while(deliveredtoken != token); - + while (deliveredtoken != token) + ; } - if ( advertising_packet_type == 4) + if (advertising_packet_type == 4) { } fflush(stdout); @@ -1276,41 +1644,37 @@ int main(int argc, char *argv[]) // end device type 3 // device type 4 = Govee H5102 - if ( device_units_type[mac_index] == 4 ) + if (config.sensors[mac_index].type == 4) { //get the time that we received the scan response packet - time( &rawtime ); - tm = *gmtime( &rawtime ); - time_packet_received = localtime ( &rawtime ); + time(&rawtime); + tm = *gmtime(&rawtime); + time_packet_received = localtime(&rawtime); int advertising_packet_type; // type of advertising packet advertising_packet_type = (unsigned int)ble_adv_buf[5]; // sensor data is broadcast in type 0 advertising message by the H5072 - if ( advertising_packet_type == 0) + if (advertising_packet_type == 0) { - - int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type - // get rssi - int rssi_int = (signed char) (int8_t)adv_info->data[adv_info->length]; + int rssi_int = (signed char)(int8_t)adv_info->data[adv_info->length]; if (logging_level == LOG_DEBUG) { fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); fprintf(stdout, "rssi = %03d\n", rssi_int); } -// fprintf(stdout, "=========\n"); -// fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); -// fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); -// -// fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); + // fprintf(stdout, "=========\n"); + // fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); + // fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); + // + // fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); sensor_data_start = 27; @@ -1319,14 +1683,14 @@ int main(int argc, char *argv[]) if ((adv_info->data[sensor_data_start + 0] & (1 << 7)) != 0) { - below_32 = 1; - msb = adv_info->data[sensor_data_start + 0]; - msb &= ~(1UL << 7); + below_32 = 1; + msb = adv_info->data[sensor_data_start + 0]; + msb &= ~(1UL << 7); } else { - below_32 = 0; - msb = adv_info->data[sensor_data_start + 0]; + below_32 = 0; + msb = adv_info->data[sensor_data_start + 0]; } unsigned int sensor_data = adv_info->data[sensor_data_start + 2] | adv_info->data[sensor_data_start + 1] << 8 | msb << 16; @@ -1338,24 +1702,24 @@ int main(int argc, char *argv[]) // Humidity seems accurate thru range however if (below_32 == 1) { - temperature_int = temperature_int * -1; + temperature_int = temperature_int * -1; } unsigned int humidity_int = (sensor_data % 1000) / 10; int32_t answer; - answer = (((int32_t)((int8_t)adv_info->data[sensor_data_start + 0]))<<16) + (((int32_t)adv_info->data[sensor_data_start + 1])<< 8) + adv_info->data[sensor_data_start + 2]; - + answer = (((int32_t)((int8_t)adv_info->data[sensor_data_start + 0])) << 16) + (((int32_t)adv_info->data[sensor_data_start + 1]) << 8) + adv_info->data[sensor_data_start + 2]; + // convert the integer * 100 value for temperature and humidity to degrees fahrenheit and celsius (for homekit) and humidity percentage double temperature_fahrenheit; double temperature_celsius; - temperature_fahrenheit = temperature_int * 9.0/5.0 + 32.0; + temperature_fahrenheit = temperature_int * 9.0 / 5.0 + 32.0; temperature_celsius = temperature_int; double humidity = humidity_int; // get battery level percentage - int battery_precentage_int = (signed char) adv_info->data[sensor_data_start + 3]; + int battery_precentage_int = (signed char)adv_info->data[sensor_data_start + 3]; if (logging_level == LOG_DEBUG) { @@ -1367,40 +1731,41 @@ int main(int argc, char *argv[]) // count the number of advertising packets we get from each unit - device_units_reading_per_hour[mac_index] = device_units_reading_per_hour[mac_index] + 1; - - // create JSON formatted string for MQTT payload, now publishing both fahrenheit and celsius temperatures for homekit - // BE CAREFUL of playload length limits!! - // int payload_length; - // char payload_buffer[300]; - // int payload_buff_size = 300; - - payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, - "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, \ - addr, rssi_int, temperature_fahrenheit, \ - temperature_celsius, \ - humidity, battery_precentage_int, \ - device_units_nice_name[mac_index], \ - device_units_location[mac_index], - device_units_type[mac_index] - ); - - if (payload_length >= MAXIMUM_JSON_MESSAGE) - // if (payload_length >= payload_buff_size) + config.sensors[mac_index].readings_per_hour = config.sensors[mac_index].readings_per_hour + 1; + + if (config.publish_type == 1) { - fprintf(stderr, "MQTT payload too long, %d\n", payload_length); - exit(-1); + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac\":\"%s\",\"rssi\":%d,\"tempf\":%#.1F,\"units\":\"F\",\"tempc\":%#.1F,\"humidity\":%#.1F,\"batterypct\":%i,\"name\":\"%s\",\"location\":\"%s\",\"type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s/state", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } + else + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s", config.mqtt_base_topic, config.sensors[mac_index].my_id); } - // // create the MQTT topic from the base topic string and the MAC address of sensor - // int topic_length; - // char topic_buffer[200]; - // int topic_buffer_size = 200; - - topic_length = snprintf(topic_buffer, topic_buffer_size, - "%s%s", - topic_base, addr); + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } // publish the message and wait for success pubmsg.payload = payload_buffer; @@ -1412,54 +1777,49 @@ int main(int argc, char *argv[]) // wait for messqge to be delivered to server // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); - while(deliveredtoken != token); - + while (deliveredtoken != token) + ; } - if ( advertising_packet_type == 4) + if (advertising_packet_type == 4) { - } fflush(stdout); } // end device type 4 // device type 5 = Govee H5075 - if ( device_units_type[mac_index] == 5 ) + if (config.sensors[mac_index].type == 5) { //get the time that we received the scan response packet - time( &rawtime ); - tm = *gmtime( &rawtime ); - time_packet_received = localtime ( &rawtime ); + time(&rawtime); + tm = *gmtime(&rawtime); + time_packet_received = localtime(&rawtime); int advertising_packet_type; // type of advertising packet advertising_packet_type = (unsigned int)ble_adv_buf[5]; // sensor data is broadcast in type 0 advertising message by the H5072 - if ( advertising_packet_type == 0) + if (advertising_packet_type == 0) { - - int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type - // get rssi - int rssi_int = (signed char) (int8_t)adv_info->data[adv_info->length]; + int rssi_int = (signed char)(int8_t)adv_info->data[adv_info->length]; if (logging_level == LOG_DEBUG) { fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); fprintf(stdout, "rssi = %03d\n", rssi_int); } -// fprintf(stdout, "=========\n"); -// fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); -// fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); -// -// fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); + // fprintf(stdout, "=========\n"); + // fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); + // fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); + // + // fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); sensor_data_start = 26; @@ -1468,14 +1828,14 @@ int main(int argc, char *argv[]) if ((adv_info->data[sensor_data_start + 0] & (1 << 7)) != 0) { - below_32 = 1; - msb = adv_info->data[sensor_data_start + 0]; - msb &= ~(1UL << 7); + below_32 = 1; + msb = adv_info->data[sensor_data_start + 0]; + msb &= ~(1UL << 7); } else { - below_32 = 0; - msb = adv_info->data[sensor_data_start + 0]; + below_32 = 0; + msb = adv_info->data[sensor_data_start + 0]; } unsigned int sensor_data = adv_info->data[sensor_data_start + 2] | adv_info->data[sensor_data_start + 1] << 8 | msb << 16; @@ -1487,24 +1847,24 @@ int main(int argc, char *argv[]) // Humidity seems accurate thru range however if (below_32 == 1) { - temperature_int = temperature_int * -1; + temperature_int = temperature_int * -1; } unsigned int humidity_int = (sensor_data % 1000) / 10; int32_t answer; - answer = (((int32_t)((int8_t)adv_info->data[sensor_data_start + 0]))<<16) + (((int32_t)adv_info->data[sensor_data_start + 1])<< 8) + adv_info->data[sensor_data_start + 2]; - + answer = (((int32_t)((int8_t)adv_info->data[sensor_data_start + 0])) << 16) + (((int32_t)adv_info->data[sensor_data_start + 1]) << 8) + adv_info->data[sensor_data_start + 2]; + // convert the integer * 100 value for temperature and humidity to degrees fahrenheit and celsius (for homekit) and humidity percentage double temperature_fahrenheit; double temperature_celsius; - temperature_fahrenheit = temperature_int * 9.0/5.0 + 32.0; + temperature_fahrenheit = temperature_int * 9.0 / 5.0 + 32.0; temperature_celsius = temperature_int; double humidity = humidity_int; // get battery level percentage - int battery_precentage_int = (signed char) adv_info->data[sensor_data_start + 3]; + int battery_precentage_int = (signed char)adv_info->data[sensor_data_start + 3]; if (logging_level == LOG_DEBUG) { @@ -1516,40 +1876,41 @@ int main(int argc, char *argv[]) // count the number of advertising packets we get from each unit - device_units_reading_per_hour[mac_index] = device_units_reading_per_hour[mac_index] + 1; - - // create JSON formatted string for MQTT payload, now publishing both fahrenheit and celsius temperatures for homekit - // BE CAREFUL of playload length limits!! - // int payload_length; - // char payload_buffer[300]; - // int payload_buff_size = 300; - - payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, - "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, \ - addr, rssi_int, temperature_fahrenheit, \ - temperature_celsius, \ - humidity, battery_precentage_int, \ - device_units_nice_name[mac_index], \ - device_units_location[mac_index], - device_units_type[mac_index] - ); - - if (payload_length >= MAXIMUM_JSON_MESSAGE) - // if (payload_length >= payload_buff_size) + config.sensors[mac_index].readings_per_hour = config.sensors[mac_index].readings_per_hour + 1; + + if (config.publish_type == 1) { - fprintf(stderr, "MQTT payload too long, %d\n", payload_length); - exit(-1); + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac\":\"%s\",\"rssi\":%d,\"tempf\":%#.1F,\"units\":\"F\",\"tempc\":%#.1F,\"humidity\":%#.1F,\"batterypct\":%i,\"name\":\"%s\",\"location\":\"%s\",\"type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s/state", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } + else + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s", config.mqtt_base_topic, config.sensors[mac_index].my_id); } - // // create the MQTT topic from the base topic string and the MAC address of sensor - // int topic_length; - // char topic_buffer[200]; - // int topic_buffer_size = 200; - - topic_length = snprintf(topic_buffer, topic_buffer_size, - "%s%s", - topic_base, addr); + if (payload_length >= MAXIMUM_JSON_MESSAGE) + // if (payload_length >= payload_buff_size) + { + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); + } // publish the message and wait for success pubmsg.payload = payload_buffer; @@ -1561,67 +1922,61 @@ int main(int argc, char *argv[]) // wait for messqge to be delivered to server // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); - while(deliveredtoken != token); - + while (deliveredtoken != token) + ; } - if ( advertising_packet_type == 4) + if (advertising_packet_type == 4) { - } fflush(stdout); } // end device type 5 // device type 6 = Govee H5074 - if ( device_units_type[mac_index] == 6 ) + if (config.sensors[mac_index].type == 6) { //get the time that we received the scan response packet - time( &rawtime ); - tm = *gmtime( &rawtime ); - time_packet_received = localtime ( &rawtime ); + time(&rawtime); + tm = *gmtime(&rawtime); + time_packet_received = localtime(&rawtime); int advertising_packet_type; // type of advertising packet advertising_packet_type = (unsigned int)ble_adv_buf[5]; // sensor data is broadcast in type 0 advertising message by the H5072 - if ( advertising_packet_type == 0) + if (advertising_packet_type == 0) { - } if (advertising_packet_type == 4) { - - int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type - // get rssi - int rssi_int = (signed char) (int8_t)adv_info->data[adv_info->length]; + int rssi_int = (signed char)(int8_t)adv_info->data[adv_info->length]; // this device sends sensor data only on this type of scan response advertising packet if ((int8_t)adv_info->data[0] == 0x0a) { - if (logging_level == LOG_DEBUG) - { - fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); - fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); - fprintf(stdout, "rssi = %03d\n", rssi_int); - } + if (logging_level == LOG_DEBUG) + { + fprintf(stdout, "=========\n"); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); + fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); + fprintf(stdout, "rssi = %03d\n", rssi_int); + } -// fprintf(stdout, "=========\n"); -// fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); -// fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); -// -// fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); + // fprintf(stdout, "=========\n"); + // fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); + // fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); + // + // fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); sensor_data_start = 5; - + // get the lsb msb byte pairs for temperature and humidity and convert them to an integer value (in 100's) // temperature is a signed 16 bit integer to allow for temperatures below and above 0 degrees celsius signed short int temperature_int = adv_info->data[sensor_data_start + 0] | adv_info->data[sensor_data_start + 1] << 8; @@ -1631,13 +1986,13 @@ int main(int argc, char *argv[]) double temperature_fahrenheit; double temperature_celsius; - temperature_fahrenheit = (temperature_int / 100.0) * 9.0/5.0 + 32.0; + temperature_fahrenheit = (temperature_int / 100.0) * 9.0 / 5.0 + 32.0; temperature_celsius = (temperature_int / 100.0); double humidity = humidity_int / 100.0; // get battery level percentage - int battery_precentage_int = (signed char) adv_info->data[sensor_data_start + 4]; + int battery_precentage_int = (signed char)adv_info->data[sensor_data_start + 4]; if (logging_level == LOG_DEBUG) { @@ -1649,41 +2004,42 @@ int main(int argc, char *argv[]) // count the number of advertising packets we get from each unit - device_units_reading_per_hour[mac_index] = device_units_reading_per_hour[mac_index] + 1; + config.sensors[mac_index].readings_per_hour = config.sensors[mac_index].readings_per_hour + 1; - // create JSON formatted string for MQTT payload, now publishing both fahrenheit and celsius temperatures for homekit - // BE CAREFUL of playload length limits!! - // int payload_length; - // char payload_buffer[300]; - // int payload_buff_size = 300; + if (config.publish_type == 1) + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac\":\"%s\",\"rssi\":%d,\"tempf\":%#.1F,\"units\":\"F\",\"tempc\":%#.1F,\"humidity\":%#.1F,\"batterypct\":%i,\"name\":\"%s\",\"location\":\"%s\",\"type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s/state", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } + else + { + payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, + "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + addr, rssi_int, temperature_fahrenheit, + temperature_celsius, + humidity, battery_precentage_int, + config.sensors[mac_index].name, + config.sensors[mac_index].location, + config.sensors[mac_index].type); + topic_length = snprintf(topic_buffer, topic_buffer_size, "%s%s", config.mqtt_base_topic, config.sensors[mac_index].my_id); + } - payload_length = snprintf(payload_buffer, MAXIMUM_JSON_MESSAGE, - "{\"timestamp\":\"%04d%02d%02d%02d%02d%02d\",\"mac-address\":\"%s\",\"rssi\":%d,\"temperature\":%#.1F,\"units\":\"F\",\"temperature-celsius\":%#.1F,\"humidity\":%#.1F,\"battery-pct\":%i,\"sensor-name\":\"%s\",\"location\":\"%s\",\"sensor-type\":\"%d\"}", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, \ - addr, rssi_int, temperature_fahrenheit, \ - temperature_celsius, \ - humidity, battery_precentage_int, \ - device_units_nice_name[mac_index], \ - device_units_location[mac_index], - device_units_type[mac_index] - ); - - if (payload_length >= MAXIMUM_JSON_MESSAGE) + if (payload_length >= MAXIMUM_JSON_MESSAGE) // if (payload_length >= payload_buff_size) { - fprintf(stderr, "MQTT payload too long, %d\n", payload_length); - exit(-1); + fprintf(stderr, "MQTT payload too long, %d\n", payload_length); + exit(-1); } - // // create the MQTT topic from the base topic string and the MAC address of sensor - // int topic_length; - // char topic_buffer[200]; - // int topic_buffer_size = 200; - - topic_length = snprintf(topic_buffer, topic_buffer_size, - "%s%s", - topic_base, addr); - // publish the message and wait for success pubmsg.payload = payload_buffer; pubmsg.payloadlen = payload_length; @@ -1694,8 +2050,8 @@ int main(int argc, char *argv[]) // wait for messqge to be delivered to server // printf("Waiting for publication of %s\n" "on topic %s for client with ClientID: %s\n", payload_buffer, topic_buffer, z_client_id_mqtt); - while(deliveredtoken != token); - + while (deliveredtoken != token) + ; } } fflush(stdout); @@ -1703,12 +2059,12 @@ int main(int argc, char *argv[]) // end device type 6 // device type 99 = decoding - if ( device_units_type[mac_index] == 99 ) + if (config.sensors[mac_index].type == 99) { //get the time that we received the scan response packet - time( &rawtime ); - tm = *gmtime( &rawtime ); - time_packet_received = localtime ( &rawtime ); + time(&rawtime); + tm = *gmtime(&rawtime); + time_packet_received = localtime(&rawtime); int advertising_packet_type; // type of advertising packet @@ -1716,21 +2072,17 @@ int main(int argc, char *argv[]) if (advertising_packet_type == 0) { - - int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type - // counter for printing int n; // get rssi - int rssi_int = (signed char) (int8_t)adv_info->data[adv_info->length]; + int rssi_int = (signed char)(int8_t)adv_info->data[adv_info->length]; if (logging_level == LOG_DEBUG) { fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); // print whole packet printf("==>0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 \n"); @@ -1738,35 +2090,31 @@ int main(int argc, char *argv[]) printf("==> 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2\n"); printf("==> 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \n"); printf("==>"); - for(n=0; n < bluetooth_adv_packet_length; n++) - printf("%02X",(unsigned char)ble_adv_buf[n]); + for (n = 0; n < bluetooth_adv_packet_length; n++) + printf("%02X", (unsigned char)ble_adv_buf[n]); printf("\n"); printf("==>__________ad________________________mmmmmmmmmmmmtttthhbbzbzbccrr\n"); fprintf(stdout, "rssi = %03d\n", rssi_int); } -// fprintf(stdout, "=========\n"); -// fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); -// fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); -// -// fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); + // fprintf(stdout, "=========\n"); + // fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); + // fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); + // + // fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); } if (advertising_packet_type == 4) { - - int ad_length; // length of ADV_IND packet type - int ad_type; // attribute of ADV_IND packet type // counter for printing int n; // get rssi - int rssi_int = (signed char) (int8_t)adv_info->data[adv_info->length]; - + int rssi_int = (signed char)(int8_t)adv_info->data[adv_info->length]; fprintf(stdout, "=========\n"); - fprintf(stdout, "Current local time and date: %s", asctime (time_packet_received) ); - fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, device_units_location[mac_index], device_units_type[mac_index]); + fprintf(stdout, "Current local time and date: %s", asctime(time_packet_received)); + fprintf(stdout, "mac address = %s location = %s device type = %d ", addr, config.sensors[mac_index].location, config.sensors[mac_index].type); fprintf(stdout, "advertising_packet_type = %03d\n", advertising_packet_type); // print whole packet printf("==>0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 \n"); @@ -1774,8 +2122,8 @@ int main(int argc, char *argv[]) printf("==> 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2\n"); printf("==> 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \n"); printf("==>"); - for(n=0; n < bluetooth_adv_packet_length; n++) - printf("%02X",(unsigned char)ble_adv_buf[n]); + for (n = 0; n < bluetooth_adv_packet_length; n++) + printf("%02X", (unsigned char)ble_adv_buf[n]); printf("\n"); printf("==>__________ad________________________mmmmmmmmmmmmtttthhbbzbzbccrr\n"); fprintf(stdout, "rssi = %03d\n", rssi_int); @@ -1786,8 +2134,8 @@ int main(int argc, char *argv[]) } // end of Matched MAC address - // if there are multiple advertising packets loop thru them - offset = adv_info->data + adv_info->length + 2; + // if there are multiple advertising packets loop thru them + offset = adv_info->data + adv_info->length + 2; } } } @@ -1797,20 +2145,20 @@ int main(int argc, char *argv[]) fprintf(stdout, "\n-c signal received, exiting.\n"); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d -c signal received, exiting.", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_INFO, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_INFO, "%s",log_message); - + syslog(LOG_INFO, "%s", log_message); // Disable scanning. memset(&scan_cp, 0, sizeof(scan_cp)); - scan_cp.enable = 0x00; // Disable flag. + scan_cp.enable = 0x00; // Disable flag. struct hci_request disable_adv_rq = ble_hci_request(OCF_LE_SET_SCAN_ENABLE, LE_SET_SCAN_ENABLE_CP_SIZE, &status, &scan_cp); ret = hci_send_req(bluetooth_device, &disable_adv_rq, 1000); - if ( ret < 0 ) { + if (ret < 0) + { hci_close_dev(bluetooth_device); snprintf(log_message, LOGMESSAGESIZE, "%s v: %d.%d Failed to disable scan", PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR); send_remote_syslog_message(LOG_ERR, RSYSLOG_ADDRESS, PROGRAM_NAME, log_message); - syslog (LOG_ERR, "%s",log_message); + syslog(LOG_ERR, "%s", log_message); fprintf(stdout, "Failed to disable scan, return code %d\n", ret); exit(1); } @@ -1824,3 +2172,369 @@ int main(int argc, char *argv[]) exit(0); } +unsigned int +parser(config_t *config, char **argv) +{ + /* Open file & declare libyaml types */ + FILE *fp = fopen(argv[1], "r"); + + if (fp == NULL) + { + fprintf(stdout, "ERROR: Failed to open config file: %s\n", argv[1]); + exit(EXIT_FAILURE); + } + + yaml_parser_t parser; + yaml_event_t event; + + bool seq_status = 0; /* IN or OUT of sequence index, init to OUT */ + unsigned int map_seq = 0; /* Index of mapping inside sequence */ + + init_prs(fp, &parser); /* Initiliaze parser & open file */ + + do + { + parse_next(&parser, &event); /* Parse new event */ + + /* Decide what to do with each event */ + event_switch(&seq_status, &map_seq, config, &parser, &event, fp); + + if (event.type != YAML_STREAM_END_EVENT) + { + yaml_event_delete(&event); + } + + if (map_seq > MAX_SENSORS) + { + break; + } + + } while (event.type != YAML_STREAM_END_EVENT); + + clean_prs(fp, &parser, &event); /* clean parser & close file */ + + return map_seq; +} + +void event_switch(bool *seq_status, unsigned int *map_seq, config_t *config, + yaml_parser_t *parser, yaml_event_t *event, FILE *fp) +{ + switch (event->type) + { + case YAML_STREAM_START_EVENT: + break; + case YAML_STREAM_END_EVENT: + break; + case YAML_DOCUMENT_START_EVENT: + break; + case YAML_DOCUMENT_END_EVENT: + break; + case YAML_SEQUENCE_START_EVENT: + (*seq_status) = true; + break; + case YAML_SEQUENCE_END_EVENT: + (*seq_status) = false; + break; + case YAML_MAPPING_START_EVENT: + if (*seq_status == 1) + { + (*map_seq)++; + } + break; + case YAML_MAPPING_END_EVENT: + break; + case YAML_ALIAS_EVENT: + printf(" ERROR: Got alias (anchor %s)\n", + event->data.alias.anchor); + exit(EXIT_FAILURE); + break; + case YAML_SCALAR_EVENT: + to_data(seq_status, map_seq, config, parser, event, fp); + break; + case YAML_NO_EVENT: + puts(" ERROR: No event!"); + exit(EXIT_FAILURE); + break; + } +} + +void to_data(bool *seq_status, unsigned int *map_seq, config_t *config, + yaml_parser_t *parser, yaml_event_t *event, FILE *fp) +{ + char *buf = (char *)event->data.scalar.value; + + /* Dictionary */ + char *mqtt_server_url = "mqtt_server_url"; + char *mqtt_base_topic = "mqtt_base_topic"; + char *mqtt_username = "mqtt_username"; + char *mqtt_password = "mqtt_password"; + char *bluetooth_adapter = "bluetooth_adapter"; + char *scan_type = "scan_type"; + char *scan_window = "scan_window"; + char *scan_interval = "scan_interval"; + char *publish_type = "publish_type"; + char *auto_configure = "auto_configure"; + char *auto_conf_stats = "auto_conf_stats"; + char *auto_conf_tempf = "auto_conf_tempf"; + char *auto_conf_tempc = "auto_conf_tempc"; + char *auto_conf_hum = "auto_conf_hum"; + char *auto_conf_battery = "auto_conf_battery"; + char *auto_conf_voltage = "auto_conf_voltage"; + char *auto_conf_signal = "auto_conf_signal"; + char *syslog_address = "syslog_address"; + char *logging_level = "logging_level"; + char *sensors = "sensors"; + + if (!strcmp(buf, mqtt_server_url)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->mqtt_server_url, (char *)event->data.scalar.value); + } + else if (!strcmp(buf, mqtt_base_topic)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->mqtt_base_topic, (char *)event->data.scalar.value); + } + else if (!strcmp(buf, mqtt_username)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->mqtt_username, (char *)event->data.scalar.value); + } + else if (!strcmp(buf, mqtt_password)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->mqtt_password, (char *)event->data.scalar.value); + } + else if (!strcmp(buf, bluetooth_adapter)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->bluetooth_adapter = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, scan_type)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->scan_type = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, scan_window)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->scan_window = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, scan_interval)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->scan_interval = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, publish_type)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->publish_type = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_configure)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_configure = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_conf_stats)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_conf_stats = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_conf_tempf)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_conf_tempf = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_conf_tempc)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_conf_tempc = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_conf_hum)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_conf_hum = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_conf_battery)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_conf_battery = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_conf_voltage)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_conf_voltage = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, auto_conf_signal)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->auto_conf_signal = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, syslog_address)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->syslog_address, (char *)event->data.scalar.value); + } + else if (!strcmp(buf, logging_level)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->logging_level = strtol((char *)event->data.scalar.value, NULL, 10); + } + else if ((*seq_status) == true) + { + /* Data from sequence of sensors */ + to_data_from_map(buf, map_seq, config, parser, event, fp); + } + else if (!strcmp(buf, sensors)) + { + /* Do nothing, "sensors" is just the label of mapping's sequence */ + } + else + { + printf("\n -ERROR: Unknow variable in config file: %s\n", buf); + clean_prs(fp, parser, event); + exit(EXIT_FAILURE); + } +} + +void to_data_from_map(char *buf, unsigned int *map_seq, config_t *config, + yaml_parser_t *parser, yaml_event_t *event, FILE *fp) +{ + /* Dictionary */ + char *name = "name"; + char *type = "type"; + char *mac = "mac"; + char *location = "location"; + char *unique = "unique"; + + if (!strcmp(buf, name)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->sensors[(*map_seq) - 1].name, + (char *)event->data.scalar.value); + } + else if (!strcmp(buf, type)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->sensors[(*map_seq) - 1].type = + strtol((char *)event->data.scalar.value, NULL, 10); + } + else if (!strcmp(buf, mac)) + { + yaml_event_delete(event); + parse_next(parser, event); + config->sensors[(*map_seq) - 1].readings_per_hour = 0; + strcpy(config->sensors[(*map_seq) - 1].mac, + (char *)event->data.scalar.value); + } + else if (!strcmp(buf, location)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->sensors[(*map_seq) - 1].location, + (char *)event->data.scalar.value); + } + else if (!strcmp(buf, unique)) + { + yaml_event_delete(event); + parse_next(parser, event); + strcpy(config->sensors[(*map_seq) - 1].unique, + (char *)event->data.scalar.value); + } + else + { + printf("\n -ERROR: Unknow variable in config file: %s\n", buf); + clean_prs(fp, parser, event); + exit(EXIT_FAILURE); + } +} + +void parse_next(yaml_parser_t *parser, yaml_event_t *event) +{ + /* Parse next scalar. if wrong exit with error */ + if (!yaml_parser_parse(parser, event)) + { + printf("Parser error %d\n", parser->error); + exit(EXIT_FAILURE); + } +} + +void init_prs(FILE *fp, yaml_parser_t *parser) +{ + /* Parser initilization */ + if (!yaml_parser_initialize(parser)) + { + fputs("Failed to initialize parser!\n", stderr); + } + + if (fp == NULL) + { + fputs("Failed to open file!\n", stderr); + } + + yaml_parser_set_input_file(parser, fp); +} + +void clean_prs(FILE *fp, yaml_parser_t *parser, yaml_event_t *event) +{ + yaml_event_delete(event); /* Delete event */ + yaml_parser_delete(parser); /* Delete parser */ + fclose(fp); /* Close file */ +} + +void print_data(unsigned int sensor_count, config_t *config) +{ + puts("\n --- data structure after parsing ---"); + printf(" mqtt_server_url = %s\n", config->mqtt_server_url); + printf(" mqtt_base_topic = %s\n", config->mqtt_base_topic); + printf(" mqtt_username = %s\n", config->mqtt_username); + printf(" mqtt_password = %s\n", config->mqtt_password); + printf(" bluetooth_adapter = %i\n", config->bluetooth_adapter); + printf(" scan_type = %i\n", config->scan_type); + printf(" scan_window = %i\n", config->scan_window); + printf(" scan_interval = %i\n", config->scan_interval); + printf(" publish_type = %i\n", config->publish_type); + printf(" auto_configure = %i\n", config->auto_configure); + printf(" auto_conf_stats = %i\n", config->auto_conf_stats); + printf(" auto_conf_tempf = %i\n", config->auto_conf_tempf); + printf(" auto_conf_tempc = %i\n", config->auto_conf_tempc); + printf(" auto_conf_hum = %i\n", config->auto_conf_hum); + printf(" auto_conf_battery = %i\n", config->auto_conf_battery); + printf(" auto_conf_voltage = %i\n", config->auto_conf_voltage); + printf(" auto_conf_signal = %i\n", config->auto_conf_signal); + printf(" syslog_address = %s\n", config->syslog_address); + printf(" logging_level = %i\n", config->logging_level); + + puts(" sensor configs:"); + puts("\t -----------------"); + for (int i = 0; i < (int)sensor_count; i++) + { + printf("\t name = %s\n", config->sensors[i].name); + printf("\t unique = %s\n", config->sensors[i].unique); + printf("\t location = %s\n", config->sensors[i].location); + printf("\t type = %i\n", config->sensors[i].type); + printf("\t mac = %s\n", config->sensors[i].mac); + puts("\t -----------------"); + } +} diff --git a/ble_sensor_mqtt_pub.csv b/ble_sensor_mqtt_pub.csv deleted file mode 100644 index 5bb1d5a..0000000 --- a/ble_sensor_mqtt_pub.csv +++ /dev/null @@ -1,23 +0,0 @@ -tcp://172.148.5.11:1883 -homeassistant/sensor/ble-temp/ -mac address, type, location -DD:C1:38:70:0C:24, 1, LYWSD03MMC Living Room -DD:C1:38:AC:28:A2, 1, LYWSD03MMC Shared Bathroom -DD:C1:38:4B:80:4A, 1, LYWSD03MMC Backyard -DD:C1:38:C7:74:F1, 1, LYWSD03MMC Kitchen -DD:C1:38:DD:CF:CD, 1, LYWSD03MMC Front Door -DD:C1:38:EC:BE:94, 1, LYWSD03MMC Master Bedroom -DD:C1:38:E2:32:B3, 1, LYWSD03MMC Medium Bedroom -DD:C1:38:54:A4:70, 1, LYWSD03MMC Dining Room -DD:C1:38:38:5C:72, 1, LYWSD03MMC Attic -DD:44:0D:81:75:08, 2, H5052 Refrigerator -DD:44:0D:70:F0:E4, 2, H5052 Backyard -DD:44:0D:70:C1:CC, 2, H5052 Freezer -DD:44:0D:70:28:64, 2, H5052 Small Bedroom -DD:44:0D:80:D8:25, 2, H5052 Garage -DD:C1:38:DD:4C:B2, 3, H5072 Living Room -DD:C1:38:AC:77:44, 3, H5072 Shared Bathroom -DD:C1:38:FF:AD:EB, 3, H5072 Kitchen -DD:C1:38:DB:44:04, 4, H5102 test unit -DD:C1:38:38:5F:C2, 5, H5075 test unit -DD:12:1D:22:80:77, 6, H5074 test unit diff --git a/ble_sensor_mqtt_pub.service b/ble_sensor_mqtt_pub.service new file mode 100644 index 0000000..1e56fd4 --- /dev/null +++ b/ble_sensor_mqtt_pub.service @@ -0,0 +1,15 @@ +[Unit] +Description=BLE Sensor MQTT service +Documentation=https://github.com/deepcoder/bluetooth-temperature-sensors +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +Restart=always +RestartSec=10s +TimeoutSec=3s +ExecStart=/usr/bin/ble_sensor_mqtt_pub /etc/ble_sensor_mqtt_pub.yaml + +[Install] +WantedBy=multi-user.target diff --git a/ble_sensor_mqtt_pub.yaml b/ble_sensor_mqtt_pub.yaml new file mode 100644 index 0000000..394cdc0 --- /dev/null +++ b/ble_sensor_mqtt_pub.yaml @@ -0,0 +1,113 @@ +# config.yaml file for configuring blue tooth sensors + +# MQTT server URL with port number +mqtt_server_url: "tcp://172.148.5.11:1883" + +# MQTT base topic with a trailing slash +# for HA autoconfig to work this should be either "homeassistant/sensor/" or "homeassistant/sensor/[something]/" +mqtt_base_topic: "homeassistant/sensor/ble-temp/" + +# set to empty if not used +mqtt_username: "mosquitto" +mqtt_password: "mosquitto_pass" + +# bluetooth adapter = integer number of bluetooth devices, run hciconfig to see your adapters, 1st adapter is referenced as 0 in this program +bluetooth_adapter: 0 + +# scan type = 0 for passive, 1 for active advertising scan, some BLE sensors only share data on type 4 response active advertising packets +scan_type: 1 + +# integer number that is multiplied by 0.625 to set advertising scanning window in milliseconds. Try 100 to start. +scan_window: 100 + +# integer number that is multiplied by 0.625 to set advertising scanning interval in milliseconds. Try 1000 to start. +scan_interval: 1000 + +# 0 to publish via legecy style (by MAC directly into base), 1 to publish new style (by unique id into 'state'). Must be 1 for auto_configure to work. +publish_type: 1 + +# create HomeAssistant autoconfiguration entries, 1 to enable, 0 to disable +auto_configure: 1 + +# Create Sesnor for hourly statistics in Home Assitant if auto_confiutgure is enabled, 1 to enable, 0 to disable +auto_conf_stats: 1 + +# Create temp sensor in C in Home Assitant if auto_confiutgure is enabled, 1 to enable, 0 to disable +# Note that HA will convert this to F if your settings are for F +# will be created as [name]-T +auto_conf_tempc: 1 + +# Create temp sensor in F in Home Assitant if auto_confiutgure is enabled, 1 to enable, 0 to disable +# will be created as [name]-F +auto_conf_tempf: 1 + +# Create hummity sensor in Home Assitant if auto_confiutgure is enabled, 1 to enable, 0 to disable +# will be created as [name]-H +auto_conf_hum: 1 + +# Create battery percentage sensors in Home Assitant if auto_confiutgure is enabled, 1 to enable, 0 to disable +# will be created as [name]-B +auto_conf_battery: 1 + +# Create battery voltage sensors in Home Assitant if auto_confiutgure is enabled, 1 to enable, 0 to disable +# will be created as [name]-V +auto_conf_voltage: 1 + +# Create signal strength sensors in Home Assitant if auto_confiutgure is enabled, 1 to enable, 0 to disable +# will be created as [name]-S +auto_conf_signal: 1 + +# not implemented yet +syslog_address: "192.168.88.2" + +# set log level +# 0 = LOG_EMERG - system is unusable +# 1 = LOG_ALERT - action must be taken immediately +# 2 = LOG_CRIT - critical conditions +# 3 = LOG_ERR - error conditions +# 4 = LOG_WARNING - warning conditions +# 5 = LOG_NOTICE - normal but significant condition +# 6 = LOG_INFO - informational +# 7 = LOG_DEBUG - debug-level messages + +logging_level: "7" + +#### config for each sensor + +# Name: when auto configure is used thie will be the Name of the device, the root of the Friendly Name, as well as the basis HA uses for creating the entity name +# Unique: name used for topic publishing and used as unique ID for HA auto config - no spaces or odd characters allowed +# Location: used with HA auto config to auto assign sensors to a specific area +# Types: +# 1 = Xiaomi LYWSD03MMC-ATC https://github.com/atc1441/ATC_MiThermometer +# 2 = Govee H5052 (type 4 advertising packets) +# 3 = Govee H5072 +# 4 = Govee H5102 +# 5 = Govee H5075 +# 6 = Govee H5074 (type 4 advertising packets) +# 99 = Display raw type 0 and type 4 advertising packets for this BLE MAC address +# MAC: the MAC address of the sensor + +sensors: + - name: "Living Room Temp/Hum" + unique: "th_living_room" + location: "Living Room" + type: 1 + mac: "DD:C1:38:70:0C:24" + + - name: "Shared Bathroom Temp/Hum" + unique: "th_bathroom" + Location: "Shared Bathroom" + type: 3 + mac: "DD:C1:38:AC:77:44" + + - name: "Attic Temp/Hum" + unique: "th_attic" + location: "Attic" + type: 3 + mac: "DD:12:1D:22:80:77" + + - name: "LYWSD03MMC test" + unique: "th_test" + location: "Garage" + type: 99 + mac: "DD:C1:38:AC:28:A2" diff --git a/hack_ble.sh b/hack_ble.sh index 36ea184..c30e1d1 100644 --- a/hack_ble.sh +++ b/hack_ble.sh @@ -13,7 +13,7 @@ do date '+%Y%m%d%H%M%S' echo "Press [CTRL+C] to stop.." cd /home/pi/atc-govee - /home/pi/atc-govee/ble_sensor_mqtt_pub 0 1 200 500 + /home/pi/atc-govee/ble_sensor_mqtt_pub /home/pi/atc-govee/ble_sensor_mqtt_pub.yaml done