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 /* For KFreeBSD, which has no ENODATA .. map it to EIO */
43 #ifndef ENODATA
44 #define ENODATA EIO
45 #endif
46 
47 
48 enum msg_header {
58 };
59 
63 };
64 
70 };
71 
109 };
110 
124 };
125 
140 };
141 
153 };
154 
169 };
170 
185 };
186 
201 };
202 
203 
220 };
221 
236 };
237 
248 };
249 
276 };
277 
281 };
282 
288 };
289 
295 };
296 
297 
298 // 0 seems success, non-zero error.
299 static int decode_status(unsigned char code) {
300  switch (code) {
301  // success
302  case 0x00: return 0;
303  case 0x15: return ENODEV;
304  default: return EIO;
305  }
306 }
307 
315  if (!ctx) return NULL;
316 
317  struct lightify_node *ret = ctx->nodes;
318  while(ret) {
319  if (lightify_node_get_nodeadr(ret) == mac) return ret;
320  ret = lightify_node_get_nextnode(ret);
321  }
322  return NULL;
323 }
324 
330 static uint64_t uint64_from_msg(uint8_t *msg) {
331  uint64_t tmp;
332  tmp = msg[7]; tmp <<=8;
333  tmp |= msg[6]; tmp <<=8;
334  tmp |= msg[5]; tmp <<=8;
335  tmp |= msg[4]; tmp <<=8;
336  tmp |= msg[3]; tmp <<=8;
337  tmp |= msg[2]; tmp <<=8;
338  tmp |= msg[1]; tmp <<=8;
339  tmp |= msg[0];
340  return tmp;
341 }
342 
343 static void msg_from_uint64(unsigned char *pmsg, uint64_t mac) {
344  *pmsg++ = mac & 0xff;
345  *pmsg++ = mac >> 8 & 0xff;
346  *pmsg++ = mac >> 16 & 0xff;
347  *pmsg++ = mac >> 24 & 0xff;
348  *pmsg++ = mac >> 32 & 0xff;
349  *pmsg++ = mac >> 40 & 0xff;
350  *pmsg++ = mac >> 48 & 0xff;
351  *pmsg++ = mac >> 56 & 0xff;
352 }
353 
359 static uint16_t uint16_from_msg(uint8_t *msg) {
360  uint16_t tmp;
361  tmp = msg[0] | (msg[1]<<8);
362  return tmp;
363 }
364 
365 
375 static void fill_telegram_header(unsigned char *msg, unsigned int len, uint32_t token, unsigned char flags, unsigned char command)
376 {
377  len-=2;
378  msg[HEADER_LEN_LSB] = len & 0xff;
379  msg[HEADER_LEN_MSB] = len >> 8;
380  msg[HEADER_FLAGS] = flags;
381  msg[HEADER_CMD] = command;
382  msg[HEADER_REQ_ID_B0] = token & 0xff;
383  msg[HEADER_REQ_ID_B1] = token >> 8 & 0xff;
384  msg[HEADER_REQ_ID_B2] = token >> 16 & 0xff;
385  msg[HEADER_REQ_ID_B3] = token >> 24 & 0xff;
386 }
387 
388 
389 static int check_header_response(unsigned char *msg, uint32_t token,
390  unsigned char cmd) {
391 
392  uint32_t token2;
393  /* check the header if plausible */
394  /* check if the token we've supplied is also the returned one. */
395  token2 = msg[HEADER_REQ_ID_B0] | (msg[HEADER_REQ_ID_B1] << 8U) |
396  (msg[HEADER_REQ_ID_B1] << 16U) | (msg[HEADER_REQ_ID_B1] << 24U);
397  if (token != token2) return -EPROTO;
398  if (msg[HEADER_CMD] != cmd) return -EPROTO;
399  return 0;
400 }
401 
403  struct lightify_node *node ) {
404 
405  if(!ctx) return NULL;
406  if(node) return lightify_node_get_nextnode(node);
407  return ctx->nodes;
408 }
409 
411  struct lightify_node *node )
412 {
413  if(!ctx) return NULL;
414  return lightify_node_get_prevnode(node);
415 }
416 
418 {
419  if (!ctx) return NULL;
420  return ctx->userdata;
421 }
422 
424 {
425  if (!ctx) return -EINVAL;
426  ctx->userdata = userdata;
427  return 0;
428 }
429 
432 
433  if (!ctx) return -EINVAL;
434  if (!fpw || !fpr) {;
437  return 0;
438  }
439 
440  ctx->socket_read_fn = fpr;
441  ctx->socket_write_fn = fpw;
442  return 0;
443 }
444 
445 LIGHTIFY_EXPORT int lightify_new(struct lightify_ctx **ctx, void *reserved)
446 {
447  struct lightify_ctx *c;
448 
449  c = calloc(1, sizeof(struct lightify_ctx));
450  if (!c) return -ENOMEM;
451 
452  c->log_fn = log_stderr;
453  c->log_priority = LOG_ERR;
454 
455 #ifdef HAVE_SECURE_GETENV
456  /* environment overwrites config */
457  const char *env;
458  env = secure_getenv("lightify_LOG");
459  if (env != NULL)
461 #endif
462 
463  info(c, "ctx %p created\n", c);
464  dbg(c, "log_priority=%d\n", c->log_priority);
465  *ctx = c;
466 
467  c->socket = -1;
468  c->iotimeout.tv_sec=1;
469 
472 
473  c->gw_protocol_version = -1;
474 
475  return 0;
476 }
477 
478 static void free_all_nodes(struct lightify_ctx *ctx) {
479  if (!ctx) return;
480  while(ctx->nodes) {
481  dbg(ctx, "freeing node %p.\n", ctx->nodes);
483  }
484 }
485 
486 static void free_all_groups(struct lightify_ctx *ctx) {
487  if (!ctx) return;
488  while(ctx->groups) {
489  dbg(ctx, "freeing group %p.\n", ctx->groups);
491  }
492 }
493 
495  if (!ctx) return -EINVAL;
496 
497  free_all_nodes(ctx);
498  free_all_groups(ctx);
499 
500  dbg(ctx, "context %p freed.\n", ctx);
501  free(ctx);
502  return 0;
503 }
504 
506  int ret;
507  int n,m;
508  int no_of_nodes;
509  int read_size = 0;
510  uint32_t token;
511 
512  if (!ctx) return -EINVAL;
513 
514  /* if using standard I/O functions, fd must be valid. If the user overrode those function,
515  we won't care */
516  if (ctx->socket_read_fn == read_from_socket
517  && ctx->socket_write_fn == write_to_socket && ctx->socket < 0) {
518  return -EBADF;
519  }
520 
521  /* remove old node information */
522  free_all_nodes(ctx);
523 
524  token = ++ctx->cnt;
525 
526  /* to avoid problems with packing, we need to use a char array.
527  * to assist we'll have this fine enum */
528  uint8_t msg[ANSWER_0x13_NODE_LENGTH+2];
529 
530  /* 0x13 command to get all node's informations. */
531  fill_telegram_header(msg, QUERY_0x13_SIZE, token, 0x00, 0x13);
532  msg[QUERY_0x13_REQTYPE] = 0x01;
533 
534  n = ctx->socket_write_fn(ctx, msg, QUERY_0x13_SIZE);
535  if ( n < 0 ) {
536  info(ctx,"socket_write_fn error %d\n", n);
537  return n;
538  }
539  if ( n != QUERY_0x13_SIZE) {
540  info(ctx,"short write %d!=%d\n", QUERY_0x13_SIZE, n);
541  return -EIO;
542  }
543 
544  /* read the header */
545  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x13_SIZE);
546  if (n < 0) {
547  info(ctx,"socket_read_fn error %d\n", n);
548  return n;
549  }
550  if (n != ANSWER_0x13_SIZE) {
551  info(ctx,"short read %d!=%d\n", ANSWER_0x13_SIZE, n);
552  return -EIO;
553  }
554 
555  /* check the header if plausible */
556  /* check if the token we've supplied is also the returned one. */
557  n = check_header_response(msg, token, 0x13);
558  if ( n < 0 ) {
559  info(ctx,"Invalid response (header)\n");
560  return n;
561  }
562 
563  /* check if the message length is as expected */
564  no_of_nodes = msg[ANSWER_0x13_NODESCNT_LSB] | (msg[ANSWER_0x13_NODESCNT_MSB] <<8);
565 
566  if (!no_of_nodes) return 0;
567 
568  m = msg[HEADER_LEN_LSB] | (msg[HEADER_LEN_MSB] << 8);
569  /*info(ctx, "0x13: received %d bytes\n",m);*/
570 
571  m -= ANSWER_0x13_SIZE - 2 ;
572  if (m == ANSWER_0x13_NODE_LENGTH * no_of_nodes) {
574  read_size = ANSWER_0x13_NODE_LENGTH;
575  dbg(ctx, "0x13: Dec-15 GW protocol\n");
576 
577  } else if (m == ANSWER_0x13_UNKNOWN6 * no_of_nodes ){
579  read_size = ANSWER_0x13_UNKNOWN6;
580  dbg(ctx, "0x13: Old GW protocol\n");
581  } else {
582  info(ctx, "Response len unexpected for %d nodes: %d.\n", no_of_nodes, m);
583  return -EPROTO;
584  }
585 
586  if (msg[HEADER_PAYLOAD_START]) {
587  info(ctx, "strange byte at PAYLOAD_START: %d\n", msg[HEADER_PAYLOAD_START]);
588  }
589 
590  ret = 0;
591  /* read each node..*/
592  while(no_of_nodes--) {
593  uint64_t tmp64;
594  struct lightify_node *node = NULL;
595  n = ctx->socket_read_fn(ctx, msg, read_size);
596  if (n< 0) return n;
597  if (read_size != n ) {
598  info(ctx,"read node info: short read %d!=%d\n", read_size, n);
599  return -EIO;
600  }
601 
602  n = lightify_node_new(ctx, &node);
603  if (n < 0) {
604  info(ctx, "create node error %d", n);
605  return n;
606  }
607  tmp64 = uint64_from_msg(&msg[ANSWER_0x13_NODE_ADR64_B0]);
608  lightify_node_set_nodeadr(node, tmp64);
609 
610  lightify_node_set_zoneadr(node, uint16_from_msg(&msg[ANSWER_0x13_NODE_ADR16_LSB]));
611  lightify_node_set_grpadr(node, uint16_from_msg(&msg[ANSWER_0x13_NODE_GRP_MEMBER_LSB]));
612 
614  info(ctx, "new node: %s\n", lightify_node_get_name(node));
615 
616  n = msg[ANSWER_0x13_NODE_NODETYPE];
617 
618  if (ctx->gw_protocol_version == GW_PROT_OLD) {
619  switch (n) {
620  case 0x00 : /* Plug */
622  break;
623  case 0x02 : /* CCT light */
625  break;
626  case 0x04 : /* dimable */
628  break;
629  case 0x08 : /* RGB */
631  break;
632  case 0x0a : /* CCT, RGB */
634  break;
635  default: /* maybe the missing dimmer plug or on/off light. */
637  dbg(ctx, "unknown type %x for lamp %s. PLEASE REPORT A BUG AGAINST liblightify.\n",n, lightify_node_get_name(node));
638  break;
639  }
640  } else {
641  /* new gateway firmware (Dec 2015) returns different values.
642  * remap to avoid API Bump. */
643  switch (n) {
644  case 0x10 : // Plug
646  break;
647  case 0x00 : // CCT light
648  case 0x02 : // reported by user as CCT light as well.
650  break;
651  case 0x04 : // dimable
653  break;
654  case 0x08 : // RGB
656  break;
657  case 0x0a : // CCT, RGB
659  break;
660  case 0x41: /* 4-Way-Switch */
662  break;
663  default: // maybe the missing dimmer plug or on/off light.
665  dbg(ctx, "unknown type %x for lamp %s. PLEASE REPORT A BUG AGAINST liblightify.\n",n, lightify_node_get_name(node));
666  break;
667  }
668  }
669 
670  dbg(ctx, "xtra-data: %x -- %x %x %x %x\n", msg[ANSWER_0x13_UNKNOWN1],
673 
674  if (ctx->gw_protocol_version == GW_PROT_1512) {
675  dbg(ctx, "xtra-data-new-prot: %x %x %x %x %x %x %x %x\n", msg[ANSWER_0x13_UNKNOWN6],
679  msg[ANSWER_0x13_UNKNOWN13]);
680  }
681 
686  lightify_node_set_cct(node, uint16_from_msg(&msg[ANSWER_0x13_NODE_CCT_LSB]));
690  lightify_node_set_stale(node, 0);
691  ret++;
692  }
693  return ret;
694 }
695 
696 static int lightify_request_set_onoff(struct lightify_ctx *ctx, uint64_t adr, int isgroup, int onoff) {
697  unsigned char msg[32];
698  int n;
699  if (!ctx) return -EINVAL;
700 
701  /* normalize to boolean -- int are 16bits...*/
702  onoff = (onoff != 0);
703  isgroup = (isgroup) ? 2 : 0;
704 
705  uint32_t token = ++ctx->cnt;
706  fill_telegram_header(msg, QUERY_0x32_SIZE, token, isgroup, 0x32);
707 
708  msg_from_uint64(&msg[QUERY_0x32_NODEADR64_B0], adr);
709  msg[QUERY_0x32_ONOFF] = onoff;
710 
711  n = ctx->socket_write_fn(ctx,msg, QUERY_0x32_SIZE);
712  if ( n < 0 ) {
713  info(ctx,"socket_write_fn error %d\n", n);
714  return n;
715  }
716  if ( n != QUERY_0x32_SIZE) {
717  info(ctx,"short write %d!=%d\n", QUERY_0x32_SIZE, n);
718  return -EIO;
719  }
720 
721  /* read the header */
722  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x32_SIZE);
723  if (n < 0) {
724  info(ctx,"socket_read_fn error %d\n", n);
725  return n;
726  }
727  if (n != ANSWER_0x32_SIZE) {
728  info(ctx,"short read %d!=%d\n", ANSWER_0x32_SIZE, n);
729  int i = 0;
730  while(n--) {
731  info(ctx, " %d => %x\n ",i, msg[i]);
732  i++;
733  }
734  info(ctx, "\n");
735  return -EIO;
736  }
737 
738  /* check the header if plausible */
739  n = check_header_response(msg, token, 0x32);
740  if ( n < 0 ) {
741  info(ctx,"Invalid response (header)\n");
742  return n;
743  }
744 
745  /* check if the node address was echoed properly */
746  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x32_NODEADR64_B0]);
747  if (adr != adr2) {
748  info(ctx, "unexpected node mac / group adr %llx!=%llx", adr, adr2 );
749  return -EPROTO;
750  }
751 
752  n = -decode_status(msg[ANSWER_0x32_STATE]);
753  if (n) {
754  info(ctx, "state %d indicates error.\n", n);
755  }
756  return n;
757 }
758 
759 static int lightify_request_set_cct(struct lightify_ctx *ctx, uint64_t adr, int isgroup, unsigned int cct, unsigned int fadetime) {
760  unsigned char msg[32];
761  int n;
762  if (!ctx) return -EINVAL;
763 
764  uint32_t token = ++ctx->cnt;
765  isgroup = (isgroup) ? 2 : 0;
766  fill_telegram_header(msg, QUERY_0x33_SIZE, token, isgroup, 0x33);
767  msg_from_uint64(&msg[QUERY_0x33_NODEADR64_B0], adr);
768  msg[QUERY_0x33_CCT_LSB] = cct & 0xff;
769  msg[QUERY_0x33_CCT_MSB] = (cct >> 8 ) & 0xff;
770  msg[QUERY_0x33_FADETIME_LSB] = fadetime & 0xff;
771  msg[QUERY_0x33_FADETIME_MSB] = (fadetime >> 8 ) & 0xff;
772 
773  n = ctx->socket_write_fn(ctx,msg, QUERY_0x33_SIZE);
774  if ( n < 0 ) {
775  info(ctx,"socket_write_fn error %d\n", n);
776  return n;
777  }
778  if ( n != QUERY_0x33_SIZE) {
779  info(ctx,"short write %d!=%d\n", QUERY_0x33_SIZE, n);
780  return -EIO;
781  }
782 
783  /* read the header */
784  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x33_SIZE);
785  if (n < 0) {
786  info(ctx,"socket_read_fn error %d\n", n);
787  return n;
788  }
789  if (n != ANSWER_0x33_SIZE) {
790  info(ctx,"short read %d!=%d\n", ANSWER_0x33_SIZE, n);
791  return -EIO;
792  }
793 
794  /* check the header if plausible */
795  n = check_header_response(msg, token, 0x33);
796  if ( n < 0 ) {
797  info(ctx,"Invalid response (header)\n");
798  return n;
799  }
800 
801  /* check if the node address was echoed properly */
802  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x33_NODEADR64_B0]);
803  if (adr != adr2) {
804  info(ctx, "unexpected node mac / group adr %llx!=%llx", adr, adr2 );
805  return -EPROTO;
806  }
807 
808  n = -decode_status(msg[ANSWER_0x33_STATE]);
809  return n;
810 }
811 
812 static int lightify_request_set_rgbw(struct lightify_ctx *ctx, uint64_t adr,
813  int isgroup, unsigned int r, unsigned int g,
814  unsigned int b,unsigned int w,unsigned int fadetime) {
815  unsigned char msg[32];
816  int n;
817  if (!ctx) return -EINVAL;
818  /* does not support broadcast. */
819 
820  uint32_t token = ++ctx->cnt;
821  isgroup = (isgroup) ? 2 : 0;
822  fill_telegram_header(msg, QUERY_0x36_SIZE, token, isgroup, 0x36);
823  msg_from_uint64(&msg[QUERY_0x36_NODEADR64_B0], adr);
824  msg[QUERY_0x36_R] = r & 0xff;
825  msg[QUERY_0x36_G] = g & 0xff;
826  msg[QUERY_0x36_B] = b & 0xff;
827  msg[QUERY_0x36_W] = w & 0xff;
828  msg[QUERY_0x36_FADETIME_LSB] = fadetime & 0xff;
829  msg[QUERY_0x36_FADETIME_MSB] = (fadetime >> 8 ) & 0xff;
830 
831  n = ctx->socket_write_fn(ctx,msg, QUERY_0x36_SIZE);
832  if ( n < 0 ) {
833  info(ctx,"socket_write_fn error %d\n", n);
834  return n;
835  }
836  if ( n != QUERY_0x36_SIZE) {
837  info(ctx,"short write %d!=%d\n", QUERY_0x36_SIZE, n);
838  return -EIO;
839  }
840 
841  /* read the header */
842  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x36_SIZE);
843  if (n != ANSWER_0x36_SIZE) {
844  info(ctx,"short read %d!=%d\n", ANSWER_0x36_SIZE, n);
845  return -EIO;
846  }
847 
848  /* check the header if plausible */
849  n = check_header_response(msg, token, 0x36);
850  if ( n < 0 ) {
851  info(ctx,"Invalid response (header)\n");
852  return n;
853  }
854 
855  /* check if the node address was echoed properly */
856  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x33_NODEADR64_B0]);
857  if (adr != adr2) {
858  info(ctx, "unexpected node mac / group adr %llx!=%llx", adr, adr2 );
859  return -EPROTO;
860  }
861 
862  n = -decode_status(msg[ANSWER_0x36_STATE]);
863  return n;
864 }
865 
866 static int lightify_request_set_brightness(struct lightify_ctx *ctx, uint64_t adr,
867  int isgroup, unsigned int level, unsigned int fadetime) {
868  unsigned char msg[32];
869  int n;
870  if (!ctx) return -EINVAL;
871 
872  uint32_t token = ++ctx->cnt;
873  isgroup = (isgroup) ? 2 : 0;
874 
875  fill_telegram_header(msg, QUERY_0x31_SIZE, token, isgroup, 0x31);
876  msg_from_uint64(&msg[QUERY_0x31_NODEADR64_B0], adr);
877 
878  msg[QUERY_0x31_LEVEL] = level & 0xff;
879  msg[QUERY_0x31_FADETIME_LSB] = fadetime & 0xff;
880  msg[QUERY_0x31_FADETIME_MSB] = (fadetime >> 8 ) & 0xff;
881 
882  n = ctx->socket_write_fn(ctx,msg, QUERY_0x31_SIZE);
883  if ( n < 0 ) {
884  info(ctx,"socket_write_fn error %d\n", n);
885  return n;
886  }
887  if ( n != QUERY_0x31_SIZE) {
888  info(ctx,"short write %d!=%d\n", QUERY_0x31_SIZE, n);
889  return -EIO;
890  }
891 
892  /* read the header */
893  n = ctx->socket_read_fn(ctx,msg,ANSWER_0x31_SIZE);
894  if (n < 0) {
895  info(ctx,"socket_read_fn error %d\n", n);
896  return n;
897  }
898  if (n != ANSWER_0x31_SIZE) {
899  info(ctx,"short read %d!=%d\n", ANSWER_0x31_SIZE, n);
900  return -EIO;
901  }
902 
903  n = check_header_response(msg, token, 0x31);
904  if ( n < 0 ) {
905  info(ctx,"Invalid response (header)\n");
906  return n;
907  }
908 
909  /* check if the node address was echoed properly */
910  uint64_t adr2 = uint64_from_msg(&msg[ANSWER_0x33_NODEADR64_B0]);
911  if (adr != adr2) {
912  info(ctx, "unexpected node mac / group adr %llx!=%llx", adr, adr2 );
913  return -EPROTO;
914  }
915 
916  n = -decode_status(msg[ANSWER_0x31_STATE]);
917  dbg(ctx, "unknown-bytes: %x %x %x\n", msg[ANSWER_0x31_UNKNOWN1],msg[ANSWER_0x31_UNKNOWN2],msg[ANSWER_0x31_UNKNOWN3]);
918  return n;
919 }
920 
921 /* Node control */
922 LIGHTIFY_EXPORT int lightify_node_request_onoff(struct lightify_ctx *ctx, struct lightify_node *node, int onoff) {
923  if (!ctx) return -EINVAL;
924  uint64_t adr = -1;
925  if (node) adr = lightify_node_get_nodeadr(node);
926 
927  onoff = (onoff != 0);
928  int ret = lightify_request_set_onoff(ctx, adr, 0, onoff);
929 
930  if (node) {
931  lightify_node_set_onoff(node,onoff);
932  if (ret<0) {
933  lightify_node_set_stale(node,1);
934  }
935  } else {
936  node = NULL;
937  while((node = lightify_node_get_next(ctx, node))) {
938  lightify_node_set_onoff(node,onoff);
939  if (ret<0) {
940  lightify_node_set_stale(node,1);
941  }
942  }
943  }
944  return ret;
945 }
946 
947 LIGHTIFY_EXPORT int lightify_node_request_cct(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int cct, unsigned int fadetime) {
948  if (!ctx || !node ) return -EINVAL;
949  uint64_t adr = lightify_node_get_nodeadr(node);
950  int ret = lightify_request_set_cct(ctx, adr, 0 , cct, fadetime);
951 
952  lightify_node_set_cct(node, cct);
953  if (ret<0) {
954  lightify_node_set_stale(node,1);
955  }
956  return ret;
957 }
958 
959 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)
960 {
961  if (!ctx || !node ) return -EINVAL;
962  uint64_t adr = lightify_node_get_nodeadr(node);
963  int ret = lightify_request_set_rgbw(ctx, adr, 0, r, g ,b ,w ,fadetime);
964 
965  lightify_node_set_red(node, r);
966  lightify_node_set_green(node, g);
967  lightify_node_set_blue(node, b);
968  lightify_node_set_white(node, w);
969  if (ret<0) {
970  lightify_node_set_stale(node,1);
971  }
972  return ret;
973 }
974 
975 LIGHTIFY_EXPORT int lightify_node_request_brightness(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int level, unsigned int fadetime) {
976  if (!ctx || !node ) return -EINVAL;
977  uint64_t adr = lightify_node_get_nodeadr(node);
978  int ret = lightify_request_set_brightness(ctx, adr, 0, level, fadetime);
979  lightify_node_set_brightness(node, level);
980  lightify_node_set_onoff(node, level!=0);
981  if (ret<0) {
982  lightify_node_set_stale(node,1);
983  }
984  return ret;
985 }
986 
988  struct lightify_node *node) {
989 
990  unsigned char msg[ANSWER_0x68_SIZE+2];
991  int n;
992  int read_size;
993 
994  if (!ctx) return -EINVAL;
995  if (!node)return -EINVAL;
996 
997  uint64_t node_adr = lightify_node_get_nodeadr(node);
998  uint32_t token = ++ctx->cnt;
999  fill_telegram_header(msg, QUERY_0x68_SIZE, token, 0x00, 0x68);
1000  msg_from_uint64(&msg[QUERY_0x68_NODEADR64_B0], node_adr);
1001 
1002  n = ctx->socket_write_fn(ctx,msg, QUERY_0x68_SIZE);
1003  if ( n < 0 ) {
1004  info(ctx,"socket_write_fn error %d\n", n);
1005  return n;
1006  }
1007  if ( n != QUERY_0x68_SIZE) {
1008  info(ctx,"short write %d!=%d\n", QUERY_0x68_SIZE, n);
1009  return -EIO;
1010  }
1011 
1012  /* read the header incl. no of nodes and the byte that seems to be the status */
1013  n = ctx->socket_read_fn(ctx,msg, ANSWER_0x68_ONLINESTATE);
1014  if (n < 0) {
1015  info(ctx,"socket_read_fn error %d\n", n);
1016  return n;
1017  }
1018  if (n != ANSWER_0x68_ONLINESTATE) {
1019  info(ctx,"header short read %d!=%d\n", ANSWER_0x68_ONLINESTATE, n);
1020  return -EIO;
1021  }
1022 
1023  n = check_header_response(msg, token, 0x68);
1024  if ( n < 0 ) {
1025  info(ctx,"Invalid response (header)\n");
1026  return n;
1027  }
1028 
1029  /* no of nodes must be 1*/
1030  n = msg[ANSWER_0x68_NONODES_MSB] <<8U | msg[ANSWER_0x68_NONODES_LSB];
1031  if (n != 1) {
1032  dbg_proto(ctx, "Node count expected 1 but is %u\n", (unsigned int)n);
1033  return -EPROTO;
1034  }
1035 
1036  /* check if the node address was echoed properly */
1037  if (node_adr != uint64_from_msg(&msg[ANSWER_0x68_NODEADR64_B0])) {
1038  dbg_proto(ctx, "Node address not matching! %lx != %lx\n",
1039  node_adr, uint64_from_msg(&msg[ANSWER_0x68_NODEADR64_B0]));
1040  return -EPROTO;
1041  }
1042 
1043  if (msg[ANSWER_0x68_REQUEST_STATUS] != 0) {
1044  /* node did not answer or some other error occurred (?) */
1045  dbg_proto(ctx, "Node Status not equal 0 but %u\n",msg[ANSWER_0x68_REQUEST_STATUS]);
1046  lightify_node_set_stale(node, 1);
1047  return -ENODATA;
1048  }
1049 
1050  if (ctx->gw_protocol_version == GW_PROT_OLD) {
1052  } else {
1054  }
1055 
1056  n = ctx->socket_read_fn(ctx,&msg[ANSWER_0x68_ONLINESTATE],read_size);
1057  if (n < 0) {
1058  info(ctx,"socket_read_fn error %d\n", n);
1059  return n;
1060  }
1061  if (n != read_size) {
1062  info(ctx,"body short read %d!=%d\n", read_size, n);
1063  return -EIO;
1064  }
1065 
1066  /* update node information */
1067  lightify_node_set_online_status(node,msg[ANSWER_0x68_ONLINESTATE]);
1068  lightify_node_set_onoff(node,msg[ANSWER_0x68_ONOFF] != 0 );
1070  n = msg[ANSWER_0x68_CCT_LSB] | msg[ANSWER_0x68_CCT_MSB] << 8;
1071  lightify_node_set_cct(node,n);
1076 
1077  n = -decode_status(msg[ANSWER_0x68_STATE]);
1078  lightify_node_set_stale(node, (n!=0));
1079  return n;
1080 }
1081 
1083  int n,m;
1084  int no_of_grps;
1085  uint32_t token;
1086  int ret;
1087 
1088  if (!ctx) return -EINVAL;
1089 
1090  /* if using standard I/O functions, fd must be valid. If the user overrode those function,
1091  we won't care */
1092  if (ctx->socket_read_fn == read_from_socket &&
1093  ctx->socket_write_fn == write_to_socket && ctx->socket < 0) {
1094  return -EBADF;
1095  }
1096 
1097  /* remove old group information */
1098  free_all_groups(ctx);
1099 
1100  token = ++ctx->cnt;
1101 
1102  /* to avoid problems with packing, we need to use a char array.
1103  * to assist we'll have this fine enum */
1104  uint8_t msg[ANSWER_0x1e_GRP_LENGHT];
1105 
1106  /* 0x1e command to get all groups. */
1107  fill_telegram_header(msg, QUERY_0x1e_SIZE, token, 0x00, 0x1e);
1108 
1109  n = ctx->socket_write_fn(ctx, msg, QUERY_0x1e_SIZE);
1110  if ( n < 0 ) {
1111  info(ctx,"socket_write_fn error %d\n", n);
1112  return n;
1113  }
1114  if ( n != QUERY_0x1e_SIZE) {
1115  info(ctx,"short write %d!=%d\n", QUERY_0x1e_SIZE, n);
1116  return -EIO;
1117  }
1118 
1119  /* read the header */
1120  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x1e_SIZE);
1121  if (n < 0) {
1122  info(ctx,"socket_read_fn error %d\n", n);
1123  return n;
1124  }
1125  if (n != ANSWER_0x1e_SIZE) {
1126  info(ctx,"short read %d!=%d\n", ANSWER_0x1e_SIZE, n);
1127  return -EIO;
1128  }
1129 
1130  /* check the header if plausible */
1131  /* check if the token we've supplied is also the returned one. */
1132  n = check_header_response(msg, token, 0x1e);
1133  if ( n < 0 ) {
1134  info(ctx,"Invalid response (header)\n");
1135  return n;
1136  }
1137 
1138  /* check if the message length is as expected */
1139  no_of_grps = msg[ANSWER_0x1e_NUMGROUPS];
1140  m = msg[HEADER_LEN_LSB] | (msg[HEADER_LEN_MSB] << 8);
1141  info(ctx, "0x1e: received %d bytes\n",m);
1142  if ( no_of_grps * ANSWER_0x1e_GRP_LENGHT + ANSWER_0x1e_SIZE - 2 != m) {
1143  info(ctx, "Response len unexpected for %d groups: %d!=%d.\n", no_of_grps,
1144  no_of_grps * ANSWER_0x1e_GRP_LENGHT + ANSWER_0x1e_SIZE - 2, m);
1145  return -EPROTO;
1146  }
1147 
1148  if (msg[HEADER_PAYLOAD_START]) {
1149  info(ctx, "strange byte at PAYLOAD_START: %d\n", msg[HEADER_PAYLOAD_START]);
1150  }
1151 
1152  ret = 0;
1153  /* read each node..*/
1154  while(no_of_grps--) {
1155  struct lightify_group *group = NULL;
1156  n = ctx->socket_read_fn(ctx, msg, ANSWER_0x1e_GRP_LENGHT);
1157  if (n< 0) return n;
1158  if (ANSWER_0x1e_GRP_LENGHT != n ) {
1159  info(ctx,"read group info: short read %d!=%d\n", ANSWER_0x1e_GRP_LENGHT, n);
1160  return -EIO;
1161  }
1162 
1163  n = lightify_group_new(ctx,&group);
1164  if (n < 0) {
1165  info(ctx, "create group error %d", n);
1166  return n;
1167  }
1168 
1171  ret++;
1172  }
1173  return ret;
1174 }
1175 
1176 
1177 /* Group control */
1178 LIGHTIFY_EXPORT int lightify_group_request_onoff(struct lightify_ctx *ctx, struct lightify_group *group, int onoff) {
1179  if (!ctx || !group) return -EINVAL;
1180 
1181  onoff = (onoff != 0);
1182  int ret = lightify_request_set_onoff(ctx, lightify_group_get_id(group), 1, onoff);
1183 
1184  struct lightify_node *node = NULL;
1185  while ( (node = lightify_group_get_next_node(group,node))) {
1186  lightify_node_set_onoff(node, onoff);
1187  if (ret < 0 ) lightify_node_set_stale(node, 1);
1188  }
1189  return ret;
1190 }
1191 
1192 LIGHTIFY_EXPORT int lightify_group_request_cct(struct lightify_ctx *ctx, struct lightify_group *group, unsigned int cct, unsigned int fadetime) {
1193  if (!ctx || !group) return -EINVAL;
1194 
1195  int ret = lightify_request_set_cct(ctx, lightify_group_get_id(group), 1, cct, fadetime);
1196 
1197  struct lightify_node *node = NULL;
1198  while ( (node = lightify_group_get_next_node(group,node))) {
1199  lightify_node_set_cct(node, cct);
1200  if (ret < 0 ) lightify_node_set_stale(node, 1);
1201  }
1202  return ret;
1203 }
1204 
1206  struct lightify_group *group, unsigned int r, unsigned int g,
1207  unsigned int b,unsigned int w,unsigned int fadetime) {
1208  if (!ctx || !group) return -EINVAL;
1209 
1210  int ret = lightify_request_set_rgbw(ctx, lightify_group_get_id(group), 1, r, g, b, w , fadetime);
1211 
1212  struct lightify_node *node = NULL;
1213  while ( (node = lightify_group_get_next_node(group,node))) {
1214  lightify_node_set_red(node, r);
1215  lightify_node_set_green(node, g);
1216  lightify_node_set_blue(node, b);
1217  lightify_node_set_white(node, w);
1218  if (ret < 0 ) lightify_node_set_stale(node, 1);
1219  }
1220  return ret;
1221 }
1222 
1224  struct lightify_group *group, unsigned int level, unsigned int fadetime) {
1225  if (!ctx || !group) return -EINVAL;
1226 
1227  int ret = lightify_request_set_brightness(ctx, lightify_group_get_id(group), 1, level , fadetime);
1228 
1229  struct lightify_node *node = NULL;
1230  while ( (node = lightify_group_get_next_node(group,node))) {
1231  lightify_node_set_brightness(node, level);
1232  lightify_node_set_onoff(node, level!=0);
1233  if (ret < 0 ) lightify_node_set_stale(node, 1);
1234  }
1235  return ret;
1236 }
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:959
msg_0x36_answer
Definition: context.c:222
uint32_t cnt
Definition: context.h:96
LIGHTIFY_EXPORT int lightify_free(struct lightify_ctx *ctx)
Definition: context.c:494
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:505
#define info(ctx, arg...)
msg_header
Definition: context.c:48
int(* read_from_socket_fn)(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: liblightify.h:168
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:1178
LIGHTIFY_EXPORT int lightify_node_request_onoff(struct lightify_ctx *ctx, struct lightify_node *node, int onoff)
Definition: context.c:922
int lightify_node_remove(struct lightify_node *node)
Definition: node.c:117
#define GW_PROT_1512
Definition: context.h:52
msg_0x68_answer
Definition: context.c:250
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:1082
int write_to_socket(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: socket.c:45
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:74
struct lightify_node * nodes
Definition: context.h:90
int lightify_node_set_brightness(struct lightify_node *node, int brightness)
Definition: node.c:268
msg_0x1e_answer
Definition: context.c:283
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:417
LIGHTIFY_EXPORT int lightify_group_request_cct(struct lightify_ctx *ctx, struct lightify_group *group, unsigned int cct, unsigned int fadetime)
Definition: context.c:1192
LIGHTIFY_EXPORT int lightify_group_get_id(struct lightify_group *grp)
Definition: groups.c:128
int fadetime
Definition: lightify-util.c:99
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:430
void * userdata
Function pointer to the I/O handling – write to.
Definition: context.h:82
msg_0x33_answer
Definition: context.c:187
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:402
#define dbg(ctx, arg...)
int(* write_to_socket_fn)(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: liblightify.h:150
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:99
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:77
#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:1223
int lightify_node_set_blue(struct lightify_node *node, int blue)
Definition: node.c:224
msg_0x13_answer_node
Definition: context.c:72
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 gw_protocol_version
Definition: context.h:102
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:84
msg_0x33_query
Definition: context.c:171
LIGHTIFY_EXPORT int lightify_node_request_brightness(struct lightify_ctx *ctx, struct lightify_node *node, unsigned int level, unsigned int fadetime)
Definition: context.c:975
LIGHTIFY_EXPORT int lightify_set_userdata(struct lightify_ctx *ctx, void *userdata)
Definition: context.c:423
msg_0x13_answer
Definition: context.c:65
msg_0x32_query
Definition: context.c:142
LIGHTIFY_EXPORT struct lightify_node * lightify_node_get_previous(struct lightify_ctx *ctx, struct lightify_node *node)
Definition: context.c:410
msg_0x31_query
Definition: context.c:111
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:70
msg_0x36_query
Definition: context.c:204
msg_0x68_query
Definition: context.c:238
LIGHTIFY_EXPORT int lightify_new(struct lightify_ctx **ctx, void *reserved)
Definition: context.c:445
int read_from_socket(struct lightify_ctx *ctx, unsigned char *msg, size_t size)
Definition: socket.c:121
msg_0x1e_answerpergroup
Definition: context.c:290
#define ENODATA
Definition: context.c:44
int lightify_node_set_nodeadr(struct lightify_node *node, uint64_t adr)
Definition: node.c:169
msg_0x13_query
Definition: context.c:60
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:314
#define dbg_proto(ctx, arg...)
msg_0x32_answer
Definition: context.c:155
int log_priority(const char *priority)
Definition: log.c:62
#define GW_PROT_OLD
Definition: context.h:50
LIGHTIFY_EXPORT int lightify_node_request_update(struct lightify_ctx *ctx, struct lightify_node *node)
Definition: context.c:987
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:947
msg_0x1e_query
Definition: context.c:278
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:126
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:1205
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:93
int socket
Definition: context.h:87