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