go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/experiments/huectl/pkg/hue/bridge.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package hue 9 10 import ( 11 "context" 12 "encoding/json" 13 "errors" 14 "fmt" 15 "net/url" 16 "path" 17 "strconv" 18 "strings" 19 ) 20 21 // Bridge exposes a hardware bridge through a struct. 22 type Bridge struct { 23 ID string `json:"id,omitempty"` 24 Addr string `json:"internalipaddress,omitempty"` 25 Username string `json:"user,omitempty"` 26 } 27 28 func (b Bridge) getHostURL(pathSegments ...string) (*url.URL, error) { 29 // if the "host" parameter doesn't have a schema, add it 30 if strings.Index(strings.ToLower(b.Addr), "http://") <= -1 && strings.Index(strings.ToLower(b.Addr), "https://") <= -1 { 31 b.Addr = fmt.Sprintf("%s%s", "http://", b.Addr) 32 } 33 34 u, err := url.Parse(b.Addr) 35 if err != nil { 36 return nil, err 37 } 38 u.Path = path.Join(append([]string{ 39 u.Path, 40 "/api/", 41 b.Username, 42 }, pathSegments...)...) 43 return u, nil 44 } 45 46 // Login calls New() and passes Host on this Bridge instance. 47 func (b Bridge) Login(username string) Bridge { 48 b.Username = username 49 return b 50 } 51 52 // GetConfig returns the bridge configuration 53 func (b Bridge) GetConfig(ctx context.Context) (*Config, error) { 54 url, err := b.getHostURL("/config/") 55 if err != nil { 56 return nil, err 57 } 58 59 res, err := get(ctx, url.String()) 60 if err != nil { 61 return nil, err 62 } 63 64 var config Config 65 err = unmarshal(res, &config) 66 if err != nil { 67 return nil, err 68 } 69 70 config.Whitelist = make([]Whitelist, 0, len(config.WhitelistMap)) 71 for k, v := range config.WhitelistMap { 72 v.Username = k 73 config.Whitelist = append(config.Whitelist, v) 74 } 75 return &config, nil 76 } 77 78 // CreateUser creates a user by adding n to the list of whitelists in the bridge. 79 // The link button on the bridge must have been pressed before calling CreateUser. 80 func (b Bridge) CreateUser(ctx context.Context, n string) (string, error) { 81 wl, err := b.createUser(ctx, n, false) 82 if err != nil { 83 return "", err 84 } 85 86 return wl.Username, nil 87 } 88 89 // CreateUserWithClientKey creates a user by adding deviceType to the list of whitelisted users on the bridge 90 // The link button on the bridge must have been pressed before calling CreateUser. 91 func (b Bridge) CreateUserWithClientKey(ctx context.Context, deviceType string) (*Whitelist, error) { 92 return b.createUser(ctx, deviceType, true) 93 } 94 95 func (b Bridge) createUser(ctx context.Context, deviceType string, generateClientKey bool) (*Whitelist, error) { 96 var a []*APIResponse 97 98 body := struct { 99 DeviceType string `json:"devicetype,omitempty"` 100 GenerateClientKey bool `json:"generateclientkey,omitempty"` 101 }{deviceType, generateClientKey} 102 103 url, err := b.getHostURL("/") 104 if err != nil { 105 return nil, err 106 } 107 108 data, err := json.Marshal(&body) 109 if err != nil { 110 return nil, err 111 } 112 113 res, err := post(ctx, url.String(), data) 114 if err != nil { 115 return nil, err 116 } 117 118 err = unmarshal(res, &a) 119 if err != nil { 120 return nil, err 121 } 122 123 resp, err := handleResponse(a) 124 if err != nil { 125 return nil, err 126 } 127 128 wl := Whitelist{ 129 Name: deviceType, 130 Username: resp.Success["username"].(string), 131 } 132 133 if ck, ok := resp.Success["clientkey"]; ok { 134 wl.ClientKey = ck.(string) 135 } else if generateClientKey { 136 return nil, errors.New("no client key was returned when requested to generate") 137 } 138 139 return &wl, nil 140 } 141 142 // GetUsers returns a list of whitelists from the bridge 143 func (b Bridge) GetUsers(ctx context.Context) ([]Whitelist, error) { 144 c, err := b.GetConfig(ctx) 145 if err != nil { 146 return nil, err 147 } 148 return c.Whitelist, nil 149 } 150 151 // UpdateConfig updates the bridge configuration with c 152 func (b Bridge) UpdateConfig(ctx context.Context, c *Config) (*Response, error) { 153 var a []*APIResponse 154 url, err := b.getHostURL("/config/") 155 if err != nil { 156 return nil, err 157 } 158 159 data, err := json.Marshal(&c) 160 if err != nil { 161 return nil, err 162 } 163 164 res, err := put(ctx, url.String(), data) 165 if err != nil { 166 return nil, err 167 } 168 169 err = unmarshal(res, &a) 170 if err != nil { 171 return nil, err 172 } 173 174 resp, err := handleResponse(a) 175 if err != nil { 176 return nil, err 177 } 178 179 return resp, nil 180 } 181 182 // DeleteUser removes a whitelist item from whitelists on the bridge 183 func (b Bridge) DeleteUser(ctx context.Context, n string) error { 184 var a []*APIResponse 185 186 url, err := b.getHostURL("/config/whitelist/", n) 187 if err != nil { 188 return err 189 } 190 191 res, err := delete(ctx, url.String()) 192 if err != nil { 193 return err 194 } 195 196 _ = unmarshal(res, &a) 197 198 _, err = handleResponse(a) 199 if err != nil { 200 return err 201 } 202 203 return nil 204 205 } 206 207 // GetFullState returns the entire bridge configuration. 208 func (b Bridge) GetFullState(ctx context.Context) (map[string]interface{}, error) { 209 var n map[string]interface{} 210 211 url, err := b.getHostURL("/") 212 if err != nil { 213 return nil, err 214 } 215 216 res, err := get(ctx, url.String()) 217 if err != nil { 218 return nil, err 219 } 220 221 err = unmarshal(res, &n) 222 if err != nil { 223 return nil, err 224 } 225 226 return n, nil 227 } 228 229 // GetGroups returns all groups known to the bridge 230 func (b Bridge) GetGroups(ctx context.Context) ([]Group, error) { 231 var m map[string]Group 232 233 url, err := b.getHostURL("/groups/") 234 if err != nil { 235 return nil, err 236 } 237 238 res, err := get(ctx, url.String()) 239 if err != nil { 240 return nil, err 241 } 242 243 err = unmarshal(res, &m) 244 if err != nil { 245 return nil, err 246 } 247 248 groups := make([]Group, 0, len(m)) 249 250 for i, g := range m { 251 g.ID, err = strconv.Atoi(i) 252 if err != nil { 253 return nil, err 254 } 255 g.bridge = b 256 groups = append(groups, g) 257 } 258 259 return groups, err 260 } 261 262 // GetGroup returns one group known to the bridge by its id 263 func (b Bridge) GetGroup(ctx context.Context, i int) (*Group, error) { 264 g := &Group{ 265 bridge: b, 266 ID: i, 267 } 268 269 url, err := b.getHostURL("/groups/", strconv.Itoa(i)) 270 if err != nil { 271 return nil, err 272 } 273 274 res, err := get(ctx, url.String()) 275 if err != nil { 276 return nil, err 277 } 278 279 err = unmarshal(res, &g) 280 if err != nil { 281 return nil, err 282 } 283 284 g.bridge = b 285 286 return g, nil 287 } 288 289 // SetGroupState allows for setting the state of one group, controlling the state of all lights in that group. 290 func (b Bridge) SetGroupState(ctx context.Context, i int, l LightState) (*Response, error) { 291 var a []*APIResponse 292 293 id := strconv.Itoa(i) 294 url, err := b.getHostURL("/groups/", id, "/action/") 295 if err != nil { 296 return nil, err 297 } 298 299 data, err := json.Marshal(&l) 300 if err != nil { 301 return nil, err 302 } 303 304 res, err := put(ctx, url.String(), data) 305 if err != nil { 306 return nil, err 307 } 308 309 err = unmarshal(res, &a) 310 if err != nil { 311 return nil, err 312 } 313 314 resp, err := handleResponse(a) 315 if err != nil { 316 return nil, err 317 } 318 319 return resp, nil 320 } 321 322 // UpdateGroup updates one group known to the bridge 323 func (b Bridge) UpdateGroup(ctx context.Context, i int, l Group) (*Response, error) { 324 325 var a []*APIResponse 326 327 id := strconv.Itoa(i) 328 url, err := b.getHostURL("/groups/", id) 329 if err != nil { 330 return nil, err 331 } 332 333 data, err := json.Marshal(&l) 334 if err != nil { 335 return nil, err 336 } 337 338 res, err := put(ctx, url.String(), data) 339 if err != nil { 340 return nil, err 341 } 342 343 err = unmarshal(res, &a) 344 if err != nil { 345 return nil, err 346 } 347 348 resp, err := handleResponse(a) 349 if err != nil { 350 return nil, err 351 } 352 353 return resp, nil 354 } 355 356 // CreateGroup creates one new group with attributes defined by g 357 func (b Bridge) CreateGroup(ctx context.Context, g Group) (*Response, error) { 358 var a []*APIResponse 359 360 url, err := b.getHostURL("/groups/") 361 if err != nil { 362 return nil, err 363 } 364 365 data, err := json.Marshal(&g) 366 if err != nil { 367 return nil, err 368 } 369 370 res, err := post(ctx, url.String(), data) 371 if err != nil { 372 return nil, err 373 } 374 375 err = unmarshal(res, &a) 376 if err != nil { 377 return nil, err 378 } 379 380 resp, err := handleResponse(a) 381 if err != nil { 382 return nil, err 383 } 384 385 return resp, nil 386 } 387 388 // DeleteGroup deletes one group with the id of i 389 func (b Bridge) DeleteGroup(ctx context.Context, i int) error { 390 var a []*APIResponse 391 392 id := strconv.Itoa(i) 393 url, err := b.getHostURL("/groups/", id) 394 if err != nil { 395 return err 396 } 397 398 res, err := delete(ctx, url.String()) 399 if err != nil { 400 return err 401 } 402 403 _ = unmarshal(res, &a) 404 405 _, err = handleResponse(a) 406 if err != nil { 407 return err 408 } 409 410 return nil 411 } 412 413 // GetLights returns all lights known to the bridge 414 func (b Bridge) GetLights(ctx context.Context) ([]Light, error) { 415 m := map[string]Light{} 416 417 url, err := b.getHostURL("/lights/") 418 if err != nil { 419 return nil, err 420 } 421 422 res, err := get(ctx, url.String()) 423 if err != nil { 424 return nil, err 425 } 426 427 err = unmarshal(res, &m) 428 if err != nil { 429 return nil, err 430 } 431 432 lights := make([]Light, 0, len(m)) 433 434 for i, l := range m { 435 l.ID, err = strconv.Atoi(i) 436 if err != nil { 437 return nil, err 438 } 439 l.bridge = b 440 lights = append(lights, l) 441 } 442 443 return lights, nil 444 } 445 446 // GetLight returns one light with the id of i 447 func (b Bridge) GetLight(ctx context.Context, id int) (*Light, error) { 448 light := &Light{ 449 ID: id, 450 } 451 452 url, err := b.getHostURL("/lights/", strconv.Itoa(id)) 453 if err != nil { 454 return nil, err 455 } 456 457 res, err := get(ctx, url.String()) 458 if err != nil { 459 return light, err 460 } 461 462 err = unmarshal(res, &light) 463 if err != nil { 464 return light, err 465 } 466 467 light.bridge = b 468 return light, nil 469 } 470 471 // IdentifyLight allows identifying a light 472 func (b Bridge) IdentifyLight(ctx context.Context, i int) (*Response, error) { 473 var a []*APIResponse 474 475 url, err := b.getHostURL("/lights/", strconv.Itoa(i), "/state") 476 if err != nil { 477 return nil, err 478 } 479 res, err := put(ctx, url.String(), []byte(`{"alert":"select"}`)) 480 if err != nil { 481 return nil, err 482 } 483 484 err = unmarshal(res, &a) 485 if err != nil { 486 return nil, err 487 } 488 489 resp, err := handleResponse(a) 490 if err != nil { 491 return nil, err 492 } 493 494 return resp, nil 495 } 496 497 // SetLightState allows for controlling one light's state 498 func (b Bridge) SetLightState(ctx context.Context, i int, l LightState) (*Response, error) { 499 var a []*APIResponse 500 501 l.Reachable = false 502 l.ColorMode = "" 503 504 data, err := json.Marshal(&l) 505 if err != nil { 506 return nil, err 507 } 508 509 url, err := b.getHostURL("/lights/", strconv.Itoa(i), "/state") 510 if err != nil { 511 return nil, err 512 } 513 res, err := put(ctx, url.String(), data) 514 if err != nil { 515 return nil, err 516 } 517 518 err = unmarshal(res, &a) 519 if err != nil { 520 return nil, err 521 } 522 523 resp, err := handleResponse(a) 524 if err != nil { 525 return nil, err 526 } 527 528 return resp, nil 529 } 530 531 // FindLights starts a search for new lights on the bridge. 532 // 533 // Use GetNewLights() verify if new lights have been detected. 534 func (b Bridge) FindLights(ctx context.Context) (*Response, error) { 535 var a []*APIResponse 536 537 url, err := b.getHostURL("/lights/") 538 if err != nil { 539 return nil, err 540 } 541 542 res, err := post(ctx, url.String(), nil) 543 if err != nil { 544 return nil, err 545 } 546 547 err = unmarshal(res, &a) 548 if err != nil { 549 return nil, err 550 } 551 552 resp, err := handleResponse(a) 553 if err != nil { 554 return nil, err 555 } 556 557 return resp, nil 558 } 559 560 // GetNewLights returns a list of lights that were discovered last time FindLights() was executed. 561 func (b Bridge) GetNewLights(ctx context.Context) (*Lights, error) { 562 var n map[string]interface{} 563 564 url, err := b.getHostURL("/lights/new") 565 if err != nil { 566 return nil, err 567 } 568 569 res, err := get(ctx, url.String()) 570 if err != nil { 571 return nil, err 572 } 573 574 _ = unmarshal(res, &n) 575 576 lights := make([]string, 0, len(n)) 577 var lastscan string 578 579 for k := range n { 580 if k == "lastscan" { 581 lastscan = n[k].(string) 582 } else { 583 lights = append(lights, k) 584 } 585 } 586 587 result := &Lights{ 588 Lights: lights, 589 LastScan: lastscan, 590 } 591 return result, nil 592 } 593 594 // DeleteLight deletes one lights from the bridge 595 func (b Bridge) DeleteLight(ctx context.Context, i int) error { 596 var a []*APIResponse 597 598 id := strconv.Itoa(i) 599 url, err := b.getHostURL("/lights/", id) 600 if err != nil { 601 return err 602 } 603 604 res, err := delete(ctx, url.String()) 605 if err != nil { 606 return err 607 } 608 609 _ = unmarshal(res, &a) 610 611 _, err = handleResponse(a) 612 if err != nil { 613 return err 614 } 615 616 return nil 617 } 618 619 // UpdateLight updates one light's attributes and state properties 620 func (b Bridge) UpdateLight(ctx context.Context, i int, light Light) (*Response, error) { 621 var a []*APIResponse 622 623 id := strconv.Itoa(i) 624 url, err := b.getHostURL("/lights/", id) 625 if err != nil { 626 return nil, err 627 } 628 629 data, err := json.Marshal(&light) 630 if err != nil { 631 return nil, err 632 } 633 634 res, err := put(ctx, url.String(), data) 635 if err != nil { 636 return nil, err 637 } 638 639 err = unmarshal(res, &a) 640 if err != nil { 641 return nil, err 642 } 643 644 resp, err := handleResponse(a) 645 if err != nil { 646 return nil, err 647 } 648 649 return resp, nil 650 } 651 652 // GetResourcelinks returns all resourcelinks known to the bridge 653 func (b Bridge) GetResourcelinks(ctx context.Context) ([]*Resourcelink, error) { 654 var r map[string]Resourcelink 655 656 url, err := b.getHostURL("/resourcelinks/") 657 if err != nil { 658 return nil, err 659 } 660 661 res, err := get(ctx, url.String()) 662 if err != nil { 663 return nil, err 664 } 665 666 err = unmarshal(res, &r) 667 if err != nil { 668 return nil, err 669 } 670 671 resourcelinks := make([]*Resourcelink, 0, len(r)) 672 673 for i, s := range r { 674 s.ID, err = strconv.Atoi(i) 675 if err != nil { 676 return nil, err 677 } 678 sCopy := s 679 resourcelinks = append(resourcelinks, &sCopy) 680 } 681 682 return resourcelinks, nil 683 } 684 685 // GetResourcelink returns one resourcelink by its id defined by i 686 func (b Bridge) GetResourcelink(ctx context.Context, i int) (*Resourcelink, error) { 687 g := &Resourcelink{ 688 ID: i, 689 } 690 691 url, err := b.getHostURL("/resourcelinks/", strconv.Itoa(i)) 692 if err != nil { 693 return nil, err 694 } 695 696 res, err := get(ctx, url.String()) 697 if err != nil { 698 return nil, err 699 } 700 701 err = unmarshal(res, &g) 702 if err != nil { 703 return nil, err 704 } 705 706 return g, nil 707 } 708 709 // CreateResourcelink creates one new resourcelink on the bridge 710 func (b Bridge) CreateResourcelink(ctx context.Context, s *Resourcelink) (*Response, error) { 711 var a []*APIResponse 712 713 data, err := json.Marshal(&s) 714 if err != nil { 715 return nil, err 716 } 717 718 url, err := b.getHostURL("/resourcelinks/") 719 if err != nil { 720 return nil, err 721 } 722 723 res, err := post(ctx, url.String(), data) 724 if err != nil { 725 return nil, err 726 } 727 728 err = unmarshal(res, &a) 729 if err != nil { 730 return nil, err 731 } 732 733 resp, err := handleResponse(a) 734 if err != nil { 735 return nil, err 736 } 737 738 return resp, nil 739 } 740 741 // UpdateResourcelink updates one resourcelink with attributes defined by resourcelink 742 func (b Bridge) UpdateResourcelink(ctx context.Context, i int, resourcelink *Resourcelink) (*Response, error) { 743 var a []*APIResponse 744 745 data, err := json.Marshal(&resourcelink) 746 if err != nil { 747 return nil, err 748 } 749 750 url, err := b.getHostURL("/resourcelinks/", strconv.Itoa(i)) 751 if err != nil { 752 return nil, err 753 } 754 755 res, err := put(ctx, url.String(), data) 756 if err != nil { 757 return nil, err 758 } 759 760 err = unmarshal(res, &a) 761 if err != nil { 762 return nil, err 763 } 764 765 resp, err := handleResponse(a) 766 if err != nil { 767 return nil, err 768 } 769 770 return resp, nil 771 } 772 773 // DeleteResourcelink deletes one resourcelink with the id of i 774 func (b Bridge) DeleteResourcelink(ctx context.Context, i int) error { 775 var a []*APIResponse 776 777 id := strconv.Itoa(i) 778 url, err := b.getHostURL("/resourcelinks/", id) 779 if err != nil { 780 return err 781 } 782 783 res, err := delete(ctx, url.String()) 784 if err != nil { 785 return err 786 } 787 788 _ = unmarshal(res, &a) 789 790 _, err = handleResponse(a) 791 if err != nil { 792 return err 793 } 794 795 return nil 796 } 797 798 // GetRules returns all rules known to the bridge 799 func (b Bridge) GetRules(ctx context.Context) ([]*Rule, error) { 800 var r map[string]Rule 801 802 url, err := b.getHostURL("/rules/") 803 if err != nil { 804 return nil, err 805 } 806 807 res, err := get(ctx, url.String()) 808 if err != nil { 809 return nil, err 810 } 811 812 err = unmarshal(res, &r) 813 if err != nil { 814 return nil, err 815 } 816 817 rules := make([]*Rule, 0, len(r)) 818 819 for i, s := range r { 820 s.ID, err = strconv.Atoi(i) 821 if err != nil { 822 return nil, err 823 } 824 sCopy := s 825 rules = append(rules, &sCopy) 826 } 827 828 return rules, nil 829 } 830 831 // GetRule returns one rule by its id of i 832 func (b Bridge) GetRule(ctx context.Context, i int) (*Rule, error) { 833 g := &Rule{ 834 ID: i, 835 } 836 837 url, err := b.getHostURL("/rules/", strconv.Itoa(i)) 838 if err != nil { 839 return nil, err 840 } 841 842 res, err := get(ctx, url.String()) 843 if err != nil { 844 return nil, err 845 } 846 847 err = unmarshal(res, &g) 848 if err != nil { 849 return nil, err 850 } 851 852 return g, nil 853 } 854 855 // CreateRule creates one rule with attribues defined in s 856 func (b Bridge) CreateRule(ctx context.Context, s *Rule) (*Response, error) { 857 var a []*APIResponse 858 859 data, err := json.Marshal(&s) 860 if err != nil { 861 return nil, err 862 } 863 864 url, err := b.getHostURL("/rules/") 865 if err != nil { 866 return nil, err 867 } 868 869 res, err := post(ctx, url.String(), data) 870 if err != nil { 871 return nil, err 872 } 873 874 err = unmarshal(res, &a) 875 if err != nil { 876 return nil, err 877 } 878 879 resp, err := handleResponse(a) 880 if err != nil { 881 return nil, err 882 } 883 884 return resp, nil 885 } 886 887 // UpdateRule updates one rule by its id of i and rule configuration of rule 888 func (b Bridge) UpdateRule(ctx context.Context, i int, rule *Rule) (*Response, error) { 889 var a []*APIResponse 890 891 data, err := json.Marshal(&rule) 892 if err != nil { 893 return nil, err 894 } 895 896 url, err := b.getHostURL("/rules/", strconv.Itoa(i)) 897 if err != nil { 898 return nil, err 899 } 900 901 res, err := put(ctx, url.String(), data) 902 if err != nil { 903 return nil, err 904 } 905 906 err = unmarshal(res, &a) 907 if err != nil { 908 return nil, err 909 } 910 911 resp, err := handleResponse(a) 912 if err != nil { 913 return nil, err 914 } 915 916 return resp, nil 917 } 918 919 // DeleteRule deletes one rule from the bridge 920 func (b Bridge) DeleteRule(ctx context.Context, i int) error { 921 var a []*APIResponse 922 923 id := strconv.Itoa(i) 924 url, err := b.getHostURL("/rules/", id) 925 if err != nil { 926 return err 927 } 928 929 res, err := delete(ctx, url.String()) 930 if err != nil { 931 return err 932 } 933 934 _ = unmarshal(res, &a) 935 936 _, err = handleResponse(a) 937 if err != nil { 938 return err 939 } 940 941 return nil 942 } 943 944 // GetScenes returns all scenes known to the bridge 945 func (b Bridge) GetScenes(ctx context.Context) ([]Scene, error) { 946 var m map[string]Scene 947 948 url, err := b.getHostURL("/scenes/") 949 if err != nil { 950 return nil, err 951 } 952 953 res, err := get(ctx, url.String()) 954 if err != nil { 955 return nil, err 956 } 957 958 err = unmarshal(res, &m) 959 if err != nil { 960 return nil, err 961 } 962 scenes := make([]Scene, 0, len(m)) 963 964 for i, s := range m { 965 s.ID = i 966 s.bridge = b 967 scenes = append(scenes, s) 968 } 969 970 return scenes, nil 971 } 972 973 // GetScene returns one scene by its id of i 974 func (b Bridge) GetScene(ctx context.Context, i string) (*Scene, error) { 975 g := &Scene{ID: i} 976 l := struct { 977 LightStates map[int]LightState `json:"lightstates"` 978 }{} 979 980 url, err := b.getHostURL("/scenes/", i) 981 if err != nil { 982 return nil, err 983 } 984 985 res, err := get(ctx, url.String()) 986 if err != nil { 987 return nil, err 988 } 989 990 err = unmarshal(res, &l) 991 if err != nil { 992 return nil, err 993 } 994 995 err = unmarshal(res, &g) 996 if err != nil { 997 return nil, err 998 } 999 1000 g.bridge = b 1001 1002 return g, nil 1003 } 1004 1005 // UpdateScene updates one scene and its attributes by id of i 1006 func (b Bridge) UpdateScene(ctx context.Context, id string, s *Scene) (*Response, error) { 1007 var a []*APIResponse 1008 1009 url, err := b.getHostURL("/scenes/", id) 1010 if err != nil { 1011 return nil, err 1012 } 1013 1014 data, err := json.Marshal(&s) 1015 if err != nil { 1016 return nil, err 1017 } 1018 1019 res, err := put(ctx, url.String(), data) 1020 if err != nil { 1021 return nil, err 1022 } 1023 1024 err = unmarshal(res, &a) 1025 if err != nil { 1026 return nil, err 1027 } 1028 1029 resp, err := handleResponse(a) 1030 if err != nil { 1031 return nil, err 1032 } 1033 1034 return resp, nil 1035 } 1036 1037 // SetSceneLightState allows for setting the state of a light in a scene. 1038 func (b Bridge) SetSceneLightState(ctx context.Context, id string, iid int, l *LightState) (*Response, error) { 1039 var a []*APIResponse 1040 1041 lightid := strconv.Itoa(iid) 1042 url, err := b.getHostURL("scenes", id, "lightstates", lightid) 1043 if err != nil { 1044 return nil, err 1045 } 1046 1047 data, err := json.Marshal(&l) 1048 if err != nil { 1049 return nil, err 1050 } 1051 1052 res, err := put(ctx, url.String(), data) 1053 if err != nil { 1054 return nil, err 1055 } 1056 1057 err = unmarshal(res, &a) 1058 if err != nil { 1059 return nil, err 1060 } 1061 1062 resp, err := handleResponse(a) 1063 if err != nil { 1064 return nil, err 1065 } 1066 1067 return resp, nil 1068 } 1069 1070 // RecallScene will recall a scene in a group identified by both scene and group identifiers 1071 func (b Bridge) RecallScene(ctx context.Context, id string, gid int) (*Response, error) { 1072 var a []*APIResponse 1073 1074 data, err := json.Marshal(struct { 1075 Scene string `json:"scene"` 1076 }{id}) 1077 1078 if err != nil { 1079 return nil, err 1080 } 1081 1082 url, err := b.getHostURL("/groups/", strconv.Itoa(gid), "/action") 1083 if err != nil { 1084 return nil, err 1085 } 1086 1087 res, err := put(ctx, url.String(), data) 1088 if err != nil { 1089 return nil, err 1090 } 1091 1092 err = unmarshal(res, &a) 1093 if err != nil { 1094 return nil, err 1095 } 1096 1097 resp, err := handleResponse(a) 1098 if err != nil { 1099 return nil, err 1100 } 1101 1102 return resp, err 1103 } 1104 1105 // CreateScene creates one new scene with its attributes defined in s 1106 func (b Bridge) CreateScene(ctx context.Context, s *Scene) (*Response, error) { 1107 var a []*APIResponse 1108 1109 data, err := json.Marshal(&s) 1110 if err != nil { 1111 return nil, err 1112 } 1113 1114 url, err := b.getHostURL("/scenes/") 1115 if err != nil { 1116 return nil, err 1117 } 1118 1119 res, err := post(ctx, url.String(), data) 1120 if err != nil { 1121 return nil, err 1122 } 1123 1124 err = unmarshal(res, &a) 1125 if err != nil { 1126 return nil, err 1127 } 1128 1129 resp, err := handleResponse(a) 1130 if err != nil { 1131 return nil, err 1132 } 1133 1134 return resp, nil 1135 } 1136 1137 // DeleteScene deletes one scene from the bridge 1138 func (b Bridge) DeleteScene(ctx context.Context, id string) error { 1139 var a []*APIResponse 1140 1141 url, err := b.getHostURL("/scenes/", id) 1142 if err != nil { 1143 return err 1144 } 1145 1146 res, err := delete(ctx, url.String()) 1147 if err != nil { 1148 return err 1149 } 1150 1151 _ = unmarshal(res, &a) 1152 1153 _, err = handleResponse(a) 1154 if err != nil { 1155 return err 1156 } 1157 1158 return nil 1159 } 1160 1161 // GetSchedules returns all schedules known to the bridge 1162 func (b Bridge) GetSchedules(ctx context.Context) ([]*Schedule, error) { 1163 var r map[string]Schedule 1164 1165 url, err := b.getHostURL("/schedules/") 1166 if err != nil { 1167 return nil, err 1168 } 1169 1170 res, err := get(ctx, url.String()) 1171 if err != nil { 1172 return nil, err 1173 } 1174 1175 err = unmarshal(res, &r) 1176 if err != nil { 1177 return nil, err 1178 } 1179 1180 schedules := make([]*Schedule, 0, len(r)) 1181 1182 for i, s := range r { 1183 s.ID, err = strconv.Atoi(i) 1184 if err != nil { 1185 return nil, err 1186 } 1187 sCopy := s 1188 schedules = append(schedules, &sCopy) 1189 } 1190 1191 return schedules, nil 1192 } 1193 1194 // GetSchedule returns one schedule by id defined in i 1195 func (b Bridge) GetSchedule(ctx context.Context, i int) (*Schedule, error) { 1196 g := &Schedule{ 1197 ID: i, 1198 } 1199 1200 url, err := b.getHostURL("/schedules/", strconv.Itoa(i)) 1201 if err != nil { 1202 return nil, err 1203 } 1204 1205 res, err := get(ctx, url.String()) 1206 if err != nil { 1207 return nil, err 1208 } 1209 1210 err = unmarshal(res, &g) 1211 if err != nil { 1212 return nil, err 1213 } 1214 1215 return g, nil 1216 } 1217 1218 // CreateSchedule creates one schedule and sets its attributes defined in s 1219 func (b Bridge) CreateSchedule(ctx context.Context, s *Schedule) (*Response, error) { 1220 var a []*APIResponse 1221 1222 data, err := json.Marshal(&s) 1223 if err != nil { 1224 return nil, err 1225 } 1226 1227 url, err := b.getHostURL("/schedules/") 1228 if err != nil { 1229 return nil, err 1230 } 1231 1232 res, err := post(ctx, url.String(), data) 1233 if err != nil { 1234 return nil, err 1235 } 1236 1237 err = unmarshal(res, &a) 1238 if err != nil { 1239 return nil, err 1240 } 1241 1242 resp, err := handleResponse(a) 1243 if err != nil { 1244 return nil, err 1245 } 1246 1247 return resp, nil 1248 } 1249 1250 // UpdateSchedule updates one schedule by its id of i and attributes by schedule 1251 func (b Bridge) UpdateSchedule(ctx context.Context, i int, schedule *Schedule) (*Response, error) { 1252 1253 var a []*APIResponse 1254 1255 data, err := json.Marshal(&schedule) 1256 if err != nil { 1257 return nil, err 1258 } 1259 1260 url, err := b.getHostURL("/schedules/", strconv.Itoa(i)) 1261 if err != nil { 1262 return nil, err 1263 } 1264 1265 res, err := put(ctx, url.String(), data) 1266 if err != nil { 1267 return nil, err 1268 } 1269 1270 err = unmarshal(res, &a) 1271 if err != nil { 1272 return nil, err 1273 } 1274 1275 resp, err := handleResponse(a) 1276 if err != nil { 1277 return nil, err 1278 } 1279 1280 return resp, nil 1281 } 1282 1283 // DeleteSchedule deletes one schedule from the bridge by its id of i 1284 func (b Bridge) DeleteSchedule(ctx context.Context, i int) error { 1285 var a []*APIResponse 1286 1287 id := strconv.Itoa(i) 1288 url, err := b.getHostURL("/schedules/", id) 1289 if err != nil { 1290 return err 1291 } 1292 1293 res, err := delete(ctx, url.String()) 1294 if err != nil { 1295 return err 1296 } 1297 1298 _ = unmarshal(res, &a) 1299 1300 _, err = handleResponse(a) 1301 if err != nil { 1302 return err 1303 } 1304 1305 return nil 1306 } 1307 1308 // GetSensors returns all sensors known to the bridge 1309 func (b Bridge) GetSensors(ctx context.Context) ([]Sensor, error) { 1310 s := map[string]Sensor{} 1311 1312 url, err := b.getHostURL("/sensors/") 1313 if err != nil { 1314 return nil, err 1315 } 1316 1317 res, err := get(ctx, url.String()) 1318 if err != nil { 1319 return nil, err 1320 } 1321 1322 err = unmarshal(res, &s) 1323 if err != nil { 1324 return nil, err 1325 } 1326 1327 sensors := make([]Sensor, 0, len(s)) 1328 1329 for i, k := range s { 1330 k.ID, err = strconv.Atoi(i) 1331 if err != nil { 1332 return nil, err 1333 } 1334 sensors = append(sensors, k) 1335 } 1336 return sensors, err 1337 } 1338 1339 // GetSensor returns one sensor by its id of i 1340 func (b Bridge) GetSensor(ctx context.Context, i int) (*Sensor, error) { 1341 r := &Sensor{ 1342 ID: i, 1343 } 1344 1345 id := strconv.Itoa(i) 1346 url, err := b.getHostURL("/sensors/", id) 1347 if err != nil { 1348 return nil, err 1349 } 1350 1351 res, err := get(ctx, url.String()) 1352 if err != nil { 1353 return r, err 1354 } 1355 1356 err = unmarshal(res, &r) 1357 if err != nil { 1358 return r, err 1359 } 1360 1361 return r, err 1362 } 1363 1364 // CreateSensor creates one new sensor 1365 func (b Bridge) CreateSensor(ctx context.Context, s *Sensor) (*Response, error) { 1366 var a []*APIResponse 1367 1368 data, err := json.Marshal(&s) 1369 if err != nil { 1370 return nil, err 1371 } 1372 1373 url, err := b.getHostURL("/sensors/") 1374 if err != nil { 1375 return nil, err 1376 } 1377 1378 res, err := post(ctx, url.String(), data) 1379 if err != nil { 1380 return nil, err 1381 } 1382 1383 err = unmarshal(res, &a) 1384 if err != nil { 1385 return nil, err 1386 } 1387 1388 resp, err := handleResponse(a) 1389 if err != nil { 1390 return nil, err 1391 } 1392 1393 return resp, nil 1394 } 1395 1396 // FindSensors starts a search for new sensors. 1397 func (b Bridge) FindSensors(ctx context.Context) (*Response, error) { 1398 var a []*APIResponse 1399 1400 url, err := b.getHostURL("/sensors/") 1401 if err != nil { 1402 return nil, err 1403 } 1404 1405 res, err := post(ctx, url.String(), nil) 1406 if err != nil { 1407 return nil, err 1408 } 1409 1410 err = unmarshal(res, &a) 1411 if err != nil { 1412 return nil, err 1413 } 1414 1415 resp, err := handleResponse(a) 1416 if err != nil { 1417 return nil, err 1418 } 1419 1420 return resp, nil 1421 } 1422 1423 // GetNewSensors returns a list of sensors that were discovered last time GetNewSensors() was executed. 1424 func (b Bridge) GetNewSensors(ctx context.Context) (*Sensors, error) { 1425 var n map[string]Sensor 1426 var result *Sensors 1427 1428 url, err := b.getHostURL("/sensors/new") 1429 if err != nil { 1430 return nil, err 1431 } 1432 1433 res, err := get(ctx, url.String()) 1434 if err != nil { 1435 return nil, err 1436 } 1437 1438 _ = unmarshal(res, &n) 1439 1440 sensors := make([]Sensor, 0, len(n)) 1441 1442 for i, l := range n { 1443 if i != "lastscan" { 1444 l.ID, err = strconv.Atoi(i) 1445 if err != nil { 1446 return nil, err 1447 } 1448 lCopy := l 1449 sensors = append(sensors, lCopy) 1450 } 1451 } 1452 1453 err = unmarshal(res, &result) 1454 if err != nil { 1455 return nil, err 1456 } 1457 1458 resu := &Sensors{sensors, result.LastScan} 1459 return resu, nil 1460 } 1461 1462 // UpdateSensor updates one sensor by its id and attributes by sensor 1463 func (b Bridge) UpdateSensor(ctx context.Context, i int, sensor *Sensor) (*Response, error) { 1464 var a []*APIResponse 1465 1466 data, err := json.Marshal(&sensor) 1467 if err != nil { 1468 return nil, err 1469 } 1470 1471 url, err := b.getHostURL("/sensors/", strconv.Itoa(i)) 1472 if err != nil { 1473 return nil, err 1474 } 1475 1476 res, err := put(ctx, url.String(), data) 1477 if err != nil { 1478 return nil, err 1479 } 1480 1481 err = unmarshal(res, &a) 1482 if err != nil { 1483 return nil, err 1484 } 1485 1486 resp, err := handleResponse(a) 1487 if err != nil { 1488 return nil, err 1489 } 1490 1491 return resp, nil 1492 } 1493 1494 // DeleteSensor deletes one sensor from the bridge 1495 func (b Bridge) DeleteSensor(ctx context.Context, i int) error { 1496 var a []*APIResponse 1497 1498 id := strconv.Itoa(i) 1499 url, err := b.getHostURL("/sensors/", id) 1500 if err != nil { 1501 return err 1502 } 1503 1504 res, err := delete(ctx, url.String()) 1505 if err != nil { 1506 return err 1507 } 1508 1509 _ = unmarshal(res, &a) 1510 1511 _, err = handleResponse(a) 1512 if err != nil { 1513 return err 1514 } 1515 1516 return nil 1517 } 1518 1519 // UpdateSensorConfig updates the configuration of one sensor. The allowed configuration parameters depend on the sensor type 1520 func (b Bridge) UpdateSensorConfig(ctx context.Context, i int, c interface{}) (*Response, error) { 1521 var a []*APIResponse 1522 1523 data, err := json.Marshal(&c) 1524 if err != nil { 1525 return nil, err 1526 } 1527 1528 url, err := b.getHostURL("/sensors/", strconv.Itoa(i), "/config") 1529 if err != nil { 1530 return nil, err 1531 } 1532 1533 res, err := put(ctx, url.String(), data) 1534 if err != nil { 1535 return nil, err 1536 } 1537 1538 err = unmarshal(res, &a) 1539 if err != nil { 1540 return nil, err 1541 } 1542 1543 resp, err := handleResponse(a) 1544 if err != nil { 1545 return nil, err 1546 } 1547 1548 return resp, nil 1549 } 1550 1551 // GetCapabilities returns a list of capabilities of resources supported in the bridge. 1552 func (b Bridge) GetCapabilities(ctx context.Context) (*Capabilities, error) { 1553 url, err := b.getHostURL("/capabilities/") 1554 if err != nil { 1555 return nil, err 1556 } 1557 1558 res, err := get(ctx, url.String()) 1559 if err != nil { 1560 return nil, err 1561 } 1562 1563 var s Capabilities 1564 err = unmarshal(res, &s) 1565 if err != nil { 1566 return nil, err 1567 } 1568 1569 return &s, err 1570 }