homekit.go_dis 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package homekit
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "strconv"
  9. "strings"
  10. "imuslab.com/arozos/mod/iot"
  11. "imuslab.com/arozos/mod/network/mdns"
  12. "github.com/brutella/hc"
  13. "github.com/brutella/hc/accessory"
  14. )
  15. /*
  16. Home Dynamic 2 Controller
  17. This is a module that handles HDSv2 protocol devices scannings
  18. */
  19. type Handler struct {
  20. scanner *mdns.MDNSHost
  21. lastScanTime int64
  22. }
  23. //Create a new HDSv2 Protocol Handler
  24. func NewProtocolHandler(scanner *mdns.MDNSHost) *Handler {
  25. //Homekit
  26. info := accessory.Info{Name: "ArOZ Bridge", ID: 1}
  27. ac := accessory.NewBridge(info)
  28. outlet := accessory.NewOutlet(accessory.Info{Name: "ArOZ Speaker", ID: 2})
  29. // configure the ip transport
  30. config := hc.Config{Pin: "00102003"}
  31. t, err := hc.NewIPTransport(config, ac.Accessory, outlet.Accessory)
  32. if err != nil {
  33. log.Panic(err)
  34. }
  35. hc.OnTermination(func(){
  36. <-t.Stop()
  37. })
  38. t.Start()
  39. //Create a new MDNS Host
  40. return &Handler{
  41. scanner,
  42. 0,
  43. }
  44. }
  45. func (h *Handler) Start() error {
  46. log.Println("*IoT* Homekit Loaded")
  47. return nil
  48. }
  49. //Scan the devices within the LAN
  50. func (h *Handler) Scan() ([]*iot.Device, error) {
  51. foundDevices := []*iot.Device{}
  52. hosts := h.scanner.Scan(3, "hds.arozos.com")
  53. for _, host := range hosts {
  54. thisDevice := iot.Device{
  55. Name: host.HostName,
  56. Port: host.Port,
  57. Model: host.Model,
  58. Version: host.BuildVersion + "-" + host.MinorVersion,
  59. Manufacturer: host.Vendor,
  60. DeviceUUID: host.UUID,
  61. IPAddr: host.IPv4[0].String(),
  62. RequireAuth: false,
  63. RequireConnect: false,
  64. Status: map[string]interface{}{},
  65. Handler: h,
  66. }
  67. //Try to get the device status
  68. status, err := getStatusForDevice(&thisDevice)
  69. if err != nil {
  70. //This might be not a valid HDSv2 device. Skip this
  71. log.Println("*HDSv2* Get status failed for device: ", host.HostName, err.Error())
  72. continue
  73. }
  74. thisDevice.Status = status
  75. //Get the device content endpoints
  76. eps, err := getEndpoints(&thisDevice)
  77. if err != nil {
  78. //This might be not a valid HDSv2 device. Skip this
  79. log.Println("*HDSv2* Get endpoints failed for device: ", host.HostName, err.Error())
  80. continue
  81. }
  82. thisDevice.ControlEndpoints = eps
  83. //Push this host into found device list
  84. foundDevices = append(foundDevices, &thisDevice)
  85. }
  86. return foundDevices, nil
  87. }
  88. //Home Dynamic system's devices no need to established conenction before executing anything
  89. func (h *Handler) Connect(device *iot.Device, authInfo *iot.AuthInfo) error {
  90. return nil
  91. }
  92. //Same rules also apply to disconnect
  93. func (h *Handler) Disconnect(device *iot.Device) error {
  94. return nil
  95. }
  96. //Get the status of the device
  97. func (h *Handler) Status(device *iot.Device) (map[string]interface{}, error) {
  98. return getStatusForDevice(device)
  99. }
  100. //Get the status of the device
  101. func (h *Handler) Execute(device *iot.Device, endpoint *iot.Endpoint, payload interface{}) (interface{}, error) {
  102. var result interface{}
  103. targetURL := "http://" + device.IPAddr + ":" + strconv.Itoa(device.Port) + "/" + endpoint.RelPath
  104. //Check if there are payload for this request
  105. if payload == nil {
  106. //No payload. Just call it
  107. } else {
  108. //Payload exists. Append it to the end with value=?
  109. targetURL += "?value=" + payload.(string)
  110. }
  111. result, err := tryGet(targetURL)
  112. if err != nil {
  113. return nil, err
  114. }
  115. return result, nil
  116. }
  117. func (h *Handler) Stats() iot.Stats {
  118. return iot.Stats{
  119. Name: "Home Dynamic v2",
  120. Desc: "A basic IoT communication protocol for ESP8266 made by Makers",
  121. Version: "2.0",
  122. ProtocolVer: "2.0",
  123. Author: "tobychui",
  124. AuthorWebsite: "http://arozos.com",
  125. AuthorEmail: "[email protected]",
  126. ReleaseDate: 1614524498,
  127. }
  128. }
  129. //Get endpoint of the given device object
  130. func getEndpoints(device *iot.Device) ([]*iot.Endpoint, error) {
  131. //Parse the URL of the endpoint apis location (eps)
  132. requestURL := "http://" + device.IPAddr + ":" + strconv.Itoa(device.Port) + "/eps"
  133. resp, err := http.Get(requestURL)
  134. if err != nil {
  135. return nil, err
  136. }
  137. //Get the body content
  138. content, err := ioutil.ReadAll(resp.Body)
  139. if err != nil {
  140. return nil, err
  141. }
  142. //Convert the results to Endpoints
  143. endpoints := []iot.Endpoint{}
  144. err = json.Unmarshal(content, &endpoints)
  145. if err != nil {
  146. return nil, err
  147. }
  148. //Convert the structure to array pointers
  149. results := []*iot.Endpoint{}
  150. for _, ep := range endpoints {
  151. thisEp := ep
  152. results = append(results, &thisEp)
  153. }
  154. return results, nil
  155. }
  156. //Get status given the device object.
  157. func getStatusForDevice(device *iot.Device) (map[string]interface{}, error) {
  158. //Parse the URL for its status api endpoint
  159. requestURL := "http://" + device.IPAddr + ":" + strconv.Itoa(device.Port) + "/status"
  160. resp, err := http.Get(requestURL)
  161. if err != nil {
  162. return map[string]interface{}{}, err
  163. }
  164. //Get the body content
  165. content, err := ioutil.ReadAll(resp.Body)
  166. if err != nil {
  167. return map[string]interface{}{}, err
  168. }
  169. //Check if the resp is json
  170. if !isJSON(strings.TrimSpace(string(content))) {
  171. return map[string]interface{}{}, errors.New("Invalid HDSv2 protocol")
  172. }
  173. //Ok. Parse it
  174. status := map[string]interface{}{}
  175. err = json.Unmarshal(content, &status)
  176. if err != nil {
  177. return map[string]interface{}{}, err
  178. }
  179. return status, nil
  180. }