MotionEyeOS Day and Night Python script: Difference between revisions

From WickyWiki
mNo edit summary
mNo edit summary
Line 226: Line 226:


   # Restart MotionEye
   # Restart MotionEye
   print(datetime.now().strftime('%Y-%m-%d_%H:%M:%S ') + 'Restart motionEye...')
   print(datetime.now().strftime('%Y-%m-%d_%H:%M:%S ') + 'Restart MotionEye...')
   os.system("/etc/init.d/S85motioneye restart")
   os.system("/etc/init.d/S85motioneye restart")
   print(datetime.now().strftime('%Y-%m-%d_%H:%M:%S ') + 'MotionEye restarted.')
   print(datetime.now().strftime('%Y-%m-%d_%H:%M:%S ') + 'MotionEye restarted.')

Revision as of 09:16, 6 March 2019


This Python script allows you to have day and night -time settings for your MotionEyeOS camera, based on calculated sunrise and sunset time-of-day. The script replaces the settings file (/data/etc/thread-1.conf) for Camera1 with one the files with specific settings:

  • /data/etc/thread-1.conf.day
  • /data/etc/thread-1.conf.night

This module seems to be created by Michel Anders, source:

See also:

Setup crontab to execute the script every 10 minutes. On MotionEyeOS we can place the files in /data/etc/daynight, this way it is part of the configuration backup. Logging is sent to /data/log/daynight.log.

crontab -e
  02,12,22,32,42,52 * * * * /usr/bin/python /data/etc/daynight/daynight.py >> /data/log/daynight.log

Go here to see how you can setup LogRotate to limit the size of the logfile.

Script

nano /data/etc/daynight/daynight.py
# Based on: https://blog.ligos.net/2016-04-18/Day-Night-Cycle-For-MotionEye.html (Michel Anders)
from math import cos,sin,acos,asin,tan
from math import degrees as deg, radians as rad
from datetime import date,datetime,time
from pytz import timezone
import os
import os.path
import shutil

# Variant of time zone available on MotionEyeOS
localTimezone = timezone('Europe/Amsterdam')

# Location of here in lat / long
localLat = 51.49
localLong = 5.48

# Sunrise and sunset delay (in minutes)
# + is later, - is earlier.
sunriseDelayMins = -10
sunsetDelayMins = +10

# Current state of day / night is stored in this file.
dayNightStateFile = '/data/etc/daynight/daynight.dat'

# sun module
class sun:
 """ 
 Calculate sunrise and sunset based on equations from NOAA
 http://www.srrb.noaa.gov/highlights/sunrise/calcdetails.html
 """
 def __init__(self,lat=52.37,long=4.90): # default Amsterdam
  self.lat=lat
  self.long=long
  
 def sunrise(self,when=None):
  """
  return the time of sunrise as a datetime.time object
  when is a datetime.datetime object. If none is given
  a local time zone is assumed (including daylight saving
  if present)
  """
  if when is None : when = datetime.now(tz=localTimezone)
  self.__preptime(when)
  self.__calc()
  return sun.timefromdecimalday(self.sunrise_t)
  
 def sunset(self,when=None):
  if when is None : when = datetime.now(tz=localTimezone)
  self.__preptime(when)
  self.__calc()
  return sun.timefromdecimalday(self.sunset_t)
  
 def solarnoon(self,when=None):
  if when is None : when = datetime.now(tz=localTimezone)
  self.__preptime(when)
  self.__calc()
  return sun.timefromdecimalday(self.solarnoon_t)
 
 """
 The two fudge factors are in minutes. Negative is earlier. 
 """
 def dayornight(self,now=None,sunriseFudge=0,sunsetFudge=0):
  if now is None : now = datetime.now(tz=localTimezone)
  self.__preptime(when=now)
  self.__calc()
  sunriseFudge = sunriseFudge/60.0/24.0
  sunsetFudge = sunsetFudge/60.0/24.0
  if (self.time >= (self.sunrise_t + sunriseFudge) and self.time <= (self.sunset_t + sunsetFudge)):
   return 'day'
  else:
   return 'night'
  
 @staticmethod
 def timefromdecimalday(day):
  """
  returns a datetime.time object.
  day is a decimal day between 0.0 and 1.0, e.g. noon = 0.5
  """
  hours  = 24.0*day
  h      = int(hours)
  minutes= (hours-h)*60
  m      = int(minutes)
  seconds= (minutes-m)*60
  s      = int(seconds)
  return time(hour=h,minute=m,second=s)

 def __preptime(self,when):
  """
  Extract information in a suitable format from when, 
  a datetime.datetime object.
  """
  # datetime days are numbered in the Gregorian calendar
  # while the calculations from NOAA are distibuted as
  # OpenOffice spreadsheets with days numbered from
  # 1/1/1900. The difference are those numbers taken for 
  # 18/12/2010
  self.day = when.toordinal()-(734124-40529)
  t=when.time()
  self.time= (t.hour + t.minute/60.0 + t.second/3600.0)/24.0
  
  self.timezone=0
  offset=when.utcoffset()
  if not offset is None:
   self.timezone=offset.seconds/3600.0
  
 def __calc(self):
  """
  Perform the actual calculations for sunrise, sunset and
  a number of related quantities. 
  
  The results are stored in the instance variables
  sunrise_t, sunset_t and solarnoon_t
  """
  timezone = self.timezone # in hours, east is positive
  longitude= self.long     # in decimal degrees, east is positive
  latitude = self.lat      # in decimal degrees, north is positive

  time     = self.time    # percentage past midnight, i.e. noon  is 0.5
  day      = self.day     # daynumber 1=1/1/1900
 
  Jday     = day+2415018.5+time-timezone/24 # Julian day
  Jcent    = (Jday-2451545)/36525           # Julian century

  Manom    = 357.52911+Jcent*(35999.05029-0.0001537*Jcent)
  Mlong    = 280.46646+Jcent*(36000.76983+Jcent*0.0003032)%360
  Eccent   = 0.016708634-Jcent*(0.000042037+0.0001537*Jcent)
  Mobliq   = 23+(26+((21.448-Jcent*(46.815+Jcent*(0.00059-Jcent*0.001813))))/60)/60
  obliq    = Mobliq+0.00256*cos(rad(125.04-1934.136*Jcent))
  vary     = tan(rad(obliq/2))*tan(rad(obliq/2))
  Seqcent  = sin(rad(Manom))*(1.914602-Jcent*(0.004817+0.000014*Jcent))+sin(rad(2*Manom))*(0.019993-0.000101*Jcent)+sin(rad(3*Manom))*0.000289
  Struelong= Mlong+Seqcent
  Sapplong = Struelong-0.00569-0.00478*sin(rad(125.04-1934.136*Jcent))
  declination = deg(asin(sin(rad(obliq))*sin(rad(Sapplong))))
  
  eqtime   = 4*deg(vary*sin(2*rad(Mlong))-2*Eccent*sin(rad(Manom))+4*Eccent*vary*sin(rad(Manom))*cos(2*rad(Mlong))-0.5*vary*vary*sin(4*rad(Mlong))-1.25*Eccent*Eccent*sin(2*rad(Manom)))

  hourangle= deg(acos(cos(rad(90.833))/(cos(rad(latitude))*cos(rad(declination)))-tan(rad(latitude))*tan(rad(declination))))

  self.solarnoon_t=(720-4*longitude-eqtime+timezone*60)/1440
  self.sunrise_t  =self.solarnoon_t-hourangle*4/1440
  self.sunset_t   =self.solarnoon_t+hourangle*4/1440

def readLineOfFile(path):
 if not os.path.isfile(path): return ""
 f = open(path, "r")
 result = f.readline()
 f.close()
 return result.rstrip()
 
def updateContentsOfFile(path, data):
 f = open(path, "w")
 f.write(data)
 f.close()

# The main program
if __name__ == "__main__":
 
 # Calculate
 s=sun(lat=localLat,long=localLong)
 s.solarnoon()
 
 # Track day/night -state in a file.
 fileDayOrNight = readLineOfFile(dayNightStateFile)
 
 # Log
 print(datetime.now().strftime('%Y-%m-%d_%H.%M.%S ') 
  + 'Day from ' + sun.timefromdecimalday(s.sunrise_t).strftime('%H.%M') + ['(', '(+'][sunriseDelayMins > 0] + str(sunriseDelayMins)+')'
  + ' to ' + sun.timefromdecimalday(s.sunset_t).strftime('%H.%M') + ['(', '(+'][sunsetDelayMins > 0] + str(sunsetDelayMins)+')'
  + ', current state: ' + fileDayOrNight
  )
 
 # Calculate current day/night -state based on current time of day.
 currentDayOrNight = s.dayornight(sunriseFudge=sunriseDelayMins,sunsetFudge=sunsetDelayMins)

 # Did the day/night -state change?
 if fileDayOrNight != currentDayOrNight:
  print(datetime.now().strftime('%Y-%m-%d_%H:%M:%S ') + 'Changing state to: ' + currentDayOrNight)
  
  # Backup the old settings if the selected config is newer  
  if os.stat("/data/etc/thread-1.conf").st_mtime > os.stat("/data/etc/thread-1.conf." + fileDayOrNight).st_mtime:
    # Backup old day/night settings file
    shutil.copy2("/data/etc/thread-1.conf." + fileDayOrNight
      , "/data/etc/thread-1.conf." + fileDayOrNight + "." + datetime.now().strftime('%Y-%m-%d_%H.%M'))
    # Store new day/night settings file
    shutil.copy2("/data/etc/thread-1.conf", "/data/etc/thread-1.conf." + fileDayOrNight)

  # Select the other day/night settings file
  shutil.copy2("/data/etc/thread-1.conf." + currentDayOrNight, "/data/etc/thread-1.conf")

  # Restart MotionEye
  print(datetime.now().strftime('%Y-%m-%d_%H:%M:%S ') + 'Restart MotionEye...')
  os.system("/etc/init.d/S85motioneye restart")
  print(datetime.now().strftime('%Y-%m-%d_%H:%M:%S ') + 'MotionEye restarted.')
  
  # Update day/night -state file
  updateContentsOfFile(dayNightStateFile, currentDayOrNight)