Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

1569 wiersze
44 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/gob"
  5. "encoding/json"
  6. "flag"
  7. "fmt"
  8. "log"
  9. "math"
  10. "net/http"
  11. "os"
  12. "os/signal"
  13. "strconv"
  14. "strings"
  15. "sync"
  16. "time"
  17. "io/ioutil"
  18. //"./utils"
  19. "gopkg.in/natefinch/lumberjack.v2"
  20. "os/exec"
  21. "github.com/boltdb/bolt"
  22. "github.com/yosssi/gmq/mqtt"
  23. "github.com/yosssi/gmq/mqtt/client"
  24. "github.com/gorilla/mux"
  25. "github.com/gorilla/websocket"
  26. "github.com/gorilla/handlers"
  27. )
  28. const (
  29. // Time allowed to write the file to the client.
  30. writeWait = 10 * time.Second
  31. // Time allowed to read the next pong message from the client.
  32. pongWait = 60 * time.Second
  33. // Send pings to client with this period. Must be less than pongWait.
  34. pingPeriod = (pongWait * 9) / 10
  35. beaconPeriod = 2 * time.Second
  36. )
  37. // data structures
  38. type Settings struct {
  39. Location_confidence int64 `json:"location_confidence"`
  40. Last_seen_threshold int64 `json:"last_seen_threshold"`
  41. Beacon_metrics_size int `json:"beacon_metrics_size"`
  42. HA_send_interval int64 `json:"ha_send_interval"`
  43. HA_send_changes_only bool `json:"ha_send_changes_only"`
  44. }
  45. type Incoming_json struct {
  46. Hostname string `json:"hostname"`
  47. MAC string `json:"mac"`
  48. RSSI int64 `json:"rssi"`
  49. Is_scan_response string `json:"is_scan_response"`
  50. Ttype string `json:"type"`
  51. Data string `json:"data"`
  52. Beacon_type string `json:"beacon_type"`
  53. UUID string `json:"uuid"`
  54. Major string `json:"major"`
  55. Minor string `json:"minor"`
  56. TX_power string `json:"tx_power"`
  57. Namespace string `json:"namespace"`
  58. Instance_id string `json:"instance_id"`
  59. // button stuff
  60. HB_ButtonCounter int64 `json:"hb_button_counter"`
  61. HB_ButtonCounter_Prev int64 `json:"hb_button_counter"`
  62. HB_Battery int64 `json:"hb_button_battery"`
  63. HB_RandomNonce string `json:"hb_button_random"`
  64. HB_ButtonMode string `json:"hb_button_mode"`
  65. }
  66. type Advertisement struct {
  67. ttype string
  68. content string
  69. seen int64
  70. }
  71. type beacon_metric struct {
  72. location string
  73. distance float64
  74. rssi int64
  75. timestamp int64
  76. }
  77. type Location struct {
  78. name string
  79. lock sync.RWMutex
  80. }
  81. type Best_location struct {
  82. distance float64
  83. name string
  84. last_seen int64
  85. }
  86. type HTTP_location struct {
  87. Previous_confident_location string `json:"previous_confident_location"`
  88. Distance float64 `json:"distance"`
  89. Name string `json:"name"`
  90. Beacon_name string `json:"beacon_name"`
  91. Beacon_id string `json:"beacon_id"`
  92. Beacon_type string `json:"beacon_type"`
  93. HB_Battery int64 `json:"hb_button_battery"`
  94. HB_ButtonMode string `json:"hb_button_mode"`
  95. HB_ButtonCounter int64 `json:"hb_button_counter"`
  96. Location string `json:"location"`
  97. Last_seen int64 `json:"last_seen"`
  98. }
  99. type Location_change struct {
  100. Beacon_ref Beacon `json:"beacon_info"`
  101. Name string `json:"name"`
  102. Beacon_name string `json:"beacon_name"`
  103. Previous_location string `json:"previous_location"`
  104. New_location string `json:"new_location"`
  105. Timestamp int64 `json:"timestamp"`
  106. }
  107. type HA_message struct {
  108. Beacon_id string `json:"id"`
  109. Beacon_name string `json:"name"`
  110. Distance float64 `json:"distance"`
  111. }
  112. type HTTP_locations_list struct {
  113. Beacons []HTTP_location `json:"beacons"`
  114. //Buttons []Button `json:"buttons"`
  115. }
  116. type Beacon struct {
  117. Name string `json:"name"`
  118. Beacon_id string `json:"beacon_id"`
  119. Beacon_type string `json:"beacon_type"`
  120. Beacon_location string `json:"beacon_location"`
  121. Last_seen int64 `json:"last_seen"`
  122. Incoming_JSON Incoming_json `json:"incoming_json"`
  123. Distance float64 `json:"distance"`
  124. Previous_location string
  125. Previous_confident_location string
  126. expired_location string
  127. Location_confidence int64
  128. Location_history []string
  129. beacon_metrics []beacon_metric
  130. HB_ButtonCounter int64 `json:"hb_button_counter"`
  131. HB_ButtonCounter_Prev int64 `json:"hb_button_counter"`
  132. HB_Battery int64 `json:"hb_button_battery"`
  133. HB_RandomNonce string `json:"hb_button_random"`
  134. HB_ButtonMode string `json:"hb_button_mode"`
  135. }
  136. type Button struct {
  137. Name string `json:"name"`
  138. Button_id string `json:"button_id"`
  139. Button_type string `json:"button_type"`
  140. Button_location string `json:"button_location"`
  141. Incoming_JSON Incoming_json `json:"incoming_json"`
  142. Distance float64 `json:"distance"`
  143. Last_seen int64 `json:"last_seen"`
  144. HB_ButtonCounter int64 `json:"hb_button_counter"`
  145. HB_Battery int64 `json:"hb_button_battery"`
  146. HB_RandomNonce string `json:"hb_button_random"`
  147. HB_ButtonMode string `json:"hb_button_mode"`
  148. }
  149. type Beacons_list struct {
  150. Beacons map[string]Beacon `json:"beacons"`
  151. lock sync.RWMutex
  152. }
  153. type Locations_list struct {
  154. locations map[string]Location
  155. lock sync.RWMutex
  156. }
  157. var clients = make(map[*websocket.Conn]bool) // connected clients
  158. var broadcast = make(chan Message) // broadcast channel
  159. // Define our message object
  160. type Message struct {
  161. Email string `json:"email"`
  162. Username string `json:"username"`
  163. Message string `json:"message"`
  164. }
  165. // Struttura per il parsing JSON multiplo
  166. type RawReading struct {
  167. Timestamp string `json:"timestamp"`
  168. Type string `json:"type"`
  169. MAC string `json:"mac"`
  170. RSSI int `json:"rssi"`
  171. RawData string `json:"rawData"`
  172. }
  173. // GLOBALS
  174. var BEACONS Beacons_list
  175. var Buttons_list map[string]Button
  176. var cli *client.Client
  177. var http_results HTTP_locations_list
  178. var http_results_lock sync.RWMutex
  179. var Latest_beacons_list map[string]Beacon
  180. var latest_list_lock sync.RWMutex
  181. var db *bolt.DB
  182. var err error
  183. var world = []byte("presence")
  184. var (
  185. ///logpath = flag.String("logpath", "/data/var/log/presence/presence.log", "Log Path")
  186. )
  187. var (
  188. // Websocket http upgrader
  189. upgrader = websocket.Upgrader{
  190. ReadBufferSize: 1024,
  191. WriteBufferSize: 1024,
  192. CheckOrigin: func(r *http.Request) bool {
  193. return true
  194. },
  195. }
  196. )
  197. var settings = Settings{
  198. Location_confidence: 4,
  199. Last_seen_threshold: 15,
  200. Beacon_metrics_size: 30,
  201. HA_send_interval: 5,
  202. HA_send_changes_only: false,
  203. }
  204. // utility function
  205. func parseButtonState(raw string) int64 {
  206. raw = strings.ToUpper(raw)
  207. // Minew B7 / C7 / D7 - frame tipo: 0201060303E1FF1216E1FFA103...
  208. if strings.HasPrefix(raw, "0201060303E1FF12") && len(raw) >= 38 {
  209. // La posizione 34-38 (indice 26:30) contiene il buttonCounter su 2 byte (hex)
  210. buttonField := raw[34:38] // NB: offset 34-38 zero-based
  211. if buttonValue, err := strconv.ParseInt(buttonField, 16, 64); err == nil {
  212. return buttonValue
  213. }
  214. }
  215. // Ingics (02010612FF590)
  216. if strings.HasPrefix(raw, "02010612FF590") && len(raw) >= 24 {
  217. counterField := raw[22:24]
  218. buttonState, err := strconv.ParseInt(counterField, 16, 64)
  219. if err == nil {
  220. return buttonState
  221. }
  222. }
  223. // Aggiungeremo qui facilmente nuovi beacon in futuro
  224. return 0
  225. }
  226. func twos_comp(inp string) int64 {
  227. i, _ := strconv.ParseInt("0x"+inp, 0, 64)
  228. return i - 256
  229. }
  230. func getBeaconID(incoming Incoming_json) string {
  231. unique_id := fmt.Sprintf("%s", incoming.MAC)
  232. /*if incoming.Beacon_type == "ibeacon" {
  233. unique_id = fmt.Sprintf("%s_%s_%s", incoming.UUID, incoming.Major, incoming.Minor)
  234. } else if incoming.Beacon_type == "eddystone" {
  235. unique_id = fmt.Sprintf("%s_%s", incoming.Namespace, incoming.Instance_id)
  236. } else if incoming.Beacon_type == "hb_button" {
  237. unique_id = fmt.Sprintf("%s_%s", incoming.Namespace, incoming.Instance_id)
  238. }*/
  239. return unique_id
  240. }
  241. func incomingBeaconFilter(incoming Incoming_json) Incoming_json {
  242. out_json := incoming
  243. if incoming.Beacon_type == "hb_button" {
  244. //do additional checks here to detect if a Habby Bubbles Button
  245. // looks like 020104020a0011ff045600012d3859db59e1000b9453
  246. raw_data := incoming.Data
  247. //company_id := []byte{0x04, 0x56}
  248. //product_id := []byte{0x00, 0x01}
  249. hb_button_prefix_str := fmt.Sprintf("02010612FF5900")
  250. if strings.HasPrefix(raw_data, hb_button_prefix_str) {
  251. out_json.Namespace = "ddddeeeeeeffff5544ff"
  252. //out_json.Instance_id = raw_data[24:36]
  253. counter_str := fmt.Sprintf("0x%s", raw_data[22:24])
  254. counter, _ := strconv.ParseInt(counter_str, 0, 64)
  255. out_json.HB_ButtonCounter = counter
  256. battery_str := fmt.Sprintf("0x%s%s", raw_data[20:22],raw_data[18:20])
  257. ////fmt.Println("battery has %s\n", battery_str)
  258. battery, _ := strconv.ParseInt(battery_str, 0, 64)
  259. out_json.HB_Battery = battery
  260. out_json.TX_power = fmt.Sprintf("0x%s", "4")
  261. out_json.Beacon_type = "hb_button"
  262. out_json.HB_ButtonMode = "presence_button"
  263. ///fmt.Println("Button adv has %#v\n", out_json)
  264. }
  265. }
  266. return out_json
  267. }
  268. func processButton(bbeacon Beacon, cl *client.Client) {
  269. btn := Button{Name: bbeacon.Name}
  270. btn.Button_id = bbeacon.Beacon_id
  271. btn.Button_type = bbeacon.Beacon_type
  272. btn.Button_location = bbeacon.Previous_location
  273. btn.Incoming_JSON = bbeacon.Incoming_JSON
  274. btn.Distance = bbeacon.Distance
  275. btn.Last_seen = bbeacon.Last_seen
  276. btn.HB_ButtonCounter = bbeacon.HB_ButtonCounter
  277. btn.HB_Battery = bbeacon.HB_Battery
  278. btn.HB_RandomNonce = bbeacon.HB_RandomNonce
  279. btn.HB_ButtonMode = bbeacon.HB_ButtonMode
  280. nonce, ok := Buttons_list[btn.Button_id]
  281. if !ok || nonce.HB_RandomNonce != btn.HB_RandomNonce {
  282. // send the button message to MQTT
  283. sendButtonMessage(btn, cl)
  284. }
  285. Buttons_list[btn.Button_id] = btn
  286. }
  287. func getiBeaconDistance(rssi int64, power string) float64 {
  288. ratio := float64(rssi) * (1.0 / float64(twos_comp(power)))
  289. //fmt.Printf("beaconpower: rssi %d ratio %e power %e \n",rssi, ratio, float64(twos_comp(power)))
  290. distance := 100.0
  291. if ratio < 1.0 {
  292. distance = math.Pow(ratio, 10)
  293. } else {
  294. distance = (0.89976)*math.Pow(ratio, 7.7095) + 0.111
  295. }
  296. return distance
  297. }
  298. func getBeaconDistance(incoming Incoming_json) float64 {
  299. distance := 1000.0
  300. distance = getiBeaconDistance(incoming.RSSI, incoming.TX_power)
  301. //distance = math.Abs(float64(incoming.RSSI))
  302. return distance
  303. }
  304. func getAverageDistance(beacon_metrics []beacon_metric) float64 {
  305. total := 0.0
  306. for _, v := range beacon_metrics {
  307. total += v.distance
  308. }
  309. return (total / float64(len(beacon_metrics)))
  310. }
  311. func sendHARoomMessage(beacon_id string, beacon_name string, distance float64, location string, cl *client.Client) {
  312. //first make the json
  313. ha_msg, err := json.Marshal(HA_message{Beacon_id: beacon_id, Beacon_name: beacon_name, Distance: distance})
  314. if err != nil {
  315. panic(err)
  316. }
  317. //send the message to HA
  318. err = cl.Publish(&client.PublishOptions{
  319. QoS: mqtt.QoS1,
  320. TopicName: []byte("afa-systems/presence/ha/" + location),
  321. Message: ha_msg,
  322. })
  323. if err != nil {
  324. panic(err)
  325. }
  326. }
  327. func sendButtonMessage(btn Button, cl *client.Client) {
  328. //first make the json
  329. btn_msg, err := json.Marshal(btn)
  330. if err != nil {
  331. panic(err)
  332. }
  333. //send the message to HA
  334. err = cl.Publish(&client.PublishOptions{
  335. QoS: mqtt.QoS1,
  336. TopicName: []byte("afa-systems/presence/button/" + btn.Button_id),
  337. Message: btn_msg,
  338. })
  339. if err != nil {
  340. panic(err)
  341. }
  342. }
  343. func sendButtonPressed(bcn Beacon, cl *client.Client) {
  344. //first make the json
  345. btn_msg, err := json.Marshal(bcn)
  346. if err != nil {
  347. panic(err)
  348. }
  349. //send the message to HA
  350. err = cl.Publish(&client.PublishOptions{
  351. QoS: mqtt.QoS1,
  352. TopicName: []byte("afa-systems/presence/button/" + bcn.Beacon_id),
  353. Message: btn_msg,
  354. })
  355. if err != nil {
  356. panic(err)
  357. }
  358. ///utils.Log.Printf("%s pressed ",bcn.Beacon_id)
  359. s := fmt.Sprintf("/usr/bin/php /usr/local/presence/alarm_handler.php --idt=%s --idr=%s --st=%d",bcn.Beacon_id,bcn.Incoming_JSON.Hostname,bcn.HB_ButtonCounter)
  360. ///utils.Log.Printf("%s",s)
  361. err, out, errout := Shellout(s)
  362. if err != nil {
  363. log.Printf("error: %v\n", err)
  364. }
  365. fmt.Println("--- stdout ---")
  366. fmt.Println(out)
  367. fmt.Println("--- stderr ---")
  368. fmt.Println(errout)
  369. // create the file if it doesn't exists with O_CREATE, Set the file up for read write, add the append flag and set the permission
  370. //f, err := os.OpenFile("/data/conf/presence/db.json", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
  371. //if err != nil {
  372. // log.Fatal(err)
  373. //}
  374. // write to file, f.Write()
  375. //f.Write(btn_msg)
  376. }
  377. func getLikelyLocations(settings Settings, locations_list Locations_list, cl *client.Client) {
  378. // create the http results structure
  379. http_results_lock.Lock()
  380. http_results = HTTP_locations_list{}
  381. http_results.Beacons = make([]HTTP_location, 0)
  382. ///http_results.Buttons = make([]Button, 0)
  383. http_results_lock.Unlock()
  384. should_persist := false
  385. // iterate through the beacons we want to search for
  386. for _, beacon := range BEACONS.Beacons {
  387. if len(beacon.beacon_metrics) == 0 {
  388. ////fmt.Printf("beacon_metrics = 0:\n")
  389. continue
  390. }
  391. if (int64(time.Now().Unix()) - (beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp)) > settings.Last_seen_threshold {
  392. ////fmt.Printf("beacon_metrics timestamp = %s %s \n",beacon.Name, beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp )
  393. if (beacon.expired_location == "expired") {
  394. //beacon.Location_confidence = - 1
  395. continue
  396. } else {
  397. beacon.expired_location = "expired"
  398. msg := Message{
  399. Email: beacon.Previous_confident_location,
  400. Username: beacon.Name,
  401. Message: beacon.expired_location,}
  402. res1B, _ := json.Marshal(msg)
  403. fmt.Println(string(res1B))
  404. if err != nil {
  405. log.Printf("error: %v", err)
  406. }
  407. // Send the newly received message to the broadcast channel
  408. broadcast <- msg
  409. }
  410. } else {
  411. beacon.expired_location = ""
  412. }
  413. best_location := Best_location{}
  414. // go through its beacon metrics and pick out the location that appears most often
  415. loc_list := make(map[string]float64)
  416. seen_weight := 1.5
  417. rssi_weight := 0.75
  418. for _, metric := range beacon.beacon_metrics {
  419. loc, ok := loc_list[metric.location]
  420. if !ok {
  421. loc = seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
  422. } else {
  423. loc = loc + seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
  424. }
  425. loc_list[metric.location] = loc
  426. }
  427. //fmt.Printf("beacon: %s list: %#v\n", beacon.Name, loc_list)
  428. // now go through the list and find the largest, that's the location
  429. best_name := ""
  430. ts := 0.0
  431. for name, times_seen := range loc_list {
  432. if times_seen > ts {
  433. best_name = name
  434. ts = times_seen
  435. }
  436. }
  437. /////fmt.Printf("BEST LOCATION FOR %s IS: %s with score: %f\n", beacon.Name, best_name, ts)
  438. best_location = Best_location{name: best_name, distance: beacon.beacon_metrics[len(beacon.beacon_metrics)-1].distance, last_seen: beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp}
  439. // //filter, only let this location become best if it was X times in a row
  440. // if best_location.name == beacon.Previous_location {
  441. // beacon.Location_confidence = beacon.Location_confidence + 1
  442. // } else {
  443. // beacon.Location_confidence = 0
  444. // /////fmt.Printf("beacon.Location_confidence %f\n", beacon.Location_confidence)
  445. // }
  446. // Aggiungiamo il nuovo best_location allo storico
  447. beacon.Location_history = append(beacon.Location_history, best_location.name)
  448. if len(beacon.Location_history) > 10 {
  449. beacon.Location_history = beacon.Location_history[1:] // manteniamo solo gli ultimi 10
  450. }
  451. // Calcoliamo la location più votata nello storico
  452. location_counts := make(map[string]int)
  453. for _, loc := range beacon.Location_history {
  454. location_counts[loc]++
  455. }
  456. max_count := 0
  457. most_common_location := ""
  458. for loc, count := range location_counts {
  459. if count > max_count {
  460. max_count = count
  461. most_common_location = loc
  462. }
  463. }
  464. // Applichiamo un filtro: consideriamo il cambio solo se almeno 7 su 10 votano per una location
  465. if max_count >= 7 {
  466. beacon.Previous_location = most_common_location
  467. if most_common_location == beacon.Previous_confident_location {
  468. beacon.Location_confidence++
  469. } else {
  470. beacon.Location_confidence = 1
  471. beacon.Previous_confident_location = most_common_location
  472. }
  473. }
  474. //create an http result from this
  475. r := HTTP_location{}
  476. r.Distance = best_location.distance
  477. r.Name = beacon.Name
  478. r.Beacon_name = beacon.Name
  479. r.Beacon_id = beacon.Beacon_id
  480. r.Beacon_type = beacon.Beacon_type
  481. r.HB_Battery = beacon.HB_Battery
  482. r.HB_ButtonMode = beacon.HB_ButtonMode
  483. r.HB_ButtonCounter = beacon.HB_ButtonCounter
  484. r.Location = best_location.name
  485. r.Last_seen = best_location.last_seen
  486. ////fmt.Printf("beacon.Location_confidence %s, settings.Location_confidence %s, beacon.Previous_confident_location %s: best_location.name %s\n",beacon.Location_confidence, settings.Location_confidence, beacon.Previous_confident_location, best_location.name)
  487. if (beacon.Location_confidence == settings.Location_confidence && beacon.Previous_confident_location != best_location.name) || beacon.expired_location == "expired" {
  488. // location has changed, send an mqtt message
  489. should_persist = true
  490. fmt.Printf("detected a change!!! %#v\n\n", beacon)
  491. if (beacon.Previous_confident_location== "expired"&& beacon.expired_location=="") {
  492. msg := Message{
  493. Email: beacon.Previous_confident_location,
  494. Username: beacon.Name,
  495. Message: "OK",}
  496. res1B, _ := json.Marshal(msg)
  497. fmt.Println(string(res1B))
  498. if err != nil {
  499. log.Printf("error: %v", err)
  500. }
  501. // Send the newly received message to the broadcast channel
  502. broadcast <- msg
  503. }
  504. beacon.Location_confidence = 0
  505. location := ""
  506. if (beacon.expired_location == "expired") {
  507. location = "expired"
  508. } else {
  509. location = best_location.name
  510. }
  511. //first make the json
  512. js, err := json.Marshal(Location_change{Beacon_ref: beacon, Name: beacon.Name, Beacon_name: beacon.Name, Previous_location: beacon.Previous_confident_location, New_location: location, Timestamp: time.Now().Unix()})
  513. if err != nil {
  514. continue
  515. }
  516. //send the message
  517. err = cl.Publish(&client.PublishOptions{
  518. QoS: mqtt.QoS1,
  519. TopicName: []byte("afa-systems/presence/changes"),
  520. Message: js,
  521. })
  522. if err != nil {
  523. panic(err)
  524. }
  525. // Read in a new message as JSON and map it to a Message object
  526. //err := ws.ReadJSON(&msg)
  527. /*msg := Message{
  528. Email: "apple",
  529. Username: "peach",
  530. Message: "change",}
  531. res1B, _ := json.Marshal(msg)
  532. fmt.Println(string(res1B))
  533. if err != nil {
  534. log.Printf("error: %v", err)
  535. }
  536. // Send the newly received message to the broadcast channel
  537. broadcast <- msg*/
  538. ///utils.Log.Printf("%s changes ",beacon.Beacon_id)
  539. s := fmt.Sprintf("/usr/bin/php /usr/local/presence/alarm_handler.php --idt=%s --idr=%s --loct=%s",beacon.Beacon_id,beacon.Incoming_JSON.Hostname,location)
  540. ///utils.Log.Printf("%s",s)
  541. err, out, errout := Shellout(s)
  542. if err != nil {
  543. log.Printf("error: %v\n", err)
  544. }
  545. fmt.Println("--- stdout ---")
  546. fmt.Println(out)
  547. fmt.Println("--- stderr ---")
  548. fmt.Println(errout)
  549. //////beacon.logger.Printf("Log content: user id %v \n", best_location.name)
  550. if settings.HA_send_changes_only {
  551. sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
  552. }
  553. if (beacon.expired_location == "expired") {
  554. beacon.Previous_confident_location = "expired"
  555. r.Location = "expired"
  556. } else {
  557. beacon.Previous_confident_location = best_location.name
  558. }
  559. ///beacon.Previous_confident_location = best_location.name
  560. }
  561. beacon.Previous_location = best_location.name
  562. r.Previous_confident_location = beacon.expired_location
  563. BEACONS.Beacons[beacon.Beacon_id] = beacon
  564. http_results_lock.Lock()
  565. http_results.Beacons = append(http_results.Beacons, r)
  566. http_results_lock.Unlock()
  567. if best_location.name != "" {
  568. if !settings.HA_send_changes_only {
  569. secs := int64(time.Now().Unix())
  570. if secs%settings.HA_send_interval == 0 {
  571. sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
  572. }
  573. }
  574. }
  575. /////fmt.Printf("\n\n%s is most likely in %s with average distance %f \n\n", beacon.Name, best_location.name, best_location.distance)
  576. ////beacon.logger.Printf("Log content: user id %v \n", beacon.Name)
  577. // publish this to a topic
  578. // Publish a message.
  579. err := cl.Publish(&client.PublishOptions{
  580. QoS: mqtt.QoS0,
  581. TopicName: []byte("afa-systems/presence"),
  582. Message: []byte(fmt.Sprintf("%s is most likely in %s with average distance %f", beacon.Name, best_location.name, best_location.distance)),
  583. })
  584. if err != nil {
  585. panic(err)
  586. }
  587. }
  588. /*for _, button := range Buttons_list {
  589. http_results.Buttons = append(http_results.Buttons, button)
  590. }*/
  591. if should_persist {
  592. persistBeacons()
  593. }
  594. }
  595. /*func doSomething(bcon Beacon, testo string ) {
  596. bcon.logger.Printf("Log content: user id %v \n", beacon.Name)
  597. }*/
  598. func IncomingMQTTProcessor(updateInterval time.Duration, cl *client.Client, db *bolt.DB, logger []*user) chan<- Incoming_json {
  599. incoming_msgs_chan := make(chan Incoming_json, 2000)
  600. // load initial BEACONS
  601. BEACONS.Beacons = make(map[string]Beacon)
  602. // retrieve the data
  603. // create bucket if not exist
  604. err = db.Update(func(tx *bolt.Tx) error {
  605. _, err := tx.CreateBucketIfNotExists(world)
  606. if err != nil {
  607. return err
  608. }
  609. return nil
  610. })
  611. err = db.View(func(tx *bolt.Tx) error {
  612. bucket := tx.Bucket(world)
  613. if bucket == nil {
  614. return err
  615. }
  616. key := []byte("beacons_list")
  617. val := bucket.Get(key)
  618. if val != nil {
  619. buf := bytes.NewBuffer(val)
  620. dec := gob.NewDecoder(buf)
  621. err = dec.Decode(&BEACONS)
  622. if err != nil {
  623. log.Fatal("decode error:", err)
  624. }
  625. }
  626. key = []byte("buttons_list")
  627. val = bucket.Get(key)
  628. if val != nil {
  629. buf := bytes.NewBuffer(val)
  630. dec := gob.NewDecoder(buf)
  631. err = dec.Decode(&Buttons_list)
  632. if err != nil {
  633. log.Fatal("decode error:", err)
  634. }
  635. }
  636. key = []byte("settings")
  637. val = bucket.Get(key)
  638. if val != nil {
  639. buf := bytes.NewBuffer(val)
  640. dec := gob.NewDecoder(buf)
  641. err = dec.Decode(&settings)
  642. if err != nil {
  643. log.Fatal("decode error:", err)
  644. }
  645. }
  646. return nil
  647. })
  648. if err != nil {
  649. log.Fatal(err)
  650. }
  651. //debug list them out
  652. /*fmt.Println("Database beacons:")
  653. for _, beacon := range BEACONS.Beacons {
  654. fmt.Println("Database has known beacon: " + beacon.Beacon_id + " " + beacon.Name)
  655. dog := new(user)
  656. //createUser( beacon.Name, true)
  657. //user1 := createUser( beacon.Name, true)
  658. //doSomething(beacon, "hello")
  659. //
  660. userFIle := &lumberjack.Logger{
  661. Filename: "/data/presence/presence/beacon_log_" + beacon.Name + ".log",
  662. MaxSize: 250, // mb
  663. MaxBackups: 5,
  664. MaxAge: 10, // in days
  665. }
  666. dog.id = beacon.Name
  667. dog.logger = log.New(userFIle, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
  668. dog.logger.Printf("Log content: user id %v \n", beacon.Name)
  669. logger=append(logger,dog)
  670. }
  671. fmt.Println("leng has %d\n",len(logger))
  672. fmt.Printf("%v", logger)
  673. fmt.Println("Settings has %#v\n", settings)*/
  674. /**/
  675. Latest_beacons_list = make(map[string]Beacon)
  676. Buttons_list = make(map[string]Button)
  677. //create a map of locations, looked up by hostnames
  678. locations_list := Locations_list{}
  679. ls := make(map[string]Location)
  680. locations_list.locations = ls
  681. ticker := time.NewTicker(updateInterval)
  682. go func() {
  683. for {
  684. select {
  685. case <-ticker.C:
  686. getLikelyLocations(settings, locations_list, cl)
  687. case incoming := <-incoming_msgs_chan:
  688. func() {
  689. defer func() {
  690. if err := recover(); err != nil {
  691. log.Println("work failed:", err)
  692. }
  693. }()
  694. incoming = incomingBeaconFilter(incoming)
  695. this_beacon_id := getBeaconID(incoming)
  696. now := time.Now().Unix()
  697. ///fmt.Println("sawbeacon " + this_beacon_id + " at " + incoming.Hostname)
  698. //logger["FCB8351F5A21"].logger.Printf("Log content: user id \n")
  699. //if this beacon isn't in our search list, add it to the latest_beacons pile.
  700. beacon, ok := BEACONS.Beacons[this_beacon_id]
  701. if !ok {
  702. //should be unique
  703. //if it's already in list, forget it.
  704. latest_list_lock.Lock()
  705. x, ok := Latest_beacons_list[this_beacon_id]
  706. if ok {
  707. //update its timestamp
  708. x.Last_seen = now
  709. x.Incoming_JSON = incoming
  710. x.Distance = getBeaconDistance(incoming)
  711. Latest_beacons_list[this_beacon_id] = x
  712. } else {
  713. Latest_beacons_list[this_beacon_id] = Beacon{Beacon_id: this_beacon_id, Beacon_type: incoming.Beacon_type, Last_seen: now, Incoming_JSON: incoming, Beacon_location: incoming.Hostname, Distance: getBeaconDistance(incoming)}
  714. }
  715. for k, v := range Latest_beacons_list {
  716. if (now - v.Last_seen) > 10 { // 10 seconds
  717. delete(Latest_beacons_list, k)
  718. }
  719. }
  720. latest_list_lock.Unlock()
  721. //continue
  722. return
  723. }
  724. beacon.Incoming_JSON = incoming
  725. beacon.Last_seen = now
  726. beacon.Beacon_type = incoming.Beacon_type
  727. beacon.HB_ButtonCounter = incoming.HB_ButtonCounter
  728. beacon.HB_Battery = incoming.HB_Battery
  729. beacon.HB_RandomNonce = incoming.HB_RandomNonce
  730. beacon.HB_ButtonMode = incoming.HB_ButtonMode
  731. ////fmt.Println("button pressed " + this_beacon_id + " at " + strconv.Itoa(int(incoming.HB_ButtonCounter)) )
  732. if beacon.beacon_metrics == nil {
  733. beacon.beacon_metrics = make([]beacon_metric, settings.Beacon_metrics_size)
  734. }
  735. //create metric for this beacon
  736. this_metric := beacon_metric{}
  737. this_metric.distance = getBeaconDistance(incoming)
  738. this_metric.timestamp = now
  739. this_metric.rssi = int64(incoming.RSSI)
  740. this_metric.location = incoming.Hostname
  741. beacon.beacon_metrics = append(beacon.beacon_metrics, this_metric)
  742. ///fmt.Printf("APPENDING a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
  743. if len(beacon.beacon_metrics) > settings.Beacon_metrics_size {
  744. //fmt.Printf("deleting a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
  745. beacon.beacon_metrics = append(beacon.beacon_metrics[:0], beacon.beacon_metrics[0+1:]...)
  746. }
  747. //fmt.Printf("%#v\n", beacon.beacon_metrics)
  748. if beacon.HB_ButtonCounter_Prev != beacon.HB_ButtonCounter {
  749. beacon.HB_ButtonCounter_Prev = incoming.HB_ButtonCounter
  750. // send the button message to MQTT
  751. sendButtonPressed(beacon, cl)
  752. }
  753. BEACONS.Beacons[beacon.Beacon_id] = beacon
  754. /*if beacon.Beacon_type == "hb_button" {
  755. processButton(beacon, cl)
  756. }*/
  757. //lookup location by hostname in locations
  758. location, ok := locations_list.locations[incoming.Hostname]
  759. if !ok {
  760. //create the location
  761. locations_list.locations[incoming.Hostname] = Location{}
  762. location, ok = locations_list.locations[incoming.Hostname]
  763. location.name = incoming.Hostname
  764. }
  765. locations_list.locations[incoming.Hostname] = location
  766. }()
  767. }
  768. }
  769. }()
  770. return incoming_msgs_chan
  771. }
  772. func ParseTimeStamp(utime string) (string, error) {
  773. i, err := strconv.ParseInt(utime, 10, 64)
  774. if err != nil {
  775. return "", err
  776. }
  777. t := time.Unix(i, 0)
  778. return t.Format(time.UnixDate), nil
  779. }
  780. var http_host_path_ptr *string
  781. //var https_host_path_ptr *string
  782. var httpws_host_path_ptr *string
  783. //var httpwss_host_path_ptr *string
  784. type Todo struct {
  785. Id string `json:"id"`
  786. Value string `json:"value" binding:"required"`
  787. }
  788. type Job interface {
  789. ExitChan() chan error
  790. Run(todos map[string]Todo) (map[string]Todo, error)
  791. }
  792. func ProcessJobs(jobs chan Job, db string) {
  793. for {
  794. j := <-jobs
  795. todos := make(map[string]Todo, 0)
  796. content, err := ioutil.ReadFile(db)
  797. if err == nil {
  798. if err = json.Unmarshal(content, &todos); err == nil {
  799. todosMod, err := j.Run(todos)
  800. if err == nil && todosMod != nil {
  801. b, err := json.Marshal(todosMod)
  802. if err == nil {
  803. err = ioutil.WriteFile(db, b, 0644)
  804. }
  805. }
  806. }
  807. }
  808. j.ExitChan() <- err
  809. }
  810. }
  811. type user struct {
  812. id string
  813. logger *log.Logger
  814. }
  815. const ShellToUse = "bash"
  816. func Shellout(command string) (error, string, string) {
  817. var stdout bytes.Buffer
  818. var stderr bytes.Buffer
  819. ///utils.Log.Printf("command: %s",command)
  820. cmd := exec.Command(ShellToUse, "-c", command)
  821. cmd.Stdout = &stdout
  822. cmd.Stderr = &stderr
  823. err := cmd.Run()
  824. return err, stdout.String(), stderr.String()
  825. }
  826. func createUser(id string, logWanted bool) user {
  827. var l *log.Logger
  828. if logWanted {
  829. // Here the log content will be added in the user log file
  830. userFIle := &lumberjack.Logger{
  831. Filename: "/data/var/log/presence/presence/log_" + id + ".log",
  832. MaxSize: 250, // mb
  833. MaxBackups: 5,
  834. MaxAge: 10, // in days
  835. }
  836. l = log.New(userFIle, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
  837. } else {
  838. // Here the log content will go nowhere
  839. l = log.New(ioutil.Discard, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
  840. }
  841. return user{id, l}
  842. }
  843. func main() {
  844. loggers:=[]*user{}
  845. // initialize empty-object json file if not found
  846. //if _, err := ioutil.ReadFile(Db); err != nil {
  847. // str := "{}"
  848. // if err = ioutil.WriteFile(Db, []byte(str), 0644); err != nil {
  849. // log.Fatal(err)
  850. // }
  851. //}
  852. // create channel to communicate over
  853. //jobs := make(chan Job)
  854. // start watching jobs channel for work
  855. //go ProcessJobs(jobs, Db)
  856. // create dependencies
  857. //client := &TodoClient{Jobs: jobs}
  858. //handlers := &TodoHandlers{Client: client}
  859. //work := WorkRequest{Name: name, Delay: delay}
  860. //jobs <- work
  861. http_host_path_ptr = flag.String("http_host_path", "0.0.0.0:8080", "The host:port that the HTTP server should listen on")
  862. //https_host_path_ptr = flag.String("https_host_path", "0.0.0.0:5443", "The host:port that the HTTP server should listen on")
  863. httpws_host_path_ptr = flag.String("httpws_host_path", "0.0.0.0:8088", "The host:port websocket listen")
  864. //httpwss_host_path_ptr = flag.String("httpwss_host_path", "0.0.0.0:8443", "The host:port secure websocket listen")
  865. mqtt_host_ptr := flag.String("mqtt_host", "localhost:1883", "The host:port of the MQTT server to listen for beacons on")
  866. mqtt_username_ptr := flag.String("mqtt_username", "none", "The username needed to connect to the MQTT server, 'none' if it doesn't need one")
  867. mqtt_password_ptr := flag.String("mqtt_password", "none", "The password needed to connect to the MQTT server, 'none' if it doesn't need one")
  868. mqtt_client_id_ptr := flag.String("mqtt_client_id", "presence-detector", "The client ID for the MQTT server")
  869. flag.Parse()
  870. ///utils.NewLog(*logpath)
  871. ///utils.Log.Println("hello")
  872. // Set up channel on which to send signal notifications.
  873. sigc := make(chan os.Signal, 1)
  874. signal.Notify(sigc, os.Interrupt, os.Kill)
  875. // Create an MQTT Client.
  876. cli := client.New(&client.Options{
  877. // Define the processing of the error handler.
  878. ErrorHandler: func(err error) {
  879. fmt.Println(err)
  880. },
  881. })
  882. // Terminate the Client.
  883. defer cli.Terminate()
  884. //open the database
  885. db, err = bolt.Open("/data/conf/presence/presence.db", 0644, nil)
  886. if err != nil {
  887. log.Fatal(err)
  888. }
  889. defer db.Close()
  890. // Connect to the MQTT Server.
  891. err = cli.Connect(&client.ConnectOptions{
  892. Network: "tcp",
  893. Address: *mqtt_host_ptr,
  894. ClientID: []byte(*mqtt_client_id_ptr),
  895. UserName: []byte(*mqtt_username_ptr),
  896. Password: []byte(*mqtt_password_ptr),
  897. })
  898. if err != nil {
  899. panic(err)
  900. }
  901. incoming_updates_chan := IncomingMQTTProcessor(1*time.Second, cli, db, loggers)
  902. // Subscribe to topics.
  903. err = cli.Subscribe(&client.SubscribeOptions{
  904. SubReqs: []*client.SubReq{
  905. &client.SubReq{
  906. TopicFilter: []byte("publish_out/#"),
  907. QoS: mqtt.QoS0,
  908. Handler: func(topicName, message []byte) {
  909. msgStr := string(message)
  910. t := strings.Split(string(topicName), "/")
  911. hostname := t[1]
  912. //Formato JSON multiplo
  913. //publish_out/170361001234 [{"timestamp":"2025-06-11T11:27:28.492Z","type":"Gateway","mac":"E4B3230DB5CC","nums":10},{"timestamp":"2025-06-11T11:27:28.483Z","mac":"36CE2D7CA4E5","rssi":-27,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.586Z","mac":"36CE2D7CA4E5","rssi":-30,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.612Z","mac":"406260A302FC","rssi":-35,"rawData":"02011A020A0B0BFF4C001006371AAE2F6F5B"},{"timestamp":"2025-06-11T11:27:28.798Z","mac":"36CE2D7CA4E5","rssi":-28,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.905Z","mac":"36CE2D7CA4E5","rssi":-30,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.945Z","mac":"C300003947DF","rssi":-32,"rawData":"0201061AFF4C000215FDA50693A4E24FB1AFCFC6EB0764782500000000C5"},{"timestamp":"2025-06-11T11:27:29.013Z","mac":"36CE2D7CA4E5","rssi":-29,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:29.120Z","mac":"36CE2D7CA4E5","rssi":-27,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:29.166Z","mac":"406260A302FC","rssi":-34,"rawData":"02011A020A0B0BFF4C001006371AAE2F6F5B"},{"timestamp":"2025-06-11T11:27:29.337Z","mac":"36CE2D7CA4E5","rssi":-26,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"}]
  914. if strings.HasPrefix(msgStr, "[") {
  915. var readings []RawReading
  916. err := json.Unmarshal(message, &readings)
  917. if err != nil {
  918. log.Printf("Errore parsing JSON: %v", err)
  919. return
  920. }
  921. for _, reading := range readings {
  922. if reading.Type == "Gateway" {
  923. continue
  924. }
  925. incoming := Incoming_json{
  926. Hostname: hostname,
  927. MAC: reading.MAC,
  928. RSSI: int64(reading.RSSI),
  929. Data: reading.RawData,
  930. HB_ButtonCounter: parseButtonState(reading.RawData),
  931. }
  932. incoming_updates_chan <- incoming
  933. }
  934. } else {
  935. //Formato CSV
  936. //ingics solo annuncio
  937. //publish_out/171061001180 $GPRP,C83F8F17DB35,F5B0B0419FEF,-44,02010612FF590080BC280102FFFFFFFF000000000000,1749648798
  938. //ingics tasto premuto
  939. //publish_out/171061001180 $GPRP,C83F8F17DB35,F5B0B0419FEF,-44,02010612FF590080BC280103FFFFFFFF000000000000,1749648798
  940. s := strings.Split(string(message), ",")
  941. if len(s) < 6 {
  942. log.Printf("Messaggio CSV non valido: %s", msgStr)
  943. return
  944. }
  945. rawdata := s[4]
  946. buttonCounter := parseButtonState(rawdata)
  947. if buttonCounter > 0 {
  948. incoming := Incoming_json{}
  949. i, _ := strconv.ParseInt(s[3], 10, 64)
  950. incoming.Hostname = hostname
  951. incoming.Beacon_type = "hb_button"
  952. incoming.MAC = s[1]
  953. incoming.RSSI = i
  954. incoming.Data = rawdata
  955. incoming.HB_ButtonCounter = buttonCounter
  956. read_line := strings.TrimRight(string(s[5]), "\r\n")
  957. it, err33 := strconv.Atoi(read_line)
  958. if err33 != nil {
  959. fmt.Println(it)
  960. fmt.Println(err33)
  961. os.Exit(2)
  962. }
  963. incoming_updates_chan <- incoming
  964. }
  965. }
  966. },
  967. },
  968. },
  969. })
  970. if err != nil {
  971. panic(err)
  972. }
  973. fmt.Println("CONNECTED TO MQTT")
  974. fmt.Println("\n ")
  975. fmt.Println("Visit http://" + *http_host_path_ptr + " on your browser to see the web interface")
  976. fmt.Println("\n ")
  977. go startServer()
  978. // Wait for receiving a signal.
  979. <-sigc
  980. // Disconnect the Network Connection.
  981. if err := cli.Disconnect(); err != nil {
  982. panic(err)
  983. }
  984. }
  985. func startServer() {
  986. headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
  987. originsOk := handlers.AllowedOrigins([]string{"*"})
  988. methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE","OPTIONS"})
  989. // Set up HTTP server
  990. r := mux.NewRouter()
  991. r.HandleFunc("/api/results", resultsHandler)
  992. r.HandleFunc("/api/beacons/{beacon_id}", beaconsDeleteHandler).Methods("DELETE")
  993. r.HandleFunc("/api/beacons", beaconsListHandler).Methods("GET")
  994. r.HandleFunc("/api/beacons", beaconsAddHandler).Methods("POST") //since beacons are hashmap, just have put and post be same thing. it'll either add or modify that entry
  995. r.HandleFunc("/api/beacons", beaconsAddHandler).Methods("PUT")
  996. r.HandleFunc("/api/latest-beacons", latestBeaconsListHandler).Methods("GET")
  997. r.HandleFunc("/api/settings", settingsListHandler).Methods("GET")
  998. r.HandleFunc("/api/settings", settingsEditHandler).Methods("POST")
  999. r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir("static_html/js/"))))
  1000. r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("static_html/css/"))))
  1001. r.PathPrefix("/img/").Handler(http.StripPrefix("/img/", http.FileServer(http.Dir("static_html/img/"))))
  1002. r.PathPrefix("/").Handler(http.FileServer(http.Dir("static_html/")))
  1003. http.Handle("/", r)
  1004. mxWS := mux.NewRouter()
  1005. mxWS.HandleFunc("/ws/api/beacons", serveWs)
  1006. mxWS.HandleFunc("/ws/api/beacons/latest", serveLatestBeaconsWs)
  1007. mxWS.HandleFunc("/ws/broadcast", handleConnections)
  1008. http.Handle("/ws/", mxWS)
  1009. go func() {
  1010. log.Fatal(http.ListenAndServe(*httpws_host_path_ptr, nil))
  1011. }()
  1012. // Start listening for incoming chat messages
  1013. go handleMessages()
  1014. ///"/conf/etc/cert/certs/services/htdocs/majornet.crt", "/conf/etc/cert/private/services/htdocs/majornet.key"
  1015. http.ListenAndServe(*http_host_path_ptr, handlers.CORS(originsOk, headersOk, methodsOk)(r))
  1016. }
  1017. func handleConnections(w http.ResponseWriter, r *http.Request) {
  1018. // Upgrade initial GET request to a websocket
  1019. ws, err := upgrader.Upgrade(w, r, nil)
  1020. if err != nil {
  1021. log.Fatal(err)
  1022. }
  1023. // Make sure we close the connection when the function returns
  1024. defer ws.Close()
  1025. // Register our new client
  1026. clients[ws] = true
  1027. for {
  1028. var msg Message
  1029. // Read in a new message as JSON and map it to a Message object
  1030. err := ws.ReadJSON(&msg)
  1031. if err != nil {
  1032. log.Printf("error: %v", err)
  1033. delete(clients, ws)
  1034. break
  1035. }
  1036. // Send the newly received message to the broadcast channel
  1037. broadcast <- msg
  1038. }
  1039. }
  1040. func handleMessages() {
  1041. for {
  1042. // Grab the next message from the broadcast channel
  1043. msg := <-broadcast
  1044. // Send it out to every client that is currently connected
  1045. for client := range clients {
  1046. err := client.WriteJSON(msg)
  1047. if err != nil {
  1048. log.Printf("error: %v", err)
  1049. client.Close()
  1050. delete(clients, client)
  1051. }
  1052. }
  1053. }
  1054. }
  1055. func resultsHandler(w http.ResponseWriter, r *http.Request) {
  1056. http_results_lock.RLock()
  1057. js, err := json.Marshal(http_results)
  1058. http_results_lock.RUnlock()
  1059. if err != nil {
  1060. http.Error(w, err.Error(), http.StatusInternalServerError)
  1061. return
  1062. }
  1063. w.Write(js)
  1064. }
  1065. func beaconsListHandler(w http.ResponseWriter, r *http.Request) {
  1066. latest_list_lock.RLock()
  1067. js, err := json.Marshal(BEACONS)
  1068. latest_list_lock.RUnlock()
  1069. if err != nil {
  1070. http.Error(w, err.Error(), http.StatusInternalServerError)
  1071. return
  1072. }
  1073. w.Write(js)
  1074. }
  1075. func persistBeacons() error {
  1076. // gob it first
  1077. buf := &bytes.Buffer{}
  1078. enc := gob.NewEncoder(buf)
  1079. if err := enc.Encode(BEACONS); err != nil {
  1080. return err
  1081. }
  1082. key := []byte("beacons_list")
  1083. // store some data
  1084. err = db.Update(func(tx *bolt.Tx) error {
  1085. bucket, err := tx.CreateBucketIfNotExists(world)
  1086. if err != nil {
  1087. return err
  1088. }
  1089. err = bucket.Put(key, []byte(buf.String()))
  1090. if err != nil {
  1091. return err
  1092. }
  1093. return nil
  1094. })
  1095. return nil
  1096. }
  1097. func persistSettings() error {
  1098. // gob it first
  1099. buf := &bytes.Buffer{}
  1100. enc := gob.NewEncoder(buf)
  1101. if err := enc.Encode(settings); err != nil {
  1102. return err
  1103. }
  1104. key := []byte("settings")
  1105. // store some data
  1106. err = db.Update(func(tx *bolt.Tx) error {
  1107. bucket, err := tx.CreateBucketIfNotExists(world)
  1108. if err != nil {
  1109. return err
  1110. }
  1111. err = bucket.Put(key, []byte(buf.String()))
  1112. if err != nil {
  1113. return err
  1114. }
  1115. return nil
  1116. })
  1117. return nil
  1118. }
  1119. func beaconsAddHandler(w http.ResponseWriter, r *http.Request) {
  1120. decoder := json.NewDecoder(r.Body)
  1121. var in_beacon Beacon
  1122. err = decoder.Decode(&in_beacon)
  1123. if err != nil {
  1124. http.Error(w, err.Error(), 400)
  1125. return
  1126. }
  1127. //make sure name and beacon_id are present
  1128. if (len(strings.TrimSpace(in_beacon.Name)) == 0) || (len(strings.TrimSpace(in_beacon.Beacon_id)) == 0) {
  1129. http.Error(w, "name and beacon_id cannot be blank", 400)
  1130. return
  1131. }
  1132. BEACONS.Beacons[in_beacon.Beacon_id] = in_beacon
  1133. err := persistBeacons()
  1134. if err != nil {
  1135. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  1136. return
  1137. }
  1138. w.Write([]byte("ok"))
  1139. }
  1140. func beaconsDeleteHandler(w http.ResponseWriter, r *http.Request) {
  1141. vars := mux.Vars(r)
  1142. beacon_id := vars["beacon_id"]
  1143. delete(BEACONS.Beacons, beacon_id)
  1144. _, ok := Buttons_list[beacon_id]
  1145. if ok {
  1146. delete(Buttons_list, beacon_id)
  1147. }
  1148. err := persistBeacons()
  1149. if err != nil {
  1150. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  1151. return
  1152. }
  1153. w.Write([]byte("ok"))
  1154. }
  1155. func latestBeaconsListHandler(w http.ResponseWriter, r *http.Request) {
  1156. latest_list_lock.RLock()
  1157. var la = make([]Beacon, 0)
  1158. for _, b := range Latest_beacons_list {
  1159. la = append(la, b)
  1160. }
  1161. latest_list_lock.RUnlock()
  1162. js, err := json.Marshal(la)
  1163. if err != nil {
  1164. http.Error(w, err.Error(), http.StatusInternalServerError)
  1165. return
  1166. }
  1167. w.Write(js)
  1168. }
  1169. func settingsListHandler(w http.ResponseWriter, r *http.Request) {
  1170. js, err := json.Marshal(settings)
  1171. if err != nil {
  1172. http.Error(w, err.Error(), http.StatusInternalServerError)
  1173. return
  1174. }
  1175. w.Write(js)
  1176. }
  1177. func settingsEditHandler(w http.ResponseWriter, r *http.Request) {
  1178. decoder := json.NewDecoder(r.Body)
  1179. var in_settings Settings
  1180. err = decoder.Decode(&in_settings)
  1181. if err != nil {
  1182. http.Error(w, err.Error(), 400)
  1183. return
  1184. }
  1185. //make sure values are > 0
  1186. if (in_settings.Location_confidence <= 0) ||
  1187. (in_settings.Last_seen_threshold <= 0) ||
  1188. (in_settings.HA_send_interval <= 0) {
  1189. http.Error(w, "values must be greater than 0", 400)
  1190. return
  1191. }
  1192. settings = in_settings
  1193. err := persistSettings()
  1194. if err != nil {
  1195. http.Error(w, "trouble persisting settings, create bucket", 500)
  1196. return
  1197. }
  1198. w.Write([]byte("ok"))
  1199. }
  1200. //websocket stuff
  1201. func reader(ws *websocket.Conn) {
  1202. defer ws.Close()
  1203. ws.SetReadLimit(512)
  1204. ws.SetReadDeadline(time.Now().Add(pongWait))
  1205. ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
  1206. for {
  1207. _, _, err := ws.ReadMessage()
  1208. if err != nil {
  1209. break
  1210. }
  1211. }
  1212. }
  1213. func writer(ws *websocket.Conn) {
  1214. pingTicker := time.NewTicker(pingPeriod)
  1215. beaconTicker := time.NewTicker(beaconPeriod)
  1216. defer func() {
  1217. pingTicker.Stop()
  1218. beaconTicker.Stop()
  1219. ws.Close()
  1220. }()
  1221. for {
  1222. select {
  1223. case <-beaconTicker.C:
  1224. http_results_lock.RLock()
  1225. js, err := json.Marshal(http_results)
  1226. http_results_lock.RUnlock()
  1227. if err != nil {
  1228. js = []byte("error")
  1229. }
  1230. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1231. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  1232. return
  1233. }
  1234. case <-pingTicker.C:
  1235. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1236. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  1237. return
  1238. }
  1239. }
  1240. }
  1241. }
  1242. func serveWs(w http.ResponseWriter, r *http.Request) {
  1243. ws, err := upgrader.Upgrade(w, r, nil)
  1244. if err != nil {
  1245. if _, ok := err.(websocket.HandshakeError); !ok {
  1246. log.Println(err)
  1247. }
  1248. return
  1249. }
  1250. go writer(ws)
  1251. reader(ws)
  1252. }
  1253. func latestBeaconWriter(ws *websocket.Conn) {
  1254. pingTicker := time.NewTicker(pingPeriod)
  1255. beaconTicker := time.NewTicker(beaconPeriod)
  1256. defer func() {
  1257. pingTicker.Stop()
  1258. beaconTicker.Stop()
  1259. ws.Close()
  1260. }()
  1261. for {
  1262. select {
  1263. case <-beaconTicker.C:
  1264. latest_list_lock.RLock()
  1265. var la = make([]Beacon, 0)
  1266. for _, b := range Latest_beacons_list {
  1267. la = append(la, b)
  1268. }
  1269. latest_list_lock.RUnlock()
  1270. js, err := json.Marshal(la)
  1271. if err != nil {
  1272. js = []byte("error")
  1273. }
  1274. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1275. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  1276. return
  1277. }
  1278. case <-pingTicker.C:
  1279. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1280. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  1281. return
  1282. }
  1283. }
  1284. }
  1285. }
  1286. func serveLatestBeaconsWs(w http.ResponseWriter, r *http.Request) {
  1287. ws, err := upgrader.Upgrade(w, r, nil)
  1288. if err != nil {
  1289. if _, ok := err.(websocket.HandshakeError); !ok {
  1290. log.Println(err)
  1291. }
  1292. return
  1293. }
  1294. go latestBeaconWriter(ws)
  1295. reader(ws)
  1296. }