228 lines
4.5 KiB
C
228 lines
4.5 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];
|
||
|
snprintf(topic, TOPIC_MAXLEN, "tic2mqtt/%s/%s", tic_name, tag);
|
||
|
|
||
|
log_info("%ld %s %s\n", stamp, 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;
|
||
|
}
|