#include "logger.h" #include "tic.h" #include "mqtt.h" #include "homeassistant.h" #include #include #include #include #include #include #define TIC2MQTT_VERSION "1.0.1" #define DEFAULT_TTY "/dev/ttyUSB0" #define DEFAULT_PORT 1883 #define DEFAULT_KEEPALIVE 60 #define DEFAULT_TIC_NAME "Linky" static tic_info_t *tic_info = NULL; struct tag_desc { const char *tag; // Name of tag. const int len; // Length of data. time_t stamp; // last time received char *data; // Last data received. }; static struct tag_desc tag_descs[] = { { "ADCO", 12 }, { "OPTARIF", 4 }, { "ISOUSC", 2 }, { "BASE", 9 }, { "HCHC", 9 }, { "HCHP", 9 }, { "EJPHN", 9 }, { "EJPHPM", 9 }, { "BBRHCJB", 9 }, { "BBRHPJB", 9 }, { "BBRHCJW", 9 }, { "BBRHPJW", 9 }, { "BBRHCJR", 9 }, { "BBRHPJR", 9 }, { "PEJP", 2 }, { "PTEC", 4 }, { "DEMAIN", 4 }, { "IINST", 3 }, { "ADPS", 3 }, { "IMAX", 3 }, { "PAPP", 5 }, { "HHPHC", 1 }, { "MOTDETAT", 6 }, { NULL, 0 } /* End of table marker. */ }; static struct mosquitto *mosq_tic = NULL; static int log_level = LOG_WARNING; static int ha_config = 0; char *tic_name = DEFAULT_TIC_NAME; static void tic2mqtt_process_group(const char *tag, const char *data, time_t date) { struct tag_desc *ptag_desc; for (ptag_desc = tag_descs; ptag_desc->tag != NULL; ptag_desc++) { if (strcmp(ptag_desc->tag, tag) == 0) { if (ptag_desc->data == NULL) { ptag_desc->data = calloc(1, ptag_desc->len + 1); if (ptag_desc->data == NULL) { log_error("Cannot alloc data for tag %s: %s\n", tag, strerror(errno)); } } time_t stamp = time(NULL); double elapsed = difftime(stamp, ptag_desc->stamp); if (elapsed >= 60 || strcmp(ptag_desc->data, data)) { ptag_desc->stamp = stamp; strncpy(ptag_desc->data, data, ptag_desc->len); char topic[TOPIC_MAXLEN + 1]; char stamp_str[32]; snprintf(topic, TOPIC_MAXLEN, "tic2mqtt/%s/%s", tic_name, tag); strftime(stamp_str, 32, "%Y-%m-%d %H:%M:%S", localtime(&stamp)); log_info("%s %s %s\n", stamp_str, topic, data); if (mosq_tic) { int res = mqtt_publish(mosq_tic, topic, NULL, data, TIC_QOS); if (res != 0) log_error("Cannot publish topic %s: %s\n", topic, mqtt_strerror(res)); } } return; // No more processing. } } } static void sighandler(int signum) { log_info("Catch signal #%d (%s)\n", signum, strsignal(signum)); exit(EXIT_SUCCESS); } static void cleanup(void) { tic_exit(tic_info); if (mosq_tic) mqtt_close(mosq_tic); } static void usage(const char *progname) { fprintf(stderr, "Usage: %s [-Hv] [-a] [-t tty] [-n name] [-h host] [-p port] [-k keepalive]\n", progname); } static void set_progname(char *argv0) { char *p; if ((p = strrchr(argv0, '/')) != NULL) strcpy(argv0, p + 1); // argv[0] contains a slash. } int main(int argc, char *argv[]) { int opt; const char *tty = DEFAULT_TTY; const char *host = NULL; int port = DEFAULT_PORT; int keepalive = DEFAULT_KEEPALIVE; set_progname(argv[0]); /* Decode options. */ opterr = 1; while ((opt = getopt(argc, argv, "vdat:n:h:p:k:H")) != -1) { switch (opt) { case 'v': log_level = LOG_INFO; break; case 'd': log_level = LOG_DEBUG; break; case 'a': ha_config = 1; break; case 't': tty = optarg; break; case 'n': tic_name = optarg; break; case 'h': host = optarg; break; case 'p': port = atoi(optarg); break; case 'k': keepalive = atoi(optarg); break; case 'H': printf("version " TIC2MQTT_VERSION "\n"); usage(argv[0]); exit(EXIT_SUCCESS); break; default: usage(argv[0]); exit(EXIT_FAILURE); break; } } atexit(cleanup); signal(SIGINT, sighandler); signal(SIGQUIT, sighandler); signal(SIGTERM, sighandler); signal(SIGHUP, sighandler); if (host) { mosq_tic = mqtt_open(host, port, keepalive); if (mosq_tic == NULL) return EXIT_FAILURE; } tic_info = tic_init(tty); if (tic_info == NULL) return EXIT_FAILURE; if (ha_config) ha_config_init(tic_name, mosq_tic); tic_set_cb_data(tic_info, tic2mqtt_process_group); for (;;) { if (tic_read_frame(tic_info) < 0) return EXIT_FAILURE; tic_process_frame(tic_info); } return EXIT_SUCCESS; }