Advertisement
Guest User

Untitled

a guest
Jun 7th, 2018
171
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.42 KB | None | 0 0
  1. #
  2. # calc_gps_coords.py
  3. #
  4. # based on meta_device_tracker.py by oakbrad
  5. # https://github.com/oakbrad/brad-homeassistant-config/blob/master/python_scripts/meta_device_tracker.py
  6. #
  7. # adapted GPS Haversine calculation from nathan rooy
  8. # https://nathanrooy.github.io/posts/2016-09-07/haversine-with-python/
  9. #
  10.  
  11. # Enable debugging
  12. ENABLE_DEBUG = 0
  13.  
  14. # Suggested max_gps_accuracy for Owntracks is 200m (656ft)
  15. # https://www.home-assistant.io/components/device_tracker.owntracks/
  16. #
  17. # We'll use it too but use it to determine if the new value is greater-than current
  18. # value to indicate significant movement. The idea behind this is to prevent false
  19. # positive zone-transitions when stationary without line-of-sight of satellites
  20. # and GPS lock drifts. Calculation is determined by using the Haversine formula. If
  21. # the new value is greater-than current, we allow the meta_tracker to update otherwise
  22. # we retain previous value.
  23. #
  24. MAX_GPS_METERS = 200
  25.  
  26. # Get Data from Automation Trigger
  27. triggeredEntity = data.get('entity_id')
  28. metatrackerName = "device_tracker." + data.get('meta_entity')
  29.  
  30. # Get zone data, if it exists
  31. zoneEntity = data.get('zone_entity')
  32. zoneState = hass.states.get(zoneEntity)
  33. if zoneState is not None:
  34. zoneLatitude = zoneState.attributes.get('latitude')
  35. zoneLongitude = zoneState.attributes.get('longitude')
  36. zoneRadius = zoneState.attributes.get('radius')
  37. zoneName = zoneState.attributes.get('friendly_name')
  38. zoneTransition = data.get('zone_data')
  39.  
  40. # Get New data & status
  41. newState = hass.states.get(triggeredEntity)
  42. newSource = newState.attributes.get('source_type')
  43. newStatus = newState.state
  44. newLatitude = newState.attributes.get('latitude')
  45. newLongitude = newState.attributes.get('longitude')
  46. newgpsAccuracy = newState.attributes.get('gps_accuracy')
  47. newMeters = 1000 if newState.attributes.get('meters') is None \
  48. else newState.attributes.get('meters')
  49.  
  50. # Get current GPS data. If we have no data, assign new data.
  51. currentState = hass.states.get(metatrackerName)
  52. currentLatitude = newLatitude if currentState.attributes.get('latitude') is None \
  53. else currentState.attributes.get('latitude')
  54. currentLongitude = newLongitude if currentState.attributes.get('longitude') is None \
  55. else currentState.attributes.get('longitude')
  56. currentgpsAccuracy = newgpsAccuracy if currentState.attributes.get('gps_accuracy') is None \
  57. else currentState.attributes.get('gps_accuracy')
  58. currentMeters = 1000 if currentState.attributes.get('meters') is None \
  59. else currentState.attributes.get('meters')
  60.  
  61. # If GPS source, calculate GPS movement in meters
  62. if newSource == 'gps':
  63. # If a zone trasition is triggered, use zone data to populate device_tracker.meta_entity
  64. # We set the distance for the zone to 0.5m to prevent GPS drift and lock the
  65. # device_tracker.meta_entity to the zone unless a leave event happens in which case we
  66. # begin calculating distance.
  67. if zoneState is not None:
  68. logger.info("GPS zone transition: %s (%s) [zone=%s lat=%f long=%f radius=%d]", metatrackerName[15:], \
  69. zoneTransition, zoneName, zoneLatitude, zoneLongitude, zoneRadius)
  70.  
  71. if zoneTransition == 'enter':
  72. finalLatitude = zoneLatitude
  73. finalLongitude = zoneLongitude
  74. finalgpsAccuracy = 1
  75. finalMeters = 0.5
  76. else:
  77. finalLatitude = newLatitude
  78. finalLongitude = newLongitude
  79. finalgpsAccuracy = newgpsAccuracy
  80. finalMeters = 100
  81.  
  82. # Simple case-check for zone.home - if state was home, set not_home else
  83. # set state to zoneName. Ensure zone entity name matches in your config.
  84. if (newState.state == 'home' or newState.state == 'Home'):
  85. newStatus = 'not_home'
  86. else:
  87. newStatus = zoneName
  88. else:
  89. # Begin Haversine formula calculation. This calculation will take
  90. # newCoords and compare against currentCoords and determine the distance
  91. # in meters. As each calculation happens, if the result is smaller than
  92. # the last, keep it. The idea is that when in a particular zone, it allows
  93. # the device_tracker.meta_entity to zero in as close as possible and
  94. # eliminate GPS drift.
  95. R = 6371000
  96. phi_1 = math.radians(currentLatitude)
  97. phi_2 = math.radians(newLatitude)
  98.  
  99. delta_phi = math.radians(newLongitude-currentLongitude)
  100. delta_lambda = math.radians(newLongitude-currentLongitude)
  101.  
  102. a = math.sin(delta_phi/2.0)**2+\
  103. math.cos(phi_1)*math.cos(phi_2)*\
  104. math.sin(delta_lambda/2.0)**2
  105. c = 2*math.atan2(math.sqrt(a),math.sqrt(1-a))
  106.  
  107. meters = round(R*c, 2)
  108. if (meters > MAX_GPS_METERS):
  109. logger.info("GPS met threshold for %s: %.2f meters (%d) [new=%f,%f old=%f,%f acc=%d state=%s]", metatrackerName[15:], \
  110. meters, MAX_GPS_METERS, newLatitude, newLongitude, currentLatitude, currentLongitude, newgpsAccuracy, newStatus)
  111. finalLatitude = newLatitude
  112. finalLongitude = newLongitude
  113. finalgpsAccuracy = newgpsAccuracy
  114. finalMeters = meters
  115. else:
  116. # keep values that are lowest in meters from previous to ensure
  117. # we stay within a zone measurement similar to max_gps_accuracy
  118. if (meters != 0):
  119. logger.info("GPS below threshold for %s: %.2f meters (%d) [new=%f,%f old=%f,%f acc=%d meters=%.2f state=%s]", metatrackerName[15:], \
  120. meters, MAX_GPS_METERS, newLatitude, newLongitude, currentLatitude, currentLongitude, currentgpsAccuracy, currentMeters, currentState.state)
  121. if (meters <= currentMeters and (newLatitude != currentLatitude or newLongitude != currentLongitude)):
  122. finalLatitude = newLatitude
  123. finalLongitude = newLongitude
  124. finalgpsAccuracy = newgpsAccuracy
  125. finalMeters = meters
  126. else:
  127. finalLatitude = currentLatitude
  128. finalLongitude = currentLongitude
  129. finalgpsAccuracy = currentgpsAccuracy
  130. finalMeters = currentMeters
  131. else:
  132. # If not, keep last known coordinates.
  133. if newSource is not None:
  134. finalLatitude = currentLatitude
  135. finalLongitude = currentLongitude
  136. finalgpsAccuracy = currentgpsAccuracy
  137. finalMeters = currentMeters
  138.  
  139. # Get Battery
  140. if newState.attributes.get('battery') is not None:
  141. newBattery = newState.attributes.get('battery')
  142. elif currentState.attributes.get('battery') is not None:
  143. newBattery = currentState.attributes.get('battery')
  144. else:
  145. newBattery = "0"
  146.  
  147. # Get icon
  148. finalIcon = newState.attributes.get('icon')
  149.  
  150. if ENABLE_DEBUG:
  151. logger.info("DEBUG latitude: %f longitude: %f meters: %f", finalLatitude, finalLongitude, finalMeters)
  152. logger.info("DEBUG metatrackerName: %s", metatrackerName)
  153. logger.info("DEBUG status: %s", newStatus)
  154. logger.info("DEBUG friendly_name: %s", metatrackerName[15:])
  155. logger.info("DEBUG battery: %s", newBattery)
  156. logger.info("DEBUG source: %s", newSource)
  157. logger.info("DEBUG gpsAccuracy: %f", finalgpsAccuracy)
  158. logger.info("DEBUG icon: %s", finalIcon)
  159. logger.info("DEBUG last_update_src: %s", triggeredEntity)
  160.  
  161. # Create device_tracker.meta_entity
  162. hass.states.set(metatrackerName, newStatus, {
  163. 'name': metatrackerName,
  164. 'friendly_name': metatrackerName[15:],
  165. 'source_type': newSource,
  166. 'battery': newBattery,
  167. 'gps_accuracy': finalgpsAccuracy,
  168. 'latitude': finalLatitude,
  169. 'longitude': finalLongitude,
  170. 'meters': finalMeters,
  171. 'icon': finalIcon,
  172. 'last_update_source': triggeredEntity,
  173. 'custom_ui_state_card': 'state-card-custom-ui',
  174. 'show_last_changed': 'true',
  175. 'state_card_mode': 'badges'
  176. })
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement