#!/usr/bin/python3 import sys import datetime import math import os import time sys.path.append("/etc/argon/") from argonregister import argonregister_initializebusobj import argonrtc # Initialize I2C Bus bus = argonregister_initializebusobj() ADDR_RTC=0x51 ################# # Common/Helpers ################# RTC_CONFIGFILE = "/etc/argoneonrtc.conf" RTC_ALARM_BIT = 0x8 RTC_TIMER_BIT = 0x4 # PCF8563 number system Binary Coded Decimal (BCD) # BCD to Decimal def numBCDtoDEC(val): return (val & 0xf)+(((val >> 4) & 0xf)*10) # Decimal to BCD def numDECtoBCD(val): return (math.floor(val/10)<<4) + (val % 10) # Check if Event Bit is raised def hasRTCEventFlag(flagbit): if bus is None: return False bus.write_byte(ADDR_RTC,1) out = bus.read_byte_data(ADDR_RTC, 1) return (out & flagbit) != 0 # Clear Event Bit if raised def clearRTCEventFlag(flagbit): if bus is None: return False out = bus.read_byte_data(ADDR_RTC, 1) if (out & flagbit) != 0: # Unset only if fired bus.write_byte_data(ADDR_RTC, 1, out&(0xff-flagbit)) return True return False # Enable Event Flag def setRTCEventFlag(flagbit, enabled): if bus is None: return # 0x10 = TI_TP flag, 0 by default ti_tp_flag = 0x10 # flagbit=0x4 for timer flag, 0x1 for enable timer flag # flagbit=0x8 for alarm flag, 0x2 for enable alarm flag enableflagbit = flagbit>>2 disableflagbit = 0 if enabled == False: disableflagbit = enableflagbit enableflagbit = 0 out = bus.read_byte_data(ADDR_RTC, 1) bus.write_byte_data(ADDR_RTC, 1, (out&(0xff-flagbit-disableflagbit - ti_tp_flag))|enableflagbit) ######### # Describe Methods ######### # Describe Timer Setting def describeTimer(showsetting): if bus is None: return "Error" out = bus.read_byte_data(ADDR_RTC, 14) tmp = out & 3 if tmp == 3: outstr = " Minute(s)" elif tmp == 2: outstr = " Second(s)" elif tmp == 1: outstr = "/64th Second" elif tmp == 0: outstr = "/4096th Second" if (out & 0x80) != 0: out = bus.read_byte_data(ADDR_RTC, 15) return "Every "+(numBCDtoDEC(out)+1)+outstr elif showsetting == True: return "Disabled (Interval every 1"+outstr+")" # Setting might matter to save resources return "None" # Describe Alarm Setting def describeAlarm(): if bus is None: return "Error" minute = -1 hour = -1 caldate = -1 weekday = -1 out = bus.read_byte_data(ADDR_RTC, 9) if (out & 0x80) == 0: minute = numBCDtoDEC(out & 0x7f) out = bus.read_byte_data(ADDR_RTC, 10) if (out & 0x80) == 0: hour = numBCDtoDEC(out & 0x3f) out = bus.read_byte_data(ADDR_RTC, 11) if (out & 0x80) == 0: caldate = numBCDtoDEC(out & 0x3f) out = bus.read_byte_data(ADDR_RTC, 12) if (out & 0x80) == 0: weekday = numBCDtoDEC(out & 0x7) if weekday < 0 and caldate < 0 and hour < 0 and minute < 0: return "None" # Convert from UTC utcschedule = argonrtc.describeSchedule([-1], [weekday], [caldate], [hour], [minute]) weekday, caldate, hour, minute = argonrtc.convertAlarmTimezone(weekday, caldate, hour, minute, False) return argonrtc.describeSchedule([-1], [weekday], [caldate], [hour], [minute]) + " Local (RTC Schedule: "+utcschedule+" UTC)" # Describe Control Flags def describeControlRegisters(): if bus is None: print("Error") return out = bus.read_byte_data(ADDR_RTC, 1) print("\n***************") print("Control Status 2") print("\tTI_TP Flag:", ((out & 0x10) != 0)) print("\tAlarm Flag:", ((out & RTC_ALARM_BIT) != 0),"( Enabled =", (out & (RTC_ALARM_BIT>>2)) != 0, ")") print("\tTimer Flag:", ((out & RTC_TIMER_BIT) != 0),"( Enabled =", (out & (RTC_TIMER_BIT>>2)) != 0, ")") print("Alarm Setting:") print("\t"+describeAlarm()) print("Timer Setting:") print("\t"+describeTimer(True)) print("***************\n") ######### # Alarm ######### # Check if RTC Alarm Flag is ON def hasRTCAlarmFlag(): return hasRTCEventFlag(RTC_ALARM_BIT) # Clear RTC Alarm Flag def clearRTCAlarmFlag(): return clearRTCEventFlag(RTC_ALARM_BIT) # Enables RTC Alarm Register def enableAlarm(registeraddr, value, mask): if bus is None: return # 0x00 is Enabled bus.write_byte_data(ADDR_RTC, registeraddr, (numDECtoBCD(value)&mask)) # Disables RTC Alarm Register def disableAlarm(registeraddr): if bus is None: return # 0x80 is disabled bus.write_byte_data(ADDR_RTC, registeraddr, 0x80) # Removes all alarm settings def removeRTCAlarm(): setRTCEventFlag(RTC_ALARM_BIT, False) disableAlarm(9) disableAlarm(10) disableAlarm(11) disableAlarm(12) # Set RTC Alarm (Negative values ignored) def setRTCAlarm(enableflag, weekday, caldate, hour, minute): weekday, caldate, hour, minute = argonrtc.getRTCAlarm(weekday, caldate, hour, minute) if caldate < 1 and weekday < 0 and hour < 0 and minute < 0: return -1 clearRTCAlarmFlag() setRTCEventFlag(RTC_ALARM_BIT, enableflag) if minute >= 0: enableAlarm(9, minute, 0x7f) else: disableAlarm(9) if hour >= 0: enableAlarm(10, hour, 0x7f) else: disableAlarm(10) if caldate >= 0: enableAlarm(11, caldate, 0x7f) else: disableAlarm(11) if weekday >= 0: # 0 - Sun (datetime 0 - Mon) if weekday > 5: weekday = 0 else: weekday = weekday + 1 enableAlarm(12, weekday, 0x7f) else: disableAlarm(12) return 0 # Set RTC Hourly Alarm def setRTCAlarmHourly(enableflag, minute): return setRTCAlarm(enableflag, -1, -1, -1, minute) # Set RTC Daily Alarm def setRTCAlarmDaily(enableflag, hour, minute): return setRTCAlarm(enableflag, -1, -1, hour, minute) # Set RTC Weekly Alarm def setRTCAlarmWeekly(enableflag, dayofweek, hour, minute): return setRTCAlarm(enableflag, dayofweek, -1, hour, minute) # Set RTC Monthly Alarm def setRTCAlarmMonthly(enableflag, caldate, hour, minute): return setRTCAlarm(enableflag, -1, caldate, hour, minute) ######### # Timer ######### # Check if RTC Timer Flag is ON def hasRTCTimerFlag(): return hasRTCEventFlag(RTC_TIMER_BIT) # Clear RTC Timer Flag def clearRTCTimerFlag(): return clearRTCEventFlag(RTC_TIMER_BIT) # Remove RTC Timer Setting def removeRTCTimer(): if bus is None: return setRTCEventFlag(RTC_TIMER_BIT, False) # Timer disable and Set Timer frequency to lowest (0x3=1 per minute) bus.write_byte_data(ADDR_RTC, 14, 3) bus.write_byte_data(ADDR_RTC, 15, 0) # Set RTC Timer Interval def setRTCTimerInterval(enableflag, value, inSeconds = False): if bus is None: return -1 if value > 255 or value < 1: return -1 clearRTCTimerFlag() setRTCEventFlag(RTC_TIMER_BIT, enableflag) # 0x80 Timer Enabled, mode: 0x3=1/Min, 0x2=1/Sec, 0x1=Per 64th Sec, 0=Per 4096th Sec timerconfigFlag = 0x83 if inSeconds == True: timerconfigFlag = 0x82 bus.write_byte_data(ADDR_RTC, 14, timerconfigFlag) bus.write_byte_data(ADDR_RTC, 15, numDECtoBCD(value&0xff)) return 0 ############# # Date/Time ############# # Returns RTC timestamp as datetime object def getRTCdatetime(): if bus is None: return datetime.datetime(2000, 1, 1, 0, 0, 0) # Data Sheet Recommends to read this manner (instead of from registers) bus.write_byte(ADDR_RTC,2) out = bus.read_byte(ADDR_RTC) out = numBCDtoDEC(out & 0x7f) second = out #warningflag = (out & 0x80)>>7 out = bus.read_byte(ADDR_RTC) minute = numBCDtoDEC(out & 0x7f) out = bus.read_byte(ADDR_RTC) hour = numBCDtoDEC(out & 0x3f) out = bus.read_byte(ADDR_RTC) caldate = numBCDtoDEC(out & 0x3f) out = bus.read_byte(ADDR_RTC) #weekDay = numBCDtoDEC(out & 7) out = bus.read_byte(ADDR_RTC) month = numBCDtoDEC(out & 0x1f) out = bus.read_byte(ADDR_RTC) year = numBCDtoDEC(out) #print({"year":year, "month": month, "date": caldate, "hour": hour, "minute": minute, "second": second}) if month == 0: # Reset, uninitialized RTC month = 1 # Timezone is GMT/UTC +0 # Year is from 2000 try: return datetime.datetime(year+2000, month, caldate, hour, minute, second)+argonrtc.getLocaltimeOffset() except: return datetime.datetime(2000, 1, 1, 0, 0, 0) # set RTC time using datetime object (Local time) def setRTCdatetime(localdatetime): if bus is None: return # Set local time to UTC localdatetime = localdatetime - argonrtc.getLocaltimeOffset() # python Sunday = 6, RTC Sunday = 0 weekDay = localdatetime.weekday() if weekDay == 6: weekDay = 0 else: weekDay = weekDay + 1 # Write to respective registers bus.write_byte_data(ADDR_RTC,2,numDECtoBCD(localdatetime.second)) bus.write_byte_data(ADDR_RTC,3,numDECtoBCD(localdatetime.minute)) bus.write_byte_data(ADDR_RTC,4,numDECtoBCD(localdatetime.hour)) bus.write_byte_data(ADDR_RTC,5,numDECtoBCD(localdatetime.day)) bus.write_byte_data(ADDR_RTC,6,numDECtoBCD(weekDay)) bus.write_byte_data(ADDR_RTC,7,numDECtoBCD(localdatetime.month)) # Year is from 2000 bus.write_byte_data(ADDR_RTC,8,numDECtoBCD(localdatetime.year-2000)) ######### # Config ######### # Set Next Alarm on RTC def setNextAlarm(commandschedulelist, prevdatetime): nextcommandtime, weekday, caldate, hour, minute = argonrtc.getNextAlarm(commandschedulelist, prevdatetime) if prevdatetime >= nextcommandtime: return prevdatetime if weekday < 0 and caldate < 0 and hour < 0 and minute < 0: # No schedule # nextcommandtime is current time, which will be replaced/checked next iteration removeRTCAlarm() return nextcommandtime setRTCAlarm(True, nextcommandtime.weekday(), nextcommandtime.day, nextcommandtime.hour, nextcommandtime.minute) return nextcommandtime def allowshutdown(): uptime = 0.0 errorflag = False try: cpuctr = 0 tempfp = open("/proc/uptime", "r") alllines = tempfp.readlines() for temp in alllines: infolist = temp.split(" ") if len(infolist) > 1: uptime = float(infolist[0]) break tempfp.close() except IOError: errorflag = True # 120=2mins minimum up time return uptime > 120 ###### if len(sys.argv) > 1: cmd = sys.argv[1].upper() # Enable Alarm/Timer Flags enableflag = True if cmd == "CLEAN": removeRTCAlarm() removeRTCTimer() elif cmd == "SHUTDOWN": clearRTCAlarmFlag() clearRTCTimerFlag() elif cmd == "GETRTCSCHEDULE": print("Alarm Setting:") print("\t"+describeAlarm()) #print("Timer Setting:") #print("\t"+describeTimer(True)) elif cmd == "GETRTCTIME": print("RTC Time:", getRTCdatetime()) elif cmd == "UPDATERTCTIME": setRTCdatetime(datetime.datetime.now()) print("RTC Time:", getRTCdatetime()) elif cmd == "GETSCHEDULELIST": argonrtc.describeConfigList(RTC_CONFIGFILE) elif cmd == "SHOWSCHEDULE": if len(sys.argv) > 2: if sys.argv[2].isdigit(): # Display starts at 2, maps to 0-based index configidx = int(sys.argv[2])-2 configlist = argonrtc.loadConfigList(RTC_CONFIGFILE) if len(configlist) > configidx: print (" ",argonrtc.describeConfigListEntry(configlist[configidx])) else: print(" Invalid Schedule") elif cmd == "REMOVESCHEDULE": if len(sys.argv) > 2: if sys.argv[2].isdigit(): # Display starts at 2, maps to 0-based index configidx = int(sys.argv[2])-2 argonrtc.removeConfigEntry(RTC_CONFIGFILE, configidx) elif cmd == "SERVICE": argonrtc.updateSystemTime(getRTCdatetime()) commandschedulelist = argonrtc.formCommandScheduleList(argonrtc.loadConfigList(RTC_CONFIGFILE)) nextrtcalarmtime = setNextAlarm(commandschedulelist, datetime.datetime.now()) serviceloop = True while serviceloop==True: clearRTCAlarmFlag() clearRTCTimerFlag() tmpcurrenttime = datetime.datetime.now() if nextrtcalarmtime <= tmpcurrenttime: # Update RTC Alarm to next iteration nextrtcalarmtime = setNextAlarm(commandschedulelist, nextrtcalarmtime) if len(argonrtc.getCommandForTime(commandschedulelist, tmpcurrenttime, "off")) > 0: # Shutdown detected, issue command then end service loop if allowshutdown(): os.system("shutdown now -h") serviceloop = False # Don't break to sleep while command executes (prevents service to restart) time.sleep(60) elif False: print("System Time: ", datetime.datetime.now()) print("RTC Time: ", getRTCdatetime()) describeControlRegisters()