summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan McGee <dpmcgee@gmail.com>2011-11-14 14:53:14 -0600
committerDan McGee <dpmcgee@gmail.com>2011-11-14 14:53:14 -0600
commit22946032e36a2d54f61136642e02f9968ce082b5 (patch)
treeb0c2085889453b87ca891afa57213c1c545e719d
parent763609505726c662ae97deb2db4116a7365afb23 (diff)
downloadonkyocontrol-22946032e36a2d54f61136642e02f9968ce082b5.tar.gz
onkyocontrol-22946032e36a2d54f61136642e02f9968ce082b5.zip
Implement an in-daemon timer for zone2 and zone3 sleep commands
This is done by tracking the timeval we should shut off the zone, and setting timeouts appropriately so we are woken up at the point where we need to issue a power off command. Note that this has some minor shortfalls compared to the "real" main zone timer on the receiver itself: * We don't get every-minute updates on the sleep status- this should not be that hard to implement * The receiver knows nothing about this timer, so restarting the daemon will obviously clear it, and we should be smarter about turning it off if the zone flips off and on to prevent unexpected power-offs. Signed-off-by: Dan McGee <dpmcgee@gmail.com>
-rw-r--r--command.c66
-rw-r--r--onkyo.c162
-rw-r--r--onkyo.h6
3 files changed, 174 insertions, 60 deletions
diff --git a/command.c b/command.c
index 58b0ec0..bd82551 100644
--- a/command.c
+++ b/command.c
@@ -455,6 +455,66 @@ static int handle_sleep(struct receiver *rcvr,
return cmd_attempt(rcvr, cmd, cmdstr);
}
+static int handle_fakesleep(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
+{
+ struct timeval now;
+ long mins;
+ char *test;
+ char msg[128];
+ char zone;
+
+ gettimeofday(&now, NULL);
+ zone = cmd->prefix[0];
+
+ if(zone != '2' && zone != '3')
+ return(-1);
+
+ if(!arg || strcmp(arg, "status") == 0) {
+ struct timeval when = { 0, 0 };
+
+ if(zone == '2')
+ when = rcvr->zone2_sleep;
+ else if(zone == '3')
+ when = rcvr->zone3_sleep;
+
+ mins = when.tv_sec > now.tv_sec ?
+ (when.tv_sec - now.tv_sec + 59) / 60 : 0;
+ } else if(strcmp(arg, "off") == 0) {
+ /* clear out any future receiver set sleep time */
+ if(zone == '2') {
+ rcvr->zone2_sleep.tv_sec = 0;
+ rcvr->zone2_sleep.tv_usec = 0;
+ } else if(zone == '3') {
+ rcvr->zone3_sleep.tv_sec = 0;
+ rcvr->zone3_sleep.tv_usec = 0;
+ }
+ mins = 0;
+ } else {
+ /* otherwise we probably have a number */
+ mins = strtol(arg, &test, 10);
+ if(*test != '\0') {
+ /* parse error, not a number */
+ return(-1);
+ }
+ if(mins < 0) {
+ /* range error */
+ return(-1);
+ }
+ if(zone == '2') {
+ rcvr->zone2_sleep = now;
+ rcvr->zone2_sleep.tv_sec += 60 * mins;
+ } else if(zone == '3') {
+ rcvr->zone3_sleep = now;
+ rcvr->zone3_sleep.tv_sec += 60 * mins;
+ }
+ }
+
+ snprintf(msg, sizeof(msg), "OK:zone%csleep:%ld\n", zone, mins);
+ write_to_connections(rcvr, msg);
+ return(0);
+}
+
static int handle_memory(struct receiver *rcvr,
const struct command *cmd, const char *arg)
{
@@ -589,9 +649,9 @@ void init_commands(void)
add_command("zone3status", NULL, handle_status, 0);
- add_command("sleep", "SLP", handle_sleep, 0);
- add_command("zone2sleep", "ZSP", handle_sleep, 1);
- add_command("zone3sleep", "SP3", handle_sleep, 1);
+ add_command("sleep", "SLP", handle_sleep, 0);
+ add_command("zone2sleep", "2", handle_fakesleep, 1);
+ add_command("zone3sleep", "3", handle_fakesleep, 1);
add_command("raw", "", handle_raw, 0);
add_command("quit", "", handle_quit, 0);
diff --git a/onkyo.c b/onkyo.c
index 3bcbff3..79de789 100644
--- a/onkyo.c
+++ b/onkyo.c
@@ -271,6 +271,8 @@ static void show_status(void)
r->power & MAIN_POWER ? "ON" : "off",
r->power & ZONE2_POWER ? "ON" : "off",
r->power & ZONE3_POWER ? "ON" : "off");
+ printf("sleep: : zone2 (%ld) zone3 (%ld)\n",
+ r->zone2_sleep.tv_sec, r->zone3_sleep.tv_sec);
r = r->next;
}
@@ -575,46 +577,49 @@ static int open_socket_listener(const char *path)
return(listen_and_add(fd));
}
+static void diff_timeval(struct timeval * restrict a,
+ struct timeval * restrict b, struct timeval * restrict result)
+{
+ /* Calculate time difference as `a - b`.
+ * Make sure we end up with an in-range usecs value. */
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ result->tv_usec = a->tv_usec - b->tv_usec;
+ if(result->tv_usec < 0) {
+ result->tv_usec += 1000000;
+ result->tv_sec -= 1;
+ }
+}
+
/**
* Determine if we can send a command to the receiver by ensuring it has been
* a certain time since the previous sent command. If we can send a command,
* 1 is returned and timeoutval is left undefined. If we cannot send, then 0
* is returned and the timeoutval is set accordingly.
* @param last the last time we sent a command to the receiver
+ * @param last time value to use as 'now'
* @param timeoutval location to store timeout before next permitted send
* @return 1 if we can send a command, 0 if we cannot (and timeoutval is set)
*/
static int can_send_command(struct timeval * restrict last,
+ struct timeval * restrict now,
struct timeval * restrict timeoutval)
{
/* ensure it has been long enough since the last sent command */
- struct timeval now;
- time_t secs, wait_sec;
- suseconds_t usecs, wait_usec;
- gettimeofday(&now, NULL);
- /* Calculate our time difference between now and previous.
- * Make sure we end up with an in-range usecs value. */
- secs = now.tv_sec - last->tv_sec;
- usecs = now.tv_usec - last->tv_usec;
- if(usecs < 0) {
- usecs += 1000000;
- secs -= 1;
- }
- wait_usec = 1000 * COMMAND_WAIT;
- wait_sec = wait_usec / 1000000;
- wait_usec -= wait_sec * 1000000;
+ struct timeval diff, wait;
+ diff_timeval(now, last, &diff);
+
+ wait.tv_usec = 1000 * COMMAND_WAIT;
+ wait.tv_sec = wait.tv_usec / 1000000;
+ wait.tv_usec -= wait.tv_sec * 1000000;
/* check if both of our difference values are > wait values */
- if(secs > wait_sec || (secs == wait_sec && usecs > wait_usec)) {
+ if(diff.tv_sec > wait.tv_sec ||
+ (diff.tv_sec == wait.tv_sec && diff.tv_usec > wait.tv_usec)) {
/* it has been long enough, note that timeoutval is untouched */
return(1);
}
+
/* it hasn't been long enough, set the timeout as necessary */
- timeoutval->tv_sec = wait_sec - secs;
- timeoutval->tv_usec = wait_usec - usecs;
- if(timeoutval->tv_usec < 0) {
- timeoutval->tv_usec += 1000000;
- timeoutval->tv_sec -= 1;
- }
+ diff_timeval(&wait, &diff, timeoutval);
return(0);
}
@@ -703,9 +708,31 @@ static int process_input(struct conn *c)
return(ret);
}
+int write_to_connections(struct receiver *r, const char *msg)
+{
+ struct conn *c;
+ size_t len = strlen(msg);
+ /* print to stdout and all current open connections */
+ printf("response: %s", msg);
+ c = connections;
+ while(c) {
+ if(c->fd > -1) {
+ ssize_t ret = xwrite(c->fd, msg, len);
+ if(ret == -1)
+ end_connection(c, 0);
+ }
+ c = c->next;
+ }
+ /* check for power messages- update our power state variable */
+ r->power = update_power_status(r->power, msg);
+ return 0;
+}
+
static struct timeval min_timeval(struct timeval *tv1, struct timeval *tv2)
{
- if(tv1->tv_sec < tv2->tv_sec) {
+ if(tv1->tv_sec == 0 && tv1->tv_usec == 0) {
+ return(*tv2);
+ } if(tv1->tv_sec < tv2->tv_sec) {
return(*tv1);
} else if(tv1->tv_sec > tv2->tv_sec) {
return(*tv2);
@@ -871,7 +898,7 @@ int main(int argc, char *argv[])
for(;;) {
int maxfd = -1;
fd_set readfds, writefds;
- struct timeval timeoutval;
+ struct timeval now, timeoutval = { 0, 0 };
struct timeval *timeout = NULL;
struct receiver *r;
@@ -884,29 +911,57 @@ int main(int argc, char *argv[])
/* add our signal pipe file descriptor */
FD_SET(signalpipe[READ], &readfds);
maxfd = signalpipe[READ] > maxfd ? signalpipe[READ] : maxfd;
+
+ /* used for all timeout, etc. calculations */
+ gettimeofday(&now, NULL);
+
/* add our receiver list */
r = receivers;
while(r) {
- if(r->fd > -1) {
- FD_SET(r->fd, &readfds);
- maxfd = r->fd > maxfd ? r->fd : maxfd;
- /* check for write possibility if we have commands in queue */
- if(r->queue) {
- struct timeval tv;
- if(can_send_command(&(r->last_cmd), &tv)) {
- FD_SET(r->fd, &writefds);
+ if(r->fd < 0) {
+ r = r->next;
+ continue;
+ }
+ FD_SET(r->fd, &readfds);
+ maxfd = r->fd > maxfd ? r->fd : maxfd;
+
+ /* do we need to queue a power off command for sleep? */
+ if(r->zone2_sleep.tv_sec || r->zone3_sleep.tv_sec) {
+ struct timeval diff;
+ if(r->zone2_sleep.tv_sec) {
+ diff_timeval(&r->zone2_sleep, &now, &diff);
+ if(diff.tv_sec >= 0 && diff.tv_usec > 0) {
+ timeoutval = min_timeval(&timeoutval, &diff);
+ } else {
+ process_command(r, "zone2power off");
+ r->zone2_sleep.tv_sec = 0;
+ r->zone2_sleep.tv_usec = 0;
+ }
+ }
+ if(r->zone3_sleep.tv_sec) {
+ diff_timeval(&r->zone3_sleep, &now, &diff);
+ if(diff.tv_sec >= 0 && diff.tv_usec > 0) {
+ timeoutval = min_timeval(&timeoutval, &diff);
} else {
- if(!timeout) {
- timeoutval = tv;
- timeout = &timeoutval;
- } else {
- /* We want the smallest timeout, so replace the
- * existing if new is smaller. */
- timeoutval = min_timeval(timeout, &tv);
- }
+ process_command(r, "zone3power off");
+ r->zone3_sleep.tv_sec = 0;
+ r->zone3_sleep.tv_usec = 0;
}
}
}
+
+ /* check for write possibility if we have commands in queue */
+ if(r->queue) {
+ struct timeval tv;
+ if(can_send_command(&(r->last_cmd), &now, &tv)) {
+ FD_SET(r->fd, &writefds);
+ } else {
+ /* We want the smallest timeout, so replace the
+ * existing if new is smaller. */
+ timeoutval = min_timeval(&timeoutval, &tv);
+ }
+ }
+
r = r->next;
}
/* add all of our listeners */
@@ -928,6 +983,9 @@ int main(int argc, char *argv[])
c = c->next;
}
+ if(timeoutval.tv_sec != 0 || timeoutval.tv_usec != 0) {
+ timeout = &timeoutval;
+ }
/* our main waiting point */
retval = select(maxfd + 1, &readfds, &writefds, NULL, timeout);
if(retval == -1 && errno == EINTR)
@@ -946,28 +1004,18 @@ int main(int argc, char *argv[])
}
r = receivers;
while(r) {
+ if(r->fd < 0) {
+ r = r->next;
+ continue;
+ }
/* check if we have a status message from the receivers */
- if(r->fd > -1 && FD_ISSET(r->fd, &readfds)) {
- size_t len;
+ if(FD_ISSET(r->fd, &readfds)) {
char *msg = process_incoming_message(r->fd, logfd);
- len = strlen(msg);
- /* print to stdout and all current open connections */
- printf("response: %s", msg);
- c = connections;
- while(c) {
- if(c->fd > -1) {
- ssize_t ret = xwrite(c->fd, msg, len);
- if(ret == -1)
- end_connection(c, 0);
- }
- c = c->next;
- }
- /* check for power messages- update our power state variable */
- r->power = update_power_status(r->power, msg);
+ write_to_connections(r, msg);
free(msg);
}
/* check if we have outgoing messages to send to receiver */
- if(r->fd > -1 && r->queue != NULL && FD_ISSET(r->fd, &writefds)) {
+ if(r->queue != NULL && FD_ISSET(r->fd, &writefds)) {
rcvr_send_command(r);
}
r = r->next;
diff --git a/onkyo.h b/onkyo.h
index a97a884..e42a055 100644
--- a/onkyo.h
+++ b/onkyo.h
@@ -59,10 +59,16 @@ struct receiver {
int type;
enum power power;
struct timeval last_cmd;
+ struct timeval zone2_sleep;
+ struct timeval zone3_sleep;
struct cmdqueue *queue;
struct receiver *next;
};
+
+/* onkyo.c - general functions */
+int write_to_connections(struct receiver *r, const char *msg);
+
/* receiver.c - receiver interaction functions, status processing */
void init_statuses(void);
void free_statuses(void);