liblightify
context.c
Go to the documentation of this file.
1 /*
2  liblightify -- library to control OSRAM's LIGHTIFY
3 
4 Copyright (c) 2015, Tobias Frost <tobi@coldtobi.de>
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  * Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in the
13  documentation and/or other materials provided with the distribution.
14  * Neither the name of the author nor the
15  names of its contributors may be used to endorse or promote products
16  derived from this software without specific prior written permission.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 #include "liblightify-private.h"
31 #include "context.h"
32 #include "log.h"
33 #include "node.h"
34 #include "groups.h"
35 
36 #include "socket.h"
37 
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 
42 enum msg_header {
52 };
53 
57 };
58 
64 };
65 
95 };
96 
110 };
111 
126 };
127 
139 };
140 
155 };
156 
171 };
172 
187 };
188 
189 
206 };
207 
222 };
223 
234 };
235 
259 };
260 
264 };
265 
271 };
272 
278 };
279 
280 
281 // 0 seems success, non-zero error.
282 static int decode_status(unsigned char code) {
283  switch (code) {
284  // success
285  case 0x00: return 0;
286  case 0x15: return ENODEV;
287  default: return EIO;
288  }
289 }
290 
298  if (!ctx) return NULL;
299 
300  struct lightify_node *ret = ctx->nodes;
301  while(ret) {
302  if (lightify_node_get_nodeadr(ret) == mac) return ret;
303  ret = lightify_node_get_nextnode(ret);
304  }
305  return NULL;
306 }
307 
313 static uint64_t uint64_from_msg(uint8_t *msg) {
314  uint64_t tmp;
315  tmp = msg[7]; tmp <<=8;
316  tmp |= msg[6]; tmp <<=8;
317  tmp |= msg[5]; tmp <<=8;
318  tmp |= msg[4]; tmp <<=8;
319  tmp |= msg[3]; tmp <<=8;
320  tmp |= msg[2]; tmp <<=8;
321  tmp |= msg[1]; tmp <<=8;
322  tmp |= msg[0];
323  return tmp;
324 }
325 
326 static void msg_from_uint64(unsigned char *pmsg, uint64_t mac) {
327  *pmsg++ = mac & 0xff;
328  *pmsg++ = mac >> 8 & 0xff;
329  *pmsg++ = mac >> 16 & 0xff;
330  *pmsg++ = mac >> 24 & 0xff;
331  *pmsg++ = mac >> 32 & 0xff;
332  *pmsg++ = mac >> 40 & 0xff;
333  *pmsg++ = mac >> 48 & 0xff;
334  *pmsg++ = mac >> 56 & 0xff;
335 }
336 
342 static uint16_t uint16_from_msg(uint8_t *msg) {
343  uint16_t tmp;
344  tmp = msg[0] | (msg[1]<<8);
345  return tmp;
346 }
347 
348 
358 static void fill_telegram_header(unsigned char *msg, unsigned int len, unsigned long token, unsigned char flags, unsigned char command)
359 {
360  len-=2;
361  msg[HEADER_LEN_LSB] = len & 0xff;
362  msg[HEADER_LEN_MSB] = len >> 8;
363  msg[HEADER_FLAGS] = flags;
364  msg[HEADER_CMD] = command;
365  msg[HEADER_REQ_ID_B0] = token & 0xff;
366  msg[HEADER_REQ_ID_B1] = token >> 8 & 0xff;
367  msg[HEADER_REQ_ID_B2] = token >> 16 & 0xff;
368  msg[HEADER_REQ_ID_B3] = token >> 24 & 0xff;
369 }
370 
371 
372 static int check_header_response(unsigned char *msg, unsigned long token,
373  unsigned char cmd) {
374 
375  unsigned long token2;
376  /* check the header if plausible */
377  /* check if the token we've supplied is also the returned one. */
378  token2 = msg[HEADER_REQ_ID_B0] | (msg[HEADER_REQ_ID_B1] << 8U) |
379  (msg[HEADER_REQ_ID_B1] << 16U) | (msg[HEADER_REQ_ID_B1] << 24U);
380  if (token != token2) return -EPROTO;
381  if (msg[HEADER_CMD] != cmd) return -EPROTO;
382  return 0;
383 }
384 
386  struct lightify_node *node ) {
387 
388  if(!ctx) return NULL;
389  if(node) return lightify_node_get_nextnode(node);
390  return ctx->nodes;
391 }
392 
394  struct lightify_node *node )
395 {
396  if(!ctx) return NULL;
397  return lightify_node_get_prevnode(node);
398 }
399 
401 {
402  if (!ctx) return NULL;
403  return ctx->userdata;
404 }
405 
407 {
408  if (!ctx) return -EINVAL;
409  ctx->userdata = userdata;
410  return 0;
411 }
412 
415 
416  if (!ctx) return -EINVAL;
417  if (!fpw || !fpr) {;
420  return 0;
421  }
422 
423  ctx->socket_read_fn = fpr;
424  ctx->socket_write_fn = fpw;
425  return 0;
426 }
427 
428 LIGHTIFY_EXPORT int lightify_new(struct lightify_ctx **ctx, void *reserved)
429 {
430  struct lightify_ctx *c;
431 
432  c = calloc(1, sizeof(struct lightify_ctx));
433  if (!c) return -ENOMEM;
434 
435  c->log_fn = log_stderr;
436  c->log_priority = LOG_ERR;
437 
438 #ifdef HAVE_SECURE_GETENV
439  /* environment overwrites config */
440  const char *env;
441  env = secure_getenv("lightify_LOG");
442  if (env != NULL)
444 #endif
445 
446  info(c, "ctx %p created\n", c);
447  dbg(c, "log_priority=%d\n", c->log_priority);
448  *ctx = c;
449 
450  c->socket = -1;
451  c->iotimeout.tv_sec=1;
452 
455 
456  return 0;
457 }
458 
459 static void free_all_nodes(struct lightify_ctx *ctx) {
460  if (!ctx) return;
461  while(ctx->nodes) {
462  dbg(ctx, "freeing node %p.\n", ctx->nodes);
464  }
465 }
466 
467 static void free_all_groups(struct lightify_ctx *ctx) {
468  if (!ctx) return;
469  while(ctx->groups) {
470  dbg(ctx, "freeing group %p.\n", ctx->nodes);
472  }
473 }
474 
476  if (!ctx) return -EINVAL;
477 
478  free_all_nodes(ctx);
479  free_all_groups(ctx);
480 
481  dbg(ctx, "context %p freed.\n", ctx);
482  free(ctx);
483  return 0;
484 }
485 
487  int ret;
488  int n,m;
489  int no_of_nodes;
490  long token;
491 
492  if (!ctx) return -EINVAL;
493 
494  /* if using standard I/O functions, fd must be valid. If the user overrode those function,
495  we won't care */
496  if (ctx->socket_read_fn == read_from_socket
497  && ctx->socket_write_fn == write_to_socket && ctx->socket < 0) {
498  return -EBADF;
499  }
500 
501  /* remove old node information */
502  free_all_nodes(ctx);
503 
504  token = ++ctx->cnt;
505 
506  /* to avoid problems with packing, we need to use a char array.
507  * to assist we'll have this fine enum */
508  uint8_t msg[42];
509 
510  /* 0x13 command to get all node's informations. */
511  fill_telegram_header(msg, QUERY_0x13_SIZE, token, 0x00, 0x13);
512  msg[QUERY_0x13_REQTYPE] = 0x01;
513 
514  n = ctx->socket_write_fn(ctx, msg, QUERY_0x13_SIZE);
515  if ( n < 0 ) {
516  info(ctx,"socket_write_fn error %d\n", n);
517  return n;
518  }
519  if ( n != QUERY_0x13_SIZE) {
520  info(ctx,"short write %d!=%d\n", QUERY_0x13_SIZE, n);
521  return -EIO;
522  }
523 
524  /* read the header */
525  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x13_SIZE);
526  if (n < 0) {
527  info(ctx,"socket_read_fn error %d\n", n);
528  return n;
529  }
530  if (n != ANSWER_0x13_SIZE) {
531  info(ctx,"short read %d!=%d\n", ANSWER_0x13_SIZE, n);
532  return -EIO;
533  }
534 
535  /* check the header if plausible */
536  /* check if the token we've supplied is also the returned one. */
537  n = check_header_response(msg, token, 0x13);
538  if ( n < 0 ) {
539  info(ctx,"Invalid response (header)\n");
540  return n;
541  }
542 
543  /* check if the message length is as expected */
544  no_of_nodes = msg[ANSWER_0x13_NODESCNT_LSB] | (msg[ANSWER_0x13_NODESCNT_MSB] <<8);
545  m = msg[HEADER_LEN_LSB] | (msg[HEADER_LEN_MSB] << 8);
546  /*info(ctx, "0x13: received %d bytes\n",m);*/
547  if ( no_of_nodes * ANSWER_0x13_NODE_LENGTH + ANSWER_0x13_SIZE - 2 != m) {
548  info(ctx, "Reponse len unexpected for %d nodes: %d!=%d.\n", no_of_nodes,
549  no_of_nodes * ANSWER_0x13_NODE_LENGTH + ANSWER_0x13_SIZE - 2, m);
550  return -EPROTO;
551  }
552 
553  if (msg[HEADER_PAYLOAD_START]) {
554  info(ctx, "strange byte at PAYLOAD_START: %d\n", msg[HEADER_PAYLOAD_START]);
555  }
556 
557  ret = 0;
558  /* read each node..*/
559  while(no_of_nodes--) {
560  uint64_t tmp64;
561  struct lightify_node *node = NULL;
562  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x13_NODE_LENGTH);
563  if (n< 0) return n;
564  if (ANSWER_0x13_NODE_LENGTH != n ) {
565  info(ctx,"read node info: short read %d!=%d\n", ANSWER_0x13_NODE_LENGTH, n);
566  return -EIO;
567  }
568 
569  n = lightify_node_new(ctx, &node);
570  if (n < 0) {
571  info(ctx, "create node error %d", n);
572  return n;
573  }
574  tmp64 = uint64_from_msg(&msg[ANSWER_0x13_NODE_ADR64_B0]);
575  lightify_node_set_nodeadr(node, tmp64);
576 
577  lightify_node_set_zoneadr(node, uint16_from_msg(&msg[ANSWER_0x13_NODE_ADR16_LSB]));
578  lightify_node_set_grpadr(node, uint16_from_msg(&msg[ANSWER_0x13_NODE_GRP_MEMBER_LSB]));
579 
581  info(ctx, "new node: %s\n", lightify_node_get_name(node));
582 
583  n = msg[ANSWER_0x13_NODE_NODETYPE];
584  switch (n) {
585  case 0x00 : // Plug
587  break;
588  case 0x02 : // CCT light
590  break;
591  case 0x04 : // dimable
593  break;
594  case 0x08 : // RGB
596  break;
597  case 0x0a : // CCT, RGB
599  break;
600  default: // maybe the missing dimmer plug or on/off light.
602  dbg(ctx, "unknown type %x for lamp %s. PLEASE REPORT A BUG AGAINST liblightify. \n",n, lightify_node_get_name(node));
603  break;
604  }
605 
606  dbg(ctx, "xtra-data: %x -- %x %x %x %x\n", msg[ANSWER_0x13_UNKNOWN1],
609 
610 
615  lightify_node_set_cct(node, uint16_from_msg(&msg[ANSWER_0x13_NODE_CCT_LSB]));
619  lightify_node_set_stale(node, 0);
620  ret++;
621  }
622  return ret;
623 }
624 
625 static int lightify_request_set_onoff(struct lightify_ctx *ctx, uint64_t adr, int isgroup, int onoff) {
626  unsigned char msg[32];
627  int n;
628  if (!ctx) return -EINVAL;
629 
630  /* normalize to boolean -- int are 16bits...*/
631  onoff = (onoff != 0);
632  isgroup = (isgroup) ? 2 : 0;
633 
634  long token = ++ctx->cnt;
635  fill_telegram_header(msg, QUERY_0x32_SIZE, token, isgroup, 0x32);
636 
637  msg_from_uint64(&msg[QUERY_0x32_NODEADR64_B0], adr);
638  msg[QUERY_0x32_ONOFF] = onoff;
639 
640  n = ctx->socket_write_fn(ctx,msg, QUERY_0x32_SIZE);
641  if ( n < 0 ) {
642  info(ctx,"socket_write_fn error %d\n", n);
643  return n;
644  }
645  if ( n != QUERY_0x32_SIZE) {
646  info(ctx,"short write %d!=%d\n", QUERY_0x32_SIZE, n);
647  return -EIO;
648  }
649 
650  /* read the header */
651  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x32_SIZE);
652  if (n < 0) {
653  info(ctx,"socket_read_fn error %d\n", n);
654  return n;
655  }
656  if (n != ANSWER_0x32_SIZE) {
657  info(ctx,"short read %d!=%d\n", ANSWER_0x32_SIZE, n);
658  int i = 0;
659  while(n--) {
660  info(ctx, " %d => %x\n ",i, msg[i]);
661  i++;
662  }
663  info(ctx, "\n");
664  return -EIO;
665  }
666 
667  /* check the header if plausible */
668  n = check_header_response(msg, token, 0x32);
669  if ( n < 0 ) {
670  info(ctx,"Invalid response (header)\n");
671  return n;
672  }
673 
674  /* check if the node address was echoed properly */
675  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x32_NODEADR64_B0]);
676  if (adr != adr2) {
677  info(ctx, "unexected node mac / group adr %llx!=%llx", adr, adr2 );
678  return -EPROTO;
679  }
680 
681  n = -decode_status(msg[ANSWER_0x32_STATE]);
682  if (n) {
683  info(ctx, "state %d indicates error.\n", n);
684  }
685  return n;
686 }
687 
688 static int lightify_request_set_cct(struct lightify_ctx *ctx, uint64_t adr, int isgroup, unsigned int cct, unsigned int fadetime) {
689  unsigned char msg[32];
690  int n;
691  if (!ctx) return -EINVAL;
692 
693  long token = ++ctx->cnt;
694  isgroup = (isgroup) ? 2 : 0;
695  fill_telegram_header(msg, QUERY_0x33_SIZE, token, isgroup, 0x33);
696  msg_from_uint64(&msg[QUERY_0x33_NODEADR64_B0], adr);
697  msg[QUERY_0x33_CCT_LSB] = cct & 0xff;
698  msg[QUERY_0x33_CCT_MSB] = (cct >> 8 ) & 0xff;
699  msg[QUERY_0x33_FADETIME_LSB] = fadetime & 0xff;
700  msg[QUERY_0x33_FADETIME_MSB] = (fadetime >> 8 ) & 0xff;
701 
702  n = ctx->socket_write_fn(ctx,msg, QUERY_0x33_SIZE);
703  if ( n < 0 ) {
704  info(ctx,"socket_write_fn error %d\n", n);
705  return n;
706  }
707  if ( n != QUERY_0x33_SIZE) {
708  info(ctx,"short write %d!=%d\n", QUERY_0x33_SIZE, n);
709  return -EIO;
710  }
711 
712  /* read the header */
713  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x33_SIZE);
714  if (n < 0) {
715  info(ctx,"socket_read_fn error %d\n", n);
716  return n;
717  }
718  if (n != ANSWER_0x33_SIZE) {
719  info(ctx,"short read %d!=%d\n", ANSWER_0x33_SIZE, n);
720  return -EIO;
721  }
722 
723  /* check the header if plausible */
724  n = check_header_response(msg, token, 0x33);
725  if ( n < 0 ) {
726  info(ctx,"Invalid response (header)\n");
727  return n;
728  }
729 
730  /* check if the node address was echoed properly */
731  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x33_NODEADR64_B0]);
732  if (adr != adr2) {
733  info(ctx, "unexected node mac / group adr %llx!=%llx", adr, adr2 );
734  return -EPROTO;
735  }
736 
737  n = -decode_status(msg[ANSWER_0x33_STATE]);
738  return n;
739 }
740 
741 static int lightify_request_set_rgbw(struct lightify_ctx *ctx, uint64_t adr,
742  int isgroup, unsigned int r, unsigned int g,
743  unsigned int b,unsigned int w,unsigned int fadetime) {
744  unsigned char msg[32];
745  int n;
746  if (!ctx) return -EINVAL;
747  /* does not support broadcast. */
748 
749  long token = ++ctx->cnt;
750  isgroup = (isgroup) ? 2 : 0;
751  fill_telegram_header(msg, QUERY_0x36_SIZE, token, isgroup, 0x36);
752  msg_from_uint64(&msg[QUERY_0x36_NODEADR64_B0], adr);
753  msg[QUERY_0x36_R] = r & 0xff;
754  msg[QUERY_0x36_G] = g & 0xff;
755  msg[QUERY_0x36_B] = b & 0xff;
756  msg[QUERY_0x36_W] = w & 0xff;
757  msg[QUERY_0x36_FADETIME_LSB] = fadetime & 0xff;
758  msg[QUERY_0x36_FADETIME_MSB] = (fadetime >> 8 ) & 0xff;
759 
760  n = ctx->socket_write_fn(ctx,msg, QUERY_0x36_SIZE);
761  if ( n < 0 ) {
762  info(ctx,"socket_write_fn error %d\n", n);
763  return n;
764  }
765  if ( n != QUERY_0x36_SIZE) {
766  info(ctx,"short write %d!=%d\n", QUERY_0x36_SIZE, n);
767  return -EIO;
768  }
769 
770  /* read the header */
771  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x36_SIZE);
772  if (n != ANSWER_0x36_SIZE) {
773  info(ctx,"short read %d!=%d\n", ANSWER_0x36_SIZE, n);
774  return -EIO;
775  }
776 
777  /* check the header if plausible */
778  n = check_header_response(msg, token, 0x36);
779  if ( n < 0 ) {
780  info(ctx,"Invalid response (header)\n");
781  return n;
782  }
783 
784  /* check if the node address was echoed properly */
785  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x33_NODEADR64_B0]);
786  if (adr != adr2) {
787  info(ctx, "unexected node mac / group adr %llx!=%llx", adr, adr2 );
788  return -EPROTO;
789  }
790 
791  n = -decode_status(msg[ANSWER_0x36_STATE]);
792  return n;
793 }
794 
795 static int lightify_request_set_brightness(struct lightify_ctx *ctx, uint64_t adr,
796  int isgroup, unsigned int level, unsigned int fadetime) {
797  unsigned char msg[32];
798  int n;
799  if (!ctx) return -EINVAL;
800 
801  long token = ++ctx->cnt;
802  isgroup = (isgroup) ? 2 : 0;
803 
804  fill_telegram_header(msg, QUERY_0x31_SIZE, token, isgroup, 0x31);
805  msg_from_uint64(&msg[QUERY_0x31_NODEADR64_B0], adr);
806 
807  msg[QUERY_0x31_LEVEL] = level & 0xff;
808  msg[QUERY_0x31_FADETIME_LSB] = fadetime & 0xff;
809  msg[QUERY_0x31_FADETIME_MSB] = (fadetime >> 8 ) & 0xff;
810 
811  n = ctx->socket_write_fn(ctx,msg, QUERY_0x31_SIZE);
812  if ( n < 0 ) {
813  info(ctx,"socket_write_fn error %d\n", n);
814  return n;
815  }
816  if ( n != QUERY_0x31_SIZE) {
817  info(ctx,"short write %d!=%d\n", QUERY_0x31_SIZE, n);
818  return -EIO;
819  }
820 
821  /* read the header */
822  n = ctx->socket_read_fn(ctx,msg,ANSWER_0x31_SIZE);
823  if (n < 0) {
824  info(ctx,"socket_read_fn error %d\n", n);
825  return n;
826  }
827  if (n != ANSWER_0x31_SIZE) {
828  info(ctx,"short read %d!=%d\n", ANSWER_0x31_SIZE, n);
829  return -EIO;
830  }
831 
832  n = check_header_response(msg, token, 0x31);
833  if ( n < 0 ) {
834  info(ctx,"Invalid response (header)\n");
835  return n;
836  }
837 
838  /* check if the node address was echoed properly */
839  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x33_NODEADR64_B0]);
840  if (adr != adr2) {
841  info(ctx, "unexected node mac / group adr %llx!=%llx", adr, adr2 );
842  return -EPROTO;
843  }
844 
845  n = -decode_status(msg[ANSWER_0x31_STATE]);
846  dbg(ctx, "unknown-bytes: %x %x %x\n", msg[ANSWER_0x31_UNKNOWN1],msg[ANSWER_0x31_UNKNOWN2],msg[ANSWER_0x31_UNKNOWN3]);
847  return n;
848 }
849 
850 
851 /* Node control */
852 LIGHTIFY_EXPORT int lightify_node_request_onoff(struct lightify_ctx *ctx, struct lightify_node *node, int onoff) {
853  if (!ctx) return -EINVAL;
854  uint64_t adr = -1;
855  if (node) adr = lightify_node_get_nodeadr(node);
856 
857  onoff = (onoff != 0);
858  int ret = lightify_request_set_onoff(ctx, adr, 0, onoff);
859 
860  if (node) {
861  lightify_node_set_onoff(node,onoff);
862  if (ret<0) {
863  lightify_node_set_stale(node,1);
864  }
865  } else {
866  node = NULL;
867  while((node = lightify_node_get_next(ctx, node))) {
868  lightify_node_set_onoff(node,onoff);
869  if (ret<0) {
870  lightify_node_set_stale(node,1);
871  }
872  }
873  }
874  return ret;
875 }
876 
877 LIGHTIFY_EXPORT int lightify_node_request_cct(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int cct, unsigned int fadetime) {
878  if (!ctx || !node ) return -EINVAL;
879  uint64_t adr = lightify_node_get_nodeadr(node);
880  int ret = lightify_request_set_cct(ctx, adr, 0 , cct, fadetime);
881 
882  lightify_node_set_cct(node, cct);
883  if (ret<0) {
884  lightify_node_set_stale(node,1);
885  }
886  return ret;
887 }
888 
889 LIGHTIFY_EXPORT int lightify_node_request_rgbw(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int r, unsigned int g, unsigned int b,unsigned int w,unsigned int fadetime)
890 {
891  if (!ctx || !node ) return -EINVAL;
892  uint64_t adr = lightify_node_get_nodeadr(node);
893  int ret = lightify_request_set_rgbw(ctx, adr, 0, r, g ,b ,w ,fadetime);
894 
895  lightify_node_set_red(node, r);
896  lightify_node_set_green(node, g);
897  lightify_node_set_blue(node, b);
898  lightify_node_set_white(node, w);
899  if (ret<0) {
900  lightify_node_set_stale(node,1);
901  }
902  return ret;
903 }
904 
905 LIGHTIFY_EXPORT int lightify_node_request_brightness(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int level, unsigned int fadetime) {
906  if (!ctx || !node ) return -EINVAL;
907  uint64_t adr = lightify_node_get_nodeadr(node);
908  int ret = lightify_request_set_brightness(ctx, adr, 0, level, fadetime);
909  lightify_node_set_brightness(node, level);
910  lightify_node_set_onoff(node, level!=0);
911  if (ret<0) {
912  lightify_node_set_stale(node,1);
913  }
914  return ret;
915 }
916 
917 
918 
920  struct lightify_node *node) {
921  unsigned char msg[32];
922  int n;
923  if (!ctx) return -EINVAL;
924  if (!node)return -EINVAL;
925 
926  uint64_t node_adr = lightify_node_get_nodeadr(node);
927  long token = ++ctx->cnt;
928  fill_telegram_header(msg, QUERY_0x68_SIZE, token, 0x00, 0x68);
929  msg_from_uint64(&msg[QUERY_0x68_NODEADR64_B0], node_adr);
930 
931  n = ctx->socket_write_fn(ctx,msg, QUERY_0x68_SIZE);
932  if ( n < 0 ) {
933  info(ctx,"socket_write_fn error %d\n", n);
934  return n;
935  }
936  if ( n != QUERY_0x68_SIZE) {
937  info(ctx,"short write %d!=%d\n", QUERY_0x68_SIZE, n);
938  return -EIO;
939  }
940 
941  /* read the header */
942  n = ctx->socket_read_fn(ctx,msg,ANSWER_0x68_SIZE);
943  if (n < 0) {
944  info(ctx,"socket_read_fn error %d\n", n);
945  return n;
946  }
947  if (n != ANSWER_0x68_SIZE) {
948  info(ctx,"short read %d!=%d\n", ANSWER_0x68_SIZE, n);
949  return -EIO;
950  }
951 
952  n = check_header_response(msg, token, 0x68);
953  if ( n < 0 ) {
954  info(ctx,"Invalid response (header)\n");
955  return n;
956  }
957 
958  /* check if the node adress was echoed properly */
959  if (node_adr != uint64_from_msg(&msg[ANSWER_0x68_NODEADR64_B0])) return -EPROTO;
960 
961  /* update node information */
965  n = msg[ANSWER_0x68_CCT_LSB] | msg[ANSWER_0x68_CCT_MSB] << 8;
966  lightify_node_set_cct(node,n);
971 
972  n = -decode_status(msg[ANSWER_0x68_STATE]);
973  lightify_node_set_stale(node, (n!=0));
974  return n;
975 }
976 
978  int n,m;
979  int no_of_grps;
980  long token;
981  int ret;
982 
983  if (!ctx) return -EINVAL;
984 
985  /* if using standard I/O functions, fd must be valid. If the user overrode those function,
986  we won't care */
987  if (ctx->socket_read_fn == read_from_socket &&
988  ctx->socket_write_fn == write_to_socket && ctx->socket < 0) {
989  return -EBADF;
990  }
991 
992  /* remove old group information */
993  free_all_groups(ctx);
994 
995  token = ++ctx->cnt;
996 
997  /* to avoid problems with packing, we need to use a char array.
998  * to assist we'll have this fine enum */
999  uint8_t msg[ANSWER_0x1e_GRP_LENGHT];
1000 
1001  /* 0x1e command to get all groups. */
1002  fill_telegram_header(msg, QUERY_0x1e_SIZE, token, 0x00, 0x1e);
1003 
1004  n = ctx->socket_write_fn(ctx, msg, QUERY_0x1e_SIZE);
1005  if ( n < 0 ) {
1006  info(ctx,"socket_write_fn error %d\n", n);
1007  return n;
1008  }
1009  if ( n != QUERY_0x1e_SIZE) {
1010  info(ctx,"short write %d!=%d\n", QUERY_0x1e_SIZE, n);
1011  return -EIO;
1012  }
1013 
1014  /* read the header */
1015  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x1e_SIZE);
1016  if (n < 0) {
1017  info(ctx,"socket_read_fn error %d\n", n);
1018  return n;
1019  }
1020  if (n != ANSWER_0x1e_SIZE) {
1021  info(ctx,"short read %d!=%d\n", ANSWER_0x1e_SIZE, n);
1022  return -EIO;
1023  }
1024 
1025  /* check the header if plausible */
1026  /* check if the token we've supplied is also the returned one. */
1027  n = check_header_response(msg, token, 0x1e);
1028  if ( n < 0 ) {
1029  info(ctx,"Invalid response (header)\n");
1030  return n;
1031  }
1032 
1033  /* check if the message length is as expected */
1034  no_of_grps = msg[ANSWER_0x1e_NUMGROUPS];
1035  m = msg[HEADER_LEN_LSB] | (msg[HEADER_LEN_MSB] << 8);
1036  info(ctx, "0x1e: received %d bytes\n",m);
1037  if ( no_of_grps * ANSWER_0x1e_GRP_LENGHT + ANSWER_0x1e_SIZE - 2 != m) {
1038  info(ctx, "Reponse len unexpected for %d groups: %d!=%d.\n", no_of_grps,
1039  no_of_grps * ANSWER_0x1e_GRP_LENGHT + ANSWER_0x1e_SIZE - 2, m);
1040  return -EPROTO;
1041  }
1042 
1043  if (msg[HEADER_PAYLOAD_START]) {
1044  info(ctx, "strange byte at PAYLOAD_START: %d\n", msg[HEADER_PAYLOAD_START]);
1045  }
1046 
1047  ret = 0;
1048  /* read each node..*/
1049  while(no_of_grps--) {
1050  struct lightify_group *group = NULL;
1051  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x1e_GRP_LENGHT);
1052  if (n< 0) return n;
1053  if (ANSWER_0x1e_GRP_LENGHT != n ) {
1054  info(ctx,"read group info: short read %d!=%d\n", ANSWER_0x1e_GRP_LENGHT, n);
1055  return -EIO;
1056  }
1057 
1058  n = lightify_group_new(ctx,&group);
1059  if (n < 0) {
1060  info(ctx, "create group error %d", n);
1061  return n;
1062  }
1063 
1066  ret++;
1067  }
1068  return ret;
1069 }
1070 
1071 
1072 /* Group control */
1073 LIGHTIFY_EXPORT int lightify_group_request_onoff(struct lightify_ctx *ctx, struct lightify_group *group, int onoff) {
1074  if (!ctx || !group) return -EINVAL;
1075 
1076  onoff = (onoff != 0);
1077  int ret = lightify_request_set_onoff(ctx, lightify_group_get_id(group), 1, onoff);
1078 
1079  struct lightify_node *node = NULL;
1080  while ( (node = lightify_group_get_next_node(group,node))) {
1081  lightify_node_set_onoff(node, onoff);
1082  if (ret < 0 ) lightify_node_set_stale(node, 1);
1083  }
1084  return ret;
1085 }
1086 
1087 LIGHTIFY_EXPORT int lightify_group_request_cct(struct lightify_ctx *ctx, struct lightify_group *group, unsigned int cct, unsigned int fadetime) {
1088  if (!ctx || !group) return -EINVAL;
1089 
1090  int ret = lightify_request_set_cct(ctx, lightify_group_get_id(group), 1, cct, fadetime);
1091 
1092  struct lightify_node *node = NULL;
1093  while ( (node = lightify_group_get_next_node(group,node))) {
1094  lightify_node_set_cct(node, cct);
1095  if (ret < 0 ) lightify_node_set_stale(node, 1);
1096  }
1097  return ret;
1098 }
1099 
1101  struct lightify_group *group, unsigned int r, unsigned int g,
1102  unsigned int b,unsigned int w,unsigned int fadetime) {
1103  if (!ctx || !group) return -EINVAL;
1104 
1105  int ret = lightify_request_set_rgbw(ctx, lightify_group_get_id(group), 1, r, g, b, w , fadetime);
1106 
1107  struct lightify_node *node = NULL;
1108  while ( (node = lightify_group_get_next_node(group,node))) {
1109  lightify_node_set_red(node, r);
1110  lightify_node_set_green(node, g);
1111  lightify_node_set_blue(node, b);
1112  lightify_node_set_white(node, w);
1113  if (ret < 0 ) lightify_node_set_stale(node, 1);
1114  }
1115  return ret;
1116 }
1117 
1119  struct lightify_group *group, unsigned int level, unsigned int fadetime) {
1120  if (!ctx || !group) return -EINVAL;
1121 
1122  int ret = lightify_request_set_brightness(ctx, lightify_group_get_id(group), 1, level , fadetime);
1123 
1124  struct lightify_node *node = NULL;
1125  while ( (node = lightify_group_get_next_node(group,node))) {
1126  lightify_node_set_brightness(node, level);
1127  lightify_node_set_onoff(node, level!=0);
1128  if (ret < 0 ) lightify_node_set_stale(node, 1);
1129  }
1130  return ret;
1131 }
int lightify_node_new(struct lightify_ctx *ctx, struct lightify_node **newnode)
Definition: node.c:80
LIGHTIFY_EXPORT int lightify_node_request_rgbw(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int r, unsigned int g, unsigned int b, unsigned int w, unsigned int fadetime)
Definition: context.c:889
msg_0x36_answer
Definition: context.c:208
LIGHTIFY_EXPORT int lightify_free(struct lightify_ctx *ctx)
Definition: context.c:475
int lightify_node_set_grpadr(struct lightify_node *node, uint16_t adr)
Definition: node.c:191
int lightify_node_set_onoff(struct lightify_node *node, uint8_t on)
Definition: node.c:280
LIGHTIFY_EXPORT int lightify_node_request_scan(struct lightify_ctx *ctx)
Definition: context.c:486
#define info(ctx, arg...)
msg_header
Definition: context.c:42
int(* read_from_socket_fn)(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: liblightify.h:167
const char * lightify_node_get_name(struct lightify_node *node)
Definition: node.c:164
LIGHTIFY_EXPORT int lightify_group_request_onoff(struct lightify_ctx *ctx, struct lightify_group *group, int onoff)
Definition: context.c:1073
LIGHTIFY_EXPORT int lightify_node_request_onoff(struct lightify_ctx *ctx, struct lightify_node *node, int onoff)
Definition: context.c:852
int lightify_node_remove(struct lightify_node *node)
Definition: node.c:117
msg_0x68_answer
Definition: context.c:236
int lightify_set_log_priority(struct lightify_ctx *ctx, int priority)
Definition: log.c:105
LIGHTIFY_EXPORT int lightify_group_request_scan(struct lightify_ctx *ctx)
Definition: context.c:977
int write_to_socket(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: socket.c:47
int(* socket_read_fn)(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Function pointer to the I/O handling – read from.
Definition: context.h:69
struct lightify_node * nodes
Definition: context.h:85
int lightify_node_set_brightness(struct lightify_node *node, int brightness)
Definition: node.c:268
msg_0x1e_answer
Definition: context.c:266
uint64_t lightify_node_get_nodeadr(struct lightify_node *node)
Definition: node.c:175
struct lightify_node * lightify_node_get_prevnode(struct lightify_node *node)
Definition: node.c:145
LIGHTIFY_EXPORT void * lightify_get_userdata(struct lightify_ctx *ctx)
Definition: context.c:400
LIGHTIFY_EXPORT int lightify_group_request_cct(struct lightify_ctx *ctx, struct lightify_group *group, unsigned int cct, unsigned int fadetime)
Definition: context.c:1087
LIGHTIFY_EXPORT int lightify_group_get_id(struct lightify_group *grp)
Definition: groups.c:128
int fadetime
Definition: lightify-util.c:98
int lightify_node_set_stale(struct lightify_node *node, int stale)
Definition: node.c:307
LIGHTIFY_EXPORT int lightify_set_socket_fn(struct lightify_ctx *ctx, write_to_socket_fn fpw, read_from_socket_fn fpr)
Definition: context.c:413
void * userdata
Function pointer to the I/O handling – write to.
Definition: context.h:77
msg_0x33_answer
Definition: context.c:173
int lightify_node_set_zoneadr(struct lightify_node *node, uint16_t adr)
Definition: node.c:180
LIGHTIFY_EXPORT struct lightify_node * lightify_node_get_next(struct lightify_ctx *ctx, struct lightify_node *node)
Definition: context.c:385
#define dbg(ctx, arg...)
int(* write_to_socket_fn)(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: liblightify.h:149
int lightify_node_set_lamptype(struct lightify_node *node, enum lightify_node_type type)
Definition: node.c:202
int lightify_node_set_cct(struct lightify_node *node, int cct)
Definition: node.c:257
struct timeval iotimeout
Definition: context.h:94
int(* socket_write_fn)(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Function pointer to the I/O handling – read from.
Definition: context.h:72
#define LIGHTIFY_EXPORT
LIGHTIFY_EXPORT int lightify_group_request_brightness(struct lightify_ctx *ctx, struct lightify_group *group, unsigned int level, unsigned int fadetime)
Definition: context.c:1118
int lightify_node_set_blue(struct lightify_node *node, int blue)
Definition: node.c:224
msg_0x13_answer_node
Definition: context.c:66
int lightify_node_set_red(struct lightify_node *node, int red)
Definition: node.c:213
LIGHTIFY_EXPORT struct lightify_node * lightify_group_get_next_node(struct lightify_group *grp, struct lightify_node *lastnode)
Definition: groups.c:147
int lightify_node_set_green(struct lightify_node *node, int green)
Definition: node.c:235
int lightify_node_set_online_status(struct lightify_node *node, uint8_t state)
Definition: node.c:291
int log_priority
Definition: context.h:79
msg_0x33_query
Definition: context.c:157
LIGHTIFY_EXPORT int lightify_node_request_brightness(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int level, unsigned int fadetime)
Definition: context.c:905
LIGHTIFY_EXPORT int lightify_set_userdata(struct lightify_ctx *ctx, void *userdata)
Definition: context.c:406
msg_0x13_answer
Definition: context.c:59
msg_0x32_query
Definition: context.c:128
LIGHTIFY_EXPORT struct lightify_node * lightify_node_get_previous(struct lightify_ctx *ctx, struct lightify_node *node)
Definition: context.c:393
msg_0x31_query
Definition: context.c:97
int lightify_group_set_id(struct lightify_group *grp, int id)
Definition: groups.c:122
void(* log_fn)(struct lightify_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args)
Definition: context.h:65
msg_0x36_query
Definition: context.c:190
msg_0x68_query
Definition: context.c:224
LIGHTIFY_EXPORT int lightify_new(struct lightify_ctx **ctx, void *reserved)
Definition: context.c:428
int read_from_socket(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: socket.c:123
msg_0x1e_answerpergroup
Definition: context.c:273
int lightify_node_set_nodeadr(struct lightify_node *node, uint64_t adr)
Definition: node.c:169
msg_0x13_query
Definition: context.c:54
int lightify_group_new(struct lightify_ctx *ctx, struct lightify_group **newgroup)
Definition: groups.c:60
struct lightify_node * lightify_node_get_nextnode(struct lightify_node *node)
Definition: node.c:140
LIGHTIFY_EXPORT struct lightify_node * lightify_node_get_from_mac(struct lightify_ctx *ctx, uint64_t mac)
Definition: context.c:297
msg_0x32_answer
Definition: context.c:141
int log_priority(const char *priority)
Definition: log.c:62
LIGHTIFY_EXPORT int lightify_node_request_update(struct lightify_ctx *ctx, struct lightify_node *node)
Definition: context.c:919
int lightify_group_remove(struct lightify_group *grp)
Definition: groups.c:84
LIGHTIFY_EXPORT int lightify_node_request_cct(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int cct, unsigned int fadetime)
Definition: context.c:877
msg_0x1e_query
Definition: context.c:261
long cnt
Definition: context.h:91
void log_stderr(struct lightify_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args)
Definition: log.c:54
struct lightify_ctx * ctx
Definition: node.c:46
msg_0x31_answer
Definition: context.c:112
LIGHTIFY_EXPORT int lightify_group_request_rgbw(struct lightify_ctx *ctx, struct lightify_group *group, unsigned int r, unsigned int g, unsigned int b, unsigned int w, unsigned int fadetime)
Definition: context.c:1100
int lightify_node_set_white(struct lightify_node *node, int white)
Definition: node.c:246
int lightify_node_set_name(struct lightify_node *node, char *name)
Definition: node.c:151
int lightify_group_set_name(struct lightify_group *grp, const unsigned char *name)
Definition: groups.c:104
int cct
Definition: node.c:67
struct lightify_group * groups
Definition: context.h:88
int socket
Definition: context.h:82