|  |  |  | #!/usr/bin/env python3
 | 
					
						
							|  |  |  | import json
 | 
					
						
							|  |  |  | import os
 | 
					
						
							|  |  |  | import time
 | 
					
						
							|  |  |  | import subprocess
 | 
					
						
							|  |  |  | from typing import NoReturn
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import requests
 | 
					
						
							|  |  |  | from timezonefinder import TimezoneFinder
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from common.params import Params
 | 
					
						
							|  |  |  | from selfdrive.hardware import TICI
 | 
					
						
							|  |  |  | from selfdrive.swaglog import cloudlog
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_timezone(valid_timezones, timezone):
 | 
					
						
							|  |  |  |   if timezone not in valid_timezones:
 | 
					
						
							|  |  |  |     cloudlog.error(f"Timezone not supported {timezone}")
 | 
					
						
							|  |  |  |     return
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   cloudlog.debug(f"Setting timezone to {timezone}")
 | 
					
						
							|  |  |  |   try:
 | 
					
						
							|  |  |  |     if TICI:
 | 
					
						
							|  |  |  |       tzpath = os.path.join("/usr/share/zoneinfo/", timezone)
 | 
					
						
							|  |  |  |       subprocess.check_call(f'sudo su -c "ln -snf {tzpath} /data/etc/tmptime && \
 | 
					
						
							|  |  |  |                               mv /data/etc/tmptime /data/etc/localtime"', shell=True)
 | 
					
						
							|  |  |  |       subprocess.check_call(f'sudo su -c "echo \"{timezone}\" > /data/etc/timezone"', shell=True)
 | 
					
						
							|  |  |  |     else:
 | 
					
						
							|  |  |  |       subprocess.check_call(f'sudo timedatectl set-timezone {timezone}', shell=True)
 | 
					
						
							|  |  |  |   except subprocess.CalledProcessError:
 | 
					
						
							|  |  |  |     cloudlog.exception(f"Error setting timezone to {timezone}")
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main() -> NoReturn:
 | 
					
						
							|  |  |  |   params = Params()
 | 
					
						
							|  |  |  |   tf = TimezoneFinder()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Get allowed timezones
 | 
					
						
							|  |  |  |   valid_timezones = subprocess.check_output('timedatectl list-timezones', shell=True, encoding='utf8').strip().split('\n')
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while True:
 | 
					
						
							|  |  |  |     time.sleep(60)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     is_onroad = not params.get_bool("IsOffroad")
 | 
					
						
							|  |  |  |     if is_onroad:
 | 
					
						
							|  |  |  |       continue
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Set based on param
 | 
					
						
							|  |  |  |     timezone = params.get("Timezone", encoding='utf8')
 | 
					
						
							|  |  |  |     if timezone is not None:
 | 
					
						
							|  |  |  |       cloudlog.debug("Setting timezone based on param")
 | 
					
						
							|  |  |  |       set_timezone(valid_timezones, timezone)
 | 
					
						
							|  |  |  |       continue
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     location = params.get("LastGPSPosition", encoding='utf8')
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Find timezone based on IP geolocation if no gps location is available
 | 
					
						
							|  |  |  |     if location is None:
 | 
					
						
							|  |  |  |       cloudlog.debug("Setting timezone based on IP lookup")
 | 
					
						
							|  |  |  |       try:
 | 
					
						
							|  |  |  |         r = requests.get("https://ipapi.co/timezone", timeout=10)
 | 
					
						
							|  |  |  |         if r.status_code == 200:
 | 
					
						
							|  |  |  |           set_timezone(valid_timezones, r.text)
 | 
					
						
							|  |  |  |         else:
 | 
					
						
							|  |  |  |           cloudlog.error(f"Unexpected status code from api {r.status_code}")
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         time.sleep(3600)  # Don't make too many API requests
 | 
					
						
							|  |  |  |       except requests.exceptions.RequestException:
 | 
					
						
							|  |  |  |         cloudlog.exception("Error getting timezone based on IP")
 | 
					
						
							|  |  |  |         continue
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Find timezone by reverse geocoding the last known gps location
 | 
					
						
							|  |  |  |     else:
 | 
					
						
							|  |  |  |       cloudlog.debug("Setting timezone based on GPS location")
 | 
					
						
							|  |  |  |       try:
 | 
					
						
							|  |  |  |         location = json.loads(location)
 | 
					
						
							|  |  |  |       except Exception:
 | 
					
						
							|  |  |  |         cloudlog.exception("Error parsing location")
 | 
					
						
							|  |  |  |         continue
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       timezone = tf.timezone_at(lng=location['longitude'], lat=location['latitude'])
 | 
					
						
							|  |  |  |       if timezone is None:
 | 
					
						
							|  |  |  |         cloudlog.error(f"No timezone found based on location, {location}")
 | 
					
						
							|  |  |  |         continue
 | 
					
						
							|  |  |  |       set_timezone(valid_timezones, timezone)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__":
 | 
					
						
							|  |  |  |   main()
 |