Powershell - 确定 dawn/dusk 和 运行 exe 的脚本

Powershell - Script to determine dawn/dusk and run exe

我正在开发一个 Powershell 脚本,用于在 dusk 和黎明调整安全摄像头的配置(您可能认为供应商会有调度功能...)。它应该做的是使用 sunrise-sunset.org 提取 Civil Twilight 值,开始检查黎明时间(从大到小时间范围递增),然后启动 AHK 脚本以更改网络配置。然后脚本暂停六个小时,然后开始检查 dusk,然后启动另一个 AHK 脚本。

但是,我知道我有错误,因为它似乎没有 运行。例如,虽然最终确定的脚本将 Task Scheduled 设置为在凌晨 4 点开始,但我在睡觉前手动将其启动,但当我起床时它仍然处于“至少 120 分钟到黎明”。但是如果我查看 DawnNow 的值,它“应该”有效。此外,如果我删除 API 部分并从字面上设置“然后”(假设在 30 分钟后)和“现在”并使用几个 do-until 循环,它就可以工作。

所以,

A) 有什么问题吗? B) 我几乎可以肯定这段代码可以被清理 up/shortened.

# Clear old variables
Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();

# Get Civil Twilight values and "now"
$Daylight = (Invoke-RestMethod "https://api.sunrise-sunset.org/json?lat=35.608081&lng=-78.647666&formatted=0").results
$Dawn = [datetime] $Daylight.civil_twilight_begin
$Dusk = [datetime] $Daylight.civil_twilight_end
$Now = Get-Date

# Start iterations until dawn
do {
    "At least 120 minutes til dawn."
    Start-Sleep -S 7200
} until([datetime]::Now -ge $Dawn.AddMinutes(-120))

do {
    "At least 60 minutes til dawn."
    Start-Sleep -S 3600
} until([datetime]::Now -ge $Dawn.AddMinutes(-60))

do {
    "At least 30 minutes til dawn."
    Start-Sleep -S 1800
} until([datetime]::Now -ge $Dawn.AddMinutes(-30))

do {
    "At least 15 minutes til dawn."
    Start-Sleep -S 900
} until([datetime]::Now -ge $Dawn.AddMinutes(-15))

do {
    "At least 10 minutes til dawn."
    Start-Sleep -S 600
} until([datetime]::Now -ge $Dawn.AddMinutes(-10))

do {
    "At least 5 minutes til dawn."
    Start-Sleep -S 30
} until([datetime]::Now -ge $Dawn.AddMinutes(-5))

do {
    "At least 3 minutes til dawn."
    Start-Sleep -S 180
} until([datetime]::Now -ge $Dawn.AddMinutes(-3))

do {
    "At least a minute til dawn."
    Start-Sleep -S 60
} until([datetime]::Now -ge $Dawn.AddSeconds(-60))

do {
    "About to execute..."
    Start-Sleep -S 30
} until([datetime]::Now -ge $Dawn.AddSeconds(-30))

# Execute day script
C:\AHK\day.exe | Invoke-Expression

# Rest til the afternoon
Start-Sleep -S 21600

# Update "now"
$Now = Get-Date

# Start iterations until dusk
do {
    "At least 120 minutes til dusk."
    Start-Sleep -S 7200
} until([datetime]::Now -ge $Dusk.AddMinutes(-120))

do {
    "At least 60 minutes til dusk."
    Start-Sleep -S 3600
} until([datetime]::Now -ge $Dusk.AddMinutes(-60))

do {
    "At least 30 minutes til dusk."
    Start-Sleep -S 1800
} until([datetime]::Now -ge $Dusk.AddMinutes(-30))

do {
    "At least 15 minutes til dusk."
    Start-Sleep -S 900
} until([datetime]::Now -ge $Dusk.AddMinutes(-15))

do {
    "At least 10 minutes til dusk."
    Start-Sleep -S 600
} until([datetime]::Now -ge $Dusk.AddMinutes(-10))

do {
    "At least 5 minutes til dusk."
    Start-Sleep -S 30
} until([datetime]::Now -ge $Dusk.AddMinutes(-5))

do {
    "At least 3 minutes til dusk."
    Start-Sleep -S 180
} until([datetime]::Now -ge $Dawn.AddMinutes(-3))

do {
    "At least a minute til dusk."
    Start-Sleep -S 60
} until([datetime]::Now -ge $Dusk.AddSeconds(-60))

do {
    "About to execute..."
    Start-Sleep -S 30
} until([datetime]::Now -ge $Dusk.AddSeconds(-30))

# Execute night script
C:\AHK\night.exe | Invoke-Expression

# Rest 5 minutes
Start-Sleep -S 300

# Clear variables and end
Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();
Exit

编辑:这是当前脚本,全部归功于@Darin

达林,参考我下面的问题....

Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();

function SleepUntil {...}

function ReportRelativeTimeIfSleepUntil {...}

function GetApiTodayTomorrow {...}

function GetNextChange {...}

function DoCountDown {...}

DoCountDown

Start-Sleep S 600

DoCountDown

编辑: 原始代码 re-write 的部分 re-write。仍然需要测试和验证每个功能 - 但比原始答案要好得多。专注于 SleepUntil 和 ReportRelativeTimeIfSleepUntil,两者都应该处于良好状态。但更改可能会破坏其他内容,因此必须进行进一步测试。

此代码获取下一个未来变化时间(今天黎明、dusk 今天或明天黎明)并倒计时到那个时间。您可以将 DoCountDown 函数放入一个循环中,或者您可以让脚本每天由任务计划程序启动两次,或者甚至每天使用 DoCountDown 运行 启动两次。

# Clear old variables
Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();
<#
.SYNOPSIS
Suspends the activity in a script or session until either a specified time, or calculated time relative to the
specified time.

.DESCRIPTION
SleepUntil accepts a time, and optionally a number of either minutes or seconds to adjust the time by, to
create a point in time to suspend activity until that point in time is reached.  If the point in time is in
the past, relative to the current time, then activity is not suspended and the value $false is returned.  If
the point in time is in the future, then activity is suspended until that point in time and the value $true
is returned.  SleepUntil is intended as a method to activate a script a certain number of minutes or seconds
before or after an event.

.PARAMETER When
The reference time that the computer should awake from a sleep, where the exact time to awake is calculated
based on the optionally supplied values of -RelativeSeconds, or -RelativeMinutes, added to this reference
time.

.PARAMETER RelativeSeconds
Optional parameter used to calculate the number of seconds prior to, or after, the time provided by
parameter -EventTime. If this parameter is positive, then calculated time will be after -EventTime, and if negative then it
will be prior to -EventTime.  Cannot be used with -RelativeMinutes.

.PARAMETER RelativeMinutes
Optional parameter used to calculate the number of minutes prior to, or after, the time provided by
parameter -EventTime. If this parameter is positive, then calculated time will be after -EventTime, and if negative then it
will be prior to -EventTime.  Cannot be used with -RelativeSeconds.

.EXAMPLE
#   Sleep until 10 minutes prior to DateTime in $PowerOffEvent
SleepUntil -EventTime $PowerOffEvent -RelativeMinutes -10
#   Sleep until 30 seconds after $OpeningTime
SleepUntil $OpeningTime -RelativeSeconds 30 
#   Essentially same as: Start-Sleep -s 15
SleepUntil -EventTime (Get-Date) -RelativeSeconds 30


.NOTES
Internally, SleepUntil uses Start-Sleep, so in theory Ctrl+C should break out of SleepUntil.
#>
function SleepUntil {
    [CmdLetBinding(DefaultParameterSetName = 'NotRelative')]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [datetime]$EventTime,
        [Parameter(Position = 1, ParameterSetName = 'RelSec')]
        [int]$RelativeSeconds = 0,
        [Parameter(Position = 1, ParameterSetName = 'RelMin')]
        [int]$RelativeMinutes = 0
    )
    $AwakeTime = if($PsCmdlet.ParameterSetName -eq 'RelMin') {
        $EventTime.AddMinutes($RelativeMinutes)
    } elseif($PsCmdlet.ParameterSetName -eq 'RelSec') {
        $EventTime.AddSeconds($RelativeSeconds)
    } else {
        $EventTime
    }
    $SecondsToSleep = ($AwakeTime - (Get-Date)).TotalSeconds
    if($SecondsToSleep -lt 0) {
        $false 
    } else {
        Start-Sleep -S $SecondsToSleep
        $true
    }
}
<#
.SYNOPSIS
Calls SleepUntil, and if SleepUntil reports success, then returns string reporting minutes and/or 
seconds until/after event's time.

.DESCRIPTION
ReportRelativeTimeIfSleepUntil is useful for calling SleepUntil and then getting a string describing
how much time until or after an event.

.PARAMETER EventName
Descriptive name of the vent that will be inserted into the returning string.

.PARAMETER EventTime
The time of the event, see SleepUntil for more details.

.PARAMETER RelativeSeconds
Number of seconds relative to event, see SleepUntil for more details.

.PARAMETER RelativeMinutes
Number of minutes relative to event, see SleepUntil for more details.

.EXAMPLE
ReportRelativeTimeIfSleepUntil -EventName "power off" -EventTime $PowerOffEvent -RelativeMinutes -10
10 minutes until power off.
ReportRelativeTimeIfSleepUntil "SomeEvent" (Get-Date).AddSeconds(30) -RelativeSeconds 72
1 minute and 12 seconds after SomeEvent.
ReportRelativeTimeIfSleepUntil "SomeEvent" (Get-Date).AddSeconds(30) -RelativeSeconds -30
30 seconds until SomeEvent.

.NOTES
Assigning output to a variable, and check if the variable contains $null will indicate that
SleepUntil returned $false and activity was not suspended.
#>
function ReportRelativeTimeIfSleepUntil {
    [CmdLetBinding(DefaultParameterSetName = 'NotRelative')]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$EventName,        
        [Parameter(Mandatory = $true, Position = 1)]
        [datetime]$EventTime,
        [Parameter(Position = 1, ParameterSetName = 'RelSec')]
        [int]$RelativeSeconds = 0,
        [Parameter(Position = 1, ParameterSetName = 'RelMin')]
        [int]$RelativeMinutes = 0
    )
    $RelativeTimeInSeconds = if($PsCmdlet.ParameterSetName -eq 'RelMin') {
        $RelativeMinutes * 60
    } elseif($PsCmdlet.ParameterSetName -eq 'RelSec') {
        $RelativeSeconds
    } else {
        0
    }
    $DidSleep = SleepUntil $EventTime -RelativeSeconds $RelativeTimeInSeconds
    if($DidSleep) {
        $Prior = $RelativeTimeInSeconds -lt 0
        if($Prior) {
            $RelativeTimeInSeconds = -$RelativeTimeInSeconds
            $UntilAfter = 'until'
        } else {
            $UntilAfter = 'after'
        }
        $SecondsOut = $RelativeTimeInSeconds % 60
        $MinutesOut = ($RelativeTimeInSeconds - $SecondsOut)/60
        $SPlural = if($SecondsOut -ne 1) {'s'} else {''}
        $MPlural = if($MinutesOut -ne 1) {'s'} else {''}
        if($MinutesOut -ne 0) {
            if($SecondsOut -ne 0) {
                "$MinutesOut minute$MPlural and $SecondsOut second$SPlural $UntilAfter $EventName."
            } else {
                "$MinutesOut minute$MPlural $UntilAfter $EventName."
            }
        } else {
            if($SecondsOut) {
                "$SecondsOut second$SPlural $UntilAfter $EventName."
            } else {
                "$EventName is happening now."
            }
        }
    }
}
function GetApiTodayTomorrow {
    [CmdLetBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [datetime]$When
    )
    "$($When.Year)-$($When.Month)-$($When.Day)"
    $When = $When.AddDays(1)
    "$($When.Year)-$($When.Month)-$($When.Day)"
}
function GetNextChange {
    $Now = Get-Date
    $ThisDate, $NextDate = GetApiTodayTomorrow $Now
    $Daylight = (Invoke-RestMethod "https://api.sunrise-sunset.org/json?lat=35.608081&lng=-78.647666&date=$ThisDate&formatted=0").results
    $Dawn = [datetime] $Daylight.civil_twilight_begin
    $Dusk = [datetime] $Daylight.civil_twilight_end
    if($Now -lt $Dawn) {
        'Dawn'
        $Dawn
    } elseif ($Now -lt $Dusk) {
        'Dusk'
        $Dusk
    } else {
        $Daylight = (Invoke-RestMethod "https://api.sunrise-sunset.org/json?lat=35.608081&lng=-78.647666&date=$NextDate&formatted=0").results
        $Dawn = [datetime] $Daylight.civil_twilight_begin
        $Dusk = [datetime] $Daylight.civil_twilight_end
        if($Now -lt $Dawn) {
            'Dawn'
            $Dawn
        } else {
            'Dusk'
            $Dusk
        }
    }
}
function DoCountDown {
    $EventName, $EventTime = GetNextChange

    # Start iterations until dawn
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -120
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -60
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -30
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -15
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -10
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -5
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -3
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeMinutes -1
    ReportRelativeTimeIfSleepUntil $EventName $EventTime -RelativeSeconds -30
    $null = SleepUntil $EventTime
    if($EventName -eq 'Dawn') {
        "Executing Day"
        # Execute day script
        C:\AHK\day.exe | Invoke-Expression
    } else {
        "Executing Night"
        # Execute night script
        C:\AHK\night.exe | Invoke-Expression
    }

}

DoCountDown

# Rest 5 minutes
SleepUntil (Get-Date) -RelativeMinutes 5

# Clear variables and end
Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();