Advertisement
Guest User

Untitled

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