Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #
- # calc_gps_coords.py
- #
- # based on meta_device_tracker.py by oakbrad
- # https://github.com/oakbrad/brad-homeassistant-config/blob/master/python_scripts/meta_device_tracker.py
- #
- # adapted GPS Haversine calculation from nathan rooy
- # https://nathanrooy.github.io/posts/2016-09-07/haversine-with-python/
- #
- # Enable debugging
- ENABLE_DEBUG = 0
- # Suggested max_gps_accuracy for Owntracks is 200m (656ft)
- # https://www.home-assistant.io/components/device_tracker.owntracks/
- #
- # We'll use it too but use it to determine if the new value is greater-than current
- # value to indicate significant movement. The idea behind this is to prevent false
- # positive zone-transitions when stationary without line-of-sight of satellites
- # and GPS lock drifts. Calculation is determined by using the Haversine formula. If
- # the new value is greater-than current, we allow the meta_tracker to update otherwise
- # we retain previous value.
- #
- MAX_GPS_METERS = 200
- # Get Data from Automation Trigger
- triggeredEntity = data.get('entity_id')
- metatrackerName = "device_tracker." + data.get('meta_entity')
- # Get zone data, if it exists
- zoneEntity = data.get('zone_entity')
- zoneState = hass.states.get(zoneEntity)
- if zoneState is not None:
- zoneLatitude = zoneState.attributes.get('latitude')
- zoneLongitude = zoneState.attributes.get('longitude')
- zoneRadius = zoneState.attributes.get('radius')
- zoneName = zoneState.attributes.get('friendly_name')
- zoneTransition = data.get('zone_data')
- # Get New data & status
- newState = hass.states.get(triggeredEntity)
- newSource = newState.attributes.get('source_type')
- newStatus = newState.state
- newLatitude = newState.attributes.get('latitude')
- newLongitude = newState.attributes.get('longitude')
- newgpsAccuracy = newState.attributes.get('gps_accuracy')
- newMeters = 1000 if newState.attributes.get('meters') is None \
- else newState.attributes.get('meters')
- # Get current GPS data. If we have no data, assign new data.
- currentState = hass.states.get(metatrackerName)
- currentLatitude = newLatitude if currentState.attributes.get('latitude') is None \
- else currentState.attributes.get('latitude')
- currentLongitude = newLongitude if currentState.attributes.get('longitude') is None \
- else currentState.attributes.get('longitude')
- currentgpsAccuracy = newgpsAccuracy if currentState.attributes.get('gps_accuracy') is None \
- else currentState.attributes.get('gps_accuracy')
- currentMeters = 1000 if currentState.attributes.get('meters') is None \
- else currentState.attributes.get('meters')
- # If GPS source, calculate GPS movement in meters
- if newSource == 'gps':
- # If a zone trasition is triggered, use zone data to populate device_tracker.meta_entity
- # We set the distance for the zone to 0.5m to prevent GPS drift and lock the
- # device_tracker.meta_entity to the zone unless a leave event happens in which case we
- # begin calculating distance.
- if zoneState is not None:
- logger.info("GPS zone transition: %s (%s) [zone=%s lat=%f long=%f radius=%d]", metatrackerName[15:], \
- zoneTransition, zoneName, zoneLatitude, zoneLongitude, zoneRadius)
- if zoneTransition == 'enter':
- finalLatitude = zoneLatitude
- finalLongitude = zoneLongitude
- finalgpsAccuracy = 1
- finalMeters = 0.5
- else:
- finalLatitude = newLatitude
- finalLongitude = newLongitude
- finalgpsAccuracy = newgpsAccuracy
- finalMeters = 100
- # Simple case-check for zone.home - if state was home, set not_home else
- # set state to zoneName. Ensure zone entity name matches in your config.
- if (newState.state == 'home' or newState.state == 'Home'):
- newStatus = 'not_home'
- else:
- newStatus = zoneName
- else:
- # Begin Haversine formula calculation. This calculation will take
- # newCoords and compare against currentCoords and determine the distance
- # in meters. As each calculation happens, if the result is smaller than
- # the last, keep it. The idea is that when in a particular zone, it allows
- # the device_tracker.meta_entity to zero in as close as possible and
- # eliminate GPS drift.
- R = 6371000
- phi_1 = math.radians(currentLatitude)
- phi_2 = math.radians(newLatitude)
- delta_phi = math.radians(newLongitude-currentLongitude)
- delta_lambda = math.radians(newLongitude-currentLongitude)
- a = math.sin(delta_phi/2.0)**2+\
- math.cos(phi_1)*math.cos(phi_2)*\
- math.sin(delta_lambda/2.0)**2
- c = 2*math.atan2(math.sqrt(a),math.sqrt(1-a))
- meters = round(R*c, 2)
- if (meters > MAX_GPS_METERS):
- logger.info("GPS met threshold for %s: %.2f meters (%d) [new=%f,%f old=%f,%f acc=%d state=%s]", metatrackerName[15:], \
- meters, MAX_GPS_METERS, newLatitude, newLongitude, currentLatitude, currentLongitude, newgpsAccuracy, newStatus)
- finalLatitude = newLatitude
- finalLongitude = newLongitude
- finalgpsAccuracy = newgpsAccuracy
- finalMeters = meters
- else:
- # keep values that are lowest in meters from previous to ensure
- # we stay within a zone measurement similar to max_gps_accuracy
- if (meters != 0):
- logger.info("GPS below threshold for %s: %.2f meters (%d) [new=%f,%f old=%f,%f acc=%d meters=%.2f state=%s]", metatrackerName[15:], \
- meters, MAX_GPS_METERS, newLatitude, newLongitude, currentLatitude, currentLongitude, currentgpsAccuracy, currentMeters, currentState.state)
- if (meters <= currentMeters and (newLatitude != currentLatitude or newLongitude != currentLongitude)):
- finalLatitude = newLatitude
- finalLongitude = newLongitude
- finalgpsAccuracy = newgpsAccuracy
- finalMeters = meters
- else:
- finalLatitude = currentLatitude
- finalLongitude = currentLongitude
- finalgpsAccuracy = currentgpsAccuracy
- finalMeters = currentMeters
- else:
- # If not, keep last known coordinates.
- if newSource is not None:
- finalLatitude = currentLatitude
- finalLongitude = currentLongitude
- finalgpsAccuracy = currentgpsAccuracy
- finalMeters = currentMeters
- # Get Battery
- if newState.attributes.get('battery') is not None:
- newBattery = newState.attributes.get('battery')
- elif currentState.attributes.get('battery') is not None:
- newBattery = currentState.attributes.get('battery')
- else:
- newBattery = "0"
- # Get icon
- finalIcon = newState.attributes.get('icon')
- if ENABLE_DEBUG:
- logger.info("DEBUG latitude: %f longitude: %f meters: %f", finalLatitude, finalLongitude, finalMeters)
- logger.info("DEBUG metatrackerName: %s", metatrackerName)
- logger.info("DEBUG status: %s", newStatus)
- logger.info("DEBUG friendly_name: %s", metatrackerName[15:])
- logger.info("DEBUG battery: %s", newBattery)
- logger.info("DEBUG source: %s", newSource)
- logger.info("DEBUG gpsAccuracy: %f", finalgpsAccuracy)
- logger.info("DEBUG icon: %s", finalIcon)
- logger.info("DEBUG last_update_src: %s", triggeredEntity)
- # Create device_tracker.meta_entity
- hass.states.set(metatrackerName, newStatus, {
- 'name': metatrackerName,
- 'friendly_name': metatrackerName[15:],
- 'source_type': newSource,
- 'battery': newBattery,
- 'gps_accuracy': finalgpsAccuracy,
- 'latitude': finalLatitude,
- 'longitude': finalLongitude,
- 'meters': finalMeters,
- 'icon': finalIcon,
- 'last_update_source': triggeredEntity,
- 'custom_ui_state_card': 'state-card-custom-ui',
- 'show_last_changed': 'true',
- 'state_card_mode': 'badges'
- })
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement