Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

881 righe
26 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/gob"
  5. "encoding/json"
  6. "flag"
  7. "fmt"
  8. "io/ioutil"
  9. "log"
  10. "math"
  11. "net/http"
  12. "os"
  13. "os/signal"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "time"
  18. //"./utils"
  19. "os/exec"
  20. "github.com/boltdb/bolt"
  21. "gopkg.in/natefinch/lumberjack.v2"
  22. "github.com/yosssi/gmq/mqtt"
  23. "github.com/yosssi/gmq/mqtt/client"
  24. "github.com/gorilla/websocket"
  25. )
  26. var clients = make(map[*websocket.Conn]bool) // connected clients
  27. var broadcast = make(chan Message)
  28. var BEACONS Beacons_list
  29. var Buttons_list map[string]Button
  30. var cli *client.Client
  31. var http_results HTTP_locations_list
  32. var http_results_lock sync.RWMutex
  33. var Latest_beacons_list map[string]Beacon
  34. var latest_list_lock sync.RWMutex
  35. var db *bolt.DB
  36. var err error
  37. var world = []byte("presence")
  38. var (
  39. // /logpath = flag.String("logpath", "/data/var/log/presence/presence.log", "Log Path")
  40. )
  41. var (
  42. // Websocket http upgrader
  43. upgrader = websocket.Upgrader{
  44. ReadBufferSize: 1024,
  45. WriteBufferSize: 1024,
  46. CheckOrigin: func(r *http.Request) bool {
  47. return true
  48. },
  49. }
  50. )
  51. var settings = Settings{
  52. Location_confidence: 4,
  53. Last_seen_threshold: 15,
  54. Beacon_metrics_size: 30,
  55. HA_send_interval: 5,
  56. HA_send_changes_only: false,
  57. }
  58. // utility function
  59. func parseButtonState(raw string) int64 {
  60. raw = strings.ToUpper(raw)
  61. // Minew B7 / C7 / D7 - frame tipo: 0201060303E1FF1216E1FFA103...
  62. if strings.HasPrefix(raw, "0201060303E1FF12") && len(raw) >= 38 {
  63. // La posizione 34-38 (indice 26:30) contiene il buttonCounter su 2 byte (hex)
  64. buttonField := raw[34:38] // NB: offset 34-38 zero-based
  65. if buttonValue, err := strconv.ParseInt(buttonField, 16, 64); err == nil {
  66. return buttonValue
  67. }
  68. }
  69. // Ingics (02010612FF590)
  70. if strings.HasPrefix(raw, "02010612FF590") && len(raw) >= 24 {
  71. counterField := raw[22:24]
  72. buttonState, err := strconv.ParseInt(counterField, 16, 64)
  73. if err == nil {
  74. return buttonState
  75. }
  76. }
  77. // Aggiungeremo qui facilmente nuovi beacon in futuro
  78. return 0
  79. }
  80. func twos_comp(inp string) int64 {
  81. i, _ := strconv.ParseInt("0x"+inp, 0, 64)
  82. return i - 256
  83. }
  84. func getBeaconID(incoming Incoming_json) string {
  85. unique_id := fmt.Sprintf("%s", incoming.MAC)
  86. /*if incoming.Beacon_type == "ibeacon" {
  87. unique_id = fmt.Sprintf("%s_%s_%s", incoming.UUID, incoming.Major, incoming.Minor)
  88. } else if incoming.Beacon_type == "eddystone" {
  89. unique_id = fmt.Sprintf("%s_%s", incoming.Namespace, incoming.Instance_id)
  90. } else if incoming.Beacon_type == "hb_button" {
  91. unique_id = fmt.Sprintf("%s_%s", incoming.Namespace, incoming.Instance_id)
  92. }*/
  93. return unique_id
  94. }
  95. func incomingBeaconFilter(incoming Incoming_json) Incoming_json {
  96. out_json := incoming
  97. if incoming.Beacon_type == "hb_button" {
  98. //do additional checks here to detect if a Habby Bubbles Button
  99. // looks like 020104020a0011ff045600012d3859db59e1000b9453
  100. raw_data := incoming.Data
  101. //company_id := []byte{0x04, 0x56}
  102. //product_id := []byte{0x00, 0x01}
  103. hb_button_prefix_str := fmt.Sprintf("02010612FF5900")
  104. if strings.HasPrefix(raw_data, hb_button_prefix_str) {
  105. out_json.Namespace = "ddddeeeeeeffff5544ff"
  106. //out_json.Instance_id = raw_data[24:36]
  107. counter_str := fmt.Sprintf("0x%s", raw_data[22:24])
  108. counter, _ := strconv.ParseInt(counter_str, 0, 64)
  109. out_json.HB_ButtonCounter = counter
  110. battery_str := fmt.Sprintf("0x%s%s", raw_data[20:22], raw_data[18:20])
  111. ////fmt.Println("battery has %s\n", battery_str)
  112. battery, _ := strconv.ParseInt(battery_str, 0, 64)
  113. out_json.HB_Battery = battery
  114. out_json.TX_power = fmt.Sprintf("0x%s", "4")
  115. out_json.Beacon_type = "hb_button"
  116. out_json.HB_ButtonMode = "presence_button"
  117. ///fmt.Println("Button adv has %#v\n", out_json)
  118. }
  119. }
  120. return out_json
  121. }
  122. func processButton(bbeacon Beacon, cl *client.Client) {
  123. btn := Button{Name: bbeacon.Name}
  124. btn.Button_id = bbeacon.Beacon_id
  125. btn.Button_type = bbeacon.Beacon_type
  126. btn.Button_location = bbeacon.Previous_location
  127. btn.Incoming_JSON = bbeacon.Incoming_JSON
  128. btn.Distance = bbeacon.Distance
  129. btn.Last_seen = bbeacon.Last_seen
  130. btn.HB_ButtonCounter = bbeacon.HB_ButtonCounter
  131. btn.HB_Battery = bbeacon.HB_Battery
  132. btn.HB_RandomNonce = bbeacon.HB_RandomNonce
  133. btn.HB_ButtonMode = bbeacon.HB_ButtonMode
  134. nonce, ok := Buttons_list[btn.Button_id]
  135. if !ok || nonce.HB_RandomNonce != btn.HB_RandomNonce {
  136. // send the button message to MQTT
  137. sendButtonMessage(btn, cl)
  138. }
  139. Buttons_list[btn.Button_id] = btn
  140. }
  141. func getiBeaconDistance(rssi int64, power string) float64 {
  142. ratio := float64(rssi) * (1.0 / float64(twos_comp(power)))
  143. //fmt.Printf("beaconpower: rssi %d ratio %e power %e \n",rssi, ratio, float64(twos_comp(power)))
  144. distance := 100.0
  145. if ratio < 1.0 {
  146. distance = math.Pow(ratio, 10)
  147. } else {
  148. distance = (0.89976)*math.Pow(ratio, 7.7095) + 0.111
  149. }
  150. return distance
  151. }
  152. func getBeaconDistance(incoming Incoming_json) float64 {
  153. distance := 1000.0
  154. distance = getiBeaconDistance(incoming.RSSI, incoming.TX_power)
  155. //distance = math.Abs(float64(incoming.RSSI))
  156. return distance
  157. }
  158. func getAverageDistance(beacon_metrics []beacon_metric) float64 {
  159. total := 0.0
  160. for _, v := range beacon_metrics {
  161. total += v.distance
  162. }
  163. return (total / float64(len(beacon_metrics)))
  164. }
  165. func sendHARoomMessage(beacon_id string, beacon_name string, distance float64, location string, cl *client.Client) {
  166. //first make the json
  167. ha_msg, err := json.Marshal(HA_message{Beacon_id: beacon_id, Beacon_name: beacon_name, Distance: distance})
  168. if err != nil {
  169. panic(err)
  170. }
  171. //send the message to HA
  172. err = cl.Publish(&client.PublishOptions{
  173. QoS: mqtt.QoS1,
  174. TopicName: []byte("afa-systems/presence/ha/" + location),
  175. Message: ha_msg,
  176. })
  177. if err != nil {
  178. panic(err)
  179. }
  180. }
  181. func sendButtonMessage(btn Button, cl *client.Client) {
  182. //first make the json
  183. btn_msg, err := json.Marshal(btn)
  184. if err != nil {
  185. panic(err)
  186. }
  187. //send the message to HA
  188. err = cl.Publish(&client.PublishOptions{
  189. QoS: mqtt.QoS1,
  190. TopicName: []byte("afa-systems/presence/button/" + btn.Button_id),
  191. Message: btn_msg,
  192. })
  193. if err != nil {
  194. panic(err)
  195. }
  196. }
  197. func sendButtonPressed(bcn Beacon, cl *client.Client) {
  198. //first make the json
  199. btn_msg, err := json.Marshal(bcn)
  200. if err != nil {
  201. panic(err)
  202. }
  203. //send the message to HA
  204. err = cl.Publish(&client.PublishOptions{
  205. QoS: mqtt.QoS1,
  206. TopicName: []byte("afa-systems/presence/button/" + bcn.Beacon_id),
  207. Message: btn_msg,
  208. })
  209. if err != nil {
  210. panic(err)
  211. }
  212. ///utils.Log.Printf("%s pressed ",bcn.Beacon_id)
  213. 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)
  214. ///utils.Log.Printf("%s",s)
  215. err, out, errout := Shellout(s)
  216. if err != nil {
  217. log.Printf("error: %v\n", err)
  218. }
  219. fmt.Println("--- stdout ---")
  220. fmt.Println(out)
  221. fmt.Println("--- stderr ---")
  222. fmt.Println(errout)
  223. }
  224. func getLikelyLocations(settings Settings, locations_list Locations_list, cl *client.Client) {
  225. // create the http results structure
  226. http_results_lock.Lock()
  227. http_results = HTTP_locations_list{}
  228. http_results.Beacons = make([]HTTP_location, 0)
  229. ///http_results.Buttons = make([]Button, 0)
  230. http_results_lock.Unlock()
  231. should_persist := false
  232. // iterate through the beacons we want to search for
  233. for _, beacon := range BEACONS.Beacons {
  234. if len(beacon.beacon_metrics) == 0 {
  235. ////fmt.Printf("beacon_metrics = 0:\n")
  236. continue
  237. }
  238. if (int64(time.Now().Unix()) - (beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp)) > settings.Last_seen_threshold {
  239. ////fmt.Printf("beacon_metrics timestamp = %s %s \n",beacon.Name, beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp )
  240. if beacon.expired_location == "expired" {
  241. //beacon.Location_confidence = - 1
  242. continue
  243. } else {
  244. beacon.expired_location = "expired"
  245. msg := Message{
  246. Email: beacon.Previous_confident_location,
  247. Username: beacon.Name,
  248. Message: beacon.expired_location}
  249. res1B, _ := json.Marshal(msg)
  250. fmt.Println(string(res1B))
  251. if err != nil {
  252. log.Printf("error: %v", err)
  253. }
  254. // Send the newly received message to the broadcast channel
  255. broadcast <- msg
  256. }
  257. } else {
  258. beacon.expired_location = ""
  259. }
  260. best_location := Best_location{}
  261. // go through its beacon metrics and pick out the location that appears most often
  262. loc_list := make(map[string]float64)
  263. seen_weight := 1.5
  264. rssi_weight := 0.75
  265. for _, metric := range beacon.beacon_metrics {
  266. loc, ok := loc_list[metric.location]
  267. if !ok {
  268. loc = seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
  269. } else {
  270. loc = loc + seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
  271. }
  272. loc_list[metric.location] = loc
  273. }
  274. best_name := ""
  275. ts := 0.0
  276. for name, times_seen := range loc_list {
  277. if times_seen > ts {
  278. best_name = name
  279. ts = times_seen
  280. }
  281. }
  282. /////fmt.Printf("BEST LOCATION FOR %s IS: %s with score: %f\n", beacon.Name, best_name, ts)
  283. 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}
  284. beacon.Location_history = append(beacon.Location_history, best_location.name)
  285. if len(beacon.Location_history) > 10 {
  286. beacon.Location_history = beacon.Location_history[1:] // manteniamo solo gli ultimi 10
  287. }
  288. // Calcoliamo la location più votata nello storico
  289. location_counts := make(map[string]int)
  290. for _, loc := range beacon.Location_history {
  291. location_counts[loc]++
  292. }
  293. max_count := 0
  294. most_common_location := ""
  295. for loc, count := range location_counts {
  296. if count > max_count {
  297. max_count = count
  298. most_common_location = loc
  299. }
  300. }
  301. // Applichiamo un filtro: consideriamo il cambio solo se almeno 7 su 10 votano per una location
  302. if max_count >= 7 {
  303. beacon.Previous_location = most_common_location
  304. if most_common_location == beacon.Previous_confident_location {
  305. beacon.Location_confidence++
  306. } else {
  307. beacon.Location_confidence = 1
  308. beacon.Previous_confident_location = most_common_location
  309. }
  310. }
  311. //create an http result from this
  312. r := HTTP_location{}
  313. r.Distance = best_location.distance
  314. r.Name = beacon.Name
  315. r.Beacon_name = beacon.Name
  316. r.Beacon_id = beacon.Beacon_id
  317. r.Beacon_type = beacon.Beacon_type
  318. r.HB_Battery = beacon.HB_Battery
  319. r.HB_ButtonMode = beacon.HB_ButtonMode
  320. r.HB_ButtonCounter = beacon.HB_ButtonCounter
  321. r.Location = best_location.name
  322. r.Last_seen = best_location.last_seen
  323. ////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)
  324. if (beacon.Location_confidence == settings.Location_confidence && beacon.Previous_confident_location != best_location.name) || beacon.expired_location == "expired" {
  325. // location has changed, send an mqtt message
  326. should_persist = true
  327. fmt.Printf("detected a change!!! %#v\n\n", beacon)
  328. if beacon.Previous_confident_location == "expired" && beacon.expired_location == "" {
  329. msg := Message{
  330. Email: beacon.Previous_confident_location,
  331. Username: beacon.Name,
  332. Message: "OK"}
  333. res1B, _ := json.Marshal(msg)
  334. fmt.Println(string(res1B))
  335. if err != nil {
  336. log.Printf("error: %v", err)
  337. }
  338. // Send the newly received message to the broadcast channel
  339. broadcast <- msg
  340. }
  341. beacon.Location_confidence = 0
  342. location := ""
  343. if beacon.expired_location == "expired" {
  344. location = "expired"
  345. } else {
  346. location = best_location.name
  347. }
  348. //first make the json
  349. 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()})
  350. if err != nil {
  351. continue
  352. }
  353. //send the message
  354. err = cl.Publish(&client.PublishOptions{
  355. QoS: mqtt.QoS1,
  356. TopicName: []byte("afa-systems/presence/changes"),
  357. Message: js,
  358. })
  359. if err != nil {
  360. panic(err)
  361. }
  362. 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)
  363. ///utils.Log.Printf("%s",s)
  364. err, out, errout := Shellout(s)
  365. if err != nil {
  366. log.Printf("error: %v\n", err)
  367. }
  368. fmt.Println("--- stdout ---")
  369. fmt.Println(out)
  370. fmt.Println("--- stderr ---")
  371. fmt.Println(errout)
  372. //////beacon.logger.Printf("Log content: user id %v \n", best_location.name)
  373. if settings.HA_send_changes_only {
  374. sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
  375. }
  376. if beacon.expired_location == "expired" {
  377. beacon.Previous_confident_location = "expired"
  378. r.Location = "expired"
  379. } else {
  380. beacon.Previous_confident_location = best_location.name
  381. }
  382. ///beacon.Previous_confident_location = best_location.name
  383. }
  384. beacon.Previous_location = best_location.name
  385. r.Previous_confident_location = beacon.expired_location
  386. BEACONS.Beacons[beacon.Beacon_id] = beacon
  387. http_results_lock.Lock()
  388. http_results.Beacons = append(http_results.Beacons, r)
  389. http_results_lock.Unlock()
  390. if best_location.name != "" {
  391. if !settings.HA_send_changes_only {
  392. secs := int64(time.Now().Unix())
  393. if secs%settings.HA_send_interval == 0 {
  394. sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
  395. }
  396. }
  397. }
  398. err := cl.Publish(&client.PublishOptions{
  399. QoS: mqtt.QoS0,
  400. TopicName: []byte("afa-systems/presence"),
  401. Message: []byte(fmt.Sprintf("%s is most likely in %s with average distance %f", beacon.Name, best_location.name, best_location.distance)),
  402. })
  403. if err != nil {
  404. panic(err)
  405. }
  406. }
  407. if should_persist {
  408. persistBeacons()
  409. }
  410. }
  411. func IncomingMQTTProcessor(updateInterval time.Duration, cl *client.Client, db *bolt.DB, logger []*user) chan<- Incoming_json {
  412. incoming_msgs_chan := make(chan Incoming_json, 2000)
  413. // load initial BEACONS
  414. BEACONS.Beacons = make(map[string]Beacon)
  415. // retrieve the data
  416. // create bucket if not exist
  417. err = db.Update(func(tx *bolt.Tx) error {
  418. _, err := tx.CreateBucketIfNotExists(world)
  419. if err != nil {
  420. return err
  421. }
  422. return nil
  423. })
  424. err = db.View(func(tx *bolt.Tx) error {
  425. bucket := tx.Bucket(world)
  426. if bucket == nil {
  427. return err
  428. }
  429. key := []byte("beacons_list")
  430. val := bucket.Get(key)
  431. if val != nil {
  432. buf := bytes.NewBuffer(val)
  433. dec := gob.NewDecoder(buf)
  434. err = dec.Decode(&BEACONS)
  435. if err != nil {
  436. log.Fatal("decode error:", err)
  437. }
  438. }
  439. key = []byte("buttons_list")
  440. val = bucket.Get(key)
  441. if val != nil {
  442. buf := bytes.NewBuffer(val)
  443. dec := gob.NewDecoder(buf)
  444. err = dec.Decode(&Buttons_list)
  445. if err != nil {
  446. log.Fatal("decode error:", err)
  447. }
  448. }
  449. key = []byte("settings")
  450. val = bucket.Get(key)
  451. if val != nil {
  452. buf := bytes.NewBuffer(val)
  453. dec := gob.NewDecoder(buf)
  454. err = dec.Decode(&settings)
  455. if err != nil {
  456. log.Fatal("decode error:", err)
  457. }
  458. }
  459. return nil
  460. })
  461. if err != nil {
  462. log.Fatal(err)
  463. }
  464. Latest_beacons_list = make(map[string]Beacon)
  465. Buttons_list = make(map[string]Button)
  466. //create a map of locations, looked up by hostnames
  467. locations_list := Locations_list{}
  468. ls := make(map[string]Location)
  469. locations_list.locations = ls
  470. ticker := time.NewTicker(updateInterval)
  471. go func() {
  472. for {
  473. select {
  474. case <-ticker.C:
  475. getLikelyLocations(settings, locations_list, cl)
  476. case incoming := <-incoming_msgs_chan:
  477. func() {
  478. defer func() {
  479. if err := recover(); err != nil {
  480. log.Println("work failed:", err)
  481. }
  482. }()
  483. incoming = incomingBeaconFilter(incoming)
  484. this_beacon_id := getBeaconID(incoming)
  485. now := time.Now().Unix()
  486. beacon, ok := BEACONS.Beacons[this_beacon_id]
  487. if !ok {
  488. //should be unique
  489. //if it's already in list, forget it.
  490. latest_list_lock.Lock()
  491. x, ok := Latest_beacons_list[this_beacon_id]
  492. if ok {
  493. //update its timestamp
  494. x.Last_seen = now
  495. x.Incoming_JSON = incoming
  496. x.Distance = getBeaconDistance(incoming)
  497. Latest_beacons_list[this_beacon_id] = x
  498. } else {
  499. 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)}
  500. }
  501. for k, v := range Latest_beacons_list {
  502. if (now - v.Last_seen) > 10 { // 10 seconds
  503. delete(Latest_beacons_list, k)
  504. }
  505. }
  506. latest_list_lock.Unlock()
  507. //continue
  508. return
  509. }
  510. beacon.Incoming_JSON = incoming
  511. beacon.Last_seen = now
  512. beacon.Beacon_type = incoming.Beacon_type
  513. beacon.HB_ButtonCounter = incoming.HB_ButtonCounter
  514. beacon.HB_Battery = incoming.HB_Battery
  515. beacon.HB_RandomNonce = incoming.HB_RandomNonce
  516. beacon.HB_ButtonMode = incoming.HB_ButtonMode
  517. ////fmt.Println("button pressed " + this_beacon_id + " at " + strconv.Itoa(int(incoming.HB_ButtonCounter)) )
  518. if beacon.beacon_metrics == nil {
  519. beacon.beacon_metrics = make([]beacon_metric, settings.Beacon_metrics_size)
  520. }
  521. //create metric for this beacon
  522. this_metric := beacon_metric{}
  523. this_metric.distance = getBeaconDistance(incoming)
  524. this_metric.timestamp = now
  525. this_metric.rssi = int64(incoming.RSSI)
  526. this_metric.location = incoming.Hostname
  527. beacon.beacon_metrics = append(beacon.beacon_metrics, this_metric)
  528. ///fmt.Printf("APPENDING a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
  529. if len(beacon.beacon_metrics) > settings.Beacon_metrics_size {
  530. //fmt.Printf("deleting a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
  531. beacon.beacon_metrics = append(beacon.beacon_metrics[:0], beacon.beacon_metrics[0+1:]...)
  532. }
  533. //fmt.Printf("%#v\n", beacon.beacon_metrics)
  534. if beacon.HB_ButtonCounter_Prev != beacon.HB_ButtonCounter {
  535. beacon.HB_ButtonCounter_Prev = incoming.HB_ButtonCounter
  536. // send the button message to MQTT
  537. sendButtonPressed(beacon, cl)
  538. }
  539. BEACONS.Beacons[beacon.Beacon_id] = beacon
  540. /*if beacon.Beacon_type == "hb_button" {
  541. processButton(beacon, cl)
  542. }*/
  543. //lookup location by hostname in locations
  544. location, ok := locations_list.locations[incoming.Hostname]
  545. if !ok {
  546. //create the location
  547. locations_list.locations[incoming.Hostname] = Location{}
  548. location, ok = locations_list.locations[incoming.Hostname]
  549. location.name = incoming.Hostname
  550. }
  551. locations_list.locations[incoming.Hostname] = location
  552. }()
  553. }
  554. }
  555. }()
  556. return incoming_msgs_chan
  557. }
  558. func ParseTimeStamp(utime string) (string, error) {
  559. i, err := strconv.ParseInt(utime, 10, 64)
  560. if err != nil {
  561. return "", err
  562. }
  563. t := time.Unix(i, 0)
  564. return t.Format(time.UnixDate), nil
  565. }
  566. var http_host_path_ptr *string
  567. // var https_host_path_ptr *string
  568. var httpws_host_path_ptr *string
  569. //var httpwss_host_path_ptr *string
  570. type Todo struct {
  571. Id string `json:"id"`
  572. Value string `json:"value" binding:"required"`
  573. }
  574. type Job interface {
  575. ExitChan() chan error
  576. Run(todos map[string]Todo) (map[string]Todo, error)
  577. }
  578. func ProcessJobs(jobs chan Job, db string) {
  579. for {
  580. j := <-jobs
  581. todos := make(map[string]Todo, 0)
  582. content, err := ioutil.ReadFile(db)
  583. if err == nil {
  584. if err = json.Unmarshal(content, &todos); err == nil {
  585. todosMod, err := j.Run(todos)
  586. if err == nil && todosMod != nil {
  587. b, err := json.Marshal(todosMod)
  588. if err == nil {
  589. err = ioutil.WriteFile(db, b, 0644)
  590. }
  591. }
  592. }
  593. }
  594. j.ExitChan() <- err
  595. }
  596. }
  597. type user struct {
  598. id string
  599. logger *log.Logger
  600. }
  601. const ShellToUse = "bash"
  602. func Shellout(command string) (error, string, string) {
  603. var stdout bytes.Buffer
  604. var stderr bytes.Buffer
  605. ///utils.Log.Printf("command: %s",command)
  606. cmd := exec.Command(ShellToUse, "-c", command)
  607. cmd.Stdout = &stdout
  608. cmd.Stderr = &stderr
  609. err := cmd.Run()
  610. return err, stdout.String(), stderr.String()
  611. }
  612. func createUser(id string, logWanted bool) user {
  613. var l *log.Logger
  614. if logWanted {
  615. // Here the log content will be added in the user log file
  616. userFIle := &lumberjack.Logger{
  617. Filename: "/data/var/log/presence/presence/log_" + id + ".log",
  618. MaxSize: 250, // mb
  619. MaxBackups: 5,
  620. MaxAge: 10, // in days
  621. }
  622. l = log.New(userFIle, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
  623. } else {
  624. // Here the log content will go nowhere
  625. l = log.New(ioutil.Discard, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
  626. }
  627. return user{id, l}
  628. }
  629. func main() {
  630. loggers := []*user{}
  631. http_host_path_ptr = flag.String("http_host_path", "0.0.0.0:8080", "The host:port that the HTTP server should listen on")
  632. //https_host_path_ptr = flag.String("https_host_path", "0.0.0.0:5443", "The host:port that the HTTP server should listen on")
  633. httpws_host_path_ptr = flag.String("httpws_host_path", "0.0.0.0:8088", "The host:port websocket listen")
  634. //httpwss_host_path_ptr = flag.String("httpwss_host_path", "0.0.0.0:8443", "The host:port secure websocket listen")
  635. mqtt_host_ptr := flag.String("mqtt_host", "localhost:1883", "The host:port of the MQTT server to listen for beacons on")
  636. mqtt_username_ptr := flag.String("mqtt_username", "none", "The username needed to connect to the MQTT server, 'none' if it doesn't need one")
  637. mqtt_password_ptr := flag.String("mqtt_password", "none", "The password needed to connect to the MQTT server, 'none' if it doesn't need one")
  638. mqtt_client_id_ptr := flag.String("mqtt_client_id", "presence-detector", "The client ID for the MQTT server")
  639. flag.Parse()
  640. sigc := make(chan os.Signal, 1)
  641. signal.Notify(sigc, os.Interrupt, os.Kill)
  642. // Create an MQTT Client.
  643. cli := client.New(&client.Options{
  644. // Define the processing of the error handler.
  645. ErrorHandler: func(err error) {
  646. fmt.Println(err)
  647. },
  648. })
  649. // Terminate the Client.
  650. defer cli.Terminate()
  651. //open the database
  652. db, err = bolt.Open("/data/conf/presence/presence.db", 0644, nil)
  653. if err != nil {
  654. log.Fatal(err)
  655. }
  656. defer db.Close()
  657. // Connect to the MQTT Server.
  658. err = cli.Connect(&client.ConnectOptions{
  659. Network: "tcp",
  660. Address: *mqtt_host_ptr,
  661. ClientID: []byte(*mqtt_client_id_ptr),
  662. UserName: []byte(*mqtt_username_ptr),
  663. Password: []byte(*mqtt_password_ptr),
  664. })
  665. if err != nil {
  666. panic(err)
  667. }
  668. incoming_updates_chan := IncomingMQTTProcessor(1*time.Second, cli, db, loggers)
  669. // Subscribe to topics.
  670. err = cli.Subscribe(&client.SubscribeOptions{
  671. SubReqs: []*client.SubReq{
  672. &client.SubReq{
  673. TopicFilter: []byte("publish_out/#"),
  674. QoS: mqtt.QoS0,
  675. Handler: func(topicName, message []byte) {
  676. msgStr := string(message)
  677. t := strings.Split(string(topicName), "/")
  678. hostname := t[1]
  679. //Formato JSON multiplo
  680. //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"}]
  681. if strings.HasPrefix(msgStr, "[") {
  682. var readings []RawReading
  683. err := json.Unmarshal(message, &readings)
  684. if err != nil {
  685. log.Printf("Errore parsing JSON: %v", err)
  686. return
  687. }
  688. for _, reading := range readings {
  689. if reading.Type == "Gateway" {
  690. continue
  691. }
  692. incoming := Incoming_json{
  693. Hostname: hostname,
  694. MAC: reading.MAC,
  695. RSSI: int64(reading.RSSI),
  696. Data: reading.RawData,
  697. HB_ButtonCounter: parseButtonState(reading.RawData),
  698. }
  699. incoming_updates_chan <- incoming
  700. }
  701. } else {
  702. //publish_out/171061001180 $GPRP,C83F8F17DB35,F5B0B0419FEF,-44,02010612FF590080BC280103FFFFFFFF000000000000,1749648798
  703. s := strings.Split(string(message), ",")
  704. if len(s) < 6 {
  705. log.Printf("Messaggio CSV non valido: %s", msgStr)
  706. return
  707. }
  708. rawdata := s[4]
  709. buttonCounter := parseButtonState(rawdata)
  710. if buttonCounter > 0 {
  711. incoming := Incoming_json{}
  712. i, _ := strconv.ParseInt(s[3], 10, 64)
  713. incoming.Hostname = hostname
  714. incoming.Beacon_type = "hb_button"
  715. incoming.MAC = s[1]
  716. incoming.RSSI = i
  717. incoming.Data = rawdata
  718. incoming.HB_ButtonCounter = buttonCounter
  719. read_line := strings.TrimRight(string(s[5]), "\r\n")
  720. it, err33 := strconv.Atoi(read_line)
  721. if err33 != nil {
  722. fmt.Println(it)
  723. fmt.Println(err33)
  724. os.Exit(2)
  725. }
  726. incoming_updates_chan <- incoming
  727. }
  728. }
  729. },
  730. },
  731. },
  732. })
  733. if err != nil {
  734. panic(err)
  735. }
  736. fmt.Println("CONNECTED TO MQTT")
  737. fmt.Println("\n ")
  738. fmt.Println("Visit http://" + *http_host_path_ptr + " on your browser to see the web interface")
  739. fmt.Println("\n ")
  740. go startServer()
  741. // Wait for receiving a signal.
  742. <-sigc
  743. // Disconnect the Network Connection.
  744. if err := cli.Disconnect(); err != nil {
  745. panic(err)
  746. }
  747. }