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  }