changeset 405:4d564248422e

wip
author Louis Opter <kalessin@kalessin.fr>
date Mon, 28 Dec 2015 18:10:25 +0100
parents f5bd88afa0df
children 20b1e7862d25
files add_power_transition.patch
diffstat 1 files changed, 304 insertions(+), 106 deletions(-) [+]
line wrap: on
line diff
--- a/add_power_transition.patch	Mon Dec 28 14:29:16 2015 +0100
+++ b/add_power_transition.patch	Mon Dec 28 18:10:25 2015 +0100
@@ -58,7 +58,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/core/effect.c
-@@ -0,0 +1,98 @@
+@@ -0,0 +1,128 @@
 +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr>
 +//
 +// This file is part of lighstd.
@@ -83,6 +83,29 @@
 +
 +struct lgtd_effect_list lgtd_effects = LIST_HEAD_INITIALIZER(&lgtd_effects);
 +
++void
++lgtd_effect_stop(struct lgtd_effect *effect)
++{
++    assert(effect);
++
++    LIST_REMOVE(effect, link);
++    if (effect->timer) {
++        lgtd_timer_stop(effect->timer);
++    }
++    free(effect);
++}
++
++static void
++lgtd_effect_duration_callback(struct lgtd_timer *timer, union lgtd_timer_ctx ctx)
++{
++    struct lgtd_effect *effect = (struct lgtd_effect *)ctx.as_ptr;
++    if (effect->duration_cb) {
++        effect->duration_cb(effect);
++    }
++    lgtd_timer_stop(timer);
++    lgtd_effect_stop(effect);
++}
++
 +static void
 +lgtd_effect_timer_callback(struct lgtd_timer *timer, union lgtd_timer_ctx ctx)
 +{
@@ -93,61 +116,68 @@
 +        lgtd_time_mono_t diff = now - effect->created_at + effect->duration;
 +        // maybe the computer was sleepy
 +        if (diff > LGTD_EFFECT_STALE_THRESHOLD) {
-+            lgtd_effect_remove(effect);
++            lgtd_effect_stop(effect);
 +            return;
 +        }
 +    }
 +
-+    effect->apply(effect);
-+}
-+
-+void
-+lgtd_effect_stop(struct lgtd_effect *effect)
-+{
-+    assert(effect);
-+
-+    LIST_REMOVE(effect, link);
-+    lgtd_proto_target_list_clear(effect->targets);
-+    if (effect->timer) {
-+        lgtd_timer_stop(effect->timer);
-+    }
-+    free(effect);
++    effect->apply_cb(effect);
 +}
 +
 +struct lgtd_effect *
-+lgtd_effect_start(struct lgtd_proto_target_list *targets,
++lgtd_effect_start(int duration,
++                  void (*duration_cb)(struct lgtd_effect *),
 +                  int timer_flags,
 +                  int timer_ms,
-+                  int duration,
-+                  void (*effect)(const struct lgtd_proto_target *, void *),
++                  void (*apply_cb)(struct lgtd_effect *),
 +                  void *ctx)
 +{
-+    assert(targets);
-+    assert(timer_ms >= 0);
-+    assert(duration >= 0);
-+
 +    struct lgtd_effect *effect = calloc(1, sizeof(*effect));
 +    if (!effect) {
 +        return NULL;
 +    }
 +
++    effect->created_at = lgtd_time_monotonic_msecs();
++    effect->duration = duration;
++    LIST_INSERT_HEAD(&lgtd_effects, effect, link);
++
 +    union lgtd_timer_ctx timer_ctx = { .as_ptr = effect };
-+    effect->timer = lgtd_timer_start(
-+        timer_flags, timer_ms, lgtd_effect_timer_callback, timer_ctx
-+    );
-+    if (!effect->timer) {
-+        free(effect);
-+        return NULL;
++
++    struct lgtd_timer *duration_timer = NULL;
++    if (duration) {
++        duration_timer = lgtd_timer_start(
++            LGTD_TIMER_DEFAULT_FLAGS,
++            duration,
++            lgtd_effect_duration_callback,
++            timer_ctx
++        );
++        if (!duration_timer) {
++            goto err;
++        }
 +    }
 +
-+    effect->created_at = lgtd_time_monotonic_msecs();
-+    effect->duration = duration;
-+    effect->apply = lgtd_effect_power_off;
-+    lgtd_proto_target_list_swap(&effect->targets, targets);
-+
-+    LIST_INSERT_HEAD(&lgtd_effects, effect, link);
++    if (timer_ms) {
++        effect->timer = lgtd_timer_start(
++            timer_flags, timer_ms, lgtd_effect_timer_callback, timer_ctx
++        );
++        if (!effect->timer) {
++            goto err;
++        }
++    } else {
++        effect->apply_cb(effect);
++    }
 +
 +    return effect;
++
++err:
++    if (duration_timer) {
++        lgtd_timer_stop(duration_timer);
++    }
++    if (effect->timer) {
++        lgtd_timer_stop(effect->timer);
++    }
++    free(effect);
++    return NULL;
 +}
 +
 +void
@@ -161,7 +191,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/core/effect.h
-@@ -0,0 +1,52 @@
+@@ -0,0 +1,53 @@
 +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr>
 +//
 +// This file is part of lighstd.
@@ -192,9 +222,9 @@
 +    LIST_ENTRY(lgtd_effect)         link;
 +    lgtd_time_mono_t                created_at;
 +    int                             duration;
++    void                            (*duration_cb)(const struct lgtd_effect *);
 +    struct lgtd_timer               *timer;
-+    void                            (*apply)(struct lgtd_effect *);
-+    struct lgtd_proto_target_list   targets;
++    void                            (*apply_cb)(const struct lgtd_effect *);
 +    union lgtd_effect_ctx           ctx;
 +};
 +LIST_HEAD(lgtd_effect_list, lgtd_effect);
@@ -207,10 +237,11 @@
 +    return (uintptr_t)effect;
 +}
 +
-+struct lgtd_effect *lgtd_effect_start(struct lgtd_proto_target_list *,
++struct lgtd_effect *lgtd_effect_start(int, // duration
++                                      void (*)(const struct lgtd_effect *), // duration cb
 +                                      int, // timer flags
 +                                      int, // timer ms
-+                                      int, // duration
++                                      void (*)(const struct lgtd_effect *), // apply cb
 +                                      union lgtd_effect_ctx);
 +void lgtd_effect_stop(struct lgtd_effect *);
 +void lgtd_effect_stop_all(void);
@@ -484,6 +515,21 @@
      lgtd_lifx_broadcast_close();
      lgtd_lifx_gateway_close_all();
      lgtd_timer_stop_all();
+diff --git a/core/lightsd.h b/core/lightsd.h
+--- a/core/lightsd.h
++++ b/core/lightsd.h
+@@ -121,9 +121,9 @@
+ extern struct event_base *lgtd_ev_base;
+ extern const char *lgtd_progname;
+ 
+-char *lgtd_iee8023mactoa(const uint8_t *addr, char *buf, int buflen);
++char *lgtd_ieee8023mactoa(const uint8_t *addr, char *buf, int buflen);
+ #define LGTD_IEEE8023MACTOA(addr, buf) \
+-    lgtd_iee8023mactoa((addr), (buf), sizeof(buf))
++    lgtd_ieee8023mactoa((addr), (buf), sizeof(buf))
+ char *lgtd_sockaddrtoa(const struct sockaddr *, char *buf, int buflen);
+ #define LGTD_SOCKADDRTOA(addr, buf) \
+     lgtd_sockaddrtoa((addr), (buf), sizeof(buf))
 diff --git a/core/proto.c b/core/proto.c
 --- a/core/proto.c
 +++ b/core/proto.c
@@ -495,21 +541,10 @@
  #include "time_monotonic.h"
  #include "lifx/bulb.h"
  #include "lifx/tagging.h"
-@@ -59,22 +60,55 @@
+@@ -58,23 +59,47 @@
+     }
  }
  
- void
-+lgtd_proto_target_list_swap(struct lgtd_proto_target_list *left,
-+                            struct lgtd_proto_target_list *right)
-+{
-+    assert(left && right); // pun pun pun
-+
-+    struct lgtd_proto_target_list buf;
-+    memcpy(&buf, left, sizeof(buf));
-+    memcpy(left, right, sizeof(*left));
-+    memcpy(right, &buf, sizeof(*right));
-+}
-+
 +int
 +lgtd_proto_target_list_len(const struct lgtd_proto_target_list *targets)
 +{
@@ -523,7 +558,7 @@
 +    return len;
 +}
 +
-+void
+ void
  lgtd_proto_power_on(struct lgtd_client *client,
 -                    const struct lgtd_proto_target_list *targets)
 +                    const struct lgtd_proto_target_list *targets,
@@ -538,9 +573,12 @@
 -    );
 +    bool ok;
 +    if (transition) {
++        ok = lgtd_effect_power_transition(
++            targets, LGTD_EFFECT_POWER_TRANSITION_ON, transition
++        );
 +    } else {
 +        struct lgtd_lifx_packet_power pkt = { .power = LGTD_LIFX_POWER_ON };
-+        ok = lgtd_router_send(targets, LGTD_LIFX_SET_POWER, &pkt);
++        ok = lgtd_router_send(targets, LGTD_LIFX_SET_POWER_STATE, &pkt);
 +    }
 +
 +    SEND_RESULT(client, ok);
@@ -557,18 +595,63 @@
  
      struct lgtd_router_device_list *devices = NULL;
      devices = lgtd_router_targets_to_devices(targets);
-@@ -85,6 +119,10 @@
+@@ -85,30 +110,78 @@
          return;
      }
  
-+    if (transition) {
-+        return;
-+    }
++    struct lgtd_proto_target_list targets_to_turn_off =
++        SLIST_HEAD_INITIALIZER(&targets_to_turn_off);
++    struct lgtd_proto_target_list targets_to_turn_on =
++        SLIST_HEAD_INITIALIZER(&targets_to_turn_on);
 +
++    bool ok = true;
      struct lgtd_router_device *device;
      SLIST_FOREACH(device, devices, link) {
          struct lgtd_lifx_bulb *bulb = device->device;
-@@ -101,14 +139,27 @@
+-        struct lgtd_lifx_packet_power_state pkt = {
+-            .power = ~bulb->state.power
+-        };
+-        lgtd_router_send_to_device(bulb, LGTD_LIFX_SET_POWER_STATE, &pkt);
++        if (transition) {
++            struct lgtd_proto_target *target = malloc(
++                offsetof(struct lgtd_proto_target, target)
++                + LGTD_LIFX_ADDR_STRLEN
++            );
++            if (!target) {
++                lgtd_warn("couldn't toggle bulbs");
++                ok = false;
++                break;
++            }
++            lgtd_ieee8023mactoa(
++                bulb->addr, target->target, LGTD_LIFX_ADDR_STRLEN
++            );
++            SLIST_INSERT_HEAD(
++                bulb->state.power ? &targets_to_turn_off : &targets_to_turn_on
++            );
++        } else {
++            struct lgtd_lifx_packet_power_state pkt = {
++                .power = ~bulb->state.power
++            };
++            lgtd_router_send_to_device(bulb, LGTD_LIFX_SET_POWER_STATE, &pkt);
++        }
+     }
+ 
+-    SEND_RESULT(client, true);
++    if (transition) {
++        ok = lgtd_effect_power_transition(
++            &targets_to_turn_on, LGTD_EFFECT_POWER_TRANSITION_ON, transition
++        );
++        lgtd_proto_target_list_clear(&targets_to_turn_on);
++        ok = ok && lgtd_effect_power_transition(
++            &targets_to_turn_off, LGTD_EFFECT_POWER_TRANSITION_ON, transition
++        );
++        lgtd_proto_target_list_clear(&targets_to_turn_off);
++    }
++
++    SEND_RESULT(client, ok);
+ 
+     lgtd_router_device_list_free(devices);
+ }
  
  void
  lgtd_proto_power_off(struct lgtd_client *client,
@@ -589,7 +672,7 @@
 +
 +    bool ok;
 +    if (transition) {
-+        ok = !!lgtd_effect_power_transition_off(
++        ok = lgtd_effect_power_transition(
 +            targets, LGTD_EFFECT_POWER_TRANSITION_OFF, transition
 +        );
 +    } else {
@@ -604,17 +687,15 @@
 diff --git a/core/proto.h b/core/proto.h
 --- a/core/proto.h
 +++ b/core/proto.h
-@@ -24,6 +24,9 @@
+@@ -24,6 +24,7 @@
  SLIST_HEAD(lgtd_proto_target_list, lgtd_proto_target);
  
  void lgtd_proto_target_list_clear(struct lgtd_proto_target_list *);
-+void lgtd_proto_target_list_swap(struct lgtd_proto_target_list *,
-+                                 struct lgtd_proto_target_list *);
 +int lgtd_proto_target_list_len(const struct lgtd_proto_target_list *);
  const struct lgtd_proto_target *lgtd_proto_target_list_add(struct lgtd_client *,
                                                             struct lgtd_proto_target_list *,
                                                             const char *, int);
-@@ -36,9 +39,9 @@
+@@ -36,9 +37,9 @@
                               enum lgtd_lifx_waveform_type,
                               int, int, int, int,
                               int, float, int, bool);
@@ -627,6 +708,18 @@
  void lgtd_proto_get_light_state(struct lgtd_client *, const struct lgtd_proto_target_list *);
  void lgtd_proto_tag(struct lgtd_client *, const struct lgtd_proto_target_list *, const char *);
  void lgtd_proto_untag(struct lgtd_client *, const struct lgtd_proto_target_list *, const char *);
+diff --git a/core/utils.c b/core/utils.c
+--- a/core/utils.c
++++ b/core/utils.c
+@@ -30,7 +30,7 @@
+ #include "lightsd.h"
+ 
+ char *
+-lgtd_iee8023mactoa(const uint8_t *addr, char *buf, int buflen)
++lgtd_ieee8023mactoa(const uint8_t *addr, char *buf, int buflen)
+ {
+     assert(addr);
+     assert(buf);
 diff --git a/docs/protocol.rst b/docs/protocol.rst
 --- a/docs/protocol.rst
 +++ b/docs/protocol.rst
@@ -692,7 +785,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/effects/power_transition.c
-@@ -0,0 +1,112 @@
+@@ -0,0 +1,196 @@
 +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr>
 +//
 +// This file is part of lighstd.
@@ -711,7 +804,9 @@
 +// along with lighstd.  If not, see <http://www.gnu.org/licenses/>.
 +
 +#include <sys/queue.h>
++#include <sys/tree.h>
 +#include <assert.h>
++#include <endian.h>
 +#include <stdbool.h>
 +#include <stdint.h>
 +#include <time.h>
@@ -730,25 +825,28 @@
 +#include "power_transition.h"
 +
 +static void
-+lgtd_effect_power_transition_off_callback(struct lgtd_effect *effect)
++lgtd_effect_power_transition_clear_target_list(struct lgtd_effect_power_transition_target_list *targets)
++{
++    assert(targets);
++
++    while (!SLIST_EMPTY(targets)) {
++        struct lgtd_effect_power_transition_target *target;
++        target = SLIST_FIRST(targets);
++        SLIST_REMOVE_HEAD(targets, link);
++        free(target);
++    }
++}
++
++static void
++lgtd_effect_power_transition_off_duration_callback(const struct lgtd_effect *effect)
 +{
 +    assert(effect);
 +
-+    struct lgtd_effect_power_transition_ctx *ctx = effect->ctx.as_ptr;
-+    assert(ctx->type == LGTD_EFFECT_POWER_TRANSITION_OFF);
-+
-+    struct lgtd_lifx_packet_power_state pkt = { .power = LGTD_LIFX_POWER_OFF };
-+    lgtd_router_send(targets, LGTD_LIFX_SET_POWER_STATE, &pkt);
++    struct lgtd_effect_power_transition_ctx = effect->ctx.as_ptr;
 +
-+    while (!SLIST_EMPTY(&ctx->targets)) {
-+        struct lgtd_effect_power_transition_target *target;
-+        target = SLIST_FIRST(&cts->targets);
-+
-+        union { // XXX: really need to turn everyting in uint64be_t
-+            uint64be_t  as_integer;
-+            uint8_t     as_array;
-+        } device_id = { .as_integer = target->device_id };
-+        struct lgtd_lifx_bulb *bulb = lgtd_lifx_bulb_get(&device_id.as_array);
++    struct lgtd_effect_power_transition_target *target;
++    SLIST_FOREACH(target, &ctx->targets, link) {
++        struct lgtd_lifx_bulb *bulb = lgtd_lifx_bulb_get(target->device_id);
 +        if (!bulb) {
 +            char addr[LGTD_LIFX_ADDR_LENGTH];
 +            LGTD_IEEE8023MACTOA(device_id.as_array, addr)
@@ -770,40 +868,119 @@
 +            };
 +            lgtd_lifx_wire_encode_light_color(&pkt_light_color);
 +            lgtd_router_send_to_device(
-+                bulb,
-+                LGTD_LIFX_SET_LIGHT_COLOR,
-+                &pkt_light_color
++                bulb, LGTD_LIFX_SET_LIGHT_COLOR, &pkt_light_color
 +            );
 +        }
-+
-+        SLIST_REMOVE_HEAD(&ctx->targets);
-+        free(target);
 +    }
 +
-+    free(effect->ctx);
-+    lgtd_effect_stop(effect);
++    lgtd_effect_power_transition_clear_target_list(&ctx->targets);
++    free(ctx);
 +}
 +
 +static void
-+lgtd_effect_power_transition_on_callback(struct lgtd_effect *effect)
++lgtd_effect_power_transition_on_apply_callback(const struct lgtd_effect *effect)
++{
++    assert(effect);
++
++    struct lgtd_effect_power_transition_ctx = effect->ctx.as_ptr;
++
++    struct lgtd_effect_power_transition_target *target;
++    SLIST_FOREACH(target, &ctx->targets, link) {
++        struct lgtd_lifx_packet_light_color pkt_set_brightness_to_zero = {
++            .hue = bulb->state.hue,
++            .saturation = bulb->state.saturation,
++            .brightness = 0,
++            .kelvin = bulb->state.kelvin
++        };
++        lgtd_lifx_wire_encode_light_color(&pkt_set_brightness_to_zero);
++        lgtd_router_send_to_device(
++            bulb, LGTD_LIFX_SET_LIGHT_COLOR, &pkt_set_brightness_to_zero
++        );
++
++        struct lgtd_lifx_packet_power pkt_power_on = {
++            .power = LGTD_LIFX_POWER_ON
++        };
++        lgtd_router_send_to_device(bulb, LGTD_LIFX_SET_POWER, &pkt_power_on);
++
++        struct lgtd_lifx_packet_light_color pkt_fade_out = {
++            .hue = bulb->state.hue,
++            .saturation = bulb->state.saturation,
++            .brightness = target_brightness,
++            .kelvin = bulb->state.kelvin,
++            .transition = ctx->duration
++        };
++        lgtd_lifx_wire_encode_light_color(&pkt_fade_out);
++        lgtd_router_send_to_device(
++            bulb, LGTD_LIFX_SET_LIGHT_COLOR, &pkt_fade_out
++        );
++    }
++
++    lgtd_effect_power_transition_clear_target_list(&ctx->targets);
++    free(ctx);
++}
 +
 +const struct lgtd_effect *
-+lgtd_effect_power_transition(struct lgtd_proto_target_list *targets,
++lgtd_effect_power_transition(const struct lgtd_proto_target_list *targets,
 +                             enum lgtd_effect_power_transition_type state,
 +                             int duration)
 +{
 +    assert(targets);
-+    assert(transition >= 0);
++    assert(duration >= 0);
++
++    struct lgtd_router_device_list *devices = NULL;
++    devices = lgtd_router_targets_to_devices(targets);
++    if (!devices) {
++        goto error;
++    }
++
++    struct lgtd_effect_power_transition_ctx *ctx;
++    ctx = calloc(1, sizeof(*ctx));
++    if (!ctx) {
++        goto error;
++    }
++    ctx->duration = duration;
++
++    while (!SLIST_EMPTY(devices)) {
++        struct lgtd_router_device *device = SLIST_FIRST(devices);
++        struct lgtd_lifx_bulb *bulb = device->device;
++
++        struct lgtd_effect_power_transition_target *target;
++        target = calloc(1, sizeof(*target));
++        if (!target) {
++            goto error;
++        }
++        memcpy(target->device_id, bulb->addr, sizeof(target->device_id));
++        target->initial_brightness = bulb->state.brightness;
++        SLIST_INSERT_HEAD(&ctx->targets, target, link);
 +
-+    union lgtd_effect_ctx ctx = { .as_ptr = state };
-+    return lgtd_effect_start(
-+        targets,
-+        LGTD_TIMER_DEFAULT_FLAGS,
-+        duration,
-+        0,
-+        lgtd_effect_power_transition_off_callback,
-+        ctx
++        SLIST_REMOVE_HEAD(devices, link);
++        free(device);
++    }
++    free(devices);
++
++    void (*duration_cb)(const struct lgtd_effect *) = NULL;
++    void (*apply_cb)(const struct lgtd_effect *) = NULL;
++    if (state == LGTD_EFFECT_POWER_TRANSITION_OFF) {
++        duration_cb = lgtd_effect_power_transition_off_duration_callback;
++    } else {
++        apply_cb = lgtd_effect_power_transition_on_apply_callback;
++    }
++    union lgtd_effect_ctx effect_ctx = { .as_ptr = ctx };
++    const struct lgtd_effect *effect = lgtd_effect_start(
++        duration, duration_cb 0, 0, apply_cb, effect_ctx
 +    );
++    if (effect) {
++        return effect;
++    }
++
++error:
++    lgtd_warn("can't start a power transition");
++    if (ctx) {
++        lgtd_effect_power_transition_clear_target_list(&ctx->targets);
++    }
++    if (devices) {
++        lgtd_router_device_list_free(devices);
++    }
 +}
 diff --git a/effects/power_transition.h b/effects/power_transition.h
 new file mode 100644
@@ -836,7 +1013,7 @@
 +
 +struct lgtd_effect_power_transition_target {
 +    SLIST_ENTRY(lgtd_effect_power_transition_target)    link;
-+    uint64be_t                                          device_id;
++    uint8_t                                             device_id[LGTD_LIFX_ADDR_LENGTH];
 +    uint16_t                                            initial_brightness;
 +};
 +SLIST_HEAD(
@@ -845,11 +1022,11 @@
 +);
 +
 +struct lgtd_effect_power_transition_ctx {
-+    enum lgtd_effect_power_transition_type              type;
-+    struct lgtd_effect_power_transition_target_list     targets;
-+};
++    struct lgtd_effect_power_transition_target_list targets;
++    int                                             duration;
++}
 +
-+const struct lgtd_effect *lgtd_effect_power_transition(struct lgtd_proto_target_list *,
++const struct lgtd_effect *lgtd_effect_power_transition(const struct lgtd_proto_target_list *,
 +                                                       enum lgtd_effect_power_transition_type,
 +                                                       enum lgtd_effect_ctx);
 diff --git a/examples/lightsc.py b/examples/lightsc.py
@@ -916,7 +1093,28 @@
              .name = "SET_WAVEFORM_OPTIONAL",
              .type = LGTD_LIFX_SET_WAVEFORM_OPTIONAL
          },
-@@ -960,6 +965,14 @@
+@@ -941,8 +946,6 @@
+     pkt->brightness = le16toh(pkt->brightness);
+     pkt->kelvin = le16toh(pkt->kelvin);
+     pkt->dim = le16toh(pkt->dim);
+-    // The bulbs actually return power values between 0 and 0xffff, not sure
+-    // what the intermediate values mean, let's pull them down to 0:
+     if (pkt->power != LGTD_LIFX_POWER_ON) {
+         pkt->power = LGTD_LIFX_POWER_OFF;
+     }
+@@ -954,12 +957,26 @@
+ {
+     assert(pkt);
+ 
++    // StatePower does uses the full 0-65535 range, and its value isn't related
++    // to the brightness or the power level, but is instead related to the
++    // transition in SetPower: if you do SetPower with a 4s transition at t 0s
++    // StatePower will be 0 at t 2s StatePower will be 32767 and at t 4s
++    // StatePower will be 65535 not matter what the targeted brightness is.
++    // See https://github.com/lopter/lightsd/issues/5
+     if (pkt->power != LGTD_LIFX_POWER_ON) {
+         pkt->power = LGTD_LIFX_POWER_OFF;
+     }
  }
  
  void