diff --git a/ChangeLog b/ChangeLog index 8f7d552..e682bc7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + Support scheduling on EG-PMS2 01 Apr 2020 - 4.6 Add option to specify powerstrip by USB Bus:Device Improve stability of webserver diff --git a/doc/eg-pms2.rst b/doc/eg-pms2.rst index d20e548..0ab369e 100644 --- a/doc/eg-pms2.rst +++ b/doc/eg-pms2.rst @@ -25,7 +25,9 @@ The first 5 bytes are: The next groups of five bytes indicate the scheduled actions: -* 1 byte indicating the action: 0x00 = off, 0x01 = on +* 1 byte where + bit 0 indicates the action: 0 off, 1 on, and + bit 1 indicates if the action shall be repeated in a loop. * 4 bytes seconds since 1970-01-01 in LSB format indicating the scheduled time The last 5 bytes are: diff --git a/src/Makefile.am b/src/Makefile.am index 479bb03..25e1b67 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,7 +43,8 @@ pkgdata3_DATA = \ endif libsispmctl_la_SOURCES = \ - process.c sispm_ctl.c nethelp.c socket.c sispm_ctl.h nethelp.h socket.h + process.c sispm_ctl.c nethelp.c schedule.c socket.c \ + sispm_ctl.h nethelp.h socket.h sispmctl_SOURCES = main.c diff --git a/src/schedule.c b/src/schedule.c new file mode 100644 index 0000000..0d80692 --- /dev/null +++ b/src/schedule.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Conversion routines for schedules + * + * Copyright (c) 2017 Heinrich Schuchardt + */ + + +#include +#include +#include +#include "sispm_ctl.h" + +#define PMS2_BUFFER_SIZE 0x28 + +static unsigned char +*pms2_write_block(uint8_t action, uint32_t time, unsigned char *ptr) +{ + *ptr++ = action; + for (int i = 0; i < 4; ++i) { + *ptr++ = (uint8_t)time; + time >>= 8; + } + return ptr; +} + +/** + * pms2_read_block() - read a single action for EG-PMS2 + * + * @action: action to be taken + * @time: time when to take the action + * @ptr: pointer to current action + * Return: pointer to next action + */ +static const unsigned char +*pms2_read_block(uint8_t *action, uint32_t *time, const unsigned char *ptr) +{ + *action = *ptr++; + *time = 0; + for (int i = 0; i < 4; ++i) { + *time >>= 8; + *time += (uint32_t)*ptr++ << 24; + } + return ptr; +} + +/** + * pms2_schedule_to_buffer() - convert schedule to device buffer + * + * @schedule: schedule + * @buffer: 40 character buffer + * + * Return: 0 = success + */ +int pms2_schedule_to_buffer(const struct plannif *schedule, + unsigned char *buffer) +{ + unsigned char *ptr = buffer; + uint32_t loop_ref, start = (uint32_t)schedule->timeStamp; + unsigned char action; + int i; + + memset(buffer, 0, PMS2_BUFFER_SIZE); + + ptr = pms2_write_block(3 * schedule->socket + 1, start, ptr); + + for (i = 0; i < 7; ++i) { + action = schedule->actions[i + 1].switchOn; + start += 60 * schedule->actions[i].timeForNext; + if (!i) + loop_ref = start; + if (action > 1) + break; + ptr = pms2_write_block(action, start, ptr); + } + if (action <= 1) { + fprintf(stderr, "Schedule has too many items\n"); + return -1; + } + if (schedule->actions[i].timeForNext) + start -= loop_ref; + else + start = 0; + pms2_write_block(0xe5, start, ptr); + if (start) { + /* set loop flag */ + for (ptr -= 5; ptr > buffer; ptr -= 5) + *ptr |= 2; + } + return 0; +} + +/** + * pms2_buffer_to_schedule() - device buffer to schedule + * + * @buffer: 40 character buffer + * @schedule: schedule + */ +void pms2_buffer_to_schedule(const unsigned char *buffer, + struct plannif *schedule) +{ + const unsigned char *ptr = buffer; + uint32_t start, last, loop_ref, time; + unsigned char action; + int i; + + plannif_reset(schedule); + + ptr = pms2_read_block(&action, &start, ptr); + schedule->actions[0].switchOn = 0; + schedule->socket = (action - 1) / 3; + schedule->timeStamp = start; + last = start; + for (i = 0; i < 7; ++i) { + ptr = pms2_read_block(&action, &time, ptr); + if (!i) + loop_ref = time; + if (action > 3) + break; + schedule->actions[i + 1].switchOn = action & 1; + schedule->actions[i].timeForNext = + ((int32_t)time - (int32_t)last) / 60; + last = time; + } + if (time) + schedule->actions[i].timeForNext = + ((int32_t)loop_ref + time - (int32_t)last) / 60; +} diff --git a/src/sispm_ctl.c b/src/sispm_ctl.c index 9e89704..6276605 100644 --- a/src/sispm_ctl.c +++ b/src/sispm_ctl.c @@ -270,8 +270,7 @@ void plannif_display(const struct plannif *plan, int verbose, // now read all filled rows, except the possibly last "stop" row for (action = 0; action < sizeof(plan->actions) / sizeof(struct plannifAction) && - (plan->actions[action].switchOn != -1) && - (plan->actions[action].timeForNext > 0); ++action) { + plan->actions[action].switchOn != -1; ++action) { date += 60 * plan->actions[action].timeForNext; if ((action + 1 < sizeof(plan->actions)/sizeof(struct plannifAction)) && (plan->actions[action+1].switchOn != -1)) { @@ -369,6 +368,7 @@ void usb_command_getplannif(usb_dev_handle *udev, int socket, int reqtype = 0x21 | USB_DIR_IN; /* request type */ int req = 0x01; unsigned char buffer[0x28]; + unsigned int id; if (usb_control_msg_tries(udev, /* handle */ reqtype, @@ -391,7 +391,11 @@ void usb_command_getplannif(usb_dev_handle *udev, int socket, printf("\n"); // */ - plannif_scanf(plan, buffer); + id = get_id(usb_device(udev)); + if (id == PRODUCT_ID_SISPM_EG_PMS2) + pms2_buffer_to_schedule(buffer, plan); + else + plannif_scanf(plan, buffer); } // private : prints the buffer according to the schedule structure @@ -458,11 +462,20 @@ void plannif_printf(const struct plannif *plan, unsigned char *buffer) // prepares the buffer according to plannif and sends it to the device void usb_command_setplannif(usb_dev_handle *udev, struct plannif* plan) { - int reqtype=0x21; //USB_DIR_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE /*request type*/, - int req=0x09; - unsigned char buffer[0x27]; + int reqtype=0x21; //USB_DIR_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE /*request type*/, + int req=0x09; + unsigned char buffer_size = 0x27; + unsigned char buffer[0x28]; + unsigned int id; - plannif_printf(plan, buffer); + id = get_id(usb_device(udev)); + if (id == PRODUCT_ID_SISPM_EG_PMS2) { + if (pms2_schedule_to_buffer(plan, buffer)) + exit(-2); + } else { + buffer_size = 0x27; + plannif_printf(plan, buffer); + } /*// debug int n; @@ -480,8 +493,8 @@ void usb_command_setplannif(usb_dev_handle *udev, struct plannif* plan) ((0x03 << 8) | (3 * plan->socket)) + 1, 0, /* index */ (char *) buffer, /* bytes */ - 0x27, /* size */ - 5000) < 0x27 ) { + buffer_size, /* size */ + 5000) < buffer_size ) { fprintf(stderr, "Error performing requested action\n" "Libusb error string: %s\nTerminating\n", usb_strerror()); usb_close (udev); diff --git a/src/sispm_ctl.h b/src/sispm_ctl.h index 0eff488..30c5090 100644 --- a/src/sispm_ctl.h +++ b/src/sispm_ctl.h @@ -27,6 +27,8 @@ #ifndef SISPM_CTL_H #define SISPM_CTL_H +#include + #define MAXGEMBIRD 32 #define MAXANSWER 8192 @@ -142,4 +144,9 @@ extern char *homedir; /* Base64 encoded user:password */ extern char *secret; +int pms2_schedule_to_buffer(const struct plannif *schedule, + unsigned char *buffer); +void pms2_buffer_to_schedule(const unsigned char *buffer, + struct plannif *schedule); + #endif