#!/usr/bin/env python3 import datetime import os import subprocess import time from typing import NoReturn from timezonefinder import TimezoneFinder import cereal.messaging as messaging from openpilot.common.time import system_time_valid from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import AGNOS def set_timezone(timezone): valid_timezones = subprocess.check_output('timedatectl list-timezones', shell=True, encoding='utf8').strip().split('\n') if timezone not in valid_timezones: cloudlog.error(f"Timezone not supported {timezone}") return cloudlog.debug(f"Setting timezone to {timezone}") try: if AGNOS: 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 set_time(new_time): diff = datetime.datetime.now() - new_time if diff < datetime.timedelta(seconds=10): cloudlog.debug(f"Time diff too small: {diff}") return cloudlog.debug(f"Setting time to {new_time}") try: subprocess.run(f"TZ=UTC date -s '{new_time}'", shell=True, check=True) except subprocess.CalledProcessError: cloudlog.exception("timed.failed_setting_time") def main() -> NoReturn: """ timed has two responsibilities: - getting the current time - getting the current timezone GPS directly gives time, and timezone is looked up from GPS position. AGNOS will also use NTP to update the time. """ params = Params() # Restore timezone from param tz = params.get("Timezone", encoding='utf8') tf = TimezoneFinder() if tz is not None: cloudlog.debug("Restoring timezone from param") set_timezone(tz) pm = messaging.PubMaster(['clocks']) sm = messaging.SubMaster(['liveLocationKalman']) while True: sm.update(1000) msg = messaging.new_message('clocks') msg.valid = system_time_valid() msg.clocks.wallTimeNanos = time.time_ns() pm.send('clocks', msg) llk = sm['liveLocationKalman'] if not llk.gpsOK or (time.monotonic() - sm.logMonoTime['liveLocationKalman']/1e9) > 0.2: continue # set time # TODO: account for unixTimesatmpMillis being a (usually short) time in the past gps_time = datetime.datetime.fromtimestamp(llk.unixTimestampMillis / 1000.) set_time(gps_time) # set timezone pos = llk.positionGeodetic.value if len(pos) == 3: gps_timezone = tf.timezone_at(lat=pos[0], lng=pos[1]) if gps_timezone is None: cloudlog.critical(f"No timezone found based on {pos=}") else: set_timezone(gps_timezone) params.put_nonblocking("Timezone", gps_timezone) time.sleep(10) if __name__ == "__main__": main()