tic2mqtt-c/tic2mqtt.c

231 lines
4.6 KiB
C

#include "logger.h"
#include "tic.h"
#include "mqtt.h"
#include "homeassistant.h"
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#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;
}