Implement scheduling for EG-PMS2

The EG-PMS has a storage format for schedules that is different to prior
versions of the hardware.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
This commit is contained in:
Heinrich Schuchardt 2020-04-08 19:32:51 +02:00
parent 1c9a2a848d
commit d8587a5d25
6 changed files with 163 additions and 11 deletions

View File

@ -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

View File

@ -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:

View File

@ -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

128
src/schedule.c Normal file
View File

@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Conversion routines for schedules
*
* Copyright (c) 2017 Heinrich Schuchardt
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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;
}

View File

@ -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);

View File

@ -27,6 +27,8 @@
#ifndef SISPM_CTL_H
#define SISPM_CTL_H
#include <usb.h>
#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